summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrojal <trojal@gmail.com>2013-01-12 10:02:58 -0800
committerTrojal <trojal@gmail.com>2013-01-12 10:02:58 -0800
commit733caad39f4df2734afd523b3591c9435637de63 (patch)
tree8e16265edba08aeeb22f4c8c930420e6355c9e80
parentd7619875500ede2e59960965a6d38485ece00604 (diff)
parentcb18f86c8b6481a338057b1b8ea0bfbd5f5acb6e (diff)
downloadhercules-733caad39f4df2734afd523b3591c9435637de63.tar.gz
hercules-733caad39f4df2734afd523b3591c9435637de63.tar.bz2
hercules-733caad39f4df2734afd523b3591c9435637de63.tar.xz
hercules-733caad39f4df2734afd523b3591c9435637de63.zip
Merge remote-tracking branch 'hercules-remote/master' into dev/trojal
-rw-r--r--db/pre-re/skill_db.txt2410
-rw-r--r--db/re/skill_db.txt2412
-rw-r--r--src/map/clif.c34256
-rw-r--r--src/map/script.c35563
-rw-r--r--src/map/skill.c35981
-rw-r--r--src/map/status.c22584
6 files changed, 66611 insertions, 66595 deletions
diff --git a/db/pre-re/skill_db.txt b/db/pre-re/skill_db.txt
index 580268701..8d2b9375f 100644
--- a/db/pre-re/skill_db.txt
+++ b/db/pre-re/skill_db.txt
@@ -1,1205 +1,1205 @@
-//id,range,hit,inf,element,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count,name,description
-// 01 ID
-// 02 range (combo skills do not check for range when used,
-// if range is < 5, the skill is considered melee-range)
-// 03 hit (8- repeated hitting, 6- single-hit)
-// 04 inf (0- passive, 1- enemy, 2- place, 4- self, 16- friend, 32- trap)
-// 05 element (0 - neutral, 1 - water, 2 - earth, 3 - fire, 4 - wind, 5 - poison,
-// 6 - holy, 7 - dark, 8 - ghost, 9 - undead, -1 - use weapon element
-// -2 - use endowed element, -3 - use random element.)
-// 06 nk (skill damage properties):
-// 0x01 - No damage skill
-// 0x02 - Has splash area
-// 0x04 - Damage should be split among targets
-// 0x08 - Skill ignores caster's % damage cards (misc type always ignores)
-// 0x10 - Skill ignores elemental adjustments
-// 0x20 - Skill ignores target's defense (misc type always ignores)
-// 0x40 - Skill ignores target's flee (magic type always ignores)
-// 0x80 - Skill ignores target's def cards
-// 07 splash/effect range (-1 for screen-wide)
-// 08 MaxLv
-// 09 Number of hits (when positive, damage is increased by hits,
-// negative values just show number of hits without increasing total damage)
-// 10 Cast interrupted when hit?
-// 11 defense-reduction rate during cast.
-// 12 inf2 (skill information 2):
-// 0x0001- quest skill
-// 0x0002- npc skill
-// 0x0004- wedding skill
-// 0x0008- spirit skill
-// 0x0010- guild skill
-// 0x0020- song/dance
-// 0x0040- ensemble skill
-// 0x0080- trap
-// 0x0100- skill that damages/targets yourself
-// 0x0200- cannot be casted on self (if inf = 4, auto-select target skill)
-// 0x0400- usable only on party-members (and enemies if skill is offensive)
-// 0x0800- usable only on guild-mates (and enemies if skill is offensive)
-// 0x1000- disable usage on enemies (for non-offensive skills).
-// 0x2000- skill ignores land protector (e.g. arrow shower)
-// 0x4000- chorus skill
-// 13 maxcount: max amount of skill instances to place on the ground when
-// player_land_skill_limit/monster_land_skill_limit is enabled. For skills
-// that attack using a path, this is the path length to be used.
-// 14 attack type (none, weapon, magic, misc)
-// 15 Blowcount (amount of tiles skill knockbacks)
-// 16 Name
-// 17 Description
-1,0,0,0,0,0,0,9,0,no,0,0,0,none,0, NV_BASIC,Basic Skill
-2,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, SM_SWORD,Sword Mastery
-3,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, SM_TWOHAND,Two-Handed Sword Mastery
-4,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SM_RECOVERY,Increase HP Recovery
-5,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, SM_BASH,Bash
-6,9,6,1,0,1,0,10,1,no,0,0,0,none,0, SM_PROVOKE,Provoke
-7,0,6,4,3,0x2,2,10,1,no,0,0,0,weapon,2, SM_MAGNUM,Magnum Break
-8,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, SM_ENDURE,Endure
-9,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MG_SRECOVERY,Increase SP Recovery
-10,0,6,4,3,0x3,3,1,1,yes,0,0,0,magic,0, MG_SIGHT,Sight
-11,9,6,1,8,0x6,1,10,1,yes,0,0,0,magic,0, MG_NAPALMBEAT,Napalm Beat
-12,9,8,2,8,0x1,0,10,1,yes,0,0,0,magic,0, MG_SAFETYWALL,Safety Wall
-13,9,8,1,8,0,0,10,1:1:2:2:3:3:4:4:5:5,yes,0,0,0,magic,0, MG_SOULSTRIKE,Soul Strike
-14,9,8,1,1,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_COLDBOLT,Cold Bolt
-15,9,6,1,1,0,0,10,1,yes,0,0,0,magic,0, MG_FROSTDIVER,Frost Diver
-16,2,6,1,2,0x1,0,10,1,yes,0,0,0,magic,0, MG_STONECURSE,Stone Curse
-17,9,6,1,3,0x2,2,10,1,yes,0,0,0,magic,0, MG_FIREBALL,Fire Ball
-18,9,6,2,3,0,0,10,1,yes,0,0,3,magic,2, MG_FIREWALL,Fire Wall
-19,9,8,1,3,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_FIREBOLT,Fire Bolt
-20,9,8,1,4,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_LIGHTNINGBOLT,Lightning Bolt
-21,9,8,2,4,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_THUNDERSTORM,Thunderstorm
-22,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AL_DP,Divine Protection
-23,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AL_DEMONBANE,Demon Bane
-24,0,6,4,6,0x3,2,1,1,yes,0,0,0,magic,0, AL_RUWACH,Ruwach
-25,9,6,2,0,0x1,0,1,1,yes,0,0,0,magic,0, AL_PNEUMA,Pneuma
-26,0,6,4,0,0x1,0,2,1,yes,0,0,0,magic,0, AL_TELEPORT,Teleport
-27,9,6,2,0,0x1,0,4,1,yes,0,0,3,magic,0, AL_WARP,Warp Portal
-28,9,6,16,6,0x21,0,10,1,yes,0,0,0,magic,0, AL_HEAL,Heal
-29,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, AL_INCAGI,Increase AGI
-30,9,6,1,0,0x1,0,10,1,yes,0,0,0,magic,0, AL_DECAGI,Decrease AGI
-31,0,6,4,0,0x1,0,1,1,yes,0,0,0,magic,0, AL_HOLYWATER,Aqua Benedicta
-32,0,6,4,0,0x3,15,10,1,yes,0,0,0,magic,0, AL_CRUCIS,Signum Crucis
-33,0,6,4,0,0x3,-1,10,1,yes,0,0,0,magic,0, AL_ANGELUS,Angelus
-34,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, AL_BLESSING,Blessing
-35,9,6,16,0,0x1,0,1,1,yes,0,0,0,magic,0, AL_CURE,Cure
-36,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_INCCARRY,Enlarge Weight Limit
-37,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_DISCOUNT,Discount
-38,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_OVERCHARGE,Overcharge
-39,1,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_PUSHCART,Pushcart
-40,1,6,4,0,0x1,0,1,1,no,0,0,0,none,0, MC_IDENTIFY,Item Appraisal
-41,1,6,4,0,0x1,0,10,1,no,0,0,0,none,0, MC_VENDING,Vending
-42,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, MC_MAMMONITE,Mammonite
-43,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AC_OWL,Owl's Eye
-44,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AC_VULTURE,Vulture's Eye
-45,0,6,4,0,0x3,3,10,1,no,0,0,0,weapon,0, AC_CONCENTRATION,Improve Concentration
-46,-9,8,1,-1,0,0,10,2,no,0,0,0,weapon,0, AC_DOUBLE,Double Strafe
-47,-9,6,2,-1,0x2,2,10,1,no,0,0x2000,0,weapon,2, AC_SHOWER,Arrow Shower
-48,-1,8,0,-1,0,0,10,2,no,0,0,0,weapon,0, TF_DOUBLE,Double Attack
-49,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, TF_MISS,Improve Dodge
-50,1,6,1,0,1,0,10,1,no,0,0,0,weapon,0, TF_STEAL,Steal
-51,1,6,4,0,1,0,10,1,no,0,0,0,none,0, TF_HIDING,Hiding
-52,-2,6,1,5,0,0,10,1,no,0,0,0,weapon,0, TF_POISON,Envenom
-53,9,6,16,5,0x1,0,1,1,no,0,0,0,weapon,0, TF_DETOXIFY,Detoxify
-54,9,6,16,6,0x1,0,4,1,yes,0,0,0,magic,0, ALL_RESURRECTION,Resurrection
-55,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, KN_SPEARMASTERY,Spear Mastery
-56,-2,8,1,-1,0,0,10,3,no,0,0,0,weapon,0, KN_PIERCE,Pierce
-57,-2,6,1,-1,0x1,0,10,1,no,33,0,0,weapon,3, KN_BRANDISHSPEAR,Brandish Spear
-58,-4,6,1,-1,0x2,0,10,1,no,0,0,0,weapon,6, KN_SPEARSTAB,Spear Stab
-59,3:5:7:9:11,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, KN_SPEARBOOMERANG,Spear Boomerang
-60,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, KN_TWOHANDQUICKEN,Twohand Quicken
-61,0,6,4,-1,0x20,0,5,1,no,0,0,0,weapon,0, KN_AUTOCOUNTER,Counter Attack
-62,-2,6,1,-1,0x2,1,10,1,no,33,0,0,weapon,1, KN_BOWLINGBASH,Bowling Bash
-63,0,0,0,0,0,0,1,0,no,0,0,0,weapon,0, KN_RIDING,Peco Peco Riding
-64,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, KN_CAVALIERMASTERY,Cavalier Mastery
-65,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, PR_MACEMASTERY,Mace Mastery
-66,9,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, PR_IMPOSITIO,Impositio Manus
-67,9,6,16,0,0x1,0,3,1,yes,0,0x200,0,magic,0, PR_SUFFRAGIUM,Suffragium
-68,9,6,16,6,0x31,0,5,1,yes,0,0,0,magic,0, PR_ASPERSIO,Aspersio
-69,9,6,2,0,0x23,1,5,1,yes,0,0x40,0,magic,0, PR_BENEDICTIO,B.S. Sacramenti
-70,9,6,2,6,0x21,0,10,1,yes,0,0,0,magic,1, PR_SANCTUARY,Sanctuary
-71,9,6,16,0,0x1,0,4,1,yes,0,0,0,magic,0, PR_SLOWPOISON,Slow Poison
-72,9,6,16,0,0x1,0,1,1,yes,0,0,0,magic,0, PR_STRECOVERY,Status Recovery
-73,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, PR_KYRIE,Kyrie Eleison
-74,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0, PR_MAGNIFICAT,Magnificat
-75,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0, PR_GLORIA,Gloria
-76,5,6,1,0,0x1,0,10,0,yes,0,0,0,magic,0, PR_LEXDIVINA,Lex Divina
-77,5,6,1,6,0x28,0,10,1,yes,0,0,0,magic,0, PR_TURNUNDEAD,Turn Undead
-78,9,6,1,0,0x1,0,1,0,yes,0,0,0,magic,0, PR_LEXAETERNA,Lex Aeterna
-79,9,8,2,6,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, PR_MAGNUS,Magnus Exorcismus
-80,9,8,2,3,0x20,1:1:1:1:1:2:2:2:2:2:2,10,3:4:5:6:7:8:9:10:11:12:12,yes,0,0x80,5,magic,0, WZ_FIREPILLAR,Fire Pillar
-81,0,6,4,3,0,3,10,1,yes,0,0,0,magic,5, WZ_SIGHTRASHER,Sightrasher
-83,9,8,2,3,0,3:3:3:3:3:3:3:3:3:3:14,10,1:1:2:2:3:3:4:4:5:5:15,yes,0,0,0,magic,0, WZ_METEOR,Meteor Storm
-84,9,8,1,4,0,0,10,3:4:5:6:7:8:9:10:11:12,yes,0,0,0,magic,2:3:3:4:4:5:5:6:6:7, WZ_JUPITEL,Jupitel Thunder
-85,9,8,2,4,0,0,10,-10,yes,0,0,0,magic,0, WZ_VERMILION,Lord of Vermilion
-86,9,8,1,1,0,0,5,1,yes,0,0,0,magic,0, WZ_WATERBALL,Water Ball
-87,9,6,2,1,0x1,0,10,1,yes,0,0,0,magic,0, WZ_ICEWALL,Ice Wall
-88,0,6,4,1,0x2,2,10,1,yes,0,0,0,magic,0, WZ_FROSTNOVA,Frost Nova
-89,9,6,2,1,0,0,10,1,yes,0,0,0,magic,2, WZ_STORMGUST,Storm Gust
-90,9,8,1,2,0,0,5,1:2:3:4:5,yes,0,0,0,magic,0, WZ_EARTHSPIKE,Earth Spike
-91,9,8,2,2,0,0,5,1:2:3:4:5,yes,0,0,0,magic,0, WZ_HEAVENDRIVE,Heaven's Drive
-92,9,6,2,2,0x1,0,5,1,yes,0,0,3,magic,0, WZ_QUAGMIRE,Quagmire
-93,9,6,1,0,0x1,0,1,1,yes,0,0,0,magic,0, WZ_ESTIMATION,Sense
-94,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_IRON,Iron Tempering
-95,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_STEEL,Steel Tempering
-96,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_ENCHANTEDSTONE,Enchanted Stone Craft
-97,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_ORIDEOCON,Oridecon Research
-98,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_DAGGER,Smith Dagger
-99,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_SWORD,Smith Sword
-100,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_TWOHANDSWORD,Smith Two-handed Sword
-101,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_AXE,Smith Axe
-102,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_MACE,Smith Mace
-103,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_KNUCKLE,Smith Knucklebrace
-104,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_SPEAR,Smith Spear
-105,0,0,0,0,0,0,1,0,no,0,0,0,weapon,0, BS_HILTBINDING,Hilt Binding
-106,0,0,0,0,0,0,1,0,no,0,0,0,weapon,0, BS_FINDINGORE,Ore Discovery
-107,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, BS_WEAPONRESEARCH,Weaponry Research
-108,2,6,16,0,0x1,0,1,1,yes,0,0,0,weapon,0, BS_REPAIRWEAPON,Weapon Repair
-109,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_SKINTEMPER,Skin Tempering
-110,1,6,2,0,0x3,2:2:2:2:2:14,5,1,no,0,0,0,weapon,0, BS_HAMMERFALL,Hammer Fall
-111,0,6,4,0,0x3,-1,5,1,no,0,0,0,weapon,0, BS_ADRENALINE,Adrenaline Rush
-112,0,6,4,0,0x3,-1,5,1,no,0,0,0,weapon,0, BS_WEAPONPERFECT,Weapon Perfection
-113,0,6,4,0,0x3,-1,5,1,no,0,0,0,weapon,0, BS_OVERTHRUST,Power-Thrust
-114,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, BS_MAXIMIZE,Maximize Power
-115,3,6,2,0,0x1,0,5,1,no,0,0x80,0,misc,6:7:8:9:10, HT_SKIDTRAP,Skid Trap
-116,3,6,2,2,0x42,1,5,1,no,0,0x80,0,misc,0, HT_LANDMINE,Land Mine
-117,3,6,2,0,0x1,0,5,1,no,0,0x80,0,misc,0, HT_ANKLESNARE,Ankle Snare
-118,3,6,2,0,0x2,1,5,1,no,0,0x80,0,misc,0, HT_SHOCKWAVE,Shockwave Trap
-119,3,6,2,0,0x3,2,5,1,no,0,0x80,0,misc,0, HT_SANDMAN,Sandman
-120,3,6,2,0,0x3,1,5,1,no,0,0x80,0,misc,0, HT_FLASHER,Flasher
-121,3,6,2,1,0x42,1,5,1,no,0,0x80,0,weapon,0, HT_FREEZINGTRAP,Freezing Trap
-122,3,6,2,4,0x42,1,5,1,no,0,0x80,0,misc,0, HT_BLASTMINE,Blast Mine
-123,3,6,2,3,0x42,2,5,1,no,0,0x80,0,misc,0, HT_CLAYMORETRAP,Claymore Trap
-124,2,6,32,0,0x1,0,1,1,no,0,0,0,misc,0, HT_REMOVETRAP,Remove Trap
-125,3,6,2,0,0x1,0,1,1,no,0,0x80,0,misc,0, HT_TALKIEBOX,Talkie Box
-126,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, HT_BEASTBANE,Beast Bane
-127,0,0,0,0,0,0,1,0,no,0,0,0,misc,0, HT_FALCON,Falconry Mastery
-128,0,0,0,0,0,0,10,0,no,0,0,0,misc,0, HT_STEELCROW,Steel Crow
-129,5,8,1,0,0x42,1,5,1:2:3:4:5,yes,0,0,0,misc,0, HT_BLITZBEAT,Blitz Beat
-130,3:5:7:9,6,2,0,0x3,3,4,1,no,0,0,0,misc,0, HT_DETECTING,Detect
-131,4:5:6:7:8,6,32,0,0x1,0,5,1,no,0,0,0,misc,0, HT_SPRINGTRAP,Spring Trap
-132,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, AS_RIGHT,Righthand Mastery
-133,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, AS_LEFT,Lefthand Mastery
-134,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AS_KATAR,Katar Mastery
-135,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, AS_CLOAKING,Cloaking
-136,-1,8,1,-1,0,0,10,8,no,0,0,0,weapon,0, AS_SONICBLOW,Sonic Blow
-137,3:4:5:6:7,6,1,-1,0x2,1,5,1,no,0,0,0,weapon,0,AS_GRIMTOOTH,Grimtooth
-138,1,6,16,5,0x1,0,10,1,no,0,0x400,0,weapon,0, AS_ENCHANTPOISON,Enchant Poison
-139,0,6,4,0,0,0,10,1,no,0,0,0,weapon,0, AS_POISONREACT,Poison React
-140,2,6,2,5,0x1,0,10,1,no,0,0,0,weapon,0, AS_VENOMDUST,Venom Dust
-141,1,6,1,-1,0x51,2,10,1,yes,0,0,0,weapon,0, AS_SPLASHER,Venom Splasher
-142,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, NV_FIRSTAID,First Aid
-143,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, NV_TRICKDEAD,Play Dead
-144,0,0,0,0,0,0,1,0,no,0,0x1,0,none,0, SM_MOVINGRECOVERY,Moving HP-Recovery
-145,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0, SM_FATALBLOW,Fatal Blow
-146,0,6,4,0,0x1,0,1,1,no,0,0x1,0,weapon,0, SM_AUTOBERSERK,Auto Berserk
-147,0,0,4,0,0x1,0,1,0,no,0,0x1,0,weapon,0, AC_MAKINGARROW,Arrow Crafting
-148,-9,6,1,-1,0x2,0,1,1,no,0,0x1,0,weapon,6, AC_CHARGEARROW,Arrow Repel
-149,1,6,1,2,0,0,1,1,no,0,0x1,0,weapon,0, TF_SPRINKLESAND,Sand Attack
-150,0,6,4,0,0x1,0,1,1,no,0,0x1,0,weapon,5, TF_BACKSLIDING,Back Slide
-151,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, TF_PICKSTONE,Find Stone
-152,7,6,1,0,0x40,0,1,1,no,0,0x1,0,misc,0, TF_THROWSTONE,Stone Fling
-153,1,6,1,-1,0x2,1,1,1,no,0,0x1,0,weapon,2, MC_CARTREVOLUTION,Cart Revolution
-154,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, MC_CHANGECART,Change Cart
-155,0,6,4,0,0x1,0,1,1,no,0,0x1,0,weapon,0, MC_LOUD,Crazy Uproar
-156,9,6,1,6,0,0,1,1,yes,0,0x1,0,magic,0, AL_HOLYLIGHT,Holy Light
-157,0,6,4,0,0x1,0,1,1,yes,0,0x1,0,magic,0, MG_ENERGYCOAT,Energy Coat
-158,3,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_PIERCINGATT,Piercing Attack
-159,-1,6,1,-1,0x40,0,5,1,no,0,0x2,0,weapon,0, NPC_MENTALBREAKER,Spirit Destruction
-160,9,6,1,0,0,0,10,1,no,0,0x2,0,weapon,0, NPC_RANGEATTACK,Stand off attack
-161,0,0,4,0,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_ATTRICHANGE,Attribute Change
-162,0,0,4,1,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEWATER,Water Attribute Change
-163,0,0,4,2,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEGROUND,Earth Attribute Change
-164,0,0,4,3,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEFIRE,Fire Attribute Change
-165,0,0,4,4,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEWIND,Wind Attribute Change
-166,0,0,4,5,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEPOISON,Poison Attribute Change
-167,0,0,4,6,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEHOLY,Holy Attribute Change
-168,0,0,4,7,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEDARKNESS,Shadow Attribute Change
-169,0,0,4,8,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGETELEKINESIS,Ghost Attribute Change
-170,-9,6,1,-1,0x20,0,10,1,no,0,0x2,0,weapon,0, NPC_CRITICALSLASH,Defense disregard attack
-171,-9,8,1,-1,0,0,10,-2:-3:-4:-5:-6:-7:-8:-9:-10:-11,no,0,0x2,0,weapon,0, NPC_COMBOATTACK,Multi-stage Attack
-172,-9,6,1,-1,0x40,0,10,1,no,0,0x2,0,weapon,0, NPC_GUIDEDATTACK,Guided Attack
-173,5,6,4,3,0xE2,5,10,1,no,0,0x2,0,misc,3, NPC_SELFDESTRUCTION,Suicide bombing
-174,-9,6,1,-1,0x2,3,1,1,no,0,0x2,0,weapon,0, NPC_SPLASHATTACK,Splash attack
-175,0,0,4,0,0x41,0,10,1,no,0,0x2,0,misc,0, NPC_SUICIDE,Suicide
-176,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_POISON,Poison Attack
-177,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_BLINDATTACK,Blind Attack
-178,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_SILENCEATTACK,Silence Attack
-179,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_STUNATTACK,Stun Attack
-180,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_PETRIFYATTACK,Petrify Attack
-181,-9,6,1,7,0,0,5,1,no,0,0x2,0,weapon,0, NPC_CURSEATTACK,Curse Attack
-182,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_SLEEPATTACK,Sleep attack
-183,-9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_RANDOMATTACK,Random Attack
-184,-9,6,1,1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_WATERATTACK,Water Attribute Attack
-185,-9,6,1,2,0,0,10,1,no,0,0x2,0,weapon,0, NPC_GROUNDATTACK,Earth Attribute Attack
-186,-9,6,1,3,0,0,10,1,no,0,0x2,0,weapon,0, NPC_FIREATTACK,Fire Attribute Attack
-187,-9,6,1,4,0,0,10,1,no,0,0x2,0,weapon,0, NPC_WINDATTACK,Wind Attribute Attack
-188,-9,6,1,5,0,0,10,1,no,0,0x2,0,weapon,0, NPC_POISONATTACK,Poison Attribute Attack
-189,-9,6,1,6,0,0,10,1,no,0,0x2,0,weapon,0, NPC_HOLYATTACK,Holy Attribute Attack
-190,-9,6,1,7,0,0,10,1,no,0,0x2,0,weapon,0, NPC_DARKNESSATTACK,Shadow Attribute Attack
-191,-9,6,1,8,0,0,10,1,no,0,0x2,0,weapon,0, NPC_TELEKINESISATTACK,Ghost Attribute Attack
-192,-9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_MAGICALATTACK,Demon Shock Attack
-193,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_METAMORPHOSIS,Metamorphosis
-194,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_PROVOCATION,Provocation
-195,0,6,4,0,0x50,0,10,1,no,0,0x2,0,misc,0, NPC_SMOKING,Smoking
-196,0,0,4,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_SUMMONSLAVE,Follower Summons
-197,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_EMOTION,Emotion
-198,0,0,4,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_TRANSFORMATION,Transformation
-199,9,6,1,7,0x40,0,1,1,no,0,0x2,0,weapon,0, NPC_BLOODDRAIN,Sucking Blood
-200,9,6,1,7,0,0,1,1,no,0,0x2,0,magic,0, NPC_ENERGYDRAIN,Energy Drain
-201,0,0,4,0,0x1,0,1,1,no,0,0x2,0,weapon,0, NPC_KEEPING,Keeping
-202,9,6,1,7,0,0,5,1,no,0,0x2,0,misc,0, NPC_DARKBREATH,Dark Breath
-203,9,6,1,7,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_DARKBLESSING,Dark Blessing
-204,0,0,4,0,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_BARRIER,Barrier
-205,0,0,4,0,0x1,0,1,1,no,0,0x2,0,weapon,0, NPC_DEFENDER,Defender
-206,1,6,1,-1,0x1,0,5,1,no,0,0x2,0,weapon,0, NPC_LICK,Lick
-207,9,0,1,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_HALLUCINATION,Hallucination
-208,0,0,4,0,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_REBIRTH,Rebirth
-209,0,0,4,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_SUMMONMONSTER,Monster Summons
-210,0,0,0,-1,0,0,10,0,no,0,0,0,weapon,0, RG_SNATCHER,Gank
-211,1,6,1,0,0x1,0,10,1,no,0,0,0,weapon,0, RG_STEALCOIN,Mug
-212,-1,6,1,-1,0x40,0,10,1,no,0,0,0,weapon,0, RG_BACKSTAP,Back Stab
-213,0,0,0,0,0,0,5,0,no,0,0,0,none,0, RG_TUNNELDRIVE,Stalk
-214,0,6,4,-1,0x2,1,5,1,no,0,0,0,weapon,0, RG_RAID,Sightless Mind
-215,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPWEAPON,Divest Weapon
-216,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPSHIELD,Divest Shield
-217,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPARMOR,Divest Armor
-218,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPHELM,Divest Helm
-219,1,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, RG_INTIMIDATE,Snatch
-220,1,6,2,0,0x1,0,1,1,no,0,0,0,none,0, RG_GRAFFITI,Scribble
-221,0,6,2,0,0x1,0,5,1,no,0,0,0,none,0, RG_FLAGGRAFFITI,Piece
-222,1,6,2,0,0x3,5,1,1,no,0,0,0,none,0, RG_CLEANER,Remover
-223,0,0,0,0,0,1,1,0,no,0,0,0,none,0, RG_GANGSTER,Slyness
-224,0,0,0,0,0,0,5,0,no,0,0,0,none,0, RG_COMPULSION,Haggle
-225,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RG_PLAGIARISM,Intimidate
-226,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AM_AXEMASTERY,Axe Mastery
-227,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_LEARNINGPOTION,Potion Research
-228,0,6,4,0,0x1,0,10,0,no,0,0,0,none,0, AM_PHARMACY,Prepare Potion
-229,9,6,2,3,0x9,0,5,1,yes,0,0,0,weapon,0, AM_DEMONSTRATION,Bomb
-230,9,6,1,0,0x48,0,5,1,yes,0,0,0,weapon,0, AM_ACIDTERROR,Acid Terror
-231,9,6,16,0,0x1,0,5,1,yes,0,0xC00,0,none,0, AM_POTIONPITCHER,Aid Potion
-232,4,6,2,0,0x1,0,5,1,no,0,0,5,none,0, AM_CANNIBALIZE,Summon Flora
-233,1,6,2,0,0x1,0,5,1,no,0,0,3,none,0, AM_SPHEREMINE,Summon Marine Sphere
-234,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_WEAPON,Alchemical Weapon
-235,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_SHIELD,Synthesized Shield
-236,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_ARMOR,Synthetic Armor
-237,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_HELM,Biochemical Helm
-238,0,0,0,0,0,0,1,0,no,0,0x1,0,none,0, AM_BIOETHICS,Bioethics
-//239,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_BIOTECHNOLOGY,Biotechnology
-//240,0,0,0,0,0,0,5,0,no,0,0,0,none,0, AM_CREATECREATURE,Life Creation
-//241,0,0,0,0,0,0,5,0,no,0,0,0,none,0, AM_CULTIVATION,Cultivation
-//242,0,0,0,0,0,0,5,0,no,0,0,0,none,0, AM_FLAMECONTROL,Flame Control
-243,0,0,4,0,0x1,1,1,0,no,0,0,0,none,0, AM_CALLHOMUN,Call Homunculus
-244,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, AM_REST,Vaporize
-//245,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_DRILLMASTER,Drillmaster
-//246,9,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_HEALHOMUN,Heal Homunculus
-247,9,6,4,0,0x1,1,5,0,no,0,0,0,none,0, AM_RESURRECTHOMUN,Homunculus Resurrection
-248,0,0,0,0,0,0,10,0,no,0,0,0,none,0, CR_TRUST,Faith
-249,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, CR_AUTOGUARD,Guard
-250,3,6,1,0,0,0,5,1,no,0,0,0,weapon,5:6:7:8:9, CR_SHIELDCHARGE,Smite
-251,3:5:7:9:11,6,1,0,0,0,5,1,no,0,0,0,weapon,0, CR_SHIELDBOOMERANG,Shield Boomerang
-252,0,6,4,0,0,0,10,1,no,0,0,0,weapon,0, CR_REFLECTSHIELD,Shield Reflect
-253,-2,8,1,6,0,0,10,-2,no,0,0,0,weapon,0, CR_HOLYCROSS,Holy Cross
-254,5,6,4,6,0x48,0,10,1,no,33,0x100,0,magic,0, CR_GRANDCROSS,Grand Cross
-255,7:8:9:10:11,6,16,0,0x1,0,5,1,yes,0,0x600,0,none,0, CR_DEVOTION,Sacrifice
-256,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,none,0, CR_PROVIDENCE,Resistant Souls
-257,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, CR_DEFENDER,Defending Aura
-258,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, CR_SPEARQUICKEN,Spear Quicken
-259,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, MO_IRONHAND,Iron Fists
-260,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, MO_SPIRITSRECOVERY,Spiritual Cadence
-261,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MO_CALLSPIRITS,Summon Spirit Sphere
-262,9,6,16,0,0x1,0,1,1,yes,0,0,0,weapon,0, MO_ABSORBSPIRITS,Absorb Spirit Sphere
-263,-1,8,0,-1,0,0,10,-3,no,0,0,0,weapon,0, MO_TRIPLEATTACK,Raging Trifecta Blow
-264,18,6,2,0,0x1,0,1,1,no,0,0,0,none,0, MO_BODYRELOCATION,Snap
-265,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, MO_DODGE,Dodge
-266,2,6,1,0,0x40,0,5,1,no,0,0,0,weapon,0, MO_INVESTIGATE,Occult Impaction
-267,9,8,1,-1,0,0,5,1:2:3:4:5,no,0,0,0,weapon,0, MO_FINGEROFFENSIVE,Throw Spirit Sphere
-268,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, MO_STEELBODY,Mental Strength
-269,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, MO_BLADESTOP,Root
-270,0,6,4,0,0x1,0,5,0,no,0,0,0,weapon,0, MO_EXPLOSIONSPIRITS,Fury
-271,-2,6,1,0,0x60,0,5,1,yes,0,0,0,weapon,0, MO_EXTREMITYFIST,Asura Strike
-272,-2,8,4,-1,0,0,5,-4,no,0,0x200,0,weapon,0, MO_CHAINCOMBO,Raging Quadruple Blow
-273,-2,6,4,-1,0x2,2,5,1,no,0,0x200,0,weapon,0, MO_COMBOFINISH,Raging Thrust
-274,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, SA_ADVANCEDBOOK,Study
-275,0,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, SA_CASTCANCEL,Cast Cancel
-276,0,6,4,0,0x1,0,5,1,yes,0,0,0,magic,0, SA_MAGICROD,Magic Rod
-277,9,6,1,0,0x1,0,5,1,yes,0,0,0,magic,0, SA_SPELLBREAKER,Spell Breaker
-278,0,0,0,0,0,0,10,0,no,0,0,0,magic,0, SA_FREECAST,Free Cast
-279,0,6,4,0,0x1,0,10,1,yes,0,0,0,magic,0, SA_AUTOSPELL,Hindsight
-280,9,6,16,3,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_FLAMELAUNCHER,Endow Blaze
-281,9,6,16,1,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_FROSTWEAPON,Endow Tsunami
-282,9,6,16,4,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_LIGHTNINGLOADER,Endow Tornado
-283,9,6,16,2,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_SEISMICWEAPON,Endow Quake
-284,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, SA_DRAGONOLOGY,Dragonology
-285,2,6,2,3,0x1,0,5,1,yes,0,0,0,magic,0, SA_VOLCANO,Volcano
-286,2,6,2,1,0x1,0,5,1,yes,0,0,0,magic,0, SA_DELUGE,Deluge
-287,2,6,2,4,0x1,0,5,1,yes,0,0,0,magic,0, SA_VIOLENTGALE,Whirlwind
-288,2,6,2,0,0x1,0,5,1,yes,0,0,0,magic,0, SA_LANDPROTECTOR,Magnetic Earth
-289,9,6,1,0,0x1,0:0:0:0:0:-1,5,1,yes,0,0xE00,0,magic,0, SA_DISPELL,Dispell
-290,0,6,4,0,0x1,0,10,1,yes,0,0,0,magic,0, SA_ABRACADABRA,Hocus-pocus
-291,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_MONOCELL,Monocell
-292,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_CLASSCHANGE,Class Change
-293,0,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_SUMMONMONSTER,Monster Chant
-294,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_REVERSEORCISH,Grampus Morph
-295,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_DEATH,Grim Reaper
-296,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_FORTUNE,Gold Digger
-297,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_TAMINGMONSTER,Beastly Hypnosis
-298,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_QUESTION,Questioning
-299,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_GRAVITY,Gravity
-300,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_LEVELUP,Leveling
-301,9,6,4,0,0,0,1,1,yes,0,0x2,0,magic,0, SA_INSTANTDEATH,Suicide
-302,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_FULLRECOVERY,Rejuvenation
-303,9,6,4,0,0,0,1,1,yes,0,0x2,0,magic,0, SA_COMA,Coma
-304,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, BD_ADAPTATION,Amp
-305,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, BD_ENCORE,Encore
-306,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_LULLABY,Lullaby
-307,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_RICHMANKIM,Mental Sensing
-308,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_ETERNALCHAOS,Down Tempo
-309,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_DRUMBATTLEFIELD,Battle Theme
-310,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_RINGNIBELUNGEN,Harmonic Lick
-311,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_ROKISWEIL,Classical Pluck
-312,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_INTOABYSS,Power Chord
-313,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_SIEGFRIED,Acoustic Rhythm
-//314,0,0,0,0,0,0,1,1,no,0,0x40,0,misc,0, BD_RAGNAROK,Ragnarok
-315,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, BA_MUSICALLESSON,Music Lessons
-316,9,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, BA_MUSICALSTRIKE,Melody Strike
-317,0,8,4,0,0x41,0,5,1,no,0,0x20,0,misc,0, BA_DISSONANCE,Unchained Serenade
-318,0,6,4,0,0x3,-1,5,1,no,0,0,0,misc,0, BA_FROSTJOKER,Unbarring Octave
-319,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_WHISTLE,Perfect Tablature
-320,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_ASSASSINCROSS,Impressive Riff
-321,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_POEMBRAGI,Magic Strings
-322,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_APPLEIDUN,Song of Lutie
-323,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, DC_DANCINGLESSON,Dance Lessons
-324,9,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, DC_THROWARROW,Slinging Arrow
-325,0,8,4,0,0x1,0,5,1,no,0,0x20,0,misc,0, DC_UGLYDANCE,Hip Shaker
-326,0,6,4,0,0x3,-1,5,1,no,0,0,0,misc,0, DC_SCREAM,Dazzler
-327,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_HUMMING,Focus Ballet
-328,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_DONTFORGETME,Slow Grace
-329,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_FORTUNEKISS,Lady Luck
-330,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_SERVICEFORYOU,Gypsy's Kiss
-331,0,6,4,0,0x1,0,10,0,no,0,0x2,0,none,0, NPC_RANDOMMOVE,Random Move
-332,0,6,4,0,0x1,0,10,0,no,0,0x2,0,none,0, NPC_SPEEDUP,Speed UP
-333,0,6,4,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_REVENGE,Revenge
-334,9,6,4,0,0x1,0,1,1,yes,0,0x4,0,none,0, WE_MALE,I Will Protect You
-335,9,6,4,0,0x1,0,1,1,yes,0,0x4,0,none,0, WE_FEMALE,I Look up to You
-336,9,6,4,0,0x1,3,1,1,yes,0,0x4,1,none,0, WE_CALLPARTNER,I miss You
-337,9,6,1,-1,0,0,1,1,no,0,0x2,0,weapon,0, ITM_TOMAHAWK,Throw Tomahawk
-338,-1,8,1,7,0,0,10,-2,no,0,0x2,0,weapon,0, NPC_DARKCROSS,Cross of Darkness
-339,5,6,4,7,0x48,0,10,1,no,33,0x102,0,magic,0, NPC_GRANDDARKNESS,Grand cross of Darkness
-340,9,8,1,7,0,0,10,1:1:2:2:3:3:4:4:5:5,yes,0,0x2,0,magic,0, NPC_DARKSTRIKE,Soul Strike of Darkness
-341,9,8,1,7,0,0,10,3:4:5:6:7:8:9:10:11:12,yes,0,0x2,0,magic,2:3:3:4:4:5:5:6:6:7, NPC_DARKTHUNDER,Darkness Jupitel
-342,9,6,1,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_STOP,Stop
-343,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_WEAPONBRAKER,Break weapon
-344,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_ARMORBRAKE,Break armor
-345,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_HELMBRAKE,Break helm
-346,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_SHIELDBRAKE,Break shield
-347,-9,6,1,9,0,0,10,1,no,0,0x2,0,weapon,0, NPC_UNDEADATTACK,Undead Element Attack
-348,9,0,1,9,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_CHANGEUNDEAD,Undead Attribute Change
-349,0,6,4,0,0x1,0,10,0,no,0,0x2,0,weapon,0, NPC_POWERUP,Power Up
-350,0,6,4,0,0x1,0,10,0,no,0,0x2,0,none,0, NPC_AGIUP,Agility UP
-351,0,0,0,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_SIEGEMODE,Siege Mode
-352,2,0,4,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_CALLSLAVE,Recall Slaves
-353,0,0,0,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_INVISIBLE,Invisible
-354,2,6,4,0,0x1,0,20,0,no,0,0x2,0,misc,0, NPC_RUN,Run
-355,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, LK_AURABLADE,Aura Blade
-356,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, LK_PARRYING,Parrying
-357,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, LK_CONCENTRATION,Concentration
-358,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, LK_TENSIONRELAX,Relax
-359,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, LK_BERSERK,Frenzy
-//360,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, LK_FURY,Fury
-361,9,6,16,0,0x1,1,5,1,yes,0,0,0,magic,0, HP_ASSUMPTIO,Assumptio
-362,4,6,4,0,0x1,0,5,1,yes,0,0,0,magic,2, HP_BASILICA,Basilica
-363,0,0,0,0,0,0,10,0,no,0,0,0,magic,0, HP_MEDITATIO,Meditatio
-364,0,0,0,0,0,0,10,1,no,0,0,0,magic,0, HW_SOULDRAIN,Soul Drain
-365,9,8,1,-1,0,0,1,1,yes,0,0,0,weapon,0, HW_MAGICCRASHER,Stave Crasher
-366,0,6,4,0,0x1,0,10,1,no,0,0,0,magic,0, HW_MAGICPOWER,Mystical Amplification
-367,9,8,1,0,0xD0,0,5,1,no,0,0,0,misc,0, PA_PRESSURE,Gloria Domini
-368,0,6,4,0,0x61,0,5,1,yes,0,0,0,weapon,0, PA_SACRIFICE, Martyr's Reckoning
-369,0,6,4,0,0x41,0,10,1,yes,0,0,0,misc,0, PA_GOSPEL,Battle Chant
-370,-2,6,1,-1,0,0,5,1,yes,0,0,0,weapon,3, CH_PALMSTRIKE,Raging Palm Strike
-371,-2,8,4,-1,0,0,5,1,no,0,0x200,0,weapon,0, CH_TIGERFIST,Glacier Fist
-372,-2,8,4,-1,0,0,10,-1:-1:-2:-2:-3:-3:-4:-4:-5:-5,no,0,0x200,0,weapon,0, CH_CHAINCRUSH,Chain Crush Combo
-373,0,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, PF_HPCONVERSION,Indulge
-374,9,6,1,0,0x1,0,1,1,yes,0,0xE00,0,none,0, PF_SOULCHANGE,Soul Exhale
-375,9,6,1,0,0x98,0,5,1,yes,0,0,0,magic,0, PF_SOULBURN,Soul Siphon
-376,0,0,0,0,0x1,0,5,1,no,0,0,0,weapon,0, ASC_KATAR,Advanced Katar Mastery
-//377,0,0,4,0,0x1,0,10,1,no,0,0,0,misc,0, ASC_HALLUCINATION,Hallucination Walk
-378,0,6,4,5,0x1,0,5,1,no,0,0,0,weapon,0, ASC_EDP,Enchant Deadly Poison
-379,7,6,1,-1,0x8,0,10,1,yes,0,0,0,weapon,0, ASC_BREAKER,Soul Destroyer
-380,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, SN_SIGHT,Falcon Eyes
-381,5,8,1,0,0x40,0,5,1,yes,0,0,0,misc,0, SN_FALCONASSAULT,Falcon Assault
-382,9,8,1,-1,0,2,5,1,yes,0,0,13,weapon,0, SN_SHARPSHOOTING,Focused Arrow Strike
-383,0,6,4,0,0x3,-1,10,1,yes,0,0,0,weapon,0, SN_WINDWALK,Wind Walker
-384,0,0,4,0,0x1,0,10,1,yes,0,0,0,weapon,0, WS_MELTDOWN,Shattering Strike
-//385,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, WS_CREATECOIN,Create Coins
-//386,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, WS_CREATENUGGET,Create Nuggets
-387,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, WS_CARTBOOST,Cart Boost
-//388,9,6,2,0,0x1,0,5,1,no,0,0,0,none,0, WS_SYSTEMCREATE,Auto Attack System
-389,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, ST_CHASEWALK,Stealth
-390,0,0,4,0,0,0,5,1,yes,0,0,0,weapon,0, ST_REJECTSWORD,Counter Instinct
-//391,0,0,4,0,1,0,1,1,yes,0,0,0,magic,0, ST_STEALBACKPACK,Steal Backpack
-392,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, CR_ALCHEMY,Alchemy
-393,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, CR_SYNTHESISPOTION,Potion Synthesis
-394,9,8,1,-1,0,0,10,-9,yes,0,0,0,weapon,0, CG_ARROWVULCAN,Vulcan Arrow
-395,0,0,4,0,0x1,3,1,1,yes,0,0x40,0,misc,2, CG_MOONLIT,Sheltering Bliss
-396,1,6,16,0,0x1,0,1,1,yes,0,0x600,0,none,0, CG_MARIONETTE,Marionette Control
-397,5,8,1,-1,0x20,0,5,5,no,0,0,0,weapon,0, LK_SPIRALPIERCE,Spiral Pierce
-398,4,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, LK_HEADCRUSH,Traumatic Blow
-399,4,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, LK_JOINTBEAT,Vital Strike
-400,9,8,1,8,0x6,1,5,1:2:3:4:5,yes,0,0,0,magic,0, HW_NAPALMVULCAN,Napalm Vulcan
-401,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, CH_SOULCOLLECT,Zen
-402,9,6,1,0,0x1,0,5,1,no,0,0,0,none,0, PF_MINDBREAKER,Mind Breaker
-403,0,0,4,0,0x1,0,1,1,yes,0,0,0,magic,0, PF_MEMORIZE,Foresight
-404,9,6,2,2,0x1,0,5,1,yes,0,0x100,2,magic,0, PF_FOGWALL,Blinding Mist
-405,7,6,1,0,0x1,0,1,1,no,0,0,3,magic,0, PF_SPIDERWEB,Fiber Lock
-406,0,6,4,-1,0xA,2,10,1,no,33,0,0,weapon,0, ASC_METEORASSAULT,Meteor Assault
-407,0,6,4,0,0x1,0,1,0,no,0,0,0,none,0, ASC_CDP,Create Deadly Poison
-408,9,6,4,0,0x1,0,1,1,yes,0,0x4,0,none,0, WE_BABY,Baby
-409,9,6,4,0,0x1,3,1,1,yes,0,0x4,1,none,0, WE_CALLPARENT,Call Parent
-410,9,6,4,0,0x1,3,1,1,yes,0,0x4,1,none,0, WE_CALLBABY,Call Baby
-411,0,6,4,0,0x1,0,10,1,yes,0,0,0,misc,4, TK_RUN,Running
-412,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYSTORM,Tornado Stance
-413,-2,8,4,-1,0x2,2,7,-3,no,0,0x200,0,weapon,0, TK_STORMKICK,Tornado Kick
-414,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYDOWN,Heel Drop Stance
-415,-2,8,4,-1,0,0,7,-3,no,0,0x200,0,weapon,0, TK_DOWNKICK,Heel Drop
-416,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYTURN,Roundhouse Stance
-417,-2,8,4,-1,0x2,1,7,-3,no,0,0x200,0,weapon,2, TK_TURNKICK,Roundhouse Kick
-418,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYCOUNTER,Counter Kick Stance
-419,-2,8,4,-1,0x40,0,7,-3,no,0,0x200,0,weapon,0, TK_COUNTER,Counter Kick
-420,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_DODGE,Tumbling
-421,9,8,16,-1,0x1,0,7,-3,no,0,0,0,weapon,0, TK_JUMPKICK,Flying Kick
-422,0,0,0,0,0,1,10,0,no,0,0,0,none,0, TK_HPTIME,Peaceful Break
-423,0,0,0,0,0,1,10,0,no,0,0,0,none,0, TK_SPTIME,Happy Break
-424,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, TK_POWER,Kihop
-425,0,6,4,2:4:1:3:8:7:6,0x1,0,7,1,no,0,0,0,weapon,0, TK_SEVENWIND,Mild Wind
-426,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, TK_HIGHJUMP,Taekwon Jump
-427,0,6,4,0,0x1,0,3,1,yes,0,0,0,magic,0, SG_FEEL,Feeling the Sun Moon and Stars
-428,1,6,4,-1,0x2,1,3,1,yes,0,0,0,weapon,2, SG_SUN_WARM,Warmth of the Sun
-429,1,6,4,-1,0x2,1,3,1,yes,0,0,0,weapon,2, SG_MOON_WARM,Warmth of the Moon
-430,1,6,4,-1,0x2,1,3,1,yes,0,0,0,weapon,2, SG_STAR_WARM,Warmth of the Stars
-431,0,0,4,0,0x1,0,4,1,yes,0,0,0,magic,0, SG_SUN_COMFORT,Comfort of the Sun
-432,0,0,4,0,0x1,0,4,1,yes,0,0,0,magic,0, SG_MOON_COMFORT,Comfort of the Moon
-433,0,0,4,0,0x1,0,4,1,yes,0,0,0,magic,0, SG_STAR_COMFORT,Comfort of the Stars
-434,10,6,1,0,0x1,0,3,1,yes,0,0,0,magic,0, SG_HATE,Hatred of the Sun Moon and Stars
-435,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_SUN_ANGER,Anger of the Sun
-436,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_MOON_ANGER,Anger of the Moon
-437,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_STAR_ANGER,Anger of the Stars
-438,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SG_SUN_BLESS,Blessing of the Sun
-439,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SG_MOON_BLESS,Blessing of the Moon
-440,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SG_STAR_BLESS,Blessing of the Stars
-441,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SG_DEVIL,Demon of the Sun Moon and Stars
-442,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_FRIEND,Friend of the Sun Moon and Stars
-443,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SG_KNOWLEDGE,Knowledge of the Sun Moon and Stars
-444,0,6,4,0,0x1,0,1,1,no,0,0,0,misc,0, SG_FUSION,Union of the Sun Moon and Stars
-445,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_ALCHEMIST,Spirit of the Alchemist
-446,9,6,16,0,0x1,0,1,1,yes,0,0xC08,0,none,0, AM_BERSERKPITCHER,Aid Berserk Potion
-447,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_MONK,Spirit of the Monk
-448,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_STAR,Spirit of the Star Gladiator
-449,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_SAGE,Spirit of the Sage
-450,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_CRUSADER,Spirit of the Crusader
-451,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_SUPERNOVICE,Spirit of the Supernovice
-452,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_KNIGHT,Spirit of the Knight
-453,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_WIZARD,Spirit of the Wizard
-454,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_PRIEST,Spirit of the Priest
-455,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_BARDDANCER,Spirit of the Artist
-456,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_ROGUE,Spirit of the Rogue
-457,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_ASSASIN,Spirit of the Assasin
-458,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_BLACKSMITH,Spirit of the Blacksmith
-459,0,6,4,0,0x3,-1,1,1,no,0,0x8,0,weapon,0 , BS_ADRENALINE2,Advanced Adrenaline Rush
-460,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_HUNTER,Spirit of the Hunter
-461,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_SOULLINKER,Spirit of the Soul Linker
-462,9,6,16,0,0x1,0,7,1,yes,0,0,0,magic,0, SL_KAIZEL,Kaizel
-463,9,6,16,0,0x1,0,7,1,yes,0,0,0,magic,0, SL_KAAHI,Kaahi
-464,9,6,16,0,0x1,0,3,1,yes,0,0,0,magic,0, SL_KAUPE,Kaupe
-465,9,6,16,0,0x1,0,7,1,yes,0,0,0,magic,0, SL_KAITE,Kaite
-466,0,0,0,0,0,0,7,0,yes,0,0,0,magic,0, SL_KAINA,Kaina
-467,9,6,1,-2,0,0,7,1,no,0,0,0,magic,2, SL_STIN,Estin
-468,9,6,1,-2,0,0,7,1,no,0,0,0,magic,0, SL_STUN,Estun
-469,9,8,1,-2,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, SL_SMA,Esma
-470,9,6,1,0,0x1,0,7,1,no,0,0,0,magic,0, SL_SWOO,Eswoo
-471,9,6,1,0,0x1,0,3,1,no,0,0,0,magic,0, SL_SKE,Eske
-472,9,6,1,0,0x1,0,3,1,no,0,0,0,magic,0, SL_SKA,Eska
-473,0,6,4,0,0,0,1,1,no,0,0,0,none,0, SM_SELFPROVOKE,Provoke Self
-474,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_EMOTION_ON,Emotion ON
-475,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, ST_PRESERVE,Preserve
-476,1,6,1,0,0x1,0,5,1,yes,0,0,0,weapon,0, ST_FULLSTRIP,Divest All
-477,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, WS_WEAPONREFINE,Upgrade Weapon
-478,3,6,2,0,0x3,3,10,1,no,0,0,0,none,0, CR_SLIMPITCHER,Aid Condensed Potion
-479,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, CR_FULLPROTECTION,Full Protection
-480,5,8,1,0,0,0,5,5,no,0,0,0,weapon,0, PA_SHIELDCHAIN,Shield Chain
-481,0,0,0,0,0,0,5,0,no,0,0,0,none,0, HP_MANARECHARGE,Mana Recharge
-482,0,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, PF_DOUBLECASTING,Double Casting
-483,16,6,2,0,0x1,1:2:3:4:5,1,1,no,0,0,0,none,0, HW_GANBANTEIN,Ganbantein
-484,9,6,2,2,0x91,0,5,1,yes,0,0,0,misc,0, HW_GRAVITATION,Gravitation Field
-485,-2,6,1,-1,0x8,0,10,1,no,0,0,0,weapon,0, WS_CARTTERMINATION,Cart Termination
-486,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, WS_OVERTHRUSTMAX,Maximum Power Thrust
-487,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, CG_LONGINGFREEDOM,Longing for Freedom
-488,0,6,4,0,0x1,1,5,1,no,0,0x40,0,misc,0, CG_HERMODE,Wand of Hermode
-489,9,6,1,0,0x41,0,5,1,no,0,0,0,misc,0, CG_TAROTCARD,Tarot Card of Fate
-490,9,8,1,0,0x40,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,misc,0, CR_ACIDDEMONSTRATION,Acid Demonstration
-491,1,6,2,0,0x1,0,2,1,no,0,0,0,none,0, CR_CULTIVATION,Plant Cultivation
-492,0,6,4,0:1:2:3:4:5:6:7:8:9,0x1,0,10,1,no,0,0x2,0,none,0, ITEM_ENCHANTARMS,Weapon Enchantment
-493,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, TK_MISSION,Taekwon Mission
-494,9,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, SL_HIGH,Spirit of Rebirth
-495,0,6,4,0,0x1,0,1,1,no,0,0x8,0,weapon,0, KN_ONEHAND,Onehand Quicken
-496,0,6,4,0,0x1,0,1,0,no,0,0x8,0,none,0, AM_TWILIGHT1,Twilight Alchemy 1
-497,0,6,4,0,0x1,0,1,0,no,0,0x8,0,none,0, AM_TWILIGHT2,Twilight Alchemy 2
-498,0,6,4,0,0x1,0,1,0,no,0,0x8,0,none,0, AM_TWILIGHT3,Twilight Alchemy 3
-499,-9,8,4,-1,0,0,1,2,no,0,0x208,0,weapon,0, HT_POWER,Beast Strafing
-500,0,6,4,0,0x40,0,5,1,no,0,0,0,misc,0, GS_GLITTERING,Flip the Coin
-501,9,6,1,-1,0x50,0,1,1,no,0,0,0,misc,0, GS_FLING,Fling
-502,-9,8,1,-1,0,0,1,3,no,0,0,0,weapon,0, GS_TRIPLEACTION,Triple Action
-503,-9,6,1,-1,0x8,0,1,1,no,0,0,0,weapon,0, GS_BULLSEYE,Bulls Eye
-504,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, GS_MADNESSCANCEL,Madness Canceller
-505,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, GS_ADJUSTMENT,AdJustment
-506,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, GS_INCREASING,Increasing Accuracy
-507,-9,6,1,8,0,0,1,1,no,0,0,0,weapon,0, GS_MAGICALBULLET,Magical Bullet
-508,-9,6,1,-1,0x1,0,1,1,no,0,0,0,weapon,0, GS_CRACKER,Cracker
-509,0,0,0,0,0,0,10,0,no,0,0,0,none,0, GS_SINGLEACTION,Single Action
-510,0,0,0,0,0,0,10,0,no,0,0,0,none,0, GS_SNAKEEYE,Snake Eye
-511,-9,8,0,-1,0,0,10,2,no,0,0,0,weapon,0, GS_CHAINACTION,Chain Action
-512,-9,6,1,-1,0,0,10,1,yes,0,0,0,weapon,0, GS_TRACKING,Tracking
-513,-9,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, GS_DISARM,Disarm
-514,-9,6,1,-1,0x20,0,5,1,no,0,0,0,weapon,0, GS_PIERCINGSHOT,Piercing Shot
-515,-9,8,1,-1,0,0,10,5,no,0,0,0,weapon,0, GS_RAPIDSHOWER,Rapid Shower
-516,0,8,4,-1,0x2,3,10,1,no,0,0,0,weapon,0, GS_DESPERADO,Desperado
-517,0,6,4,-1,0x1,0,10,1,no,0,0,0,weapon,0, GS_GATLINGFEVER,Gatling Fever
-518,2,6,1,-1,0,0,10,1,no,0,0,0,weapon,5, GS_DUST,Dust
-519,-9,6,1,-1,0,0,10,1,yes,0,0,0,weapon,0, GS_FULLBUSTER,Full Buster
-520,-9,6,1,-1,0x2,1:1:1:2:2:2:3:3:3:4,10,1,no,0,0,0,weapon,0, GS_SPREADATTACK,Spread Attack
-521,-9,6,2,-1,0x40,1,10,1,no,0,0,0,weapon,3, GS_GROUNDDRIFT,Ground Drift
-522,0,0,0,0,0,0,10,1,no,0,0,0,weapon,0, NJ_TOBIDOUGU,Shuriken Training
-523,9,6,1,-1,0x40,0,10,1,no,0,0,0,weapon,0, NJ_SYURIKEN,Throw Shuriken
-524,9,8,1,-1,0x40,0,5,3,no,0,0,0,weapon,0, NJ_KUNAI,Throw Kunai
-525,9,8,1,-1,0x6,1,5,-3:-3:-4:-4:-5,yes,0,0,0,weapon,0, NJ_HUUMA,Throw Huuma Shuriken
-526,9,6,1,0,0x50,0,10,1,no,0,0,0,misc,0, NJ_ZENYNAGE,Throw Zeny
-527,0,6,4,-1,0,0,5,1,no,0,0,0,weapon,3, NJ_TATAMIGAESHI,Improvised Defense
-528,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, NJ_KASUMIKIRI,Vanishing Slash
-529,7:9:11:13:15,6,2,0,0x1,0,5,1,no,0,0,0,none,0, NJ_SHADOWJUMP,Shadow Leap
-530,7:9:11:13:15,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, NJ_KIRIKAGE,Shadow Slash
-531,0,6,4,0,0x1,0,5,1,no,0,0,0,none,7, NJ_UTSUSEMI,Cicada Skin Sheeding
-532,0,6,4,0,0x1,0,10,1,yes,0,0,0,magic,0, NJ_BUNSINJYUTSU,Mirror Image
-533,0,0,0,0,0,0,10,0,no,0,0,0,none,0, NJ_NINPOU,Spirit of the Blade
-534,9,8,1,3,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, NJ_KOUENKA,Crimson Fire Petal
-535,0,8,4,3,0,0,10,1,yes,0,0,0,magic,1, NJ_KAENSIN,Crimson Fire Formation
-536,9,8,1,3,0x2,2,5,3,yes,0,0,0,magic,0, NJ_BAKUENRYU,Raging Fire Dragon
-537,9,8,1,1,0,0,10,3:4:5:6:7:8:9:10:11:12,yes,0,0,0,magic,0, NJ_HYOUSENSOU,Spear of Ice
-538,9,6,2,1,0x1,0,10,1,yes,0,0,0,magic,0, NJ_SUITON,Hidden Water
-539,0,6,4,1,0x2,3,5,1,yes,0,0,0,magic,0, NJ_HYOUSYOURAKU,Ice Meteor
-540,9,8,1,4,0,0,10,1:2:2:3:3:4:4:5:5:6,yes,0,0,0,magic,0, NJ_HUUJIN,Wind Blade
-541,9,6,4,4,0x2,2:2:3:3:4,5,1,yes,0,0,0,magic,0, NJ_RAIGEKISAI,Lightning Strike of Destruction
-542,9,8,1,4,0,3,5,1,yes,0,0,5:6:7:8:9,magic,0, NJ_KAMAITACHI,Kamaitachi
-543,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, NJ_NEN,Soul
-544,-5,6,1,0,0x40,0,10,1,no,0,0,0,weapon,0, NJ_ISSEN,Final Strike
-
-// Additional NPC Skills (Episode 11.3)
-653,0,8,4,0,0x6,5:7:9:11:13:5:7:9:11:13,10,1,no,0,0x2,0,magic,0, NPC_EARTHQUAKE,Earthquake
-654,9,6,1,3,0,5,10,1,no,0,0x2,14,weapon,0, NPC_FIREBREATH,Fire Breath
-655,9,6,1,1,0,5,10,1,no,0,0x2,14,weapon,0, NPC_ICEBREATH,Ice Breath
-656,9,6,1,4,0,5,10,1,no,0,0x2,14,weapon,0, NPC_THUNDERBREATH,Thunder Breath
-657,9,6,1,5,0,5,10,1,no,0,0x2,14,weapon,0, NPC_ACIDBREATH,Acid Breath
-658,9,6,1,7,0,5,10,1,no,0,0x2,14,weapon,0, NPC_DARKNESSBREATH,Darkness Breath
-659,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_DRAGONFEAR,Dragon Fear
-660,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_BLEEDING,Bleeding
-661,0,6,4,0,0x2,7,5,1,no,0,0x2,0,weapon,7, NPC_PULSESTRIKE,Pulse Strike
-662,0,6,4,0,0x2,14,10,1,no,0,0x2,0,weapon,0, NPC_HELLJUDGEMENT,Hell's Judgement
-663,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESILENCE,Wide Silence
-664,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDEFREEZE,Wide Freeze
-665,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDEBLEEDING,Wide Bleeding
-666,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESTONE,Wide Petrify
-667,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDECONFUSE,Wide Confusion
-668,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESLEEP,Wide Sleep
-669,0,6,4,3,0x3,5,1,1,no,0,0x2,0,magic,0, NPC_WIDESIGHT,Wide Sight
-670,9,6,2,7,0x91,0,10,1,no,0,0x2,0,magic,0, NPC_EVILLAND,Evil Land
-671,0,6,4,0,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_MAGICMIRROR,Magic Mirror
-672,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_SLOWCAST,Slow Cast
-673,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_CRITICALWOUND,Critical Wounds
-674,-9,6,1,-1,0x1,0,1,1,no,0,0x2,0,none,0, NPC_EXPULSION,Expulsion
-675,0,6,4,0,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_STONESKIN,Stone Skin
-676,0,6,4,0,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_ANTIMAGIC,Anti Magic
-677,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDECURSE,Wide Curse
-678,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESTUN,Wide Stun
-679,0,6,4,0,0x2,5:7:9:11:13:13:13:13:13:13,10,1,no,0,0x2,0,weapon,0, NPC_VAMPIRE_GIFT,Vampire Gift
-680,0,6,4,0,0x3,5:7:9:11:13:13:13:13:13:13,10,1,no,0,0x2,0,none,0, NPC_WIDESOULDRAIN,Wide Soul Drain
-
-// Cash Shop Skill
-681,0,0,0,0,0,0,10,0,no,0,0x1,0,none,0, ALL_INCCARRY,Increase Weight Limit R
-
-// Additional NPC skill (Episode 12)
-682,0,0,4,0,0x1,0,1,1,no,0,0x2,0,none,0, NPC_TALK,Talk
-683,-9,6,1,-1,0,0,1,1,no,0,0x2,0,none,0, NPC_HELLPOWER,Hell Power
-684,0,6,4,0,0x3,-1,1,1,no,0,0x2,0,none,0, NPC_WIDEHELLDIGNITY,Hell Dignity
-685,0,0,4,0,0x1,0,1,1,no,0,0x2,0,none,0, NPC_INVINCIBLE,Invincible
-686,0,0,4,0,0x1,0,1,1,no,0,0x2,0,none,0, NPC_INVINCIBLEOFF,Invincible off
-687,0,6,4,0,0x1,0,1,1,yes,0,0x2,0,none,0, NPC_ALLHEAL,Full Heal
-
-// Additional Skill (??)
-688,9,6,16,0,0x1,0,10,0,no,0,0x200,0,none,0, GM_SANDMAN,GM Sandman
-689,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0, CASH_BLESSING,Party Blessing
-690,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0, CASH_INCAGI,Party Increase AGI
-691,0,6,4,0,0x3,-1,5,1,yes,0,0x2,0,magic,0, CASH_ASSUMPTIO,Party Assumptio
-//692,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_CATCRY,Cat Cry
-693,0,6,4,0,0x3,-1,1,1,yes,0,0x2,0,magic,0, ALL_PARTYFLEE,Party Flee
-//694,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_ANGEL_PROTECT,Angel's Protection
-//695,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_DREAM_SUMMERNIGHT,Summer Night Dream
-//696,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, NPC_CHANGEUNDEAD2,Change Undead
-//697,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, ALL_REVERSEORCISH,Reverse Orcish
-698,0,6,4,0,0x01,0,1,1,no,0,0x2,0,none,0, ALL_WEWISH,Christmas Carol
-//699,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_SONKRAN,ALL_SONKRAN
-
-// New NPC Wide Status AoE Skills And Others
-//700,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDEHEALTHFEAR,Wide Health Fear
-//701,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDEBODYBURNNING,Wide Body Burnning
-//702,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDEFROSTMISTY,Wide Freezing
-//703,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDECOLD,Wide Crystalize
-//704,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDE_DEEP_SLEEP,Wide Deep Sleep
-//705,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDESIREN,Wide Siren's Voice
-//706,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_VENOMFOG,Venom Fog
-//707,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_MILLENNIUMSHIELD,Millenium Shield 2
-//708,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_COMET,Comet 2
-
-1001,9,6,1,-1,0,0,1,1,no,0,0x1,0,weapon,0, KN_CHARGEATK,Charge Attack
-1002,0,6,4,0,0x1,0,1,0,no,0,0x1,0,weapon,2, CR_SHRINK,Shrink
-1003,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0, AS_SONICACCEL,Sonic Acceleration
-1004,9,8,1,0,0x8,0,1,1,no,0,0x1,0,weapon,0, AS_VENOMKNIFE,Throw Venom Knife
-1005,1,6,1,0,0x1,0,1,1,no,0,0x1,0,weapon,0, RG_CLOSECONFINE,Close Confine
-1006,0,6,4,3,0,2,1,1,yes,0,0x1,0,magic,3, WZ_SIGHTBLASTER,Sight Blaster
-1007,0,6,4,0,0x1,0,1,0,no,0,0x1,0,none,0, SA_CREATECON,Create Elemental Converter
-1008,9,6,1,1,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTWATER,Elemental Change Water
-1009,-9,6,1,0,0,0,1,1,no,0,0x1,0,weapon,3, HT_PHANTASMIC,Phantasmic Arrow
-1010,9,6,1,0,0x1,0,1,0,no,0,0x1,0,misc,0, BA_PANGVOICE,Pang Voice
-1011,9,6,1,0,0x1,0,1,0,no,0,0x1,0,misc,0, DC_WINKCHARM,Wink of Charm
-1012,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0, BS_UNFAIRLYTRICK,Unfair Trick
-1013,0,6,4,0,0x3,2,1,0,no,0,0x1,0,weapon,0, BS_GREED,Greed
-1014,0,6,4,6,0x3,14,1,0,yes,0,0x1,0,magic,0, PR_REDEMPTIO,Redemptio
-1015,9,6,16,0,0x1,0,1,1,no,0,0x401,0,weapon,0, MO_KITRANSLATION,Ki Translation
-1016,-1,6,1,-1,0x2,1,1,1,no,0,0x1,0,weapon,5, MO_BALKYOUNG,Ki Explosion
-1017,9,6,1,2,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTGROUND,Elemental Change Earth
-1018,9,6,1,3,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTFIRE,Elemental Change Fire
-1019,9,6,1,4,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTWIND,Elemental Change Wind
-
-//****
-// RK Rune Knight
-//****
-2001,1,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, RK_ENCHANTBLADE,Enchant Blade
-2002,7:8:9:10:11,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, RK_SONICWAVE,Sonic Wave
-2003,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, RK_DEATHBOUND,Death Bound
-2004,1,8,1,-1,0,0,10,-5,no,0,0,0,weapon,0, RK_HUNDREDSPEAR,Hundred Spear
-2005,1,6,2,4,0x2,2,5,1,no,0,0,0,weapon,3, RK_WINDCUTTER,Wind Cutter
-2006,0,6,4,-1,0x2,5,5,1,no,0,0,0,weapon,0, RK_IGNITIONBREAK,Ignition Break
-2007,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, RK_DRAGONTRAINING,Dragon Training
-2008,9,6,2,3,0xC2,1:1:1:2:2:2:3:3:4:4,10,1,no,0,0,0,misc,0, RK_DRAGONBREATH,Dragon Breath //CHECK May have to change this back to a weapon type attack.
-2009,0,6,4,0,0x3,3:4:5:6:7,5,1,yes,0,0,0,weapon,0, RK_DRAGONHOWLING,Dragon Howling
-2010,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RK_RUNEMASTERY,Rune Mastery
-2011,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_MILLENNIUMSHIELD,Millenium Shield
-2012,1,6,4,-1,0,0x8,1,1,yes,0,0,0,weapon,0, RK_CRUSHSTRIKE,Crush Strike
-2013,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_REFRESH,Refresh
-2014,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_GIANTGROWTH,Giant Growth
-2015,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_STONEHARDSKIN,Stone Hard Skin
-2016,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_VITALITYACTIVATION,Vitality Activation
-2017,0,6,4,-1,0x2,3,1,1,no,0,0,0,weapon,7, RK_STORMBLAST,Storm Blast
-2018,0,6,4,0,0x3,-1,1,1,yes,0,0,0,none,0, RK_FIGHTINGSPIRIT,Fighting Spirit //CHECK Is this splash needed?
-2019,9,6,4,6,0x1,0,1,1,yes,0,0,0,none,0, RK_ABUNDANCE,Abundance
-2020,5:6:7:8:9,6,1,-1,0,0,5,1,yes,0,0,0,weapon,0, RK_PHANTOMTHRUST,Phantom Thrust
-
-//****
-// WL Warlock
-//****
-2201,11,6,16,0,0,0,5,1,yes,0,0,0,magic,0, WL_WHITEIMPRISON,White Imprison
-2202,11,8,1,8,0x2,1:1:1:2:2,5,-2,yes,0,0,0,magic,0, WL_SOULEXPANSION,Soul Expansion
-2203,0,8,4,1,0x2,13,5,-3:-4:-5:-6:-7,yes,0,0,0,magic,0, WL_FROSTMISTY,Frosty Misty
-2204,0,8,4,1,0x2,13,5,-5,yes,0,0,0,magic,0, WL_JACKFROST,Jack Frost
-2205,11,6,1,0,0x1,0,5,1,yes,0,0,0,magic,0, WL_MARSHOFABYSS,Marsh of Abyss
-2206,0,6,4,0,0x1,0,5,1,yes,0,0,0,magic,0, WL_RECOGNIZEDSPELL,Recognized Spell
-2207,7,6,1,2,0x3,1:2:2:3:3,5,1,yes,0,0,0,magic,0, WL_SIENNAEXECRATE,Sienna Execrate
-2208,0,0,0,0,0,0,3,0,no,0,0,0,none,0, WL_RADIUS,Radius
-2209,0,6,4,0,0x3,9:10:11:12:13,5,1,yes,0,0,0,magic,0, WL_STASIS,Stasis
-2210,11,6,1,0,0,0,5,1,yes,0,0,0,magic,0, WL_DRAINLIFE,Drain Life
-2211,11,8,1,3,0x2,3,5,-7,yes,0,0,0,magic,3, WL_CRIMSONROCK,Crimson Rock
-2212,11,6,1,3,0,0,5,1,yes,0,0,0,magic,0, WL_HELLINFERNO,Hell Inferno
-2213,11,8,2,0,0x2,15,5,-20,yes,0,0,0,magic,2, WL_COMET,Comet //CHECK AoE in official code appears to be 15 x 15, yet casting circle is much bigger then that.
-2214,11,6,1,0,0,3,5,1,yes,0,0,0,magic,0, WL_CHAINLIGHTNING,Chain Lightning //CHECK Is the splash being used for the target search?
-2215,11,6,1,4,0,0,5,1,no,0,0,0,magic,0, WL_CHAINLIGHTNING_ATK,Chain Lightning Attack
-2216,3,8,2,2,0,0,5,-6:-7:-8:-9:-10,yes,0,0,0,magic,0, WL_EARTHSTRAIN,Earth Strain
-2217,11,6,1,0,0,0,5,1,yes,0,0,0,magic,0, WL_TETRAVORTEX,Tetra Vortex
-2218,11,6,1,3,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_FIRE,Tetra Vortex Fire
-2219,11,6,1,1,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_WATER,Tetra Vortex Water
-2220,11,6,1,4,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_WIND,Tetra Vortex Wind
-2221,11,6,1,2,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_GROUND,Tetra Vortex Earth
-2222,0,6,4,3,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONFB,Summon Fire Ball
-2223,0,6,4,4,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONBL,Summon Lightning Ball
-2224,0,6,4,1,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONWB,Summon Water Ball
-2225,11,6,1,3,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_FIRE,Summon Attack Fire //CHECK Summon attack ID's dont appear to have a range.
-2226,11,6,1,4,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_WIND,Summon Attack Wind
-2227,11,6,1,1,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_WATER,Summon Attack Water
-2228,11,6,1,2,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_GROUND,Summon Attack Earth
-2229,0,6,4,2,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONSTONE,Summon Stone
-2230,11,8,1,0,0,0,2,1,yes,0,0,0,magic,0, WL_RELEASE,Release //CHECK Should it be left to do multi hit or single hit?
-2231,0,6,4,0,0x1,0,1,1,yes,0,0,0,magic,0, WL_READING_SB,Reading Spellbook
-2232,0,0,0,0,0,0,5,0,no,0,0,0,none,0, WL_FREEZE_SP,Freeze Spell
-
-//****
-// GC Guillotine Cross
-//****
-2021,5,6,1,0,0x1,0,5,1,no,0,0,0,none,0, GC_VENOMIMPRESS,Venom Impress
-2022,3,8,1,-1,0,0,5,-7,no,0,0,0,weapon,0, GC_CROSSIMPACT,Cross Impact
-2023,3:4:5:6:7,6,1,-1,0,0,5,1,no,0,0,0,weapon,0,GC_DARKILLUSION,Dark Illusion
-2024,0,0,0,0,0,0,10,0,no,0,0,0,none,0, GC_RESEARCHNEWPOISON,Research New Poison
-2025,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, GC_CREATENEWPOISON,Create New Poison
-2026,5,6,16,0,0x1,0,1,1,no,0,0,0,none,0, GC_ANTIDOTE,Antidote
-2027,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, GC_POISONINGWEAPON,Poisoning Weapon
-2028,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, GC_WEAPONBLOCKING,Weapon Blocking
-2029,-2,6,4,-1,0x2,1,5,1,no,0,0,0,weapon,3, GC_COUNTERSLASH,Counter Slash
-2030,-2,6,4,-1,0x1,0,5,1,no,0,0x200,0,weapon,0, GC_WEAPONCRUSH,Weapon Crush //CHECK SHould this and the above skill have INF2 0x200?
-2031,1,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, GC_VENOMPRESSURE,Venom Pressure
-2032,5,6,2,0,0x1,0,5,1,yes,0,0,1,none,0, GC_POISONSMOKE,Poison Smoke
-2033,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, GC_CLOAKINGEXCEED,Cloaking Exceed
-2034,0,6,4,-1,0x2,3,1,1,no,0,0,0,weapon,0, GC_PHANTOMMENACE,Phantom Menace
-2035,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, GC_HALLUCINATIONWALK,Hallucination Walk
-2036,0,6,4,-1,0x2,1:1:1:1:2,5,1,no,0,0,0,weapon,0, GC_ROLLINGCUTTER,Rolling Cutter
-2037,9:10:11:12:13,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, GC_CROSSRIPPERSLASHER,Cross Ripper Slasher
-
-//****
-// AB Arch Bishop
-//****
-2038,11,8,1,6,0x2,3,5,-3,yes,0,0,0,magic,0, AB_JUDEX,Judex
-2039,0,6,4,0,0x1,0,1,1,yes,0,0,0,magic,0, AB_ANCILLA,Ancilla
-2040,11,8,1,6,0,0,10,-10,yes,0,0,0,magic,0, AB_ADORAMUS,Adoramus
-2041,0,6,4,6,0x3,3:7:15,3,1,yes,0,0,0,magic,0, AB_CLEMENTIA,Crementia
-2042,0,6,4,6,0x3,3:7:15,3,1,yes,0,0,0,magic,0, AB_CANTO,Canto Candidus
-2043,0,6,4,6,0x3,3:7:15,3,1,yes,0,0,0,magic,0, AB_CHEAL,Coluceo Heal
-2044,11,6,2,6,0x1,0,5,1,yes,0,0,1,magic,0, AB_EPICLESIS,Epiclesis
-2045,0,6,4,6,0x3,15,10,1,yes,0,0,0,magic,0, AB_PRAEFATIO,Praefatio
-2046,0,6,4,6,0x3,15,10,1,yes,0,0,0,magic,0, AB_ORATIO,Oratio
-2047,0,6,4,6,0x3,15,4,1,yes,0,0,0,magic,0, AB_LAUDAAGNUS,Lauda Agnus
-2048,0,6,4,6,0x3,15,4,1,yes,0,0,0,magic,0, AB_LAUDARAMUS,Lauda Ramus
-2049,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AB_EUCHARISTICA,Eucharistica
-2050,11,6,16,6,0x1,0,1,1,yes,0,0,0,magic,0, AB_RENOVATIO,Renovatio
-2051,11,6,16,6,0x21,0,5,1,yes,0,0,0,magic,0, AB_HIGHNESSHEAL,Highness Heal //CHECK Info shows this has magic attack.
-2052,11,6,1,0,0x1,0,5,1,yes,0,0xA00,0,magic,0, AB_CLEARANCE,Clearance //CHECK Also shows this as a magic attack. Why?
-2053,0,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, AB_EXPIATIO,Expiatio //CHECK Does this also give the buff to party members?
-2054,0,6,4,6,0x1,0,10,1,yes,0,0,0,none,0, AB_DUPLELIGHT,Duple Light //CHECK Had issues adding a skill level check to make the % go higher with the skills level. Will do later.
-2055,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, AB_DUPLELIGHT_MELEE,Duple Light Melee
-2056,-1,6,1,0,0,0,10,1,no,0,0,0,magic,0, AB_DUPLELIGHT_MAGIC,Duple Light Magic
-2057,0,6,4,6,0x3,4:5:6:7:8,5,1,yes,0,0,0,magic,0, AB_SILENTIUM,Silentium //CHECk Marked magic attack as well. Hmmmm....
-
-2515,11,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, AB_SECRAMENT,Secrament
-
-//****
-// RA Ranger
-//****
-2233,9,8,1,-1,0x2,3:3:3:3:3:4:4:4:4:5,10,-3,yes,0,0,0,weapon,0, RA_ARROWSTORM,Arrow Storm
-2234,0,6,4,0,0,0,5,1,yes,0,0,0,none,0, RA_FEARBREEZE,Fear Breeze
-2235,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RA_RANGERMAIN,Ranger Main
-2236,9,8,1,-1,0,0,10,1,yes,0,0,0,weapon,0, RA_AIMEDBOLT,Aimed Bolt
-2237,9,6,2,0,0x3,3,1,1,no,0,0,0,none,0, RA_DETONATOR,Detonator
-2238,3,6,2,0,0x3,2,5,1,no,0,0x80,3,misc,0, RA_ELECTRICSHOCKER,Electric Shocker
-2239,3,6,2,0,0x42,3,5,1,no,0,0x80,3,misc,0, RA_CLUSTERBOMB,Cluster Bomb
-2240,0,6,4,0,0,0,1,1,no,0,0,0,none,0, RA_WUGMASTERY,Warg Mastery
-2241,0,6,4,0,0,0,3,1,no,0,0,0,none,0, RA_WUGRIDER,Warg Rider
-2242,0,6,4,-1,0x2,1,1,0,no,0,0,0,weapon,0, RA_WUGDASH,Warg Dash
-2243,9,6,1,0,0,0,5,1,no,0,0,0,weapon,0, RA_WUGSTRIKE,Warg Strike
-2244,9,6,1,0,0,0,5,1,no,0,0,0,weapon,0, RA_WUGBITE,Warg Bite
-2245,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RA_TOOTHOFWUG,Tooth of Warg
-2246,0,6,4,0,0x2,3:4:5:6:7,5,1,no,0,0,0,weapon,0, RA_SENSITIVEKEEN,Sensitive Keen
-2247,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, RA_CAMOUFLAGE,Camouflage
-2248,0,0,0,0,0,0,5,0,no,0,0,0,none,0, RA_RESEARCHTRAP,Research Trap
-2249,3,6,2,3,0x43,2,1,1,no,0,0x80,1,misc,0, RA_MAGENTATRAP,Magenta Trap
-2250,3,6,2,1,0x43,2,1,1,no,0,0x80,1,misc,0, RA_COBALTTRAP,Cobalt Trap
-2251,3,6,2,2,0x43,2,1,1,no,0,0x80,1,misc,0, RA_MAIZETRAP,Maize Trap
-2252,3,6,2,4,0x43,2,1,1,no,0,0x80,1,misc,0, RA_VERDURETRAP,Verdure Trap
-2253,3,6,2,0,0x42,2,5,1,no,0,0x80,2,misc,0, RA_FIRINGTRAP,Firing Trap
-2254,3,6,2,0,0x42,2,5,1,no,0,0x80,2,misc,0, RA_ICEBOUNDTRAP,Icebound Trap
-
-//****
-// NC Mechanic
-2255,0,0,0,0,0,0,5,0,no,0,0,0,none,0, NC_MADOLICENCE,Mado License
-2256,11,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, NC_BOOSTKNUCKLE,Boost Knuckle
-2257,3,6,1,-1,0,0,3,1,no,0,0,0,weapon,0, NC_PILEBUNKER,Pile Bunker
-2258,13,6,1,-1,0,0,3,1,no,0,0,0,weapon,0, NC_VULCANARM,Vulcan Arm
-2259,5,6,1,3,0,2,3,1,no,0,0,5,weapon,0, NC_FLAMELAUNCHER,Flame Launcher
-2260,7,6,2,1,0x2,2:3:4,3,1,no,0,0,0,weapon,0, NC_COLDSLOWER,Cold Slower
-2261,7,6,2,-1,0x42,3:2:1,3,1,no,0,0,0,weapon,0, NC_ARMSCANNON,Arm Cannon
-2262,0,6,4,0,0x1,0,3,1,no,0,0,0,none,0, NC_ACCELERATION,Acceleration
-2263,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, NC_HOVERING,Hovering
-2264,0,6,4,0,0x1,0,1,1,no,0,0,0,none,7, NC_F_SIDESLIDE,Front-Side Slide
-2265,0,6,4,0,0x1,0,1,1,no,0,0,0,none,7, NC_B_SIDESLIDE,Back-Side Slide
-2266,0,0,0,0,0,0,4,0,no,0,0,0,none,0, NC_MAINFRAME,Mainframe Restructure // Check me. Part of the code notes translated to "The amount of fuel have".
-2267,0,6,4,-1,0x42,2:3:4,3,1,no,0,0,0,misc,5, NC_SELFDESTRUCTION,Self Destruction
-2268,0,6,4,0,0x1,0,4,1,yes,0,0,0,none,0, NC_SHAPESHIFT,Shape Shift
-2269,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, NC_EMERGENCYCOOL,Emergency Cool
-2270,0,6,4,0,0x3,7,1,1,yes,0,0,0,none,0, NC_INFRAREDSCAN,Infrared Scan
-2271,9,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, NC_ANALYZE,Analyze
-2272,0,6,4,0,0x3,1:2:3,3,1,yes,0,0,0,none,0, NC_MAGNETICFIELD,Magnetic Field
-2273,0,6,4,0,0x1,0,3,1,yes,0,0,0,none,0, NC_NEUTRALBARRIER,Neutral Barrier
-2274,0,6,4,0,0x1,0,3,1,yes,0,0,0,none,0, NC_STEALTHFIELD,Stealth Field
-2275,5,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, NC_REPAIR,Repair
-2276,0,0,0,0,0,0,10,0,no,0,0,0,none,0, NC_TRAININGAXE,Axe Training
-2277,0,0,0,0,0,0,5,0,no,0,0,0,none,0, NC_RESEARCHFE,Research Fire/Earth
-2278,4:5:6:7:8,6,1,-1,0,0,5,1,no,0,0,0,weapon,2:3:4:5:6, NC_AXEBOOMERANG,Axe Boomerang
-2279,1,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, NC_POWERSWING,Power Swing
-2280,0,8,4,-1,0x2,2:2:3:3:3,5,-6,no,0,0,0,weapon,0, NC_AXETORNADO,Axe Tornado // Check me. Takes 20 * Skill LV amount of HP each use.
-2281,2,6,2,0,0x1,0,5,1,yes,0,0,2,none,0, NC_SILVERSNIPER,FAW - Silver Sniper
-2282,2,6,2,0,0x1,0,5,1,yes,0,0,2,none,0, NC_MAGICDECOY,FAW - Magic Decoy //CHECK FIX ME!!!! Wind and Earth stones spawning opposite decoys.
-2283,2,6,1,0,0x1,0,1,1,no,0,0,0,none,0, NC_DISJOINT,FAW Removal
-
-//****
-// SC Shadow Chaser
-2284,1,6,1,-1,0x2,1,5,1,no,0,0,0,weapon,0, SC_FATALMENACE,Fatal Menace
-2285,0,6,4,0,0x1,0,10,1,no,0,0,0,none,0, SC_REPRODUCE,Reproduce
-2286,0,6,4,0,0x1,0,10,1,yes,0,0,0,none,0, SC_AUTOSHADOWSPELL,Auto Shadow Spell
-2287,5,6,1,0,0x1,0,5,1,no,0,0,0,none,0, SC_SHADOWFORM,Shadow Form
-2288,7:7:7:9:9:9:9:11:11:11,8,1,-1,0,0,10,-3,yes,0,0,0,weapon,3, SC_TRIANGLESHOT,Triangle Shot
-2289,0,6,4,0,0x3,2,5,1,no,0,0,0,none,0, SC_BODYPAINT,Body Painting
-2290,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, SC_INVISIBILITY,Invisibility
-2291,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SC_DEADLYINFECT,Deadly Infect
-2292,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_ENERVATION,Masquerade - Enervation
-2293,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_GROOMY,Masquerade - Gloomy
-2294,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_IGNORANCE,Masquerade - Ignorance
-2295,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_LAZINESS,Masquerade - Laziness
-2296,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_UNLUCKY,Masquerade - Unlucky
-2297,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_WEAKNESS,Masquerade - Weakness
-2298,3,6,1,0,0x1,0,5,1,yes,0,0,0,weapon,0, SC_STRIPACCESSARY,Strip Accessory //CHECK Is weapon attack type needed?
-2299,7,6,2,0,0x1,0,3,1,yes,0,0,3,none,0, SC_MANHOLE,Man Hole
-2300,7,6,2,0,0x1,0,3,1,yes,0,0,1,none,0, SC_DIMENSIONDOOR,Dimension Door
-2301,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0, SC_CHAOSPANIC,Chaos Panic
-2302,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0, SC_MAELSTROM,Maelstrom
-2303,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0, SC_BLOODYLUST,Bloody Lust
-2304,0,6,4,-1,0,0,3,1,no,0,0,0,weapon,0, SC_FEINTBOMB,Feint Bomb
-
-//****
-// LG Royal Guard
-2307,11,8,1,-1,0,2,5,1,no,0,0,10,weapon,0, LG_CANNONSPEAR,Cannon Spear
-2308,7,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, LG_BANISHINGPOINT,Banishing Point
-2309,0,6,4,0,0x3,2,3,1,no,0,0,0,none,0, LG_TRAMPLE,Trample
-2310,1,6,1,0,0,0,5,1,no,0,0,0,weapon,0, LG_SHIELDPRESS,Shield Press
-2311,0,6,4,0,0x3,3,5,1,no,0,0,0,none,0, LG_REFLECTDAMAGE,Reflect Damage
-2312,5,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, LG_PINPOINTATTACK,Pinpoint Attack
-2313,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_FORCEOFVANGUARD,Force of Vanguard
-2314,1,6,1,-1,0,0,1,1,no,0,0,0,weapon,0, LG_RAGEBURST,Rage Burst
-2315,0,6,4,0,0x2,3,3,1,yes,0,0,0,none,2, LG_SHIELDSPELL,Shield Spell
-2316,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_EXEEDBREAK,Exceed Break
-2317,1,6,2,-1,0x2,5,5,1,yes,0,0,0,none,3:4:5:6:7, LG_OVERBRAND,Over Brand //CHECK I know the splash is needed somehow for the strange AoE it gives.
-2318,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_PRESTIGE,Prestige
-2319,0,6,4,0,0x1,3,5,1,no,0,0,0,weapon,0, LG_BANDING,Banding //CHECK Splash isnt needed right? Banding has its own UNIT ID.
-2320,0,6,4,-1,0x2,3,5,1,yes,0,0,0,weapon,0, LG_MOONSLASHER,Moon Slasher
-2321,1,8,2,6,0x2,5,5,-7,yes,0,0,0,weapon,0, LG_RAYOFGENESIS,Ray of Genesis
-2322,0,6,16,0,0x3,1,5,1,yes,0,0,0,none,0, LG_PIETY,Piety
-2323,0,8,4,2,0x2,1:1:2:2:3,5,-5,yes,0,0,0,weapon,0, LG_EARTHDRIVE,Earth Drive
-2324,3,8,1,-1,0,0,5,3,yes,0,0,0,weapon,0, LG_HESPERUSLIT,Hesperus Lit
-2325,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_INSPIRATION,Inspiration
-
-//****
-// SR Sura
-2326,-2,8,1,-1,0,0,10,2,no,0,0,0,weapon,0, SR_DRAGONCOMBO,Dragon Combo //CHECK Is this 2 regular hits or sub hits? Yes its 2 sub hits.
-2327,0,8,4,-1,0x2,2,5,-3,no,0,0,0,weapon,3, SR_SKYNETBLOW,Sky Net Blow //CHECK Video shows 3 hits. Its sub hits right? Data check shows no sub, one source shows 3 hits, another shows 5.
-2328,0,6,4,-1,0x2,1:2:3:4:5,5,1,no,0,0,0,weapon,0, SR_EARTHSHAKER,Earth Shaker //CHECK Must add a check in battle.c to triple damage if hitting a hidden target.
-2329,-2,8,4,-1,0,0,5,-2,no,0,0x200,0,weapon,0, SR_FALLENEMPIRE,Fallen Empire //CHECK Video shows 2 hits. Is it sub hits? Yes its divided between 2 hits.
-2330,-2,6,1,-1,0x42,1:1:1:1:1:2:2:2:2:2,10,1,yes,0,0,0,weapon,0, SR_TIGERCANNON,Tiger Cannon //CHECK Need to fix to be enemy targeted and also combo after Fallen Empire.
-2331,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SR_HELLGATE,Hell Gate
-2332,5,6,4,-1,0x2,3,5,1,no,0,0,0,weapon,0, SR_RAMPAGEBLASTER,Rampage Blaster
-2333,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SR_CRESCENTELBOW,Crescent Elbow //CHECK Check the autospell ID.
-2334,0,6,4,0,0x3,1:1:2:2:3,5,1,no,0,0,0,none,0, SR_CURSEDCIRCLE,Cursed Circle //CHECK Code shows it takes up to 5% of your HP upon use?
-2335,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SR_LIGHTNINGWALK,Lightning Walk
-2336,7:8:9:10:11,6,1,-1,0,0,5,1,no,0,0,0,weapon,2:3:4:5:6, SR_KNUCKLEARROW,Knuckle Arrow
-2337,0,6,4,-1,0x2,2,1,1,yes,0,0,0,weapon,0, SR_WINDMILL,Windmill
-2338,0,6,4,0,0x1,0,10,1,no,0,0,0,none,0, SR_RAISINGDRAGON,Raising Dragon
-2339,0,0,0,0,0,0,5,1,no,0,0,0,none,0, SR_GENTLETOUCH,Gentle Touch
-2340,0,6,4,0,0x3,2,1,1,no,0,0,0,none,0, SR_ASSIMILATEPOWER,Assimilate Power
-2341,3,6,16,0,0x1,0,1,1,yes,0,0x200,0,none,0, SR_POWERVELOCITY,Power Velocity
-2342,1,6,1,-1,0x20,0,5,1,no,0,0,0,weapon,3, SR_CRESCENTELBOW_AUTOSPELL,Crescent Elbow Autospell //CHECK Does this ignore defense?
-2343,1:2:3:3:4:4:5:5:6:7,8,1,0,0,0,10,-7,yes,0,0,0,weapon,0, SR_GATEOFHELL,Gate of Hell //CHECK Need to fix to be enemy targeted and also combo after Fallen Empire
-2344,2,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, SR_GENTLETOUCH_QUIET,Gentle Touch - Quiet
-2345,2,6,16,0,0x1,0,5,1,no,0,0,0,magic,0, SR_GENTLETOUCH_CURE,Gentle Touch - Cure //CHECK Its a healing skill. Guessing it has to be magic type? Healing isnt working.
-2346,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, SR_GENTLETOUCH_ENERGYGAIN,Gentle Touch - Energy Gain
-2347,2,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, SR_GENTLETOUCH_CHANGE,Gentle Touch - Change
-2348,2,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, SR_GENTLETOUCH_REVITALIZE,Gentle Touch - Revitalize
-//More from Sura but not following ID order
-2517,0,6,4,-1,0x2,3:4:5:6:7,5,1,no,0,0,0,weapon,0, SR_HOWLINGOFLION,Howling of Lion
-2518,11,6,2,-1,0x2,2:2:3:3:4,5,1,no,0,0,0,weapon,0, SR_RIDEINLIGHTNING,Ride In Lightening
-
-//****
-// WA Wanderer
-2350,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, WA_SWING_DANCE,Swing Dance
-2351,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, WA_SYMPHONY_OF_LOVER,Symphony of Lovers
-2352,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, WA_MOONLIT_SERENADE,Moonlit Serenade
-
-//****
-// MI Minstrel
-2381,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, MI_RUSH_WINDMILL,Windmill Rush Attack
-2382,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, MI_ECHOSONG,Echo Song
-2383,9,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, MI_HARMONIZE,Harmonize
-
-//****
-// WM Wanderer/Minstrel
-2412,0,0,0,0,0,0,10,0,no,0,0,0,none,0, WM_LESSON,Lesson
-2413,9,8,1,-1,0,0,5,-2:-2:-3:-3:-4,yes,0,0,0,magic,0, WM_METALICSOUND,Metallic Sound
-2414,9,6,2,-1,0x3,1,5,1,yes,0,0x80,3,none,0, WM_REVERBERATION,Reverberation
-2415,0,6,1,-1,0x6,1,5,1,no,0,0,0,weapon,0, WM_REVERBERATION_MELEE,Reverberation Melee
-2416,0,6,1,0,0x6,1,5,1,no,0,0,0,magic,0, WM_REVERBERATION_MAGIC,Reverberation Magic
-2417,11,6,2,0,0x3,5,1,1,no,0,0,0,none,0, WM_DOMINION_IMPULSE,Dominion Impulse
-2418,9,6,2,-1,0x1,0,5,1,yes,0,0,0,none,0, WM_SEVERE_RAINSTORM,Severe Rainstorm
-2419,9,6,2,0,0x3,1,5,1,yes,0,0x80,5,none,0, WM_POEMOFNETHERWORLD,Poem of The Netherworld //CHECK May need to recode too.
-2420,0,6,4,0,0x2,2:3:4:5:6,5,1,yes,0,0,0,none,0, WM_VOICEOFSIREN,Voice of Siren
-2421,7,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, WM_DEADHILLHERE,Valley of Death
-2422,7,6,2,0,0x3,5:6:7:8:9,5,1,yes,0,0,0,none,0, WM_LULLABY_DEEPSLEEP,Deep Sleep Lullaby
-2423,0,6,4,0,0x3,3:4:5:6:7,5,1,yes,0,0,0,none,0, WM_SIRCLEOFNATURE,Circle of Nature's Sound
-2424,9,6,4,0,0x1,0,5,1,yes,0,0,0,magic,0, WM_RANDOMIZESPELL,Improvised Song
-2425,9,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, WM_GLOOMYDAY,Gloomy Day
-2426,9,6,2,0,0x2,2:3:3:4:4,5,1,yes,0,0x4000,0,weapon,0, WM_GREAT_ECHO,Great Echo
-2427,0,6,4,0,0x3,5:6:7:8:9,5,1,yes,0,0x4000,0,none,0, WM_SONG_OF_MANA,Song of Mana
-2428,0,6,4,0,0x3,5:6:7:8:9,5,1,yes,0,0x4000,0,none,0, WM_DANCE_WITH_WUG,Dance With A Warg
-2429,9,6,1,0,0x2,2:2:3:3:4,5,1,yes,0,0x4000,0,weapon,0, WM_SOUND_OF_DESTRUCTION,Sound of Destruction //CHECK Source shows its magic attack. Need to confirm before changing.
-2430,0,6,4,0,0x3,3:4:5:6:7,5,1,yes,0,0x4000,0,none,0, WM_SATURDAY_NIGHT_FEVER,Saturday Night Fever
-2431,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,magic,0, WM_LERADS_DEW,Lerad's Dew
-2432,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,none,0, WM_MELODYOFSINK,Melody of Sink
-2433,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,none,0, WM_BEYOND_OF_WARCRY,Warcry of Beyond
-2434,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,none,0, WM_UNLIMITED_HUMMING_VOICE,Unlimited Humming Voice
-2516,11,6,1,-1,0x2,5,5,1,no,0,0,0,weapon,0, WM_SEVERE_RAINSTORM_MELEE,Severe Rainstorm Melee
-
-//****
-// SO Sorcerer
-2443,0,6,4,3,0,0,5,1,yes,0,0,8:10:12:14:16,magic,0, SO_FIREWALK,Fire Walk //CHECK Video and data shows each cell only hits once.
-2444,0,6,4,4,0,0,5,1,yes,0,0,8:10:12:14:16,magic,0, SO_ELECTRICWALK,Electric Walk
-2445,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SO_SPELLFIST,Spell Fist
-2446,9,6,2,2,0,0,5,-3,yes,0,0,0,magic,0, SO_EARTHGRAVE,Earth Grave
-2447,9,6,2,1,0,0,5,-5,yes,0,0,0,magic,0, SO_DIAMONDDUST,Diamond Dust
-2448,9,6,1,5,0x2,1:1:1:1:2,5,1,yes,0,0,0,magic,0, SO_POISON_BUSTER,Poison Buster
-2449,9,6,2,0,0,0,5,1,yes,0,0,0,magic,0, SO_PSYCHIC_WAVE,Psychic Wave
-2450,9,6,2,5,0,0,5,1,yes,0,0,0,magic,0, SO_CLOUD_KILL,Cloud Kill
-2451,9,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, SO_STRIKING,Striking //CHECK Data shows a % for increased successful refine rate. Is this true?
-2452,9,6,2,3,0x1,0,5,1,yes,0,0,0,magic,0, SO_WARMER,Warmer
-2453,9,6,2,0,0x1,0,5,1,yes,0,0,0,magic,0, SO_VACUUM_EXTREME,Vacuum Extreme
-2454,9,6,1,4,0x2,1:1:2:2:3,5,1,yes,0,0,0,magic,0, SO_VARETYR_SPEAR,Varetyr Spear
-2455,9,6,1,0,0x3,1:1:2:2:3,5,1,yes,0,0,0,magic,0, SO_ARRULLO,Arrullo
-2456,0,6,4,0,0x1,0,4,1,yes,0,0,0,none,0, SO_EL_CONTROL,Spirit Control
-2457,0,6,4,3,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_AGNI,Summon Fire Spirit Agni
-2458,0,6,4,1,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_AQUA,Summon Water Spirit Aqua
-2459,0,6,4,4,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_VENTUS,Summon Wind Spirit Ventus
-2460,0,6,4,2,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_TERA,Summon Earth Spirit Tera
-2461,5,6,1,0,0x1,0,1,1,no,0,0,0,none,0, SO_EL_ACTION,Elemental Action
-2462,0,6,4,0,0x1,0,2,1,yes,0,0,0,none,0, SO_EL_ANALYSIS,Four Spirit Analysis
-2463,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SO_EL_SYMPATHY,Spirit Sympathy
-2464,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, SO_EL_CURE,Spirit Recovery
-2465,9,6,2,3,0x1,0,3,1,yes,0,0,1,magic,0, SO_FIRE_INSIGNIA,Fire Insignia //CHECK All 4 insignia skills can be targeted and animations work
-2466,9,6,2,1,0x1,0,3,1,yes,0,0,1,magic,0, SO_WATER_INSIGNIA,Water Insignia // but its effects havent been coded yet.
-2467,9,6,2,4,0x1,0,3,1,yes,0,0,1,magic,0, SO_WIND_INSIGNIA,Wind Insignia
-2468,9,6,2,2,0x1,0,3,1,yes,0,0,1,magic,0, SO_EARTH_INSIGNIA,Earth Insignia
-
-//****
-// GN Genetic
-2474,0,0,0,0,0,0,5,0,no,0,0,0,none,0, GN_TRAINING_SWORD,Sword Training
-2475,0,0,0,0,0,0,5,0,no,0,0,0,none,0, GN_REMODELING_CART,Cart Remodeling
-2476,0,6,4,-1,0x2,2,5,1,no,0,0,0,weapon,2, GN_CART_TORNADO,Cart Tornado
-2477,7:8:9:10:11,6,1,-1,0x2,1:1:2:2:3,5,1,yes,0,0,0,weapon,0, GN_CARTCANNON,Cart Cannon
-2478,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, GN_CARTBOOST,Cart Boost
-2479,9,6,2,0,0,0,5,1,yes,0,0x80,5,misc,0, GN_THORNS_TRAP,Thorn Trap
-2480,11,6,1,0,0x1,0,5,1,yes,0,0,3,misc,0, GN_BLOOD_SUCKER,Blood Sucker //CHECK Data says its a magic attack. Hmmmm....
-2481,11,6,1,-1,0x2,1:2:3:4:5,5,1,yes,0,0,0,weapon,0, GN_SPORE_EXPLOSION,Spore Explosion //CHECK Data says its element is set to neutral. Need to confirm.
-2482,11,6,16,0,0,0,5,1,yes,0,0,1,weapon,2, GN_WALLOFTHORN,Wall of Thorns
-2483,11,6,2,0,0x3,4,10,1,yes,0,0x2000,0,weapon,0, GN_CRAZYWEED,Crazy Weed
-2484,0,6,2,2,0x2,3,10,1,no,0,0x2000,0,weapon,0, GN_CRAZYWEED_ATK,Crazy Weed Attack
-2485,9,6,2,3,0,0,5,1,yes,0,0,0,magic,0, GN_DEMONIC_FIRE,Demonic Fire
-2486,9,6,2,0,0,0,5,1,yes,0,0,0,none,0, GN_FIRE_EXPANSION,Fire Expansion //CHECK FIX ME!!!! Level 1 is reducing the damage. Should increase it by 50%
-2487,9,6,2,0,0,0,1,1,no,0,0,0,none,0, GN_FIRE_EXPANSION_SMOKE_POWDER,Fire Expansion Smoke Powder
-2488,9,6,2,0,0,0,1,1,no,0,0,0,none,0, GN_FIRE_EXPANSION_TEAR_GAS,Fire Expansion Tear Gas
-2489,11,6,1,0,0,0,10,1:2:3:4:5:6:7:8:9:10,no,0,0,0,weapon,0, GN_FIRE_EXPANSION_ACID,Fire Expansion Acid
-2490,9,6,2,0,0x3,1,5,1,yes,0,0x80,2:3:4:5:6,none,0, GN_HELLS_PLANT,Hell's Plant
-2491,0,6,1,0,0xC0,0,5,1,no,0,0,0,misc,0, GN_HELLS_PLANT_ATK,Hell's Plant Attack
-2492,0,6,4,0,0x3,6:7:8:9:10,5,1,yes,0,0,0,none,0, GN_MANDRAGORA,Howling of Mandragora
-2493,11,6,16,0,0x1,0,1,1,yes,0,0,0,none,0, GN_SLINGITEM,Sling Item
-2494,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, GN_CHANGEMATERIAL,Change Material
-2495,0,6,4,0,0x1,0,2,1,no,0,0,0,none,0, GN_MIX_COOKING,Mix Cooking
-2496,0,6,4,0,0x1,0,2,1,no,0,0,0,none,0, GN_MAKEBOMB,Create Bomb
-2497,0,6,4,0,0x1,0,10,1,no,0,0,0,none,0, GN_S_PHARMACY,Special Pharmacy
-2498,11,6,1,0,0,0,1,1,no,0,0,0,weapon,0, GN_SLINGITEM_RANGEMELEEATK,Sling Item Attack
-
-// Episode 13.3
-//2533,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, ALL_ODINS_RECALL,Odin's Recall
-2534,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, RETURN_TO_ELDICASTES,Return To Eldicastes
-2535,0,0,4,0,0x1,0,1,0,no,0,0x1,0,none,0, ALL_BUYING_STORE,Open Buying Store
-2536,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, ALL_GUARDIAN_RECALL,Guardian's Recall
-//2537,9,6,16,0,0x1,0,2,1,yes,0,0,0,magic,0, ALL_ODINS_POWER,Odin's Power
-//2538,0,0,0,0,0,0,??,0,no,0,0,0,none,0, BEER_BOTTLE_CAP,Beer Bottle Cap
-//2539,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_ASSASSINCROSS,Assassin Cross of Sunset 2
-//2540,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_DISSONANCE,Dissonance 2
-//2541,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_UGLYDANCE,Ugly Dance 2
-//2542,0,0,0,0,0,0,??,0,no,0,0,0,none,0, ALL_TETANY,Tetany
-//2543,0,0,0,0,0,0,??,0,no,0,0,0,none,0, ALL_RAY_OF_PROTECTION,Ray of Protection
-//2544,0,0,0,0,0,0,??,0,no,0,0,0,none,0, MC_CARTDECORATE,Decorate Cart
-
-//****
-// Kagerou & Oboro
-3001,0,6,4,0,0,0,1,1,no,0,0,0,none,0, KO_YAMIKUMO,Yamikumo
-3002,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, KO_RIGHT,Right Hand Mastery
-3003,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, KO_LEFT,Left Hand Mastery
-3004,3:4:5:6:7,8,1,-1,0,0,5,-2,no,0,0,0,weapon,0, KO_JYUMONJIKIRI,Cross Strike
-3005,2,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, KO_SETSUDAN,Soul Sever
-3006,7:8:9:10:11,6,2,0,0x2,2,5,0,no,0,0,0,weapon,0, KO_BAKURETSU,Bakuretsu Kunai
-3007,0,6,4,-1,0x42,4:4:4:4:5,5,0,no,0,0,0,misc,0, KO_HAPPOKUNAI,Happo Kunai
-3008,9,8,2,0,0x52,2,10,-10,no,0,0,0,misc,0, KO_MUCHANAGE,Mucha Nage
-3009,9:10:11:12:13,8,2,-1,0x2,3,5,2,no,0,0,0,weapon,0, KO_HUUMARANKA,Huuma Shuriken Ranka
-3010,3,6,4,0,0x43,0,5,1,no,0,0x80,0,misc,0, KO_MAKIBISHI,Makibishi
-3011,0,6,4,0,0x1,0,5,0,yes,0,0,0,none,0, KO_MEIKYOUSISUI,Meikyo Shisui
-3012,0,6,4,0,0x1,0,5,0,no,0,0,1,none,7, KO_ZANZOU,Zanzou
-3013,5,6,1,0,0x1,0,5,0,no,0,0,0,none,0, KO_KYOUGAKU,Kyougaku
-3014,5,6,1,0,0x1,0,5,0,no,0,0,0,none,0, KO_JYUSATSU,Jyusatsu
-3015,0,6,4,3,0x1,0,1,1,no,0,0,0,none,0, KO_KAHU_ENTEN,Kahu Enten
-3016,0,6,4,1,0x1,0,1,1,no,0,0,0,none,0, KO_HYOUHU_HUBUKI,Hyouhu Hubuki
-3017,0,6,4,4,0x1,0,1,1,no,0,0,0,none,0, KO_KAZEHU_SEIRAN,Kazehu Seiran
-3018,0,6,4,2,0x1,0,1,1,no,0,0,0,none,0, KO_DOHU_KOUKAI,Dohu Koukai
-3019,11,6,1,0,0,0,5,0,no,0,0,0,weapon,0, KO_KAIHOU,Technique Kaihou
-3020,7,6,2,0,0,0,1,3,yes,0,0,0,magic,0, KO_ZENKAI,Zenkai
-3021,5:6:7:8:9,6,16,0,0x1,0,5,1,no,0,0,0,none,0, KO_GENWAKU,Genwaku
-3022,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, KO_IZAYOI,Izayoi
-3023,0,6,4,0,0x3,2:3:4:5:6,5,0,no,0,0,0,none,0, KG_KAGEHUMI,Kagehumi
-3024,7,6,1,0,0x1,0,5,1,no,0,0,0,none,0, KG_KYOMU,Kyomu
-3025,7,6,16,0,0x1,0,5,1,no,0,0,0,none,0, KG_KAGEMUSYA,Kagemusha
-3026,7,6,16,0,0x1,0,5,1,no,0,0,0,none,0, OB_ZANGETSU,Zangetsu
-3027,7,6,16,0,0x1,0,5,1,no,0,0,0,none,0, OB_OBOROGENSOU,Oboro Gensou
-3028,1,6,4,0,0x2,3,1,1,no,0,0,0,weapon,0, OB_OBOROGENSOU_TRANSITION_ATK,
-3029,7,6,1,0,0x1,0,5,0,no,0,0,0,none,0, OB_AKAITSUKI,Akaitsuki
-
-8001,9,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, HLIF_HEAL,Healing Touch
-8002,0,6,4,0,0x3,-1,5,1,no,0,0,0,none,0, HLIF_AVOID,Avoid
-8003,0,0,0,0,0,1,5,0,no,0,0,0,none,0, HLIF_BRAIN,Brain Surgery
-8004,0,6,4,0,0x1,0,3,0,no,0,0,0,none,0, HLIF_CHANGE,Change
-8005,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HAMI_CASTLE,Castling
-8006,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HAMI_DEFENCE,Defense
-8007,0,0,0,0,0x1,0,5,0,no,0,0,0,none,0, HAMI_SKIN,Adamantium Skin
-8008,0,6,4,0,0x1,0,3,0,no,0,0,0,none,0, HAMI_BLOODLUST,Bloodlust
-8009,1,8,1,0,0,0,5,-1:-2:-2:-2:-3,no,0,0,0,weapon,0, HFLI_MOON,Moonlight
-8010,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HFLI_FLEET,Fleeting Move
-8011,0,6,4,0,0x1,0,5,0,yes,0,0,0,misc,0, HFLI_SPEED,Speed
-8012,1,6,1,0,0,0,3,0,no,0,0,0,none,0, HFLI_SBR44,S.B.R.44
-8013,9,6,1,0,0,0,5,1:2:3:4:5,no,0,0,0,magic,0, HVAN_CAPRICE,Caprice
-8014,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HVAN_CHAOTIC,Benediction of Chaos
-8015,0,0,0,0,0x1,0,5,0,no,0,0,0,none,0, HVAN_INSTRUCT,Instruct
-8016,4,6,4,-1,0xD2,4,3,1,no,0,0,0,misc,0, HVAN_EXPLOSION,Bio Explosion
-//
-8018,9,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_SUMMON_LEGION,Summon Legion
-8019,5,6,1,5,0,0,5,1,no,0,0,0,weapon,0, MH_NEEDLE_OF_PARALYZE,Needle of Paralyze
-8020,5,6,2,5,0,0,5,1,no,0,0,1,weapon,0, MH_POISON_MIST,Poison Mist
-8021,1,6,1,0,0x1,0,5,1,no,0,0,0,none,0, MH_PAIN_KILLER,Pain Killer
-8022,0,6,4,0,0,0x1,5,1,no,0,0,0,none,0, MH_LIGHT_OF_REGENE,Light of Regene
-8023,0,6,4,0,0,0x1,5,1,no,0,0,0,none,0, MH_OVERED_BOOST,Overed Boost
-8024,7,6,1,4:0:4:0:4,0,0,5,1,no,0,0,0,magic,0, MH_ERASER_CUTTER,Eraser Cutter
-8025,7,6,2,4:0:4:0:4,0,0,5,1,no,0,0,0,magic,0, MH_XENO_SLASHER,Xeno Slasher
-8026,5:5:7:7:9,6,16,0,0x1,0,5,1,no,0,0,0,magic,0, MH_SILENT_BREEZE,Silent Breeze
-8027,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, MH_STYLE_CHANGE,Style Change
-8028,1,8,1,0,0,0,5,1,no,0,0,0,weapon,0, MH_SONIC_CRAW,Sonic Claw
-8029,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_SILVERVEIN_RUSH,Silver Bain Rush
-8030,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_MIDNIGHT_FRENZY,Midnight Frenzy
-8031,5:6:7:8:9,6,1,0,0,0,5,1,no,0,0,0,weapon,3, MH_STAHL_HORN,Steel Horn
-8032,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_GOLDENE_FERSE,Golden Heel
-8033,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_STEINWAND,Stone Wall
-8034,9,6,1,6,0x2,1:1:1:1:2,5,1,no,0,0,0,magic,0, MH_HEILIGE_STANGE,Holy Pole
-8035,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_ANGRIFFS_MODUS,Attack Mode
-8036,3:4:5:6:7,6,1,0,0,0,5,1,no,0,0,0,weapon,0, MH_TINDER_BREAKER,Tinder Breaker
-8037,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_CBC,Continual Break Combo
-8038,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_EQC,Eternal Quick Combo
-8039,0,6,4,3,0x2,1:1:1:2:2,5,1,no,0,0,0,weapon,0, MH_MAGMA_FLOW,Magma Flow
-8040,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_GRANITIC_ARMOR,Granitic Armor
-8041,7,6,2,3,0x2,0,5,1,no,0,0,1,weapon,0, MH_LAVA_SLIDE,Lava Slide
-8042,0,6,4,3,0x1,0,5,1,no,0,0,0,none,0, MH_PYROCLASTIC,Pyroclastic
-8043,7,6,2,0,0x1,0,5,1,no,0,0,3,none,0, MH_VOLCANIC_ASH,Volcanic Ash
-
-// Mercenary Skill Place holders
-8201,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, MS_BASH,Bash
-8202,0,6,4,3,0x2,2,10,1,no,0,0,0,weapon,2, MS_MAGNUM,Magnum_Break
-8203,-2,6,1,-1,0x2,1,10,1,no,33,0,0,weapon,1, MS_BOWLINGBASH,Bowling_Bash
-8204,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, MS_PARRYING,Parry
-8205,0,6,4,0,0,0,10,1,no,0,0,0,weapon,0, MS_REFLECTSHIELD,Shield_Reflect
-8206,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, MS_BERSERK,Frenzy
-8207,-9,8,1,-1,0,0,10,2,no,0,0,0,weapon,0, MA_DOUBLE,Double_Strafe
-8208,-9,6,2,-1,0x2,2,10,1,no,0,0x2000,0,weapon,2, MA_SHOWER,Arrow_Shower
-8209,3,6,2,0,0x1,0,5,1,no,0,0x80,0,misc,6:7:8:9:10, MA_SKIDTRAP,Skid_Trap
-8210,3,6,2,2,0x40,0,5,1,no,0,0x80,0,misc,0, MA_LANDMINE,Land_Mine
-8211,3,6,2,0,0x3,2,5,1,no,0,0x80,0,misc,0, MA_SANDMAN,Sandman
-8212,3,6,2,1,0x42,1,5,1,no,0,0x80,0,weapon,0, MA_FREEZINGTRAP,Freezing_Trap
-8213,2,6,32,0,0x1,0,1,1,no,0,0,0,misc,0, MA_REMOVETRAP,Remove_Trap
-8214,-9,6,1,-1,0x2,0,1,1,no,0,0x1,0,weapon,6, MA_CHARGEARROW,Arrow_Repel
-8215,9,8,1,-1,0,2,5,1,yes,0,0,13,weapon,0, MA_SHARPSHOOTING,Focused_Arrow_Strike
-8216,-2,8,1,-1,0,0,10,3,no,0,0,0,weapon,0, ML_PIERCE,Pierce
-8217,-2,6,1,-1,0x1,0,10,1,no,33,0,0,weapon,3, ML_BRANDISH,Brandish_Spear
-8218,5,8,1,-1,0x20,0,5,5,no,0,0,0,weapon,0, ML_SPIRALPIERCE,Spiral_Pierce
-8219,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, ML_DEFENDER,Defending_Aura
-8220,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, ML_AUTOGUARD,Guard
-8221,7:8:9:10:11,6,16,0,0x1,0,5,1,yes,0,0x600,0,none,0, ML_DEVOTION,Sacrifice
-8222,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0, MER_MAGNIFICAT,Magnificat
-8223,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, MER_QUICKEN,Two-Hand_Quicken
-8224,0,6,4,3,0x3,3,1,1,yes,0,0,0,magic,0, MER_SIGHT,Sight
-8225,1,8,1,-1,0,0,5,3,no,0,0,0,weapon,0, MER_CRASH,Crash
-8226,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_REGAIN,Regain
-8227,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_TENDER,Tender
-8228,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_BENEDICTION,Benediction
-8229,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_RECUPERATE,Recuperate
-8230,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_MENTALCURE,Mental_Cure
-8231,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_COMPRESS,Compress
-8232,9,6,1,0,0x1,0,10,1,no,0,0,0,none,0, MER_PROVOKE,Provoke
-8233,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, MER_AUTOBERSERK,Berserk
-8234,9,6,1,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_DECAGI,Decrease_AGI
-8235,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, MER_SCAPEGOAT,Scapegoat
-8236,5,6,1,0,0x1,0,10,0,yes,0,0,0,magic,0, MER_LEXDIVINA,Lex_Divina
-8237,9,6,1,0,0x1,0,1,1,yes,0,0,0,magic,0, MER_ESTIMATION,Sense
-8238,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_KYRIE,Kyrie Eleison
-8239,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_BLESSING,Blessing
-8240,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_INCAGI,Increase Agility
-
-// Elemental Spirits Skills
-8401,0,6,4,3,0,0,1,1,no,0,0,0,weapon,2, EL_CIRCLE_OF_FIRE,Circle of Fire
-8402,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_FIRE_CLOAK,Fire Cloak
-8403,0,6,4,3,0,0,1,1,no,0,0,3,magic,2, EL_FIRE_MANTLE,Fire Mantle
-8404,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WATER_SCREEN,Water Screen
-8405,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WATER_DROP,Water Drop
-8406,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WATER_BARRIER,Water Barrier
-8407,0,6,4,0,0x1,0,1,1,no,0,0,0,none,5, EL_WIND_STEP,Wind Step
-8408,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WIND_CURTAIN,Wind Curtain
-8409,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_ZEPHYR,Zephyr
-8410,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_SOLID_SKIN,Solid Skin
-8411,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_STONE_SHIELD,Stone Shield
-8412,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_POWER_OF_GAIA,Power of Gaia
-8413,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_PYROTECHNIC,Pyrotechnic
-8414,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_HEATER,Heater
-8415,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_TROPIC,Tropic
-8416,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_AQUAPLAY,Aqua Play
-8417,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_COOLER,Cooler
-8418,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_CHILLY_AIR,Cool Air
-8419,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_GUST,Gust
-8420,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_BLAST,Blast
-8421,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WILD_STORM,Wild Storm
-8422,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_PETROLOGY,Petrology
-8423,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_CURSED_SOIL,Cursed Soil
-8424,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_UPHEAVAL,Upheaval
-8425,6,6,1,3,0,0,1,1,no,0,0,0,magic,0, EL_FIRE_ARROW,Fire Arrow
-8426,6,6,1,3,0,1,1,1,no,0,0,0,magic,0, EL_FIRE_BOMB,Fire Bomb
-8427,6,6,1,3,0,1,1,1,no,0,0,0,weapon,0, EL_FIRE_BOMB_ATK,Fire Bomb Attack
-8428,6,6,1,3,0,1,1,1,no,0,0,0,magic,0, EL_FIRE_WAVE,Fire Wave
-8429,6,6,1,3,0,1,1,1,no,0,0,0,weapon,0, EL_FIRE_WAVE_ATK,Fire Wave Attack
-8430,9,6,1,1,0,0,1,1,no,0,0,0,magic,0, EL_ICE_NEEDLE,Ice Needle
-8431,9,6,1,1,0,1,1,1,no,0,0,0,magic,0, EL_WATER_SCREW,Water Screw
-8432,9,6,1,1,0,1,1,1,no,0,0,0,weapon,0, EL_WATER_SCREW_ATK,Water Screw Attack
-8433,9,6,1,1,0,1,1,1,no,0,0,0,weapon,0, EL_TIDAL_WEAPON,Tidal Weapon
-8434,11,6,1,4,0,0,1,1,no,0,0,0,weapon,0, EL_WIND_SLASH,Wind Slasher
-8435,11,6,1,4,0,1,1,1,no,0,0,0,weapon,0, EL_HURRICANE,Hurricane Rage
-8436,7,6,1,4,0,0,1,1,no,0,0,0,magic,0, EL_HURRICANE_ATK,Hurricane Rage Attack
-8437,11,8,1,4,0,1,1,-3,no,0,0,0,weapon,0, EL_TYPOON_MIS,Typhoon Missile
-8438,11,8,1,4,0,1,1,-3,no,0,0,0,magic,0, EL_TYPOON_MIS_ATK,Typhoon Missile Attack
-8439,5,6,1,2,0,0,1,1,no,0,0,0,weapon,0, EL_STONE_HAMMER,Stone Hammer
-8440,3,6,1,2,0,1,1,1,no,0,0,0,weapon,0, EL_ROCK_CRUSHER,Rock Launcher
-8441,5,6,1,2,0,1,1,1,no,0,0,0,magic,0, EL_ROCK_CRUSHER_ATK,Rock Launcher Attack
-8442,9,6,1,2,0,1,1,-5,no,0,0,0,weapon,0, EL_STONE_RAIN,Stone Rain
-
-10000,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_APPROVAL,Official Guild Approval
-10001,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_KAFRACONTRACT,Kafra Contract
-10002,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_GUARDRESEARCH,Guardian Research
-10003,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_GUARDUP,Strengthen Guardians
-10004,0,0,0,0,0,0,10,0,no,0,0x10,0,none,0, GD_EXTENSION,Guild Extension
-10005,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_GLORYGUILD,Guild's Glory
-10006,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_LEADERSHIP,Great Leadership
-10007,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_GLORYWOUNDS,Glorious Wounds
-10008,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_SOULCOLD,Cold Heart
-10009,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_HAWKEYES,Sharp Gaze
-10010,0,0,4,0,0x3,15,1,0,yes,0,0x10,0,none,0, GD_BATTLEORDER,Battle Orders
-10011,0,0,4,0,0x3,15,3,0,yes,0,0x10,0,none,0, GD_REGENERATION,Regeneration
-10012,0,0,4,0,0x3,15,1,0,yes,0,0x10,0,none,0, GD_RESTORE,Restoration
-10013,0,0,4,0,0x3,0,1,0,yes,0,0x10,0,none,0, GD_EMERGENCYCALL,Urgent Call
-10014,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_DEVELOPMENT,Permanent Development
-//10015,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_ITEMEMERGENCYCALL,Unknown Skill
+//id,range,hit,inf,element,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count,name,description
+// 01 ID
+// 02 range (combo skills do not check for range when used,
+// if range is < 5, the skill is considered melee-range)
+// 03 hit (8- repeated hitting, 6- single-hit)
+// 04 inf (0- passive, 1- enemy, 2- place, 4- self, 16- friend, 32- trap)
+// 05 element (0 - neutral, 1 - water, 2 - earth, 3 - fire, 4 - wind, 5 - poison,
+// 6 - holy, 7 - dark, 8 - ghost, 9 - undead, -1 - use weapon element
+// -2 - use endowed element, -3 - use random element.)
+// 06 nk (skill damage properties):
+// 0x01 - No damage skill
+// 0x02 - Has splash area
+// 0x04 - Damage should be split among targets
+// 0x08 - Skill ignores caster's % damage cards (misc type always ignores)
+// 0x10 - Skill ignores elemental adjustments
+// 0x20 - Skill ignores target's defense (misc type always ignores)
+// 0x40 - Skill ignores target's flee (magic type always ignores)
+// 0x80 - Skill ignores target's def cards
+// 07 splash/effect range (-1 for screen-wide)
+// 08 MaxLv
+// 09 Number of hits (when positive, damage is increased by hits,
+// negative values just show number of hits without increasing total damage)
+// 10 Cast interrupted when hit?
+// 11 defense-reduction rate during cast.
+// 12 inf2 (skill information 2):
+// 0x0001- quest skill
+// 0x0002- npc skill
+// 0x0004- wedding skill
+// 0x0008- spirit skill
+// 0x0010- guild skill
+// 0x0020- song/dance
+// 0x0040- ensemble skill
+// 0x0080- trap
+// 0x0100- skill that damages/targets yourself
+// 0x0200- cannot be casted on self (if inf = 4, auto-select target skill)
+// 0x0400- usable only on party-members (and enemies if skill is offensive)
+// 0x0800- usable only on guild-mates (and enemies if skill is offensive)
+// 0x1000- disable usage on enemies (for non-offensive skills).
+// 0x2000- skill ignores land protector (e.g. arrow shower)
+// 0x4000- chorus skill
+// 13 maxcount: max amount of skill instances to place on the ground when
+// player_land_skill_limit/monster_land_skill_limit is enabled. For skills
+// that attack using a path, this is the path length to be used.
+// 14 attack type (none, weapon, magic, misc)
+// 15 Blowcount (amount of tiles skill knockbacks)
+// 16 Name
+// 17 Description
+1,0,0,0,0,0,0,9,0,no,0,0,0,none,0, NV_BASIC,Basic Skill
+2,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, SM_SWORD,Sword Mastery
+3,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, SM_TWOHAND,Two-Handed Sword Mastery
+4,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SM_RECOVERY,Increase HP Recovery
+5,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, SM_BASH,Bash
+6,9,6,1,0,1,0,10,1,no,0,0,0,none,0, SM_PROVOKE,Provoke
+7,0,6,4,3,0x2,2,10,1,no,0,0,0,weapon,2, SM_MAGNUM,Magnum Break
+8,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, SM_ENDURE,Endure
+9,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MG_SRECOVERY,Increase SP Recovery
+10,0,6,4,3,0x3,3,1,1,yes,0,0,0,magic,0, MG_SIGHT,Sight
+11,9,6,1,8,0x6,1,10,1,yes,0,0,0,magic,0, MG_NAPALMBEAT,Napalm Beat
+12,9,8,2,8,0x1,0,10,1,yes,0,0,0,magic,0, MG_SAFETYWALL,Safety Wall
+13,9,8,1,8,0,0,10,1:1:2:2:3:3:4:4:5:5,yes,0,0,0,magic,0, MG_SOULSTRIKE,Soul Strike
+14,9,8,1,1,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_COLDBOLT,Cold Bolt
+15,9,6,1,1,0,0,10,1,yes,0,0,0,magic,0, MG_FROSTDIVER,Frost Diver
+16,2,6,1,2,0x1,0,10,1,yes,0,0,0,magic,0, MG_STONECURSE,Stone Curse
+17,9,6,1,3,0x2,2,10,1,yes,0,0,0,magic,0, MG_FIREBALL,Fire Ball
+18,9,6,2,3,0,0,10,1,yes,0,0,3,magic,2, MG_FIREWALL,Fire Wall
+19,9,8,1,3,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_FIREBOLT,Fire Bolt
+20,9,8,1,4,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_LIGHTNINGBOLT,Lightning Bolt
+21,9,8,2,4,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_THUNDERSTORM,Thunderstorm
+22,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AL_DP,Divine Protection
+23,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AL_DEMONBANE,Demon Bane
+24,0,6,4,6,0x3,2,1,1,yes,0,0,0,magic,0, AL_RUWACH,Ruwach
+25,9,6,2,0,0x1,0,1,1,yes,0,0,0,magic,0, AL_PNEUMA,Pneuma
+26,0,6,4,0,0x1,0,2,1,yes,0,0,0,magic,0, AL_TELEPORT,Teleport
+27,9,6,2,0,0x1,0,4,1,yes,0,0,3,magic,0, AL_WARP,Warp Portal
+28,9,6,16,6,0x21,0,10,1,yes,0,0,0,magic,0, AL_HEAL,Heal
+29,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, AL_INCAGI,Increase AGI
+30,9,6,1,0,0x1,0,10,1,yes,0,0,0,magic,0, AL_DECAGI,Decrease AGI
+31,0,6,4,0,0x1,0,1,1,yes,0,0,0,magic,0, AL_HOLYWATER,Aqua Benedicta
+32,0,6,4,0,0x3,15,10,1,yes,0,0,0,magic,0, AL_CRUCIS,Signum Crucis
+33,0,6,4,0,0x3,-1,10,1,yes,0,0,0,magic,0, AL_ANGELUS,Angelus
+34,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, AL_BLESSING,Blessing
+35,9,6,16,0,0x1,0,1,1,yes,0,0,0,magic,0, AL_CURE,Cure
+36,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_INCCARRY,Enlarge Weight Limit
+37,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_DISCOUNT,Discount
+38,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_OVERCHARGE,Overcharge
+39,1,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_PUSHCART,Pushcart
+40,1,6,4,0,0x1,0,1,1,no,0,0,0,none,0, MC_IDENTIFY,Item Appraisal
+41,1,6,4,0,0x1,0,10,1,no,0,0,0,none,0, MC_VENDING,Vending
+42,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, MC_MAMMONITE,Mammonite
+43,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AC_OWL,Owl's Eye
+44,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AC_VULTURE,Vulture's Eye
+45,0,6,4,0,0x3,3,10,1,no,0,0,0,weapon,0, AC_CONCENTRATION,Improve Concentration
+46,-9,8,1,-1,0,0,10,2,no,0,0,0,weapon,0, AC_DOUBLE,Double Strafe
+47,-9,6,2,-1,0x2,2,10,1,no,0,0x2000,0,weapon,2, AC_SHOWER,Arrow Shower
+48,-1,8,0,-1,0,0,10,2,no,0,0,0,weapon,0, TF_DOUBLE,Double Attack
+49,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, TF_MISS,Improve Dodge
+50,1,6,1,0,1,0,10,1,no,0,0,0,weapon,0, TF_STEAL,Steal
+51,1,6,4,0,1,0,10,1,no,0,0,0,none,0, TF_HIDING,Hiding
+52,-2,6,1,5,0,0,10,1,no,0,0,0,weapon,0, TF_POISON,Envenom
+53,9,6,16,5,0x1,0,1,1,no,0,0,0,weapon,0, TF_DETOXIFY,Detoxify
+54,9,6,16,6,0x1,0,4,1,yes,0,0,0,magic,0, ALL_RESURRECTION,Resurrection
+55,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, KN_SPEARMASTERY,Spear Mastery
+56,-2,8,1,-1,0,0,10,3,no,0,0,0,weapon,0, KN_PIERCE,Pierce
+57,-2,6,1,-1,0x1,0,10,1,no,33,0,0,weapon,3, KN_BRANDISHSPEAR,Brandish Spear
+58,-4,6,1,-1,0x2,0,10,1,no,0,0,0,weapon,6, KN_SPEARSTAB,Spear Stab
+59,3:5:7:9:11,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, KN_SPEARBOOMERANG,Spear Boomerang
+60,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, KN_TWOHANDQUICKEN,Twohand Quicken
+61,0,6,4,-1,0x20,0,5,1,no,0,0,0,weapon,0, KN_AUTOCOUNTER,Counter Attack
+62,-2,6,1,-1,0x2,1,10,1,no,33,0,0,weapon,1, KN_BOWLINGBASH,Bowling Bash
+63,0,0,0,0,0,0,1,0,no,0,0,0,weapon,0, KN_RIDING,Peco Peco Riding
+64,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, KN_CAVALIERMASTERY,Cavalier Mastery
+65,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, PR_MACEMASTERY,Mace Mastery
+66,9,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, PR_IMPOSITIO,Impositio Manus
+67,9,6,16,0,0x1,0,3,1,yes,0,0x200,0,magic,0, PR_SUFFRAGIUM,Suffragium
+68,9,6,16,6,0x31,0,5,1,yes,0,0,0,magic,0, PR_ASPERSIO,Aspersio
+69,9,6,2,0,0x23,1,5,1,yes,0,0x40,0,magic,0, PR_BENEDICTIO,B.S. Sacramenti
+70,9,6,2,6,0x21,0,10,1,yes,0,0,0,magic,1, PR_SANCTUARY,Sanctuary
+71,9,6,16,0,0x1,0,4,1,yes,0,0,0,magic,0, PR_SLOWPOISON,Slow Poison
+72,9,6,16,0,0x1,0,1,1,yes,0,0,0,magic,0, PR_STRECOVERY,Status Recovery
+73,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, PR_KYRIE,Kyrie Eleison
+74,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0, PR_MAGNIFICAT,Magnificat
+75,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0, PR_GLORIA,Gloria
+76,5,6,1,0,0x1,0,10,0,yes,0,0,0,magic,0, PR_LEXDIVINA,Lex Divina
+77,5,6,1,6,0x28,0,10,1,yes,0,0,0,magic,0, PR_TURNUNDEAD,Turn Undead
+78,9,6,1,0,0x1,0,1,0,yes,0,0,0,magic,0, PR_LEXAETERNA,Lex Aeterna
+79,9,8,2,6,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, PR_MAGNUS,Magnus Exorcismus
+80,9,8,2,3,0x20,1:1:1:1:1:2:2:2:2:2:2,10,3:4:5:6:7:8:9:10:11:12:12,yes,0,0x80,5,magic,0, WZ_FIREPILLAR,Fire Pillar
+81,0,6,4,3,0,3,10,1,yes,0,0,0,magic,5, WZ_SIGHTRASHER,Sightrasher
+83,9,8,2,3,0,3:3:3:3:3:3:3:3:3:3:14,10,1:1:2:2:3:3:4:4:5:5:15,yes,0,0,0,magic,0, WZ_METEOR,Meteor Storm
+84,9,8,1,4,0,0,10,3:4:5:6:7:8:9:10:11:12,yes,0,0,0,magic,2:3:3:4:4:5:5:6:6:7, WZ_JUPITEL,Jupitel Thunder
+85,9,8,2,4,0,0,10,-10,yes,0,0,0,magic,0, WZ_VERMILION,Lord of Vermilion
+86,9,8,1,1,0,0,5,1,yes,0,0,0,magic,0, WZ_WATERBALL,Water Ball
+87,9,6,2,1,0x1,0,10,1,yes,0,0,0,magic,0, WZ_ICEWALL,Ice Wall
+88,0,6,4,1,0x2,2,10,1,yes,0,0,0,magic,0, WZ_FROSTNOVA,Frost Nova
+89,9,6,2,1,0,0,10,1,yes,0,0,0,magic,2, WZ_STORMGUST,Storm Gust
+90,9,8,1,2,0,0,5,1:2:3:4:5,yes,0,0,0,magic,0, WZ_EARTHSPIKE,Earth Spike
+91,9,8,2,2,0,0,5,1:2:3:4:5,yes,0,0,0,magic,0, WZ_HEAVENDRIVE,Heaven's Drive
+92,9,6,2,2,0x1,0,5,1,yes,0,0,3,magic,0, WZ_QUAGMIRE,Quagmire
+93,9,6,1,0,0x1,0,1,1,yes,0,0,0,magic,0, WZ_ESTIMATION,Sense
+94,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_IRON,Iron Tempering
+95,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_STEEL,Steel Tempering
+96,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_ENCHANTEDSTONE,Enchanted Stone Craft
+97,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_ORIDEOCON,Oridecon Research
+98,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_DAGGER,Smith Dagger
+99,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_SWORD,Smith Sword
+100,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_TWOHANDSWORD,Smith Two-handed Sword
+101,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_AXE,Smith Axe
+102,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_MACE,Smith Mace
+103,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_KNUCKLE,Smith Knucklebrace
+104,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_SPEAR,Smith Spear
+105,0,0,0,0,0,0,1,0,no,0,0,0,weapon,0, BS_HILTBINDING,Hilt Binding
+106,0,0,0,0,0,0,1,0,no,0,0,0,weapon,0, BS_FINDINGORE,Ore Discovery
+107,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, BS_WEAPONRESEARCH,Weaponry Research
+108,2,6,16,0,0x1,0,1,1,yes,0,0,0,weapon,0, BS_REPAIRWEAPON,Weapon Repair
+109,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_SKINTEMPER,Skin Tempering
+110,1,6,2,0,0x3,2:2:2:2:2:14,5,1,no,0,0,0,weapon,0, BS_HAMMERFALL,Hammer Fall
+111,0,6,4,0,0x3,-1,5,1,no,0,0,0,weapon,0, BS_ADRENALINE,Adrenaline Rush
+112,0,6,4,0,0x3,-1,5,1,no,0,0,0,weapon,0, BS_WEAPONPERFECT,Weapon Perfection
+113,0,6,4,0,0x3,-1,5,1,no,0,0,0,weapon,0, BS_OVERTHRUST,Power-Thrust
+114,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, BS_MAXIMIZE,Maximize Power
+115,3,6,2,0,0x1,0,5,1,no,0,0x80,0,misc,6:7:8:9:10, HT_SKIDTRAP,Skid Trap
+116,3,6,2,2,0x42,1,5,1,no,0,0x80,0,misc,0, HT_LANDMINE,Land Mine
+117,3,6,2,0,0x1,0,5,1,no,0,0x80,0,misc,0, HT_ANKLESNARE,Ankle Snare
+118,3,6,2,0,0x2,1,5,1,no,0,0x80,0,misc,0, HT_SHOCKWAVE,Shockwave Trap
+119,3,6,2,0,0x3,2,5,1,no,0,0x80,0,misc,0, HT_SANDMAN,Sandman
+120,3,6,2,0,0x3,1,5,1,no,0,0x80,0,misc,0, HT_FLASHER,Flasher
+121,3,6,2,1,0x42,1,5,1,no,0,0x80,0,weapon,0, HT_FREEZINGTRAP,Freezing Trap
+122,3,6,2,4,0x42,1,5,1,no,0,0x80,0,misc,0, HT_BLASTMINE,Blast Mine
+123,3,6,2,3,0x42,2,5,1,no,0,0x80,0,misc,0, HT_CLAYMORETRAP,Claymore Trap
+124,2,6,32,0,0x1,0,1,1,no,0,0,0,misc,0, HT_REMOVETRAP,Remove Trap
+125,3,6,2,0,0x1,0,1,1,no,0,0x80,0,misc,0, HT_TALKIEBOX,Talkie Box
+126,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, HT_BEASTBANE,Beast Bane
+127,0,0,0,0,0,0,1,0,no,0,0,0,misc,0, HT_FALCON,Falconry Mastery
+128,0,0,0,0,0,0,10,0,no,0,0,0,misc,0, HT_STEELCROW,Steel Crow
+129,5,8,1,0,0x42,1,5,1:2:3:4:5,yes,0,0,0,misc,0, HT_BLITZBEAT,Blitz Beat
+130,3:5:7:9,6,2,0,0x3,3,4,1,no,0,0,0,misc,0, HT_DETECTING,Detect
+131,4:5:6:7:8,6,32,0,0x1,0,5,1,no,0,0,0,misc,0, HT_SPRINGTRAP,Spring Trap
+132,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, AS_RIGHT,Righthand Mastery
+133,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, AS_LEFT,Lefthand Mastery
+134,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AS_KATAR,Katar Mastery
+135,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, AS_CLOAKING,Cloaking
+136,-1,8,1,-1,0,0,10,8,no,0,0,0,weapon,0, AS_SONICBLOW,Sonic Blow
+137,3:4:5:6:7,6,1,-1,0x2,1,5,1,no,0,0,0,weapon,0,AS_GRIMTOOTH,Grimtooth
+138,1,6,16,5,0x1,0,10,1,no,0,0x400,0,weapon,0, AS_ENCHANTPOISON,Enchant Poison
+139,0,6,4,0,0,0,10,1,no,0,0,0,weapon,0, AS_POISONREACT,Poison React
+140,2,6,2,5,0x1,0,10,1,no,0,0,0,weapon,0, AS_VENOMDUST,Venom Dust
+141,1,6,1,-1,0x51,2,10,1,yes,0,0,0,weapon,0, AS_SPLASHER,Venom Splasher
+142,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, NV_FIRSTAID,First Aid
+143,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, NV_TRICKDEAD,Play Dead
+144,0,0,0,0,0,0,1,0,no,0,0x1,0,none,0, SM_MOVINGRECOVERY,Moving HP-Recovery
+145,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0, SM_FATALBLOW,Fatal Blow
+146,0,6,4,0,0x1,0,1,1,no,0,0x1,0,weapon,0, SM_AUTOBERSERK,Auto Berserk
+147,0,0,4,0,0x1,0,1,0,no,0,0x1,0,weapon,0, AC_MAKINGARROW,Arrow Crafting
+148,-9,6,1,-1,0x2,0,1,1,no,0,0x1,0,weapon,6, AC_CHARGEARROW,Arrow Repel
+149,1,6,1,2,0,0,1,1,no,0,0x1,0,weapon,0, TF_SPRINKLESAND,Sand Attack
+150,0,6,4,0,0x1,0,1,1,no,0,0x1,0,weapon,5, TF_BACKSLIDING,Back Slide
+151,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, TF_PICKSTONE,Find Stone
+152,7,6,1,0,0x40,0,1,1,no,0,0x1,0,misc,0, TF_THROWSTONE,Stone Fling
+153,1,6,1,-1,0x2,1,1,1,no,0,0x1,0,weapon,2, MC_CARTREVOLUTION,Cart Revolution
+154,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, MC_CHANGECART,Change Cart
+155,0,6,4,0,0x1,0,1,1,no,0,0x1,0,weapon,0, MC_LOUD,Crazy Uproar
+156,9,6,1,6,0,0,1,1,yes,0,0x1,0,magic,0, AL_HOLYLIGHT,Holy Light
+157,0,6,4,0,0x1,0,1,1,yes,0,0x1,0,magic,0, MG_ENERGYCOAT,Energy Coat
+158,3,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_PIERCINGATT,Piercing Attack
+159,-1,6,1,-1,0x40,0,5,1,no,0,0x2,0,weapon,0, NPC_MENTALBREAKER,Spirit Destruction
+160,9,6,1,0,0,0,10,1,no,0,0x2,0,weapon,0, NPC_RANGEATTACK,Stand off attack
+161,0,0,4,0,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_ATTRICHANGE,Attribute Change
+162,0,0,4,1,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEWATER,Water Attribute Change
+163,0,0,4,2,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEGROUND,Earth Attribute Change
+164,0,0,4,3,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEFIRE,Fire Attribute Change
+165,0,0,4,4,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEWIND,Wind Attribute Change
+166,0,0,4,5,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEPOISON,Poison Attribute Change
+167,0,0,4,6,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEHOLY,Holy Attribute Change
+168,0,0,4,7,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEDARKNESS,Shadow Attribute Change
+169,0,0,4,8,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGETELEKINESIS,Ghost Attribute Change
+170,-9,6,1,-1,0x20,0,10,1,no,0,0x2,0,weapon,0, NPC_CRITICALSLASH,Defense disregard attack
+171,-9,8,1,-1,0,0,10,-2:-3:-4:-5:-6:-7:-8:-9:-10:-11,no,0,0x2,0,weapon,0, NPC_COMBOATTACK,Multi-stage Attack
+172,-9,6,1,-1,0x40,0,10,1,no,0,0x2,0,weapon,0, NPC_GUIDEDATTACK,Guided Attack
+173,5,6,4,3,0xE2,5,10,1,no,0,0x2,0,misc,3, NPC_SELFDESTRUCTION,Suicide bombing
+174,-9,6,1,-1,0x2,3,1,1,no,0,0x2,0,weapon,0, NPC_SPLASHATTACK,Splash attack
+175,0,0,4,0,0x41,0,10,1,no,0,0x2,0,misc,0, NPC_SUICIDE,Suicide
+176,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_POISON,Poison Attack
+177,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_BLINDATTACK,Blind Attack
+178,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_SILENCEATTACK,Silence Attack
+179,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_STUNATTACK,Stun Attack
+180,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_PETRIFYATTACK,Petrify Attack
+181,-9,6,1,7,0,0,5,1,no,0,0x2,0,weapon,0, NPC_CURSEATTACK,Curse Attack
+182,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_SLEEPATTACK,Sleep attack
+183,-9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_RANDOMATTACK,Random Attack
+184,-9,6,1,1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_WATERATTACK,Water Attribute Attack
+185,-9,6,1,2,0,0,10,1,no,0,0x2,0,weapon,0, NPC_GROUNDATTACK,Earth Attribute Attack
+186,-9,6,1,3,0,0,10,1,no,0,0x2,0,weapon,0, NPC_FIREATTACK,Fire Attribute Attack
+187,-9,6,1,4,0,0,10,1,no,0,0x2,0,weapon,0, NPC_WINDATTACK,Wind Attribute Attack
+188,-9,6,1,5,0,0,10,1,no,0,0x2,0,weapon,0, NPC_POISONATTACK,Poison Attribute Attack
+189,-9,6,1,6,0,0,10,1,no,0,0x2,0,weapon,0, NPC_HOLYATTACK,Holy Attribute Attack
+190,-9,6,1,7,0,0,10,1,no,0,0x2,0,weapon,0, NPC_DARKNESSATTACK,Shadow Attribute Attack
+191,-9,6,1,8,0,0,10,1,no,0,0x2,0,weapon,0, NPC_TELEKINESISATTACK,Ghost Attribute Attack
+192,-9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_MAGICALATTACK,Demon Shock Attack
+193,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_METAMORPHOSIS,Metamorphosis
+194,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_PROVOCATION,Provocation
+195,0,6,4,0,0x50,0,10,1,no,0,0x2,0,misc,0, NPC_SMOKING,Smoking
+196,0,0,4,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_SUMMONSLAVE,Follower Summons
+197,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_EMOTION,Emotion
+198,0,0,4,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_TRANSFORMATION,Transformation
+199,9,6,1,7,0x40,0,1,1,no,0,0x2,0,weapon,0, NPC_BLOODDRAIN,Sucking Blood
+200,9,6,1,7,0,0,1,1,no,0,0x2,0,magic,0, NPC_ENERGYDRAIN,Energy Drain
+201,0,0,4,0,0x1,0,1,1,no,0,0x2,0,weapon,0, NPC_KEEPING,Keeping
+202,9,6,1,7,0,0,5,1,no,0,0x2,0,misc,0, NPC_DARKBREATH,Dark Breath
+203,9,6,1,7,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_DARKBLESSING,Dark Blessing
+204,0,0,4,0,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_BARRIER,Barrier
+205,0,0,4,0,0x1,0,1,1,no,0,0x2,0,weapon,0, NPC_DEFENDER,Defender
+206,1,6,1,-1,0x1,0,5,1,no,0,0x2,0,weapon,0, NPC_LICK,Lick
+207,9,0,1,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_HALLUCINATION,Hallucination
+208,0,0,4,0,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_REBIRTH,Rebirth
+209,0,0,4,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_SUMMONMONSTER,Monster Summons
+210,0,0,0,-1,0,0,10,0,no,0,0,0,weapon,0, RG_SNATCHER,Gank
+211,1,6,1,0,0x1,0,10,1,no,0,0,0,weapon,0, RG_STEALCOIN,Mug
+212,-1,6,1,-1,0x40,0,10,1,no,0,0,0,weapon,0, RG_BACKSTAP,Back Stab
+213,0,0,0,0,0,0,5,0,no,0,0,0,none,0, RG_TUNNELDRIVE,Stalk
+214,0,6,4,-1,0x2,1,5,1,no,0,0,0,weapon,0, RG_RAID,Sightless Mind
+215,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPWEAPON,Divest Weapon
+216,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPSHIELD,Divest Shield
+217,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPARMOR,Divest Armor
+218,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPHELM,Divest Helm
+219,1,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, RG_INTIMIDATE,Snatch
+220,1,6,2,0,0x1,0,1,1,no,0,0,0,none,0, RG_GRAFFITI,Scribble
+221,0,6,2,0,0x1,0,5,1,no,0,0,0,none,0, RG_FLAGGRAFFITI,Piece
+222,1,6,2,0,0x3,5,1,1,no,0,0,0,none,0, RG_CLEANER,Remover
+223,0,0,0,0,0,1,1,0,no,0,0,0,none,0, RG_GANGSTER,Slyness
+224,0,0,0,0,0,0,5,0,no,0,0,0,none,0, RG_COMPULSION,Haggle
+225,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RG_PLAGIARISM,Intimidate
+226,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AM_AXEMASTERY,Axe Mastery
+227,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_LEARNINGPOTION,Potion Research
+228,0,6,4,0,0x1,0,10,0,no,0,0,0,none,0, AM_PHARMACY,Prepare Potion
+229,9,6,2,3,0x9,0,5,1,yes,0,0,0,weapon,0, AM_DEMONSTRATION,Bomb
+230,9,6,1,0,0x48,0,5,1,yes,0,0,0,weapon,0, AM_ACIDTERROR,Acid Terror
+231,9,6,16,0,0x1,0,5,1,yes,0,0xC00,0,none,0, AM_POTIONPITCHER,Aid Potion
+232,4,6,2,0,0x1,0,5,1,no,0,0,5,none,0, AM_CANNIBALIZE,Summon Flora
+233,1,6,2,0,0x1,0,5,1,no,0,0,3,none,0, AM_SPHEREMINE,Summon Marine Sphere
+234,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_WEAPON,Alchemical Weapon
+235,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_SHIELD,Synthesized Shield
+236,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_ARMOR,Synthetic Armor
+237,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_HELM,Biochemical Helm
+238,0,0,0,0,0,0,1,0,no,0,0x1,0,none,0, AM_BIOETHICS,Bioethics
+//239,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_BIOTECHNOLOGY,Biotechnology
+//240,0,0,0,0,0,0,5,0,no,0,0,0,none,0, AM_CREATECREATURE,Life Creation
+//241,0,0,0,0,0,0,5,0,no,0,0,0,none,0, AM_CULTIVATION,Cultivation
+//242,0,0,0,0,0,0,5,0,no,0,0,0,none,0, AM_FLAMECONTROL,Flame Control
+243,0,0,4,0,0x1,1,1,0,no,0,0,0,none,0, AM_CALLHOMUN,Call Homunculus
+244,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, AM_REST,Vaporize
+//245,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_DRILLMASTER,Drillmaster
+//246,9,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_HEALHOMUN,Heal Homunculus
+247,9,6,4,0,0x1,1,5,0,no,0,0,0,none,0, AM_RESURRECTHOMUN,Homunculus Resurrection
+248,0,0,0,0,0,0,10,0,no,0,0,0,none,0, CR_TRUST,Faith
+249,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, CR_AUTOGUARD,Guard
+250,3,6,1,0,0,0,5,1,no,0,0,0,weapon,5:6:7:8:9, CR_SHIELDCHARGE,Smite
+251,3:5:7:9:11,6,1,0,0,0,5,1,no,0,0,0,weapon,0, CR_SHIELDBOOMERANG,Shield Boomerang
+252,0,6,4,0,0,0,10,1,no,0,0,0,weapon,0, CR_REFLECTSHIELD,Shield Reflect
+253,-2,8,1,6,0,0,10,-2,no,0,0,0,weapon,0, CR_HOLYCROSS,Holy Cross
+254,5,6,4,6,0x48,0,10,1,no,33,0x100,0,magic,0, CR_GRANDCROSS,Grand Cross
+255,7:8:9:10:11,6,16,0,0x1,0,5,1,yes,0,0x600,0,none,0, CR_DEVOTION,Sacrifice
+256,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,none,0, CR_PROVIDENCE,Resistant Souls
+257,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, CR_DEFENDER,Defending Aura
+258,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, CR_SPEARQUICKEN,Spear Quicken
+259,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, MO_IRONHAND,Iron Fists
+260,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, MO_SPIRITSRECOVERY,Spiritual Cadence
+261,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MO_CALLSPIRITS,Summon Spirit Sphere
+262,9,6,16,0,0x1,0,1,1,yes,0,0,0,weapon,0, MO_ABSORBSPIRITS,Absorb Spirit Sphere
+263,-1,8,0,-1,0,0,10,-3,no,0,0,0,weapon,0, MO_TRIPLEATTACK,Raging Trifecta Blow
+264,18,6,2,0,0x1,0,1,1,no,0,0,0,none,0, MO_BODYRELOCATION,Snap
+265,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, MO_DODGE,Dodge
+266,2,6,1,0,0x40,0,5,1,no,0,0,0,weapon,0, MO_INVESTIGATE,Occult Impaction
+267,9,8,1,-1,0,0,5,1:2:3:4:5,no,0,0,0,weapon,0, MO_FINGEROFFENSIVE,Throw Spirit Sphere
+268,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, MO_STEELBODY,Mental Strength
+269,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, MO_BLADESTOP,Root
+270,0,6,4,0,0x1,0,5,0,no,0,0,0,weapon,0, MO_EXPLOSIONSPIRITS,Fury
+271,-2,6,1,0,0x60,0,5,1,yes,0,0,0,weapon,0, MO_EXTREMITYFIST,Asura Strike
+272,-2,8,4,-1,0,0,5,-4,no,0,0x200,0,weapon,0, MO_CHAINCOMBO,Raging Quadruple Blow
+273,-2,6,4,-1,0x2,2,5,1,no,0,0x200,0,weapon,0, MO_COMBOFINISH,Raging Thrust
+274,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, SA_ADVANCEDBOOK,Study
+275,0,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, SA_CASTCANCEL,Cast Cancel
+276,0,6,4,0,0x1,0,5,1,yes,0,0,0,magic,0, SA_MAGICROD,Magic Rod
+277,9,6,1,0,0x1,0,5,1,yes,0,0,0,magic,0, SA_SPELLBREAKER,Spell Breaker
+278,0,0,0,0,0,0,10,0,no,0,0,0,magic,0, SA_FREECAST,Free Cast
+279,0,6,4,0,0x1,0,10,1,yes,0,0,0,magic,0, SA_AUTOSPELL,Hindsight
+280,9,6,16,3,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_FLAMELAUNCHER,Endow Blaze
+281,9,6,16,1,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_FROSTWEAPON,Endow Tsunami
+282,9,6,16,4,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_LIGHTNINGLOADER,Endow Tornado
+283,9,6,16,2,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_SEISMICWEAPON,Endow Quake
+284,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, SA_DRAGONOLOGY,Dragonology
+285,2,6,2,3,0x1,0,5,1,yes,0,0,0,magic,0, SA_VOLCANO,Volcano
+286,2,6,2,1,0x1,0,5,1,yes,0,0,0,magic,0, SA_DELUGE,Deluge
+287,2,6,2,4,0x1,0,5,1,yes,0,0,0,magic,0, SA_VIOLENTGALE,Whirlwind
+288,2,6,2,0,0x1,0,5,1,yes,0,0,0,magic,0, SA_LANDPROTECTOR,Magnetic Earth
+289,9,6,1,0,0x1,0:0:0:0:0:-1,5,1,yes,0,0xE00,0,magic,0, SA_DISPELL,Dispell
+290,0,6,4,0,0x1,0,10,1,yes,0,0,0,magic,0, SA_ABRACADABRA,Hocus-pocus
+291,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_MONOCELL,Monocell
+292,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_CLASSCHANGE,Class Change
+293,0,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_SUMMONMONSTER,Monster Chant
+294,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_REVERSEORCISH,Grampus Morph
+295,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_DEATH,Grim Reaper
+296,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_FORTUNE,Gold Digger
+297,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_TAMINGMONSTER,Beastly Hypnosis
+298,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_QUESTION,Questioning
+299,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_GRAVITY,Gravity
+300,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_LEVELUP,Leveling
+301,9,6,4,0,0,0,1,1,yes,0,0x2,0,magic,0, SA_INSTANTDEATH,Suicide
+302,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_FULLRECOVERY,Rejuvenation
+303,9,6,4,0,0,0,1,1,yes,0,0x2,0,magic,0, SA_COMA,Coma
+304,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, BD_ADAPTATION,Amp
+305,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, BD_ENCORE,Encore
+306,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_LULLABY,Lullaby
+307,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_RICHMANKIM,Mental Sensing
+308,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_ETERNALCHAOS,Down Tempo
+309,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_DRUMBATTLEFIELD,Battle Theme
+310,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_RINGNIBELUNGEN,Harmonic Lick
+311,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_ROKISWEIL,Classical Pluck
+312,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_INTOABYSS,Power Chord
+313,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_SIEGFRIED,Acoustic Rhythm
+//314,0,0,0,0,0,0,1,1,no,0,0x40,0,misc,0, BD_RAGNAROK,Ragnarok
+315,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, BA_MUSICALLESSON,Music Lessons
+316,9,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, BA_MUSICALSTRIKE,Melody Strike
+317,0,8,4,0,0x41,0,5,1,no,0,0x20,0,misc,0, BA_DISSONANCE,Unchained Serenade
+318,0,6,4,0,0x3,-1,5,1,no,0,0,0,misc,0, BA_FROSTJOKER,Unbarring Octave
+319,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_WHISTLE,Perfect Tablature
+320,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_ASSASSINCROSS,Impressive Riff
+321,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_POEMBRAGI,Magic Strings
+322,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_APPLEIDUN,Song of Lutie
+323,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, DC_DANCINGLESSON,Dance Lessons
+324,9,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, DC_THROWARROW,Slinging Arrow
+325,0,8,4,0,0x1,0,5,1,no,0,0x20,0,misc,0, DC_UGLYDANCE,Hip Shaker
+326,0,6,4,0,0x3,-1,5,1,no,0,0,0,misc,0, DC_SCREAM,Dazzler
+327,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_HUMMING,Focus Ballet
+328,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_DONTFORGETME,Slow Grace
+329,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_FORTUNEKISS,Lady Luck
+330,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_SERVICEFORYOU,Gypsy's Kiss
+331,0,6,4,0,0x1,0,10,0,no,0,0x2,0,none,0, NPC_RANDOMMOVE,Random Move
+332,0,6,4,0,0x1,0,10,0,no,0,0x2,0,none,0, NPC_SPEEDUP,Speed UP
+333,0,6,4,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_REVENGE,Revenge
+334,9,6,4,0,0x1,0,1,1,yes,0,0x4,0,none,0, WE_MALE,I Will Protect You
+335,9,6,4,0,0x1,0,1,1,yes,0,0x4,0,none,0, WE_FEMALE,I Look up to You
+336,9,6,4,0,0x1,3,1,1,yes,0,0x4,1,none,0, WE_CALLPARTNER,I miss You
+337,9,6,1,-1,0,0,1,1,no,0,0x2,0,weapon,0, ITM_TOMAHAWK,Throw Tomahawk
+338,-1,8,1,7,0,0,10,-2,no,0,0x2,0,weapon,0, NPC_DARKCROSS,Cross of Darkness
+339,5,6,4,7,0x48,0,10,1,no,33,0x102,0,magic,0, NPC_GRANDDARKNESS,Grand cross of Darkness
+340,9,8,1,7,0,0,10,1:1:2:2:3:3:4:4:5:5,yes,0,0x2,0,magic,0, NPC_DARKSTRIKE,Soul Strike of Darkness
+341,9,8,1,7,0,0,10,3:4:5:6:7:8:9:10:11:12,yes,0,0x2,0,magic,2:3:3:4:4:5:5:6:6:7, NPC_DARKTHUNDER,Darkness Jupitel
+342,9,6,1,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_STOP,Stop
+343,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_WEAPONBRAKER,Break weapon
+344,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_ARMORBRAKE,Break armor
+345,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_HELMBRAKE,Break helm
+346,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_SHIELDBRAKE,Break shield
+347,-9,6,1,9,0,0,10,1,no,0,0x2,0,weapon,0, NPC_UNDEADATTACK,Undead Element Attack
+348,9,0,1,9,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_CHANGEUNDEAD,Undead Attribute Change
+349,0,6,4,0,0x1,0,10,0,no,0,0x2,0,weapon,0, NPC_POWERUP,Power Up
+350,0,6,4,0,0x1,0,10,0,no,0,0x2,0,none,0, NPC_AGIUP,Agility UP
+351,0,0,0,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_SIEGEMODE,Siege Mode
+352,2,0,4,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_CALLSLAVE,Recall Slaves
+353,0,0,0,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_INVISIBLE,Invisible
+354,2,6,4,0,0x1,0,20,0,no,0,0x2,0,misc,0, NPC_RUN,Run
+355,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, LK_AURABLADE,Aura Blade
+356,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, LK_PARRYING,Parrying
+357,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, LK_CONCENTRATION,Concentration
+358,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, LK_TENSIONRELAX,Relax
+359,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, LK_BERSERK,Frenzy
+//360,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, LK_FURY,Fury
+361,9,6,16,0,0x1,1,5,1,yes,0,0,0,magic,0, HP_ASSUMPTIO,Assumptio
+362,4,6,4,0,0x1,0,5,1,yes,0,0,0,magic,2, HP_BASILICA,Basilica
+363,0,0,0,0,0,0,10,0,no,0,0,0,magic,0, HP_MEDITATIO,Meditatio
+364,0,0,0,0,0,0,10,1,no,0,0,0,magic,0, HW_SOULDRAIN,Soul Drain
+365,9,8,1,-1,0,0,1,1,yes,0,0,0,weapon,0, HW_MAGICCRASHER,Stave Crasher
+366,0,6,4,0,0x1,0,10,1,no,0,0,0,magic,0, HW_MAGICPOWER,Mystical Amplification
+367,9,8,1,0,0xD0,0,5,1,no,0,0,0,misc,0, PA_PRESSURE,Gloria Domini
+368,0,6,4,0,0x61,0,5,1,yes,0,0,0,weapon,0, PA_SACRIFICE, Martyr's Reckoning
+369,0,6,4,0,0x41,0,10,1,yes,0,0,0,misc,0, PA_GOSPEL,Battle Chant
+370,-2,6,1,-1,0,0,5,1,yes,0,0,0,weapon,3, CH_PALMSTRIKE,Raging Palm Strike
+371,-2,8,4,-1,0,0,5,1,no,0,0x200,0,weapon,0, CH_TIGERFIST,Glacier Fist
+372,-2,8,4,-1,0,0,10,-1:-1:-2:-2:-3:-3:-4:-4:-5:-5,no,0,0x200,0,weapon,0, CH_CHAINCRUSH,Chain Crush Combo
+373,0,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, PF_HPCONVERSION,Indulge
+374,9,6,1,0,0x1,0,1,1,yes,0,0xE00,0,none,0, PF_SOULCHANGE,Soul Exhale
+375,9,6,1,0,0x98,0,5,1,yes,0,0,0,magic,0, PF_SOULBURN,Soul Siphon
+376,0,0,0,0,0x1,0,5,1,no,0,0,0,weapon,0, ASC_KATAR,Advanced Katar Mastery
+//377,0,0,4,0,0x1,0,10,1,no,0,0,0,misc,0, ASC_HALLUCINATION,Hallucination Walk
+378,0,6,4,5,0x1,0,5,1,no,0,0,0,weapon,0, ASC_EDP,Enchant Deadly Poison
+379,7,6,1,-1,0x8,0,10,1,yes,0,0,0,weapon,0, ASC_BREAKER,Soul Destroyer
+380,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, SN_SIGHT,Falcon Eyes
+381,5,8,1,0,0x40,0,5,1,yes,0,0,0,misc,0, SN_FALCONASSAULT,Falcon Assault
+382,9,8,1,-1,0,2,5,1,yes,0,0,13,weapon,0, SN_SHARPSHOOTING,Focused Arrow Strike
+383,0,6,4,0,0x3,-1,10,1,yes,0,0,0,weapon,0, SN_WINDWALK,Wind Walker
+384,0,0,4,0,0x1,0,10,1,yes,0,0,0,weapon,0, WS_MELTDOWN,Shattering Strike
+//385,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, WS_CREATECOIN,Create Coins
+//386,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, WS_CREATENUGGET,Create Nuggets
+387,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, WS_CARTBOOST,Cart Boost
+//388,9,6,2,0,0x1,0,5,1,no,0,0,0,none,0, WS_SYSTEMCREATE,Auto Attack System
+389,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, ST_CHASEWALK,Stealth
+390,0,0,4,0,0,0,5,1,yes,0,0,0,weapon,0, ST_REJECTSWORD,Counter Instinct
+//391,0,0,4,0,1,0,1,1,yes,0,0,0,magic,0, ST_STEALBACKPACK,Steal Backpack
+392,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, CR_ALCHEMY,Alchemy
+393,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, CR_SYNTHESISPOTION,Potion Synthesis
+394,9,8,1,-1,0,0,10,-9,yes,0,0,0,weapon,0, CG_ARROWVULCAN,Vulcan Arrow
+395,0,0,4,0,0x1,3,1,1,yes,0,0x40,0,misc,2, CG_MOONLIT,Sheltering Bliss
+396,1,6,16,0,0x1,0,1,1,yes,0,0x600,0,none,0, CG_MARIONETTE,Marionette Control
+397,5,8,1,-1,0x20,0,5,5,no,0,0,0,weapon,0, LK_SPIRALPIERCE,Spiral Pierce
+398,4,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, LK_HEADCRUSH,Traumatic Blow
+399,4,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, LK_JOINTBEAT,Vital Strike
+400,9,8,1,8,0x6,1,5,1:2:3:4:5,yes,0,0,0,magic,0, HW_NAPALMVULCAN,Napalm Vulcan
+401,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, CH_SOULCOLLECT,Zen
+402,9,6,1,0,0x1,0,5,1,no,0,0,0,none,0, PF_MINDBREAKER,Mind Breaker
+403,0,0,4,0,0x1,0,1,1,yes,0,0,0,magic,0, PF_MEMORIZE,Foresight
+404,9,6,2,2,0x1,0,5,1,yes,0,0x100,2,magic,0, PF_FOGWALL,Blinding Mist
+405,7,6,1,0,0x1,0,1,1,no,0,0,3,magic,0, PF_SPIDERWEB,Fiber Lock
+406,0,6,4,-1,0xA,2,10,1,no,33,0,0,weapon,0, ASC_METEORASSAULT,Meteor Assault
+407,0,6,4,0,0x1,0,1,0,no,0,0,0,none,0, ASC_CDP,Create Deadly Poison
+408,9,6,4,0,0x1,0,1,1,yes,0,0x4,0,none,0, WE_BABY,Baby
+409,9,6,4,0,0x1,3,1,1,yes,0,0x4,1,none,0, WE_CALLPARENT,Call Parent
+410,9,6,4,0,0x1,3,1,1,yes,0,0x4,1,none,0, WE_CALLBABY,Call Baby
+411,0,6,4,0,0x1,0,10,1,yes,0,0,0,misc,4, TK_RUN,Running
+412,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYSTORM,Tornado Stance
+413,-2,8,4,-1,0x2,2,7,-3,no,0,0x200,0,weapon,0, TK_STORMKICK,Tornado Kick
+414,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYDOWN,Heel Drop Stance
+415,-2,8,4,-1,0,0,7,-3,no,0,0x200,0,weapon,0, TK_DOWNKICK,Heel Drop
+416,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYTURN,Roundhouse Stance
+417,-2,8,4,-1,0x2,1,7,-3,no,0,0x200,0,weapon,2, TK_TURNKICK,Roundhouse Kick
+418,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYCOUNTER,Counter Kick Stance
+419,-2,8,4,-1,0x40,0,7,-3,no,0,0x200,0,weapon,0, TK_COUNTER,Counter Kick
+420,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_DODGE,Tumbling
+421,9,8,16,-1,0x1,0,7,-3,no,0,0,0,weapon,0, TK_JUMPKICK,Flying Kick
+422,0,0,0,0,0,1,10,0,no,0,0,0,none,0, TK_HPTIME,Peaceful Break
+423,0,0,0,0,0,1,10,0,no,0,0,0,none,0, TK_SPTIME,Happy Break
+424,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, TK_POWER,Kihop
+425,0,6,4,2:4:1:3:8:7:6,0x1,0,7,1,no,0,0,0,weapon,0, TK_SEVENWIND,Mild Wind
+426,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, TK_HIGHJUMP,Taekwon Jump
+427,0,6,4,0,0x1,0,3,1,yes,0,0,0,magic,0, SG_FEEL,Feeling the Sun Moon and Stars
+428,1,6,4,-1,0x2,1,3,1,yes,0,0,0,weapon,2, SG_SUN_WARM,Warmth of the Sun
+429,1,6,4,-1,0x2,1,3,1,yes,0,0,0,weapon,2, SG_MOON_WARM,Warmth of the Moon
+430,1,6,4,-1,0x2,1,3,1,yes,0,0,0,weapon,2, SG_STAR_WARM,Warmth of the Stars
+431,0,0,4,0,0x1,0,4,1,yes,0,0,0,magic,0, SG_SUN_COMFORT,Comfort of the Sun
+432,0,0,4,0,0x1,0,4,1,yes,0,0,0,magic,0, SG_MOON_COMFORT,Comfort of the Moon
+433,0,0,4,0,0x1,0,4,1,yes,0,0,0,magic,0, SG_STAR_COMFORT,Comfort of the Stars
+434,10,6,1,0,0x1,0,3,1,yes,0,0,0,magic,0, SG_HATE,Hatred of the Sun Moon and Stars
+435,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_SUN_ANGER,Anger of the Sun
+436,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_MOON_ANGER,Anger of the Moon
+437,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_STAR_ANGER,Anger of the Stars
+438,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SG_SUN_BLESS,Blessing of the Sun
+439,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SG_MOON_BLESS,Blessing of the Moon
+440,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SG_STAR_BLESS,Blessing of the Stars
+441,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SG_DEVIL,Demon of the Sun Moon and Stars
+442,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_FRIEND,Friend of the Sun Moon and Stars
+443,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SG_KNOWLEDGE,Knowledge of the Sun Moon and Stars
+444,0,6,4,0,0x1,0,1,1,no,0,0,0,misc,0, SG_FUSION,Union of the Sun Moon and Stars
+445,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_ALCHEMIST,Spirit of the Alchemist
+446,9,6,16,0,0x1,0,1,1,yes,0,0xC08,0,none,0, AM_BERSERKPITCHER,Aid Berserk Potion
+447,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_MONK,Spirit of the Monk
+448,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_STAR,Spirit of the Star Gladiator
+449,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_SAGE,Spirit of the Sage
+450,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_CRUSADER,Spirit of the Crusader
+451,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_SUPERNOVICE,Spirit of the Supernovice
+452,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_KNIGHT,Spirit of the Knight
+453,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_WIZARD,Spirit of the Wizard
+454,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_PRIEST,Spirit of the Priest
+455,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_BARDDANCER,Spirit of the Artist
+456,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_ROGUE,Spirit of the Rogue
+457,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_ASSASIN,Spirit of the Assasin
+458,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_BLACKSMITH,Spirit of the Blacksmith
+459,0,6,4,0,0x3,-1,1,1,no,0,0x8,0,weapon,0 , BS_ADRENALINE2,Advanced Adrenaline Rush
+460,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_HUNTER,Spirit of the Hunter
+461,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_SOULLINKER,Spirit of the Soul Linker
+462,9,6,16,0,0x1,0,7,1,yes,0,0,0,magic,0, SL_KAIZEL,Kaizel
+463,9,6,16,0,0x1,0,7,1,yes,0,0,0,magic,0, SL_KAAHI,Kaahi
+464,9,6,16,0,0x1,0,3,1,yes,0,0,0,magic,0, SL_KAUPE,Kaupe
+465,9,6,16,0,0x1,0,7,1,yes,0,0,0,magic,0, SL_KAITE,Kaite
+466,0,0,0,0,0,0,7,0,yes,0,0,0,magic,0, SL_KAINA,Kaina
+467,9,6,1,-2,0,0,7,1,no,0,0,0,magic,2, SL_STIN,Estin
+468,9,6,1,-2,0,0,7,1,no,0,0,0,magic,0, SL_STUN,Estun
+469,9,8,1,-2,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, SL_SMA,Esma
+470,9,6,1,0,0x1,0,7,1,no,0,0,0,magic,0, SL_SWOO,Eswoo
+471,9,6,1,0,0x1,0,3,1,no,0,0,0,magic,0, SL_SKE,Eske
+472,9,6,1,0,0x1,0,3,1,no,0,0,0,magic,0, SL_SKA,Eska
+473,0,6,4,0,0,0,1,1,no,0,0,0,none,0, SM_SELFPROVOKE,Provoke Self
+474,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_EMOTION_ON,Emotion ON
+475,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, ST_PRESERVE,Preserve
+476,1,6,1,0,0x1,0,5,1,yes,0,0,0,weapon,0, ST_FULLSTRIP,Divest All
+477,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, WS_WEAPONREFINE,Upgrade Weapon
+478,3,6,2,0,0x3,3,10,1,no,0,0,0,none,0, CR_SLIMPITCHER,Aid Condensed Potion
+479,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, CR_FULLPROTECTION,Full Protection
+480,5,8,1,0,0,0,5,5,no,0,0,0,weapon,0, PA_SHIELDCHAIN,Shield Chain
+481,0,0,0,0,0,0,5,0,no,0,0,0,none,0, HP_MANARECHARGE,Mana Recharge
+482,0,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, PF_DOUBLECASTING,Double Casting
+483,16,6,2,0,0x1,1:2:3:4:5,1,1,no,0,0,0,none,0, HW_GANBANTEIN,Ganbantein
+484,9,6,2,2,0x91,0,5,1,yes,0,0,0,misc,0, HW_GRAVITATION,Gravitation Field
+485,-2,6,1,-1,0x8,0,10,1,no,0,0,0,weapon,0, WS_CARTTERMINATION,Cart Termination
+486,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, WS_OVERTHRUSTMAX,Maximum Power Thrust
+487,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, CG_LONGINGFREEDOM,Longing for Freedom
+488,0,6,4,0,0x1,1,5,1,no,0,0x40,0,misc,0, CG_HERMODE,Wand of Hermode
+489,9,6,1,0,0x41,0,5,1,no,0,0,0,misc,0, CG_TAROTCARD,Tarot Card of Fate
+490,9,8,1,0,0x40,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,misc,0, CR_ACIDDEMONSTRATION,Acid Demonstration
+491,1,6,2,0,0x1,0,2,1,no,0,0,0,none,0, CR_CULTIVATION,Plant Cultivation
+492,0,6,4,0:1:2:3:4:5:6:7:8:9,0x1,0,10,1,no,0,0x2,0,none,0, ITEM_ENCHANTARMS,Weapon Enchantment
+493,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, TK_MISSION,Taekwon Mission
+494,9,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, SL_HIGH,Spirit of Rebirth
+495,0,6,4,0,0x1,0,1,1,no,0,0x8,0,weapon,0, KN_ONEHAND,Onehand Quicken
+496,0,6,4,0,0x1,0,1,0,no,0,0x8,0,none,0, AM_TWILIGHT1,Twilight Alchemy 1
+497,0,6,4,0,0x1,0,1,0,no,0,0x8,0,none,0, AM_TWILIGHT2,Twilight Alchemy 2
+498,0,6,4,0,0x1,0,1,0,no,0,0x8,0,none,0, AM_TWILIGHT3,Twilight Alchemy 3
+499,-9,8,4,-1,0,0,1,2,no,0,0x208,0,weapon,0, HT_POWER,Beast Strafing
+500,0,6,4,0,0x40,0,5,1,no,0,0,0,misc,0, GS_GLITTERING,Flip the Coin
+501,9,6,1,-1,0x50,0,1,1,no,0,0,0,misc,0, GS_FLING,Fling
+502,-9,8,1,-1,0,0,1,3,no,0,0,0,weapon,0, GS_TRIPLEACTION,Triple Action
+503,-9,6,1,-1,0x8,0,1,1,no,0,0,0,weapon,0, GS_BULLSEYE,Bulls Eye
+504,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, GS_MADNESSCANCEL,Madness Canceller
+505,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, GS_ADJUSTMENT,AdJustment
+506,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, GS_INCREASING,Increasing Accuracy
+507,-9,6,1,8,0,0,1,1,no,0,0,0,weapon,0, GS_MAGICALBULLET,Magical Bullet
+508,-9,6,1,-1,0x1,0,1,1,no,0,0,0,weapon,0, GS_CRACKER,Cracker
+509,0,0,0,0,0,0,10,0,no,0,0,0,none,0, GS_SINGLEACTION,Single Action
+510,0,0,0,0,0,0,10,0,no,0,0,0,none,0, GS_SNAKEEYE,Snake Eye
+511,-9,8,0,-1,0,0,10,2,no,0,0,0,weapon,0, GS_CHAINACTION,Chain Action
+512,-9,6,1,-1,0,0,10,1,yes,0,0,0,weapon,0, GS_TRACKING,Tracking
+513,-9,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, GS_DISARM,Disarm
+514,-9,6,1,-1,0x20,0,5,1,no,0,0,0,weapon,0, GS_PIERCINGSHOT,Piercing Shot
+515,-9,8,1,-1,0,0,10,5,no,0,0,0,weapon,0, GS_RAPIDSHOWER,Rapid Shower
+516,0,8,4,-1,0x2,3,10,1,no,0,0,0,weapon,0, GS_DESPERADO,Desperado
+517,0,6,4,-1,0x1,0,10,1,no,0,0,0,weapon,0, GS_GATLINGFEVER,Gatling Fever
+518,2,6,1,-1,0,0,10,1,no,0,0,0,weapon,5, GS_DUST,Dust
+519,-9,6,1,-1,0,0,10,1,yes,0,0,0,weapon,0, GS_FULLBUSTER,Full Buster
+520,-9,6,1,-1,0x2,1:1:1:2:2:2:3:3:3:4,10,1,no,0,0,0,weapon,0, GS_SPREADATTACK,Spread Attack
+521,-9,6,2,-1,0x40,1,10,1,no,0,0,0,weapon,3, GS_GROUNDDRIFT,Ground Drift
+522,0,0,0,0,0,0,10,1,no,0,0,0,weapon,0, NJ_TOBIDOUGU,Shuriken Training
+523,9,6,1,-1,0x40,0,10,1,no,0,0,0,weapon,0, NJ_SYURIKEN,Throw Shuriken
+524,9,8,1,-1,0x40,0,5,3,no,0,0,0,weapon,0, NJ_KUNAI,Throw Kunai
+525,9,8,1,-1,0x6,1,5,-3:-3:-4:-4:-5,yes,0,0,0,weapon,0, NJ_HUUMA,Throw Huuma Shuriken
+526,9,6,1,0,0x50,0,10,1,no,0,0,0,misc,0, NJ_ZENYNAGE,Throw Zeny
+527,0,6,4,-1,0,0,5,1,no,0,0,0,weapon,3, NJ_TATAMIGAESHI,Improvised Defense
+528,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, NJ_KASUMIKIRI,Vanishing Slash
+529,7:9:11:13:15,6,2,0,0x1,0,5,1,no,0,0,0,none,0, NJ_SHADOWJUMP,Shadow Leap
+530,7:9:11:13:15,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, NJ_KIRIKAGE,Shadow Slash
+531,0,6,4,0,0x1,0,5,1,no,0,0,0,none,7, NJ_UTSUSEMI,Cicada Skin Sheeding
+532,0,6,4,0,0x1,0,10,1,yes,0,0,0,magic,0, NJ_BUNSINJYUTSU,Mirror Image
+533,0,0,0,0,0,0,10,0,no,0,0,0,none,0, NJ_NINPOU,Spirit of the Blade
+534,9,8,1,3,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, NJ_KOUENKA,Crimson Fire Petal
+535,0,8,4,3,0,0,10,1,yes,0,0,0,magic,1, NJ_KAENSIN,Crimson Fire Formation
+536,9,8,1,3,0x2,2,5,3,yes,0,0,0,magic,0, NJ_BAKUENRYU,Raging Fire Dragon
+537,9,8,1,1,0,0,10,3:4:5:6:7:8:9:10:11:12,yes,0,0,0,magic,0, NJ_HYOUSENSOU,Spear of Ice
+538,9,6,2,1,0x1,0,10,1,yes,0,0,0,magic,0, NJ_SUITON,Hidden Water
+539,0,6,4,1,0x2,3,5,1,yes,0,0,0,magic,0, NJ_HYOUSYOURAKU,Ice Meteor
+540,9,8,1,4,0,0,10,1:2:2:3:3:4:4:5:5:6,yes,0,0,0,magic,0, NJ_HUUJIN,Wind Blade
+541,9,6,4,4,0x2,2:2:3:3:4,5,1,yes,0,0,0,magic,0, NJ_RAIGEKISAI,Lightning Strike of Destruction
+542,9,8,1,4,0,3,5,1,yes,0,0,5:6:7:8:9,magic,0, NJ_KAMAITACHI,Kamaitachi
+543,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, NJ_NEN,Soul
+544,-5,6,1,0,0x40,0,10,1,no,0,0,0,weapon,0, NJ_ISSEN,Final Strike
+
+// Additional NPC Skills (Episode 11.3)
+653,0,8,4,0,0x6,5:7:9:11:13:5:7:9:11:13,10,1,no,0,0x2,0,magic,0, NPC_EARTHQUAKE,Earthquake
+654,9,6,1,3,0,5,10,1,no,0,0x2,14,weapon,0, NPC_FIREBREATH,Fire Breath
+655,9,6,1,1,0,5,10,1,no,0,0x2,14,weapon,0, NPC_ICEBREATH,Ice Breath
+656,9,6,1,4,0,5,10,1,no,0,0x2,14,weapon,0, NPC_THUNDERBREATH,Thunder Breath
+657,9,6,1,5,0,5,10,1,no,0,0x2,14,weapon,0, NPC_ACIDBREATH,Acid Breath
+658,9,6,1,7,0,5,10,1,no,0,0x2,14,weapon,0, NPC_DARKNESSBREATH,Darkness Breath
+659,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_DRAGONFEAR,Dragon Fear
+660,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_BLEEDING,Bleeding
+661,0,6,4,0,0x2,7,5,1,no,0,0x2,0,weapon,7, NPC_PULSESTRIKE,Pulse Strike
+662,0,6,4,0,0x2,14,10,1,no,0,0x2,0,weapon,0, NPC_HELLJUDGEMENT,Hell's Judgement
+663,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESILENCE,Wide Silence
+664,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDEFREEZE,Wide Freeze
+665,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDEBLEEDING,Wide Bleeding
+666,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESTONE,Wide Petrify
+667,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDECONFUSE,Wide Confusion
+668,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESLEEP,Wide Sleep
+669,0,6,4,3,0x3,5,1,1,no,0,0x2,0,magic,0, NPC_WIDESIGHT,Wide Sight
+670,9,6,2,7,0x91,0,10,1,no,0,0x2,0,magic,0, NPC_EVILLAND,Evil Land
+671,0,6,4,0,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_MAGICMIRROR,Magic Mirror
+672,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_SLOWCAST,Slow Cast
+673,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_CRITICALWOUND,Critical Wounds
+674,-9,6,1,-1,0x1,0,1,1,no,0,0x2,0,none,0, NPC_EXPULSION,Expulsion
+675,0,6,4,0,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_STONESKIN,Stone Skin
+676,0,6,4,0,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_ANTIMAGIC,Anti Magic
+677,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDECURSE,Wide Curse
+678,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESTUN,Wide Stun
+679,0,6,4,0,0x2,5:7:9:11:13:13:13:13:13:13,10,1,no,0,0x2,0,weapon,0, NPC_VAMPIRE_GIFT,Vampire Gift
+680,0,6,4,0,0x3,5:7:9:11:13:13:13:13:13:13,10,1,no,0,0x2,0,none,0, NPC_WIDESOULDRAIN,Wide Soul Drain
+
+// Cash Shop Skill
+681,0,0,0,0,0,0,10,0,no,0,0x1,0,none,0, ALL_INCCARRY,Increase Weight Limit R
+
+// Additional NPC skill (Episode 12)
+682,0,0,4,0,0x1,0,1,1,no,0,0x2,0,none,0, NPC_TALK,Talk
+683,-9,6,1,-1,0,0,1,1,no,0,0x2,0,none,0, NPC_HELLPOWER,Hell Power
+684,0,6,4,0,0x3,-1,1,1,no,0,0x2,0,none,0, NPC_WIDEHELLDIGNITY,Hell Dignity
+685,0,0,4,0,0x1,0,1,1,no,0,0x2,0,none,0, NPC_INVINCIBLE,Invincible
+686,0,0,4,0,0x1,0,1,1,no,0,0x2,0,none,0, NPC_INVINCIBLEOFF,Invincible off
+687,0,6,4,0,0x1,0,1,1,yes,0,0x2,0,none,0, NPC_ALLHEAL,Full Heal
+
+// Additional Skill (??)
+688,9,6,16,0,0x1,0,10,0,no,0,0x200,0,none,0, GM_SANDMAN,GM Sandman
+689,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0, CASH_BLESSING,Party Blessing
+690,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0, CASH_INCAGI,Party Increase AGI
+691,0,6,4,0,0x3,-1,5,1,yes,0,0x2,0,magic,0, CASH_ASSUMPTIO,Party Assumptio
+//692,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_CATCRY,Cat Cry
+693,0,6,4,0,0x3,-1,1,1,yes,0,0x2,0,magic,0, ALL_PARTYFLEE,Party Flee
+//694,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_ANGEL_PROTECT,Angel's Protection
+//695,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_DREAM_SUMMERNIGHT,Summer Night Dream
+//696,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, NPC_CHANGEUNDEAD2,Change Undead
+//697,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, ALL_REVERSEORCISH,Reverse Orcish
+698,0,6,4,0,0x01,0,1,1,no,0,0x2,0,none,0, ALL_WEWISH,Christmas Carol
+//699,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_SONKRAN,ALL_SONKRAN
+
+// New NPC Wide Status AoE Skills And Others
+//700,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDEHEALTHFEAR,Wide Health Fear
+//701,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDEBODYBURNNING,Wide Body Burnning
+//702,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDEFROSTMISTY,Wide Freezing
+//703,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDECOLD,Wide Crystalize
+//704,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDE_DEEP_SLEEP,Wide Deep Sleep
+//705,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDESIREN,Wide Siren's Voice
+//706,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_VENOMFOG,Venom Fog
+//707,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_MILLENNIUMSHIELD,Millenium Shield 2
+//708,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_COMET,Comet 2
+
+1001,9,6,1,-1,0,0,1,1,no,0,0x1,0,weapon,0, KN_CHARGEATK,Charge Attack
+1002,0,6,4,0,0x1,0,1,0,no,0,0x1,0,weapon,2, CR_SHRINK,Shrink
+1003,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0, AS_SONICACCEL,Sonic Acceleration
+1004,9,8,1,0,0x8,0,1,1,no,0,0x1,0,weapon,0, AS_VENOMKNIFE,Throw Venom Knife
+1005,1,6,1,0,0x1,0,1,1,no,0,0x1,0,weapon,0, RG_CLOSECONFINE,Close Confine
+1006,0,6,4,3,0,2,1,1,yes,0,0x1,0,magic,3, WZ_SIGHTBLASTER,Sight Blaster
+1007,0,6,4,0,0x1,0,1,0,no,0,0x1,0,none,0, SA_CREATECON,Create Elemental Converter
+1008,9,6,1,1,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTWATER,Elemental Change Water
+1009,-9,6,1,0,0,0,1,1,no,0,0x1,0,weapon,3, HT_PHANTASMIC,Phantasmic Arrow
+1010,9,6,1,0,0x1,0,1,0,no,0,0x1,0,misc,0, BA_PANGVOICE,Pang Voice
+1011,9,6,1,0,0x1,0,1,0,no,0,0x1,0,misc,0, DC_WINKCHARM,Wink of Charm
+1012,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0, BS_UNFAIRLYTRICK,Unfair Trick
+1013,0,6,4,0,0x3,2,1,0,no,0,0x1,0,weapon,0, BS_GREED,Greed
+1014,0,6,4,6,0x3,14,1,0,yes,0,0x1,0,magic,0, PR_REDEMPTIO,Redemptio
+1015,9,6,16,0,0x1,0,1,1,no,0,0x401,0,weapon,0, MO_KITRANSLATION,Ki Translation
+1016,-1,6,1,-1,0x2,1,1,1,no,0,0x1,0,weapon,5, MO_BALKYOUNG,Ki Explosion
+1017,9,6,1,2,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTGROUND,Elemental Change Earth
+1018,9,6,1,3,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTFIRE,Elemental Change Fire
+1019,9,6,1,4,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTWIND,Elemental Change Wind
+
+//****
+// RK Rune Knight
+//****
+2001,1,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, RK_ENCHANTBLADE,Enchant Blade
+2002,7:8:9:10:11,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, RK_SONICWAVE,Sonic Wave
+2003,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, RK_DEATHBOUND,Death Bound
+2004,1,8,1,-1,0,0,10,-5,no,0,0,0,weapon,0, RK_HUNDREDSPEAR,Hundred Spear
+2005,1,6,2,4,0x2,2,5,1,no,0,0,0,weapon,3, RK_WINDCUTTER,Wind Cutter
+2006,0,6,4,-1,0x2,5,5,1,no,0,0,0,weapon,0, RK_IGNITIONBREAK,Ignition Break
+2007,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, RK_DRAGONTRAINING,Dragon Training
+2008,9,6,2,3,0xC2,1:1:1:2:2:2:3:3:4:4,10,1,no,0,0,0,misc,0, RK_DRAGONBREATH,Dragon Breath //CHECK May have to change this back to a weapon type attack.
+2009,0,6,4,0,0x3,3:4:5:6:7,5,1,yes,0,0,0,weapon,0, RK_DRAGONHOWLING,Dragon Howling
+2010,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RK_RUNEMASTERY,Rune Mastery
+2011,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_MILLENNIUMSHIELD,Millenium Shield
+2012,1,6,4,-1,0,0x8,1,1,yes,0,0,0,weapon,0, RK_CRUSHSTRIKE,Crush Strike
+2013,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_REFRESH,Refresh
+2014,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_GIANTGROWTH,Giant Growth
+2015,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_STONEHARDSKIN,Stone Hard Skin
+2016,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_VITALITYACTIVATION,Vitality Activation
+2017,0,6,4,-1,0x2,3,1,1,no,0,0,0,weapon,7, RK_STORMBLAST,Storm Blast
+2018,0,6,4,0,0x3,-1,1,1,yes,0,0,0,none,0, RK_FIGHTINGSPIRIT,Fighting Spirit //CHECK Is this splash needed?
+2019,9,6,4,6,0x1,0,1,1,yes,0,0,0,none,0, RK_ABUNDANCE,Abundance
+2020,5:6:7:8:9,6,1,-1,0,0,5,1,yes,0,0,0,weapon,0, RK_PHANTOMTHRUST,Phantom Thrust
+
+//****
+// WL Warlock
+//****
+2201,11,6,16,0,0,0,5,1,yes,0,0,0,magic,0, WL_WHITEIMPRISON,White Imprison
+2202,11,8,1,8,0x2,1:1:1:2:2,5,-2,yes,0,0,0,magic,0, WL_SOULEXPANSION,Soul Expansion
+2203,0,8,4,1,0x2,13,5,-3:-4:-5:-6:-7,yes,0,0,0,magic,0, WL_FROSTMISTY,Frosty Misty
+2204,0,8,4,1,0x2,13,5,-5,yes,0,0,0,magic,0, WL_JACKFROST,Jack Frost
+2205,11,6,1,0,0x1,0,5,1,yes,0,0,0,magic,0, WL_MARSHOFABYSS,Marsh of Abyss
+2206,0,6,4,0,0x1,0,5,1,yes,0,0,0,magic,0, WL_RECOGNIZEDSPELL,Recognized Spell
+2207,7,6,1,2,0x3,1:2:2:3:3,5,1,yes,0,0,0,magic,0, WL_SIENNAEXECRATE,Sienna Execrate
+2208,0,0,0,0,0,0,3,0,no,0,0,0,none,0, WL_RADIUS,Radius
+2209,0,6,4,0,0x3,9:10:11:12:13,5,1,yes,0,0,0,magic,0, WL_STASIS,Stasis
+2210,11,6,1,0,0,0,5,1,yes,0,0,0,magic,0, WL_DRAINLIFE,Drain Life
+2211,11,8,1,3,0x2,3,5,-7,yes,0,0,0,magic,3, WL_CRIMSONROCK,Crimson Rock
+2212,11,6,1,3,0,0,5,1,yes,0,0,0,magic,0, WL_HELLINFERNO,Hell Inferno
+2213,11,8,2,0,0x2,15,5,-20,yes,0,0,0,magic,2, WL_COMET,Comet //CHECK AoE in official code appears to be 15 x 15, yet casting circle is much bigger then that.
+2214,11,6,1,0,0,3,5,1,yes,0,0,0,magic,0, WL_CHAINLIGHTNING,Chain Lightning //CHECK Is the splash being used for the target search?
+2215,11,6,1,4,0,0,5,1,no,0,0,0,magic,0, WL_CHAINLIGHTNING_ATK,Chain Lightning Attack
+2216,3,8,2,2,0,0,5,-6:-7:-8:-9:-10,yes,0,0,0,magic,0, WL_EARTHSTRAIN,Earth Strain
+2217,11,6,1,0,0,0,5,1,yes,0,0,0,magic,0, WL_TETRAVORTEX,Tetra Vortex
+2218,11,6,1,3,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_FIRE,Tetra Vortex Fire
+2219,11,6,1,1,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_WATER,Tetra Vortex Water
+2220,11,6,1,4,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_WIND,Tetra Vortex Wind
+2221,11,6,1,2,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_GROUND,Tetra Vortex Earth
+2222,0,6,4,3,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONFB,Summon Fire Ball
+2223,0,6,4,4,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONBL,Summon Lightning Ball
+2224,0,6,4,1,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONWB,Summon Water Ball
+2225,11,6,1,3,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_FIRE,Summon Attack Fire //CHECK Summon attack ID's dont appear to have a range.
+2226,11,6,1,4,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_WIND,Summon Attack Wind
+2227,11,6,1,1,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_WATER,Summon Attack Water
+2228,11,6,1,2,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_GROUND,Summon Attack Earth
+2229,0,6,4,2,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONSTONE,Summon Stone
+2230,11,8,1,0,0,0,2,1,yes,0,0,0,magic,0, WL_RELEASE,Release //CHECK Should it be left to do multi hit or single hit?
+2231,0,6,4,0,0x1,0,1,1,yes,0,0,0,magic,0, WL_READING_SB,Reading Spellbook
+2232,0,0,0,0,0,0,5,0,no,0,0,0,none,0, WL_FREEZE_SP,Freeze Spell
+
+//****
+// GC Guillotine Cross
+//****
+2021,10,6,1,0,0x1,0,5,1,no,0,0,0,none,0, GC_VENOMIMPRESS,Venom Impress
+2022,3,8,1,-1,0,0,5,-7,no,0,0,0,weapon,0, GC_CROSSIMPACT,Cross Impact
+2023,3:4:5:6:7,6,1,-1,0,0,5,1,no,0,0,0,weapon,0,GC_DARKILLUSION,Dark Illusion
+2024,0,0,0,0,0,0,10,0,no,0,0,0,none,0, GC_RESEARCHNEWPOISON,Research New Poison
+2025,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, GC_CREATENEWPOISON,Create New Poison
+2026,5,6,16,0,0x1,0,1,1,no,0,0,0,none,0, GC_ANTIDOTE,Antidote
+2027,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, GC_POISONINGWEAPON,Poisoning Weapon
+2028,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, GC_WEAPONBLOCKING,Weapon Blocking
+2029,-2,6,4,-1,0x2,1,5,1,no,0,0,0,weapon,3, GC_COUNTERSLASH,Counter Slash
+2030,-2,6,4,-1,0x1,0,5,1,no,0,0x200,0,weapon,0, GC_WEAPONCRUSH,Weapon Crush //CHECK SHould this and the above skill have INF2 0x200?
+2031,1,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, GC_VENOMPRESSURE,Venom Pressure
+2032,5,6,2,0,0x1,0,5,1,yes,0,0,1,none,0, GC_POISONSMOKE,Poison Smoke
+2033,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, GC_CLOAKINGEXCEED,Cloaking Exceed
+2034,0,6,4,-1,0x2,3,1,1,no,0,0,0,weapon,0, GC_PHANTOMMENACE,Phantom Menace
+2035,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, GC_HALLUCINATIONWALK,Hallucination Walk
+2036,0,6,4,-1,0x2,1:1:1:1:2,5,1,no,0,0,0,weapon,0, GC_ROLLINGCUTTER,Rolling Cutter
+2037,9:10:11:12:13,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, GC_CROSSRIPPERSLASHER,Cross Ripper Slasher
+
+//****
+// AB Arch Bishop
+//****
+2038,11,8,1,6,0x2,3,5,-3,yes,0,0,0,magic,0, AB_JUDEX,Judex
+2039,0,6,4,0,0x1,0,1,1,yes,0,0,0,magic,0, AB_ANCILLA,Ancilla
+2040,11,8,1,6,0,0,10,-10,yes,0,0,0,magic,0, AB_ADORAMUS,Adoramus
+2041,0,6,4,6,0x3,3:7:15,3,1,yes,0,0,0,magic,0, AB_CLEMENTIA,Crementia
+2042,0,6,4,6,0x3,3:7:15,3,1,yes,0,0,0,magic,0, AB_CANTO,Canto Candidus
+2043,0,6,4,6,0x3,3:7:15,3,1,yes,0,0,0,magic,0, AB_CHEAL,Coluceo Heal
+2044,11,6,2,6,0x1,0,5,1,yes,0,0,1,magic,0, AB_EPICLESIS,Epiclesis
+2045,0,6,4,6,0x3,15,10,1,yes,0,0,0,magic,0, AB_PRAEFATIO,Praefatio
+2046,0,6,4,6,0x3,15,10,1,yes,0,0,0,magic,0, AB_ORATIO,Oratio
+2047,0,6,4,6,0x3,15,4,1,yes,0,0,0,magic,0, AB_LAUDAAGNUS,Lauda Agnus
+2048,0,6,4,6,0x3,15,4,1,yes,0,0,0,magic,0, AB_LAUDARAMUS,Lauda Ramus
+2049,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AB_EUCHARISTICA,Eucharistica
+2050,11,6,16,6,0x1,0,1,1,yes,0,0,0,magic,0, AB_RENOVATIO,Renovatio
+2051,11,6,16,6,0x21,0,5,1,yes,0,0,0,magic,0, AB_HIGHNESSHEAL,Highness Heal //CHECK Info shows this has magic attack.
+2052,11,6,1,0,0x1,0,5,1,yes,0,0xA00,0,magic,0, AB_CLEARANCE,Clearance //CHECK Also shows this as a magic attack. Why?
+2053,0,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, AB_EXPIATIO,Expiatio //CHECK Does this also give the buff to party members?
+2054,0,6,4,6,0x1,0,10,1,yes,0,0,0,none,0, AB_DUPLELIGHT,Duple Light //CHECK Had issues adding a skill level check to make the % go higher with the skills level. Will do later.
+2055,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, AB_DUPLELIGHT_MELEE,Duple Light Melee
+2056,-1,6,1,0,0,0,10,1,no,0,0,0,magic,0, AB_DUPLELIGHT_MAGIC,Duple Light Magic
+2057,0,6,4,6,0x3,4:5:6:7:8,5,1,yes,0,0,0,magic,0, AB_SILENTIUM,Silentium //CHECk Marked magic attack as well. Hmmmm....
+
+2515,11,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, AB_SECRAMENT,Secrament
+
+//****
+// RA Ranger
+//****
+2233,9,8,1,-1,0x2,3:3:3:3:3:4:4:4:4:5,10,-3,yes,0,0,0,weapon,0, RA_ARROWSTORM,Arrow Storm
+2234,0,6,4,0,0,0,5,1,yes,0,0,0,none,0, RA_FEARBREEZE,Fear Breeze
+2235,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RA_RANGERMAIN,Ranger Main
+2236,9,8,1,-1,0,0,10,1,yes,0,0,0,weapon,0, RA_AIMEDBOLT,Aimed Bolt
+2237,9,6,2,0,0x3,3,1,1,no,0,0,0,none,0, RA_DETONATOR,Detonator
+2238,3,6,2,0,0x3,2,5,1,no,0,0x80,3,misc,0, RA_ELECTRICSHOCKER,Electric Shocker
+2239,3,6,2,0,0x42,3,5,1,no,0,0x80,3,misc,0, RA_CLUSTERBOMB,Cluster Bomb
+2240,0,6,4,0,0,0,1,1,no,0,0,0,none,0, RA_WUGMASTERY,Warg Mastery
+2241,0,6,4,0,0,0,3,1,no,0,0,0,none,0, RA_WUGRIDER,Warg Rider
+2242,0,6,4,-1,0x2,1,1,0,no,0,0,0,weapon,0, RA_WUGDASH,Warg Dash
+2243,9,6,1,0,0,0,5,1,no,0,0,0,weapon,0, RA_WUGSTRIKE,Warg Strike
+2244,9,6,1,0,0,0,5,1,no,0,0,0,weapon,0, RA_WUGBITE,Warg Bite
+2245,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RA_TOOTHOFWUG,Tooth of Warg
+2246,0,6,4,0,0x2,3:4:5:6:7,5,1,no,0,0,0,weapon,0, RA_SENSITIVEKEEN,Sensitive Keen
+2247,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, RA_CAMOUFLAGE,Camouflage
+2248,0,0,0,0,0,0,5,0,no,0,0,0,none,0, RA_RESEARCHTRAP,Research Trap
+2249,3,6,2,3,0x43,2,1,1,no,0,0x80,1,misc,0, RA_MAGENTATRAP,Magenta Trap
+2250,3,6,2,1,0x43,2,1,1,no,0,0x80,1,misc,0, RA_COBALTTRAP,Cobalt Trap
+2251,3,6,2,2,0x43,2,1,1,no,0,0x80,1,misc,0, RA_MAIZETRAP,Maize Trap
+2252,3,6,2,4,0x43,2,1,1,no,0,0x80,1,misc,0, RA_VERDURETRAP,Verdure Trap
+2253,3,6,2,0,0x42,2,5,1,no,0,0x80,2,misc,0, RA_FIRINGTRAP,Firing Trap
+2254,3,6,2,0,0x42,2,5,1,no,0,0x80,2,misc,0, RA_ICEBOUNDTRAP,Icebound Trap
+
+//****
+// NC Mechanic
+2255,0,0,0,0,0,0,5,0,no,0,0,0,none,0, NC_MADOLICENCE,Mado License
+2256,11,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, NC_BOOSTKNUCKLE,Boost Knuckle
+2257,3,6,1,-1,0,0,3,1,no,0,0,0,weapon,0, NC_PILEBUNKER,Pile Bunker
+2258,13,6,1,-1,0,0,3,1,no,0,0,0,weapon,0, NC_VULCANARM,Vulcan Arm
+2259,5,6,1,3,0,2,3,1,no,0,0,5,weapon,0, NC_FLAMELAUNCHER,Flame Launcher
+2260,7,6,2,1,0x2,2:3:4,3,1,no,0,0,0,weapon,0, NC_COLDSLOWER,Cold Slower
+2261,7,6,2,-1,0x42,3:2:1,3,1,no,0,0,0,weapon,0, NC_ARMSCANNON,Arm Cannon
+2262,0,6,4,0,0x1,0,3,1,no,0,0,0,none,0, NC_ACCELERATION,Acceleration
+2263,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, NC_HOVERING,Hovering
+2264,0,6,4,0,0x1,0,1,1,no,0,0,0,none,7, NC_F_SIDESLIDE,Front-Side Slide
+2265,0,6,4,0,0x1,0,1,1,no,0,0,0,none,7, NC_B_SIDESLIDE,Back-Side Slide
+2266,0,0,0,0,0,0,4,0,no,0,0,0,none,0, NC_MAINFRAME,Mainframe Restructure // Check me. Part of the code notes translated to "The amount of fuel have".
+2267,0,6,4,-1,0x42,2:3:4,3,1,no,0,0,0,misc,5, NC_SELFDESTRUCTION,Self Destruction
+2268,0,6,4,0,0x1,0,4,1,yes,0,0,0,none,0, NC_SHAPESHIFT,Shape Shift
+2269,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, NC_EMERGENCYCOOL,Emergency Cool
+2270,0,6,4,0,0x3,7,1,1,yes,0,0,0,none,0, NC_INFRAREDSCAN,Infrared Scan
+2271,9,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, NC_ANALYZE,Analyze
+2272,0,6,4,0,0x3,1:2:3,3,1,yes,0,0,0,none,0, NC_MAGNETICFIELD,Magnetic Field
+2273,0,6,4,0,0x1,0,3,1,yes,0,0,0,none,0, NC_NEUTRALBARRIER,Neutral Barrier
+2274,0,6,4,0,0x1,0,3,1,yes,0,0,0,none,0, NC_STEALTHFIELD,Stealth Field
+2275,5,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, NC_REPAIR,Repair
+2276,0,0,0,0,0,0,10,0,no,0,0,0,none,0, NC_TRAININGAXE,Axe Training
+2277,0,0,0,0,0,0,5,0,no,0,0,0,none,0, NC_RESEARCHFE,Research Fire/Earth
+2278,4:5:6:7:8,6,1,-1,0,0,5,1,no,0,0,0,weapon,2:3:4:5:6, NC_AXEBOOMERANG,Axe Boomerang
+2279,1,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, NC_POWERSWING,Power Swing
+2280,0,8,4,-1,0x2,2:2:3:3:3,5,-6,no,0,0,0,weapon,0, NC_AXETORNADO,Axe Tornado // Check me. Takes 20 * Skill LV amount of HP each use.
+2281,2,6,2,0,0x1,0,5,1,yes,0,0,2,none,0, NC_SILVERSNIPER,FAW - Silver Sniper
+2282,2,6,2,0,0x1,0,5,1,yes,0,0,2,none,0, NC_MAGICDECOY,FAW - Magic Decoy //CHECK FIX ME!!!! Wind and Earth stones spawning opposite decoys.
+2283,2,6,1,0,0x1,0,1,1,no,0,0,0,none,0, NC_DISJOINT,FAW Removal
+
+//****
+// SC Shadow Chaser
+2284,1,6,1,-1,0x2,1,5,1,no,0,0,0,weapon,0, SC_FATALMENACE,Fatal Menace
+2285,0,6,4,0,0x1,0,10,1,no,0,0,0,none,0, SC_REPRODUCE,Reproduce
+2286,0,6,4,0,0x1,0,10,1,yes,0,0,0,none,0, SC_AUTOSHADOWSPELL,Auto Shadow Spell
+2287,5,6,1,0,0x1,0,5,1,no,0,0,0,none,0, SC_SHADOWFORM,Shadow Form
+2288,7:7:7:9:9:9:9:11:11:11,8,1,-1,0,0,10,-3,yes,0,0,0,weapon,3, SC_TRIANGLESHOT,Triangle Shot
+2289,0,6,4,0,0x3,2,5,1,no,0,0,0,none,0, SC_BODYPAINT,Body Painting
+2290,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, SC_INVISIBILITY,Invisibility
+2291,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SC_DEADLYINFECT,Deadly Infect
+2292,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_ENERVATION,Masquerade - Enervation
+2293,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_GROOMY,Masquerade - Gloomy
+2294,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_IGNORANCE,Masquerade - Ignorance
+2295,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_LAZINESS,Masquerade - Laziness
+2296,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_UNLUCKY,Masquerade - Unlucky
+2297,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_WEAKNESS,Masquerade - Weakness
+2298,3,6,1,0,0x1,0,5,1,yes,0,0,0,weapon,0, SC_STRIPACCESSARY,Strip Accessory //CHECK Is weapon attack type needed?
+2299,7,6,2,0,0x1,0,3,1,yes,0,0,3,none,0, SC_MANHOLE,Man Hole
+2300,7,6,2,0,0x1,0,3,1,yes,0,0,1,none,0, SC_DIMENSIONDOOR,Dimension Door
+2301,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0, SC_CHAOSPANIC,Chaos Panic
+2302,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0, SC_MAELSTROM,Maelstrom
+2303,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0, SC_BLOODYLUST,Bloody Lust
+2304,0,6,4,-1,0,0,3,1,no,0,0,0,weapon,0, SC_FEINTBOMB,Feint Bomb
+
+//****
+// LG Royal Guard
+2307,11,8,1,-1,0,2,5,1,no,0,0,10,weapon,0, LG_CANNONSPEAR,Cannon Spear
+2308,7,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, LG_BANISHINGPOINT,Banishing Point
+2309,0,6,4,0,0x3,2,3,1,no,0,0,0,none,0, LG_TRAMPLE,Trample
+2310,1,6,1,0,0,0,5,1,no,0,0,0,weapon,0, LG_SHIELDPRESS,Shield Press
+2311,0,6,4,0,0x3,3,5,1,no,0,0,0,none,0, LG_REFLECTDAMAGE,Reflect Damage
+2312,5,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, LG_PINPOINTATTACK,Pinpoint Attack
+2313,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_FORCEOFVANGUARD,Force of Vanguard
+2314,1,6,1,-1,0,0,1,1,no,0,0,0,weapon,0, LG_RAGEBURST,Rage Burst
+2315,0,6,4,0,0x2,3,3,1,yes,0,0,0,none,2, LG_SHIELDSPELL,Shield Spell
+2316,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_EXEEDBREAK,Exceed Break
+2317,1,6,2,-1,0x2,5,5,1,yes,0,0,0,none,3:4:5:6:7, LG_OVERBRAND,Over Brand //CHECK I know the splash is needed somehow for the strange AoE it gives.
+2318,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_PRESTIGE,Prestige
+2319,0,6,4,0,0x1,3,5,1,no,0,0,0,weapon,0, LG_BANDING,Banding //CHECK Splash isnt needed right? Banding has its own UNIT ID.
+2320,0,6,4,-1,0x2,3,5,1,yes,0,0,0,weapon,0, LG_MOONSLASHER,Moon Slasher
+2321,1,8,2,6,0x2,5,5,-7,yes,0,0,0,weapon,0, LG_RAYOFGENESIS,Ray of Genesis
+2322,0,6,16,0,0x3,1,5,1,yes,0,0,0,none,0, LG_PIETY,Piety
+2323,0,8,4,2,0x2,1:1:2:2:3,5,-5,yes,0,0,0,weapon,0, LG_EARTHDRIVE,Earth Drive
+2324,3,8,1,-1,0,0,5,3,yes,0,0,0,weapon,0, LG_HESPERUSLIT,Hesperus Lit
+2325,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_INSPIRATION,Inspiration
+
+//****
+// SR Sura
+2326,-2,8,1,-1,0,0,10,2,no,0,0,0,weapon,0, SR_DRAGONCOMBO,Dragon Combo //CHECK Is this 2 regular hits or sub hits? Yes its 2 sub hits.
+2327,0,8,4,-1,0x2,2,5,-3,no,0,0,0,weapon,3, SR_SKYNETBLOW,Sky Net Blow //CHECK Video shows 3 hits. Its sub hits right? Data check shows no sub, one source shows 3 hits, another shows 5.
+2328,0,6,4,-1,0x2,1:2:3:4:5,5,1,no,0,0,0,weapon,0, SR_EARTHSHAKER,Earth Shaker //CHECK Must add a check in battle.c to triple damage if hitting a hidden target.
+2329,-2,8,4,-1,0,0,5,-2,no,0,0x200,0,weapon,0, SR_FALLENEMPIRE,Fallen Empire //CHECK Video shows 2 hits. Is it sub hits? Yes its divided between 2 hits.
+2330,-2,6,1,-1,0x42,1:1:1:1:1:2:2:2:2:2,10,1,yes,0,0,0,weapon,0, SR_TIGERCANNON,Tiger Cannon //CHECK Need to fix to be enemy targeted and also combo after Fallen Empire.
+2331,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SR_HELLGATE,Hell Gate
+2332,5,6,4,-1,0x2,3,5,1,no,0,0,0,weapon,0, SR_RAMPAGEBLASTER,Rampage Blaster
+2333,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SR_CRESCENTELBOW,Crescent Elbow //CHECK Check the autospell ID.
+2334,0,6,4,0,0x3,1:1:2:2:3,5,1,no,0,0,0,none,0, SR_CURSEDCIRCLE,Cursed Circle //CHECK Code shows it takes up to 5% of your HP upon use?
+2335,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SR_LIGHTNINGWALK,Lightning Walk
+2336,7:8:9:10:11,6,1,-1,0,0,5,1,no,0,0,0,weapon,2:3:4:5:6, SR_KNUCKLEARROW,Knuckle Arrow
+2337,0,6,4,-1,0x2,2,1,1,yes,0,0,0,weapon,0, SR_WINDMILL,Windmill
+2338,0,6,4,0,0x1,0,10,1,no,0,0,0,none,0, SR_RAISINGDRAGON,Raising Dragon
+2339,0,0,0,0,0,0,5,1,no,0,0,0,none,0, SR_GENTLETOUCH,Gentle Touch
+2340,0,6,4,0,0x3,2,1,1,no,0,0,0,none,0, SR_ASSIMILATEPOWER,Assimilate Power
+2341,3,6,16,0,0x1,0,1,1,yes,0,0x200,0,none,0, SR_POWERVELOCITY,Power Velocity
+2342,1,6,1,-1,0x20,0,5,1,no,0,0,0,weapon,3, SR_CRESCENTELBOW_AUTOSPELL,Crescent Elbow Autospell //CHECK Does this ignore defense?
+2343,1:2:3:3:4:4:5:5:6:7,8,1,0,0,0,10,-7,yes,0,0,0,weapon,0, SR_GATEOFHELL,Gate of Hell //CHECK Need to fix to be enemy targeted and also combo after Fallen Empire
+2344,2,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, SR_GENTLETOUCH_QUIET,Gentle Touch - Quiet
+2345,2,6,16,0,0x1,0,5,1,no,0,0,0,magic,0, SR_GENTLETOUCH_CURE,Gentle Touch - Cure //CHECK Its a healing skill. Guessing it has to be magic type? Healing isnt working.
+2346,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, SR_GENTLETOUCH_ENERGYGAIN,Gentle Touch - Energy Gain
+2347,2,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, SR_GENTLETOUCH_CHANGE,Gentle Touch - Change
+2348,2,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, SR_GENTLETOUCH_REVITALIZE,Gentle Touch - Revitalize
+//More from Sura but not following ID order
+2517,0,6,4,-1,0x2,3:4:5:6:7,5,1,no,0,0,0,weapon,0, SR_HOWLINGOFLION,Howling of Lion
+2518,11,6,2,-1,0x2,2:2:3:3:4,5,1,no,0,0,0,weapon,0, SR_RIDEINLIGHTNING,Ride In Lightening
+
+//****
+// WA Wanderer
+2350,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, WA_SWING_DANCE,Swing Dance
+2351,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, WA_SYMPHONY_OF_LOVER,Symphony of Lovers
+2352,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, WA_MOONLIT_SERENADE,Moonlit Serenade
+
+//****
+// MI Minstrel
+2381,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, MI_RUSH_WINDMILL,Windmill Rush Attack
+2382,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, MI_ECHOSONG,Echo Song
+2383,9,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, MI_HARMONIZE,Harmonize
+
+//****
+// WM Wanderer/Minstrel
+2412,0,0,0,0,0,0,10,0,no,0,0,0,none,0, WM_LESSON,Lesson
+2413,9,8,1,-1,0,0,5,-2:-2:-3:-3:-4,yes,0,0,0,magic,0, WM_METALICSOUND,Metallic Sound
+2414,9,6,2,-1,0x3,1,5,1,yes,0,0x80,3,none,0, WM_REVERBERATION,Reverberation
+2415,0,6,1,-1,0x6,1,5,1,no,0,0,0,weapon,0, WM_REVERBERATION_MELEE,Reverberation Melee
+2416,0,6,1,0,0x6,1,5,1,no,0,0,0,magic,0, WM_REVERBERATION_MAGIC,Reverberation Magic
+2417,11,6,2,0,0x3,5,1,1,no,0,0,0,none,0, WM_DOMINION_IMPULSE,Dominion Impulse
+2418,9,6,2,-1,0x1,0,5,1,yes,0,0,0,none,0, WM_SEVERE_RAINSTORM,Severe Rainstorm
+2419,9,6,2,0,0x3,1,5,1,yes,0,0x80,5,none,0, WM_POEMOFNETHERWORLD,Poem of The Netherworld //CHECK May need to recode too.
+2420,0,6,4,0,0x2,2:3:4:5:6,5,1,yes,0,0,0,none,0, WM_VOICEOFSIREN,Voice of Siren
+2421,7,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, WM_DEADHILLHERE,Valley of Death
+2422,7,6,4,0,0x3,5:6:7:8:9,5,1,yes,0,0,0,none,0, WM_LULLABY_DEEPSLEEP,Deep Sleep Lullaby
+2423,0,6,4,0,0x3,3:4:5:6:7,5,1,yes,0,0,0,none,0, WM_SIRCLEOFNATURE,Circle of Nature's Sound
+2424,9,6,4,0,0x1,0,5,1,yes,0,0,0,magic,0, WM_RANDOMIZESPELL,Improvised Song
+2425,9,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, WM_GLOOMYDAY,Gloomy Day
+2426,9,6,2,0,0x2,2:3:3:4:4,5,1,yes,0,0x4000,0,weapon,0, WM_GREAT_ECHO,Great Echo
+2427,0,6,4,0,0x3,5:6:7:8:9,5,1,yes,0,0x4000,0,none,0, WM_SONG_OF_MANA,Song of Mana
+2428,0,6,4,0,0x3,5:6:7:8:9,5,1,yes,0,0x4000,0,none,0, WM_DANCE_WITH_WUG,Dance With A Warg
+2429,9,6,1,0,0x2,2:2:3:3:4,5,1,yes,0,0x4000,0,weapon,0, WM_SOUND_OF_DESTRUCTION,Sound of Destruction //CHECK Source shows its magic attack. Need to confirm before changing.
+2430,0,6,4,0,0x3,3:4:5:6:7,5,1,yes,0,0x4000,0,none,0, WM_SATURDAY_NIGHT_FEVER,Saturday Night Fever
+2431,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,magic,0, WM_LERADS_DEW,Lerad's Dew
+2432,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,none,0, WM_MELODYOFSINK,Melody of Sink
+2433,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,none,0, WM_BEYOND_OF_WARCRY,Warcry of Beyond
+2434,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,none,0, WM_UNLIMITED_HUMMING_VOICE,Unlimited Humming Voice
+2516,11,6,1,-1,0x2,5,5,1,no,0,0,0,weapon,0, WM_SEVERE_RAINSTORM_MELEE,Severe Rainstorm Melee
+
+//****
+// SO Sorcerer
+2443,0,6,4,3,0,0,5,1,yes,0,0,8:10:12:14:16,magic,0, SO_FIREWALK,Fire Walk //CHECK Video and data shows each cell only hits once.
+2444,0,6,4,4,0,0,5,1,yes,0,0,8:10:12:14:16,magic,0, SO_ELECTRICWALK,Electric Walk
+2445,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SO_SPELLFIST,Spell Fist
+2446,9,6,2,2,0,0,5,-3,yes,0,0,0,magic,0, SO_EARTHGRAVE,Earth Grave
+2447,9,6,2,1,0,0,5,-5,yes,0,0,0,magic,0, SO_DIAMONDDUST,Diamond Dust
+2448,9,6,1,5,0x2,1:1:1:1:2,5,1,yes,0,0,0,magic,0, SO_POISON_BUSTER,Poison Buster
+2449,9,6,2,0,0,0,5,1,yes,0,0,0,magic,0, SO_PSYCHIC_WAVE,Psychic Wave
+2450,9,6,2,5,0,0,5,1,yes,0,0,0,magic,0, SO_CLOUD_KILL,Cloud Kill
+2451,9,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, SO_STRIKING,Striking //CHECK Data shows a % for increased successful refine rate. Is this true?
+2452,9,6,2,3,0x1,0,5,1,yes,0,0,0,magic,0, SO_WARMER,Warmer
+2453,9,6,2,0,0x1,0,5,1,yes,0,0,0,magic,0, SO_VACUUM_EXTREME,Vacuum Extreme
+2454,9,6,1,4,0x2,1:1:2:2:3,5,1,yes,0,0,0,magic,0, SO_VARETYR_SPEAR,Varetyr Spear
+2455,9,6,2,0,0x3,1:1:2:2:3,5,1,yes,0,0,0,magic,0, SO_ARRULLO,Arrullo
+2456,0,6,4,0,0x1,0,4,1,yes,0,0,0,none,0, SO_EL_CONTROL,Spirit Control
+2457,0,6,4,3,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_AGNI,Summon Fire Spirit Agni
+2458,0,6,4,1,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_AQUA,Summon Water Spirit Aqua
+2459,0,6,4,4,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_VENTUS,Summon Wind Spirit Ventus
+2460,0,6,4,2,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_TERA,Summon Earth Spirit Tera
+2461,5,6,1,0,0x1,0,1,1,no,0,0,0,none,0, SO_EL_ACTION,Elemental Action
+2462,0,6,4,0,0x1,0,2,1,yes,0,0,0,none,0, SO_EL_ANALYSIS,Four Spirit Analysis
+2463,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SO_EL_SYMPATHY,Spirit Sympathy
+2464,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, SO_EL_CURE,Spirit Recovery
+2465,9,6,2,3,0x1,0,3,1,yes,0,0,1,magic,0, SO_FIRE_INSIGNIA,Fire Insignia //CHECK All 4 insignia skills can be targeted and animations work
+2466,9,6,2,1,0x1,0,3,1,yes,0,0,1,magic,0, SO_WATER_INSIGNIA,Water Insignia // but its effects havent been coded yet.
+2467,9,6,2,4,0x1,0,3,1,yes,0,0,1,magic,0, SO_WIND_INSIGNIA,Wind Insignia
+2468,9,6,2,2,0x1,0,3,1,yes,0,0,1,magic,0, SO_EARTH_INSIGNIA,Earth Insignia
+
+//****
+// GN Genetic
+2474,0,0,0,0,0,0,5,0,no,0,0,0,none,0, GN_TRAINING_SWORD,Sword Training
+2475,0,0,0,0,0,0,5,0,no,0,0,0,none,0, GN_REMODELING_CART,Cart Remodeling
+2476,0,6,4,-1,0x2,2,5,1,no,0,0,0,weapon,2, GN_CART_TORNADO,Cart Tornado
+2477,7:8:9:10:11,6,1,-1,0x2,1:1:2:2:3,5,1,yes,0,0,0,weapon,0, GN_CARTCANNON,Cart Cannon
+2478,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, GN_CARTBOOST,Cart Boost
+2479,9,6,2,0,0,0,5,1,yes,0,0x80,5,misc,0, GN_THORNS_TRAP,Thorn Trap
+2480,11,6,1,0,0x1,0,5,1,yes,0,0,3,misc,0, GN_BLOOD_SUCKER,Blood Sucker //CHECK Data says its a magic attack. Hmmmm....
+2481,11,6,1,-1,0x2,1:2:3:4:5,5,1,yes,0,0,0,weapon,0, GN_SPORE_EXPLOSION,Spore Explosion //CHECK Data says its element is set to neutral. Need to confirm.
+2482,11,6,16,0,0,0,5,1,yes,0,0,1,weapon,2, GN_WALLOFTHORN,Wall of Thorns
+2483,11,6,2,0,0x3,4,10,1,yes,0,0x2000,0,weapon,0, GN_CRAZYWEED,Crazy Weed
+2484,0,6,2,2,0x2,3,10,1,no,0,0x2000,0,weapon,0, GN_CRAZYWEED_ATK,Crazy Weed Attack
+2485,9,6,2,3,0,0,5,1,yes,0,0,0,magic,0, GN_DEMONIC_FIRE,Demonic Fire
+2486,9,6,2,0,0,0,5,1,yes,0,0,0,none,0, GN_FIRE_EXPANSION,Fire Expansion //CHECK FIX ME!!!! Level 1 is reducing the damage. Should increase it by 50%
+2487,9,6,2,0,0,0,1,1,no,0,0,0,none,0, GN_FIRE_EXPANSION_SMOKE_POWDER,Fire Expansion Smoke Powder
+2488,9,6,2,0,0,0,1,1,no,0,0,0,none,0, GN_FIRE_EXPANSION_TEAR_GAS,Fire Expansion Tear Gas
+2489,11,6,1,0,0,0,10,1:2:3:4:5:6:7:8:9:10,no,0,0,0,weapon,0, GN_FIRE_EXPANSION_ACID,Fire Expansion Acid
+2490,9,6,2,0,0x3,1,5,1,yes,0,0x80,2:3:4:5:6,none,0, GN_HELLS_PLANT,Hell's Plant
+2491,0,6,1,0,0xC0,0,5,1,no,0,0,0,misc,0, GN_HELLS_PLANT_ATK,Hell's Plant Attack
+2492,0,6,4,0,0x3,6:7:8:9:10,5,1,yes,0,0,0,none,0, GN_MANDRAGORA,Howling of Mandragora
+2493,11,6,16,0,0x1,0,1,1,yes,0,0,0,none,0, GN_SLINGITEM,Sling Item
+2494,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, GN_CHANGEMATERIAL,Change Material
+2495,0,6,4,0,0x1,0,2,1,no,0,0,0,none,0, GN_MIX_COOKING,Mix Cooking
+2496,0,6,4,0,0x1,0,2,1,no,0,0,0,none,0, GN_MAKEBOMB,Create Bomb
+2497,0,6,4,0,0x1,0,10,1,no,0,0,0,none,0, GN_S_PHARMACY,Special Pharmacy
+2498,11,6,1,0,0,0,1,1,no,0,0,0,weapon,0, GN_SLINGITEM_RANGEMELEEATK,Sling Item Attack
+
+// Episode 13.3
+//2533,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, ALL_ODINS_RECALL,Odin's Recall
+2534,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, RETURN_TO_ELDICASTES,Return To Eldicastes
+2535,0,0,4,0,0x1,0,1,0,no,0,0x1,0,none,0, ALL_BUYING_STORE,Open Buying Store
+2536,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, ALL_GUARDIAN_RECALL,Guardian's Recall
+//2537,9,6,16,0,0x1,0,2,1,yes,0,0,0,magic,0, ALL_ODINS_POWER,Odin's Power
+//2538,0,0,0,0,0,0,??,0,no,0,0,0,none,0, BEER_BOTTLE_CAP,Beer Bottle Cap
+//2539,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_ASSASSINCROSS,Assassin Cross of Sunset 2
+//2540,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_DISSONANCE,Dissonance 2
+//2541,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_UGLYDANCE,Ugly Dance 2
+//2542,0,0,0,0,0,0,??,0,no,0,0,0,none,0, ALL_TETANY,Tetany
+//2543,0,0,0,0,0,0,??,0,no,0,0,0,none,0, ALL_RAY_OF_PROTECTION,Ray of Protection
+//2544,0,0,0,0,0,0,??,0,no,0,0,0,none,0, MC_CARTDECORATE,Decorate Cart
+
+//****
+// Kagerou & Oboro
+3001,0,6,4,0,0,0,1,1,no,0,0,0,none,0, KO_YAMIKUMO,Yamikumo
+3002,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, KO_RIGHT,Right Hand Mastery
+3003,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, KO_LEFT,Left Hand Mastery
+3004,3:4:5:6:7,8,1,-1,0,0,5,-2,no,0,0,0,weapon,0, KO_JYUMONJIKIRI,Cross Strike
+3005,2,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, KO_SETSUDAN,Soul Sever
+3006,7:8:9:10:11,6,2,0,0x2,2,5,0,no,0,0,0,weapon,0, KO_BAKURETSU,Bakuretsu Kunai
+3007,0,6,4,-1,0x42,4:4:4:4:5,5,0,no,0,0,0,misc,0, KO_HAPPOKUNAI,Happo Kunai
+3008,9,8,2,0,0x52,2,10,-10,no,0,0,0,misc,0, KO_MUCHANAGE,Mucha Nage
+3009,9:10:11:12:13,8,2,-1,0x2,3,5,2,no,0,0,0,weapon,0, KO_HUUMARANKA,Huuma Shuriken Ranka
+3010,3,6,4,0,0x43,0,5,1,no,0,0x80,0,misc,0, KO_MAKIBISHI,Makibishi
+3011,0,6,4,0,0x1,0,5,0,yes,0,0,0,none,0, KO_MEIKYOUSISUI,Meikyo Shisui
+3012,0,6,4,0,0x1,0,5,0,no,0,0,1,none,7, KO_ZANZOU,Zanzou
+3013,5,6,1,0,0x1,0,5,0,no,0,0,0,none,0, KO_KYOUGAKU,Kyougaku
+3014,5,6,1,0,0x1,0,5,0,no,0,0,0,none,0, KO_JYUSATSU,Jyusatsu
+3015,0,6,4,3,0x1,0,1,1,no,0,0,0,none,0, KO_KAHU_ENTEN,Kahu Enten
+3016,0,6,4,1,0x1,0,1,1,no,0,0,0,none,0, KO_HYOUHU_HUBUKI,Hyouhu Hubuki
+3017,0,6,4,4,0x1,0,1,1,no,0,0,0,none,0, KO_KAZEHU_SEIRAN,Kazehu Seiran
+3018,0,6,4,2,0x1,0,1,1,no,0,0,0,none,0, KO_DOHU_KOUKAI,Dohu Koukai
+3019,11,6,1,0,0,0,5,0,no,0,0,0,weapon,0, KO_KAIHOU,Technique Kaihou
+3020,7,6,2,0,0,0,1,3,yes,0,0,0,magic,0, KO_ZENKAI,Zenkai
+3021,5:6:7:8:9,6,16,0,0x1,0,5,1,no,0,0,0,none,0, KO_GENWAKU,Genwaku
+3022,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, KO_IZAYOI,Izayoi
+3023,0,6,4,0,0x3,2:3:4:5:6,5,0,no,0,0,0,none,0, KG_KAGEHUMI,Kagehumi
+3024,7,6,1,0,0x1,0,5,1,no,0,0,0,none,0, KG_KYOMU,Kyomu
+3025,7,6,16,0,0x1,0,5,1,no,0,0,0,none,0, KG_KAGEMUSYA,Kagemusha
+3026,7,6,16,0,0x1,0,5,1,no,0,0,0,none,0, OB_ZANGETSU,Zangetsu
+3027,7,6,16,0,0x1,0,5,1,no,0,0,0,none,0, OB_OBOROGENSOU,Oboro Gensou
+3028,1,6,4,0,0x2,3,1,1,no,0,0,0,weapon,0, OB_OBOROGENSOU_TRANSITION_ATK,
+3029,7,6,1,0,0x1,0,5,0,no,0,0,0,none,0, OB_AKAITSUKI,Akaitsuki
+
+8001,9,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, HLIF_HEAL,Healing Touch
+8002,0,6,4,0,0x3,-1,5,1,no,0,0,0,none,0, HLIF_AVOID,Avoid
+8003,0,0,0,0,0,1,5,0,no,0,0,0,none,0, HLIF_BRAIN,Brain Surgery
+8004,0,6,4,0,0x1,0,3,0,no,0,0,0,none,0, HLIF_CHANGE,Change
+8005,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HAMI_CASTLE,Castling
+8006,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HAMI_DEFENCE,Defense
+8007,0,0,0,0,0x1,0,5,0,no,0,0,0,none,0, HAMI_SKIN,Adamantium Skin
+8008,0,6,4,0,0x1,0,3,0,no,0,0,0,none,0, HAMI_BLOODLUST,Bloodlust
+8009,1,8,1,0,0,0,5,-1:-2:-2:-2:-3,no,0,0,0,weapon,0, HFLI_MOON,Moonlight
+8010,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HFLI_FLEET,Fleeting Move
+8011,0,6,4,0,0x1,0,5,0,yes,0,0,0,misc,0, HFLI_SPEED,Speed
+8012,1,6,1,0,0,0,3,0,no,0,0,0,none,0, HFLI_SBR44,S.B.R.44
+8013,9,6,1,0,0,0,5,1:2:3:4:5,no,0,0,0,magic,0, HVAN_CAPRICE,Caprice
+8014,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HVAN_CHAOTIC,Benediction of Chaos
+8015,0,0,0,0,0x1,0,5,0,no,0,0,0,none,0, HVAN_INSTRUCT,Instruct
+8016,4,6,4,-1,0xD2,4,3,1,no,0,0,0,misc,0, HVAN_EXPLOSION,Bio Explosion
+//
+8018,9,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_SUMMON_LEGION,Summon Legion
+8019,5,6,1,5,0,0,5,1,no,0,0,0,weapon,0, MH_NEEDLE_OF_PARALYZE,Needle of Paralyze
+8020,5,6,2,5,0,0,5,1,no,0,0,1,weapon,0, MH_POISON_MIST,Poison Mist
+8021,1,6,1,0,0x1,0,5,1,no,0,0,0,none,0, MH_PAIN_KILLER,Pain Killer
+8022,0,6,4,0,0,0x1,5,1,no,0,0,0,none,0, MH_LIGHT_OF_REGENE,Light of Regene
+8023,0,6,4,0,0,0x1,5,1,no,0,0,0,none,0, MH_OVERED_BOOST,Overed Boost
+8024,7,6,1,4:0:4:0:4,0,0,5,1,no,0,0,0,magic,0, MH_ERASER_CUTTER,Eraser Cutter
+8025,7,6,2,4:0:4:0:4,0,0,5,1,no,0,0,0,magic,0, MH_XENO_SLASHER,Xeno Slasher
+8026,5:5:7:7:9,6,16,0,0x1,0,5,1,no,0,0,0,magic,0, MH_SILENT_BREEZE,Silent Breeze
+8027,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, MH_STYLE_CHANGE,Style Change
+8028,1,8,1,0,0,0,5,1,no,0,0,0,weapon,0, MH_SONIC_CRAW,Sonic Claw
+8029,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_SILVERVEIN_RUSH,Silver Bain Rush
+8030,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_MIDNIGHT_FRENZY,Midnight Frenzy
+8031,5:6:7:8:9,6,1,0,0,0,5,1,no,0,0,0,weapon,3, MH_STAHL_HORN,Steel Horn
+8032,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_GOLDENE_FERSE,Golden Heel
+8033,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_STEINWAND,Stone Wall
+8034,9,6,1,6,0x2,1:1:1:1:2,5,1,no,0,0,0,magic,0, MH_HEILIGE_STANGE,Holy Pole
+8035,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_ANGRIFFS_MODUS,Attack Mode
+8036,3:4:5:6:7,6,1,0,0,0,5,1,no,0,0,0,weapon,0, MH_TINDER_BREAKER,Tinder Breaker
+8037,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_CBC,Continual Break Combo
+8038,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_EQC,Eternal Quick Combo
+8039,0,6,4,3,0x2,1:1:1:2:2,5,1,no,0,0,0,weapon,0, MH_MAGMA_FLOW,Magma Flow
+8040,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_GRANITIC_ARMOR,Granitic Armor
+8041,7,6,2,3,0x2,0,5,1,no,0,0,1,weapon,0, MH_LAVA_SLIDE,Lava Slide
+8042,0,6,4,3,0x1,0,5,1,no,0,0,0,none,0, MH_PYROCLASTIC,Pyroclastic
+8043,7,6,2,0,0x1,0,5,1,no,0,0,3,none,0, MH_VOLCANIC_ASH,Volcanic Ash
+
+// Mercenary Skill Place holders
+8201,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, MS_BASH,Bash
+8202,0,6,4,3,0x2,2,10,1,no,0,0,0,weapon,2, MS_MAGNUM,Magnum_Break
+8203,-2,6,1,-1,0x2,1,10,1,no,33,0,0,weapon,1, MS_BOWLINGBASH,Bowling_Bash
+8204,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, MS_PARRYING,Parry
+8205,0,6,4,0,0,0,10,1,no,0,0,0,weapon,0, MS_REFLECTSHIELD,Shield_Reflect
+8206,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, MS_BERSERK,Frenzy
+8207,-9,8,1,-1,0,0,10,2,no,0,0,0,weapon,0, MA_DOUBLE,Double_Strafe
+8208,-9,6,2,-1,0x2,2,10,1,no,0,0x2000,0,weapon,2, MA_SHOWER,Arrow_Shower
+8209,3,6,2,0,0x1,0,5,1,no,0,0x80,0,misc,6:7:8:9:10, MA_SKIDTRAP,Skid_Trap
+8210,3,6,2,2,0x40,0,5,1,no,0,0x80,0,misc,0, MA_LANDMINE,Land_Mine
+8211,3,6,2,0,0x3,2,5,1,no,0,0x80,0,misc,0, MA_SANDMAN,Sandman
+8212,3,6,2,1,0x42,1,5,1,no,0,0x80,0,weapon,0, MA_FREEZINGTRAP,Freezing_Trap
+8213,2,6,32,0,0x1,0,1,1,no,0,0,0,misc,0, MA_REMOVETRAP,Remove_Trap
+8214,-9,6,1,-1,0x2,0,1,1,no,0,0x1,0,weapon,6, MA_CHARGEARROW,Arrow_Repel
+8215,9,8,1,-1,0,2,5,1,yes,0,0,13,weapon,0, MA_SHARPSHOOTING,Focused_Arrow_Strike
+8216,-2,8,1,-1,0,0,10,3,no,0,0,0,weapon,0, ML_PIERCE,Pierce
+8217,-2,6,1,-1,0x1,0,10,1,no,33,0,0,weapon,3, ML_BRANDISH,Brandish_Spear
+8218,5,8,1,-1,0x20,0,5,5,no,0,0,0,weapon,0, ML_SPIRALPIERCE,Spiral_Pierce
+8219,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, ML_DEFENDER,Defending_Aura
+8220,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, ML_AUTOGUARD,Guard
+8221,7:8:9:10:11,6,16,0,0x1,0,5,1,yes,0,0x600,0,none,0, ML_DEVOTION,Sacrifice
+8222,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0, MER_MAGNIFICAT,Magnificat
+8223,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, MER_QUICKEN,Two-Hand_Quicken
+8224,0,6,4,3,0x3,3,1,1,yes,0,0,0,magic,0, MER_SIGHT,Sight
+8225,1,8,1,-1,0,0,5,3,no,0,0,0,weapon,0, MER_CRASH,Crash
+8226,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_REGAIN,Regain
+8227,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_TENDER,Tender
+8228,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_BENEDICTION,Benediction
+8229,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_RECUPERATE,Recuperate
+8230,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_MENTALCURE,Mental_Cure
+8231,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_COMPRESS,Compress
+8232,9,6,1,0,0x1,0,10,1,no,0,0,0,none,0, MER_PROVOKE,Provoke
+8233,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, MER_AUTOBERSERK,Berserk
+8234,9,6,1,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_DECAGI,Decrease_AGI
+8235,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, MER_SCAPEGOAT,Scapegoat
+8236,5,6,1,0,0x1,0,10,0,yes,0,0,0,magic,0, MER_LEXDIVINA,Lex_Divina
+8237,9,6,1,0,0x1,0,1,1,yes,0,0,0,magic,0, MER_ESTIMATION,Sense
+8238,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_KYRIE,Kyrie Eleison
+8239,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_BLESSING,Blessing
+8240,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_INCAGI,Increase Agility
+
+// Elemental Spirits Skills
+8401,0,6,4,3,0,0,1,1,no,0,0,0,weapon,2, EL_CIRCLE_OF_FIRE,Circle of Fire
+8402,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_FIRE_CLOAK,Fire Cloak
+8403,0,6,4,3,0,0,1,1,no,0,0,3,magic,2, EL_FIRE_MANTLE,Fire Mantle
+8404,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WATER_SCREEN,Water Screen
+8405,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WATER_DROP,Water Drop
+8406,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WATER_BARRIER,Water Barrier
+8407,0,6,4,0,0x1,0,1,1,no,0,0,0,none,5, EL_WIND_STEP,Wind Step
+8408,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WIND_CURTAIN,Wind Curtain
+8409,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_ZEPHYR,Zephyr
+8410,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_SOLID_SKIN,Solid Skin
+8411,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_STONE_SHIELD,Stone Shield
+8412,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_POWER_OF_GAIA,Power of Gaia
+8413,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_PYROTECHNIC,Pyrotechnic
+8414,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_HEATER,Heater
+8415,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_TROPIC,Tropic
+8416,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_AQUAPLAY,Aqua Play
+8417,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_COOLER,Cooler
+8418,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_CHILLY_AIR,Cool Air
+8419,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_GUST,Gust
+8420,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_BLAST,Blast
+8421,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WILD_STORM,Wild Storm
+8422,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_PETROLOGY,Petrology
+8423,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_CURSED_SOIL,Cursed Soil
+8424,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_UPHEAVAL,Upheaval
+8425,6,6,1,3,0,0,1,1,no,0,0,0,magic,0, EL_FIRE_ARROW,Fire Arrow
+8426,6,6,1,3,0,1,1,1,no,0,0,0,magic,0, EL_FIRE_BOMB,Fire Bomb
+8427,6,6,1,3,0,1,1,1,no,0,0,0,weapon,0, EL_FIRE_BOMB_ATK,Fire Bomb Attack
+8428,6,6,1,3,0,1,1,1,no,0,0,0,magic,0, EL_FIRE_WAVE,Fire Wave
+8429,6,6,1,3,0,1,1,1,no,0,0,0,weapon,0, EL_FIRE_WAVE_ATK,Fire Wave Attack
+8430,9,6,1,1,0,0,1,1,no,0,0,0,magic,0, EL_ICE_NEEDLE,Ice Needle
+8431,9,6,1,1,0,1,1,1,no,0,0,0,magic,0, EL_WATER_SCREW,Water Screw
+8432,9,6,1,1,0,1,1,1,no,0,0,0,weapon,0, EL_WATER_SCREW_ATK,Water Screw Attack
+8433,9,6,1,1,0,1,1,1,no,0,0,0,weapon,0, EL_TIDAL_WEAPON,Tidal Weapon
+8434,11,6,1,4,0,0,1,1,no,0,0,0,weapon,0, EL_WIND_SLASH,Wind Slasher
+8435,11,6,1,4,0,1,1,1,no,0,0,0,weapon,0, EL_HURRICANE,Hurricane Rage
+8436,7,6,1,4,0,0,1,1,no,0,0,0,magic,0, EL_HURRICANE_ATK,Hurricane Rage Attack
+8437,11,8,1,4,0,1,1,-3,no,0,0,0,weapon,0, EL_TYPOON_MIS,Typhoon Missile
+8438,11,8,1,4,0,1,1,-3,no,0,0,0,magic,0, EL_TYPOON_MIS_ATK,Typhoon Missile Attack
+8439,5,6,1,2,0,0,1,1,no,0,0,0,weapon,0, EL_STONE_HAMMER,Stone Hammer
+8440,3,6,1,2,0,1,1,1,no,0,0,0,weapon,0, EL_ROCK_CRUSHER,Rock Launcher
+8441,5,6,1,2,0,1,1,1,no,0,0,0,magic,0, EL_ROCK_CRUSHER_ATK,Rock Launcher Attack
+8442,9,6,1,2,0,1,1,-5,no,0,0,0,weapon,0, EL_STONE_RAIN,Stone Rain
+
+10000,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_APPROVAL,Official Guild Approval
+10001,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_KAFRACONTRACT,Kafra Contract
+10002,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_GUARDRESEARCH,Guardian Research
+10003,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_GUARDUP,Strengthen Guardians
+10004,0,0,0,0,0,0,10,0,no,0,0x10,0,none,0, GD_EXTENSION,Guild Extension
+10005,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_GLORYGUILD,Guild's Glory
+10006,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_LEADERSHIP,Great Leadership
+10007,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_GLORYWOUNDS,Glorious Wounds
+10008,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_SOULCOLD,Cold Heart
+10009,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_HAWKEYES,Sharp Gaze
+10010,0,0,4,0,0x3,15,1,0,yes,0,0x10,0,none,0, GD_BATTLEORDER,Battle Orders
+10011,0,0,4,0,0x3,15,3,0,yes,0,0x10,0,none,0, GD_REGENERATION,Regeneration
+10012,0,0,4,0,0x3,15,1,0,yes,0,0x10,0,none,0, GD_RESTORE,Restoration
+10013,0,0,4,0,0x3,0,1,0,yes,0,0x10,0,none,0, GD_EMERGENCYCALL,Urgent Call
+10014,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_DEVELOPMENT,Permanent Development
+//10015,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_ITEMEMERGENCYCALL,Unknown Skill
diff --git a/db/re/skill_db.txt b/db/re/skill_db.txt
index f81cd5b05..13ade9c02 100644
--- a/db/re/skill_db.txt
+++ b/db/re/skill_db.txt
@@ -1,1206 +1,1206 @@
-//id,range,hit,inf,element,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count,name,description
-// 01 ID
-// 02 range (combo skills do not check for range when used,
-// if range is < 5, the skill is considered melee-range)
-// 03 hit (8- repeated hitting, 6- single-hit)
-// 04 inf (0- passive, 1- enemy, 2- place, 4- self, 16- friend, 32- trap)
-// 05 element (0 - neutral, 1 - water, 2 - earth, 3 - fire, 4 - wind, 5 - poison,
-// 6 - holy, 7 - dark, 8 - ghost, 9 - undead, -1 - use weapon element
-// -2 - use endowed element, -3 - use random element.)
-// 06 nk (skill damage properties):
-// 0x01 - No damage skill
-// 0x02 - Has splash area
-// 0x04 - Damage should be split among targets
-// 0x08 - Skill ignores caster's % damage cards (misc type always ignores)
-// 0x10 - Skill ignores elemental adjustments
-// 0x20 - Skill ignores target's defense (misc type always ignores)
-// 0x40 - Skill ignores target's flee (magic type always ignores)
-// 0x80 - Skill ignores target's def cards
-// 07 splash/effect range (-1 for screen-wide)
-// 08 MaxLv
-// 09 Number of hits (when positive, damage is increased by hits,
-// negative values just show number of hits without increasing total damage)
-// 10 Cast interrupted when hit?
-// 11 defense-reduction rate during cast.
-// 12 inf2 (skill information 2):
-// 0x0001- quest skill
-// 0x0002- npc skill
-// 0x0004- wedding skill
-// 0x0008- spirit skill
-// 0x0010- guild skill
-// 0x0020- song/dance
-// 0x0040- ensemble skill
-// 0x0080- trap
-// 0x0100- skill that damages/targets yourself
-// 0x0200- cannot be casted on self (if inf = 4, auto-select target skill)
-// 0x0400- usable only on party-members (and enemies if skill is offensive)
-// 0x0800- usable only on guild-mates (and enemies if skill is offensive)
-// 0x1000- disable usage on enemies (for non-offensive skills).
-// 0x2000- skill ignores land protector (e.g. arrow shower)
-// 0x4000- chorus skill
-// 13 maxcount: max amount of skill instances to place on the ground when
-// player_land_skill_limit/monster_land_skill_limit is enabled. For skills
-// that attack using a path, this is the path length to be used.
-// 14 attack type (none, weapon, magic, misc)
-// 15 Blowcount (amount of tiles skill knockbacks)
-// 16 Name
-// 17 Description
-1,0,0,0,0,0,0,9,0,no,0,0,0,none,0, NV_BASIC,Basic Skill
-2,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, SM_SWORD,Sword Mastery
-3,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, SM_TWOHAND,Two-Handed Sword Mastery
-4,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SM_RECOVERY,Increase HP Recovery
-5,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, SM_BASH,Bash
-6,9,6,1,0,1,0,10,1,no,0,0,0,none,0, SM_PROVOKE,Provoke
-7,0,6,4,3,0x2,2,10,1,no,0,0,0,weapon,2, SM_MAGNUM,Magnum Break
-8,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, SM_ENDURE,Endure
-9,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MG_SRECOVERY,Increase SP Recovery
-10,0,6,4,3,0x3,3,1,1,yes,0,0,0,magic,0, MG_SIGHT,Sight
-11,9,6,1,8,0x6,1,10,1,yes,0,0,0,magic,0, MG_NAPALMBEAT,Napalm Beat
-12,9,8,2,8,0x1,0,10,1,yes,0,0,0,magic,0, MG_SAFETYWALL,Safety Wall
-13,9,8,1,8,0,0,10,1:1:2:2:3:3:4:4:5:5,yes,0,0,0,magic,0, MG_SOULSTRIKE,Soul Strike
-14,9,8,1,1,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_COLDBOLT,Cold Bolt
-15,9,6,1,1,0,0,10,1,yes,0,0,0,magic,0, MG_FROSTDIVER,Frost Diver
-16,2,6,1,2,0x1,0,10,1,yes,0,0,0,magic,0, MG_STONECURSE,Stone Curse
-17,9,6,1,3,0x2,2,10,1,yes,0,0,0,magic,0, MG_FIREBALL,Fire Ball
-18,9,6,2,3,0,0,10,1,yes,0,0,3,magic,2, MG_FIREWALL,Fire Wall
-19,9,8,1,3,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_FIREBOLT,Fire Bolt
-20,9,8,1,4,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_LIGHTNINGBOLT,Lightning Bolt
-21,9,8,2,4,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_THUNDERSTORM,Thunderstorm
-22,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AL_DP,Divine Protection
-23,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AL_DEMONBANE,Demon Bane
-24,0,6,4,6,0x3,2,1,1,yes,0,0,0,magic,0, AL_RUWACH,Ruwach
-25,9,6,2,0,0x1,0,1,1,yes,0,0,0,magic,0, AL_PNEUMA,Pneuma
-26,0,6,4,0,0x1,0,2,1,yes,0,0,0,magic,0, AL_TELEPORT,Teleport
-27,9,6,2,0,0x1,0,4,1,yes,0,0,3,magic,0, AL_WARP,Warp Portal
-28,9,6,16,6,0x21,0,10,1,yes,0,0,0,magic,0, AL_HEAL,Heal
-29,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, AL_INCAGI,Increase AGI
-30,9,6,1,0,0x1,0,10,1,yes,0,0,0,magic,0, AL_DECAGI,Decrease AGI
-31,0,6,4,0,0x1,0,1,1,yes,0,0,0,magic,0, AL_HOLYWATER,Aqua Benedicta
-32,0,6,4,0,0x3,15,10,1,yes,0,0,0,magic,0, AL_CRUCIS,Signum Crucis
-33,0,6,4,0,0x3,-1,10,1,yes,0,0,0,magic,0, AL_ANGELUS,Angelus
-34,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, AL_BLESSING,Blessing
-35,9,6,16,0,0x1,0,1,1,yes,0,0,0,magic,0, AL_CURE,Cure
-36,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_INCCARRY,Enlarge Weight Limit
-37,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_DISCOUNT,Discount
-38,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_OVERCHARGE,Overcharge
-39,1,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_PUSHCART,Pushcart
-40,1,6,4,0,0x1,0,1,1,no,0,0,0,none,0, MC_IDENTIFY,Item Appraisal
-41,1,6,4,0,0x1,0,10,1,no,0,0,0,none,0, MC_VENDING,Vending
-42,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, MC_MAMMONITE,Mammonite
-43,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AC_OWL,Owl's Eye
-44,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AC_VULTURE,Vulture's Eye
-45,0,6,4,0,0x3,3,10,1,no,0,0,0,weapon,0, AC_CONCENTRATION,Improve Concentration
-46,-9,8,1,-1,0,0,10,2,no,0,0,0,weapon,0, AC_DOUBLE,Double Strafe
-47,-9,6,2,-1,0x2,2,10,1,no,0,0x2000,0,weapon,2, AC_SHOWER,Arrow Shower
-48,-1,8,0,-1,0,0,10,2,no,0,0,0,weapon,0, TF_DOUBLE,Double Attack
-49,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, TF_MISS,Improve Dodge
-50,1,6,1,0,1,0,10,1,no,0,0,0,weapon,0, TF_STEAL,Steal
-51,1,6,4,0,1,0,10,1,no,0,0,0,none,0, TF_HIDING,Hiding
-52,-2,6,1,5,0,0,10,1,no,0,0,0,weapon,0, TF_POISON,Envenom
-53,9,6,16,5,0x1,0,1,1,no,0,0,0,weapon,0, TF_DETOXIFY,Detoxify
-54,9,6,16,6,0x1,0,4,1,yes,0,0,0,magic,0, ALL_RESURRECTION,Resurrection
-55,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, KN_SPEARMASTERY,Spear Mastery
-56,-2,8,1,-1,0,0,10,3,no,0,0,0,weapon,0, KN_PIERCE,Pierce
-57,-2,6,1,-1,0x1,0,10,1,no,33,0,0,weapon,3, KN_BRANDISHSPEAR,Brandish Spear
-58,-4,6,1,-1,0x2,0,10,1,no,0,0,0,weapon,6, KN_SPEARSTAB,Spear Stab
-59,3:5:7:9:11,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, KN_SPEARBOOMERANG,Spear Boomerang
-60,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, KN_TWOHANDQUICKEN,Twohand Quicken
-61,0,6,4,-1,0x20,0,5,1,no,0,0,0,weapon,0, KN_AUTOCOUNTER,Counter Attack
-62,-2,6,1,-1,0x2,1,10,1,no,33,0,0,weapon,1, KN_BOWLINGBASH,Bowling Bash
-63,0,0,0,0,0,0,1,0,no,0,0,0,weapon,0, KN_RIDING,Peco Peco Riding
-64,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, KN_CAVALIERMASTERY,Cavalier Mastery
-65,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, PR_MACEMASTERY,Mace Mastery
-66,9,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, PR_IMPOSITIO,Impositio Manus
-67,9,6,16,0,0x1,0,3,1,yes,0,0x200,0,magic,0, PR_SUFFRAGIUM,Suffragium
-68,9,6,16,6,0x31,0,5,1,yes,0,0,0,magic,0, PR_ASPERSIO,Aspersio
-69,9,6,2,0,0x23,1,5,1,yes,0,0x40,0,magic,0, PR_BENEDICTIO,B.S. Sacramenti
-70,9,6,2,6,0x21,0,10,1,yes,0,0,0,magic,1, PR_SANCTUARY,Sanctuary
-71,9,6,16,0,0x1,0,4,1,yes,0,0,0,magic,0, PR_SLOWPOISON,Slow Poison
-72,9,6,16,0,0x1,0,1,1,yes,0,0,0,magic,0, PR_STRECOVERY,Status Recovery
-73,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, PR_KYRIE,Kyrie Eleison
-74,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0, PR_MAGNIFICAT,Magnificat
-75,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0, PR_GLORIA,Gloria
-76,5,6,1,0,0x1,0,10,0,yes,0,0,0,magic,0, PR_LEXDIVINA,Lex Divina
-77,5,6,1,6,0x28,0,10,1,yes,0,0,0,magic,0, PR_TURNUNDEAD,Turn Undead
-78,9,6,1,0,0x1,0,1,0,yes,0,0,0,magic,0, PR_LEXAETERNA,Lex Aeterna
-79,9,8,2,6,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, PR_MAGNUS,Magnus Exorcismus
-80,9,8,2,3,0x20,1:1:1:1:1:2:2:2:2:2:2,10,3:4:5:6:7:8:9:10:11:12:12,yes,0,0x80,5,magic,0, WZ_FIREPILLAR,Fire Pillar
-81,0,6,4,3,0,3,10,1,yes,0,0,0,magic,5, WZ_SIGHTRASHER,Sightrasher
-83,9,8,2,3,0,3:3:3:3:3:3:3:3:3:3:14,10,1:1:2:2:3:3:4:4:5:5:15,yes,0,0,0,magic,0, WZ_METEOR,Meteor Storm
-84,9,8,1,4,0,0,10,3:4:5:6:7:8:9:10:11:12,yes,0,0,0,magic,2:3:3:4:4:5:5:6:6:7, WZ_JUPITEL,Jupitel Thunder
-85,9,8,2,4,0,0,10,-10,yes,0,0,0,magic,0, WZ_VERMILION,Lord of Vermilion
-86,9,8,1,1,0,0,5,1,yes,0,0,0,magic,0, WZ_WATERBALL,Water Ball
-87,9,6,2,1,0x1,0,10,1,yes,0,0,0,magic,0, WZ_ICEWALL,Ice Wall
-88,0,6,4,1,0x2,2,10,1,yes,0,0,0,magic,0, WZ_FROSTNOVA,Frost Nova
-89,9,6,2,1,0,0,10,1,yes,0,0,0,magic,2, WZ_STORMGUST,Storm Gust
-90,9,8,1,2,0,0,5,1:2:3:4:5,yes,0,0,0,magic,0, WZ_EARTHSPIKE,Earth Spike
-91,9,8,2,2,0,0,5,1:2:3:4:5,yes,0,0,0,magic,0, WZ_HEAVENDRIVE,Heaven's Drive
-92,9,6,2,2,0x1,0,5,1,yes,0,0,3,magic,0, WZ_QUAGMIRE,Quagmire
-93,9,6,1,0,0x1,0,1,1,yes,0,0,0,magic,0, WZ_ESTIMATION,Sense
-94,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_IRON,Iron Tempering
-95,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_STEEL,Steel Tempering
-96,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_ENCHANTEDSTONE,Enchanted Stone Craft
-97,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_ORIDEOCON,Oridecon Research
-98,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_DAGGER,Smith Dagger
-99,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_SWORD,Smith Sword
-100,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_TWOHANDSWORD,Smith Two-handed Sword
-101,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_AXE,Smith Axe
-102,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_MACE,Smith Mace
-103,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_KNUCKLE,Smith Knucklebrace
-104,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_SPEAR,Smith Spear
-105,0,0,0,0,0,0,1,0,no,0,0,0,weapon,0, BS_HILTBINDING,Hilt Binding
-106,0,0,0,0,0,0,1,0,no,0,0,0,weapon,0, BS_FINDINGORE,Ore Discovery
-107,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, BS_WEAPONRESEARCH,Weaponry Research
-108,2,6,16,0,0x1,0,1,1,yes,0,0,0,weapon,0, BS_REPAIRWEAPON,Weapon Repair
-109,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_SKINTEMPER,Skin Tempering
-110,1,6,2,0,0x3,2:2:2:2:2:14,5,1,no,0,0,0,weapon,0, BS_HAMMERFALL,Hammer Fall
-111,0,6,4,0,0x3,-1,5,1,no,0,0,0,weapon,0, BS_ADRENALINE,Adrenaline Rush
-112,0,6,4,0,0x3,-1,5,1,no,0,0,0,weapon,0, BS_WEAPONPERFECT,Weapon Perfection
-113,0,6,4,0,0x3,-1,5,1,no,0,0,0,weapon,0, BS_OVERTHRUST,Power-Thrust
-114,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, BS_MAXIMIZE,Maximize Power
-115,3,6,2,0,0x1,0,5,1,no,0,0x80,0,misc,6:7:8:9:10, HT_SKIDTRAP,Skid Trap
-116,3,6,2,2,0x42,1,5,1,no,0,0x80,0,misc,0, HT_LANDMINE,Land Mine
-117,3,6,2,0,0x1,0,5,1,no,0,0x80,0,misc,0, HT_ANKLESNARE,Ankle Snare
-118,3,6,2,0,0x2,1,5,1,no,0,0x80,0,misc,0, HT_SHOCKWAVE,Shockwave Trap
-119,3,6,2,0,0x3,2,5,1,no,0,0x80,0,misc,0, HT_SANDMAN,Sandman
-120,3,6,2,0,0x3,1,5,1,no,0,0x80,0,misc,0, HT_FLASHER,Flasher
-121,3,6,2,1,0x42,1,5,1,no,0,0x80,0,weapon,0, HT_FREEZINGTRAP,Freezing Trap
-122,3,6,2,4,0x42,1,5,1,no,0,0x80,0,misc,0, HT_BLASTMINE,Blast Mine
-123,3,6,2,3,0x42,2,5,1,no,0,0x80,0,misc,0, HT_CLAYMORETRAP,Claymore Trap
-124,2,6,32,0,0x1,0,1,1,no,0,0,0,misc,0, HT_REMOVETRAP,Remove Trap
-125,3,6,2,0,0x1,0,1,1,no,0,0x80,0,misc,0, HT_TALKIEBOX,Talkie Box
-126,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, HT_BEASTBANE,Beast Bane
-127,0,0,0,0,0,0,1,0,no,0,0,0,misc,0, HT_FALCON,Falconry Mastery
-128,0,0,0,0,0,0,10,0,no,0,0,0,misc,0, HT_STEELCROW,Steel Crow
-129,5,8,1,0,0x42,1,5,1:2:3:4:5,yes,0,0,0,misc,0, HT_BLITZBEAT,Blitz Beat
-130,3:5:7:9,6,2,0,0x3,3,4,1,no,0,0,0,misc,0, HT_DETECTING,Detect
-131,4:5:6:7:8,6,32,0,0x1,0,5,1,no,0,0,0,misc,0, HT_SPRINGTRAP,Spring Trap
-132,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, AS_RIGHT,Righthand Mastery
-133,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, AS_LEFT,Lefthand Mastery
-134,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AS_KATAR,Katar Mastery
-135,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, AS_CLOAKING,Cloaking
-136,-1,8,1,-1,0,0,10,8,no,0,0,0,weapon,0, AS_SONICBLOW,Sonic Blow
-137,3:4:5:6:7,6,1,-1,0x2,1,5,1,no,0,0,0,weapon,0,AS_GRIMTOOTH,Grimtooth
-138,1,6,16,5,0x1,0,10,1,no,0,0x400,0,weapon,0, AS_ENCHANTPOISON,Enchant Poison
-139,0,6,4,0,0,0,10,1,no,0,0,0,weapon,0, AS_POISONREACT,Poison React
-140,2,6,2,5,0x1,0,10,1,no,0,0,0,weapon,0, AS_VENOMDUST,Venom Dust
-141,1,6,1,-1,0x51,2,10,1,yes,0,0,0,weapon,0, AS_SPLASHER,Venom Splasher
-142,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, NV_FIRSTAID,First Aid
-143,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, NV_TRICKDEAD,Play Dead
-144,0,0,0,0,0,0,1,0,no,0,0x1,0,none,0, SM_MOVINGRECOVERY,Moving HP-Recovery
-145,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0, SM_FATALBLOW,Fatal Blow
-146,0,6,4,0,0x1,0,1,1,no,0,0x1,0,weapon,0, SM_AUTOBERSERK,Auto Berserk
-147,0,0,4,0,0x1,0,1,0,no,0,0x1,0,weapon,0, AC_MAKINGARROW,Arrow Crafting
-148,-9,6,1,-1,0x2,0,1,1,no,0,0x1,0,weapon,6, AC_CHARGEARROW,Arrow Repel
-149,1,6,1,2,0,0,1,1,no,0,0x1,0,weapon,0, TF_SPRINKLESAND,Sand Attack
-150,0,6,4,0,0x1,0,1,1,no,0,0x1,0,weapon,5, TF_BACKSLIDING,Back Slide
-151,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, TF_PICKSTONE,Find Stone
-152,7,6,1,0,0x40,0,1,1,no,0,0x1,0,misc,0, TF_THROWSTONE,Stone Fling
-153,1,6,1,-1,0x2,1,1,1,no,0,0x1,0,weapon,2, MC_CARTREVOLUTION,Cart Revolution
-154,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, MC_CHANGECART,Change Cart
-155,0,6,4,0,0x1,0,1,1,no,0,0x1,0,weapon,0, MC_LOUD,Crazy Uproar
-156,9,6,1,6,0,0,1,1,yes,0,0x1,0,magic,0, AL_HOLYLIGHT,Holy Light
-157,0,6,4,0,0x1,0,1,1,yes,0,0x1,0,magic,0, MG_ENERGYCOAT,Energy Coat
-158,3,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_PIERCINGATT,Piercing Attack
-159,-1,6,1,-1,0x40,0,5,1,no,0,0x2,0,weapon,0, NPC_MENTALBREAKER,Spirit Destruction
-160,9,6,1,0,0,0,10,1,no,0,0x2,0,weapon,0, NPC_RANGEATTACK,Stand off attack
-161,0,0,4,0,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_ATTRICHANGE,Attribute Change
-162,0,0,4,1,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEWATER,Water Attribute Change
-163,0,0,4,2,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEGROUND,Earth Attribute Change
-164,0,0,4,3,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEFIRE,Fire Attribute Change
-165,0,0,4,4,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEWIND,Wind Attribute Change
-166,0,0,4,5,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEPOISON,Poison Attribute Change
-167,0,0,4,6,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEHOLY,Holy Attribute Change
-168,0,0,4,7,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEDARKNESS,Shadow Attribute Change
-169,0,0,4,8,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGETELEKINESIS,Ghost Attribute Change
-170,-9,6,1,-1,0x20,0,10,1,no,0,0x2,0,weapon,0, NPC_CRITICALSLASH,Defense disregard attack
-171,-9,8,1,-1,0,0,10,-2:-3:-4:-5:-6:-7:-8:-9:-10:-11,no,0,0x2,0,weapon,0, NPC_COMBOATTACK,Multi-stage Attack
-172,-9,6,1,-1,0x40,0,10,1,no,0,0x2,0,weapon,0, NPC_GUIDEDATTACK,Guided Attack
-173,5,6,4,3,0xE2,5,10,1,no,0,0x2,0,misc,3, NPC_SELFDESTRUCTION,Suicide bombing
-174,-9,6,1,-1,0x2,3,1,1,no,0,0x2,0,weapon,0, NPC_SPLASHATTACK,Splash attack
-175,0,0,4,0,0x41,0,10,1,no,0,0x2,0,misc,0, NPC_SUICIDE,Suicide
-176,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_POISON,Poison Attack
-177,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_BLINDATTACK,Blind Attack
-178,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_SILENCEATTACK,Silence Attack
-179,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_STUNATTACK,Stun Attack
-180,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_PETRIFYATTACK,Petrify Attack
-181,-9,6,1,7,0,0,5,1,no,0,0x2,0,weapon,0, NPC_CURSEATTACK,Curse Attack
-182,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_SLEEPATTACK,Sleep attack
-183,-9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_RANDOMATTACK,Random Attack
-184,-9,6,1,1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_WATERATTACK,Water Attribute Attack
-185,-9,6,1,2,0,0,10,1,no,0,0x2,0,weapon,0, NPC_GROUNDATTACK,Earth Attribute Attack
-186,-9,6,1,3,0,0,10,1,no,0,0x2,0,weapon,0, NPC_FIREATTACK,Fire Attribute Attack
-187,-9,6,1,4,0,0,10,1,no,0,0x2,0,weapon,0, NPC_WINDATTACK,Wind Attribute Attack
-188,-9,6,1,5,0,0,10,1,no,0,0x2,0,weapon,0, NPC_POISONATTACK,Poison Attribute Attack
-189,-9,6,1,6,0,0,10,1,no,0,0x2,0,weapon,0, NPC_HOLYATTACK,Holy Attribute Attack
-190,-9,6,1,7,0,0,10,1,no,0,0x2,0,weapon,0, NPC_DARKNESSATTACK,Shadow Attribute Attack
-191,-9,6,1,8,0,0,10,1,no,0,0x2,0,weapon,0, NPC_TELEKINESISATTACK,Ghost Attribute Attack
-192,-9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_MAGICALATTACK,Demon Shock Attack
-193,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_METAMORPHOSIS,Metamorphosis
-194,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_PROVOCATION,Provocation
-195,0,6,4,0,0x50,0,10,1,no,0,0x2,0,misc,0, NPC_SMOKING,Smoking
-196,0,0,4,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_SUMMONSLAVE,Follower Summons
-197,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_EMOTION,Emotion
-198,0,0,4,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_TRANSFORMATION,Transformation
-199,9,6,1,7,0x40,0,1,1,no,0,0x2,0,weapon,0, NPC_BLOODDRAIN,Sucking Blood
-200,9,6,1,7,0,0,1,1,no,0,0x2,0,magic,0, NPC_ENERGYDRAIN,Energy Drain
-201,0,0,4,0,0x1,0,1,1,no,0,0x2,0,weapon,0, NPC_KEEPING,Keeping
-202,9,6,1,7,0,0,5,1,no,0,0x2,0,misc,0, NPC_DARKBREATH,Dark Breath
-203,9,6,1,7,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_DARKBLESSING,Dark Blessing
-204,0,0,4,0,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_BARRIER,Barrier
-205,0,0,4,0,0x1,0,1,1,no,0,0x2,0,weapon,0, NPC_DEFENDER,Defender
-206,1,6,1,-1,0x1,0,5,1,no,0,0x2,0,weapon,0, NPC_LICK,Lick
-207,9,0,1,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_HALLUCINATION,Hallucination
-208,0,0,4,0,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_REBIRTH,Rebirth
-209,0,0,4,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_SUMMONMONSTER,Monster Summons
-210,0,0,0,-1,0,0,10,0,no,0,0,0,weapon,0, RG_SNATCHER,Gank
-211,1,6,1,0,0x1,0,10,1,no,0,0,0,weapon,0, RG_STEALCOIN,Mug
-212,-1,6,1,-1,0x40,0,10,1,no,0,0,0,weapon,0, RG_BACKSTAP,Back Stab
-213,0,0,0,0,0,0,5,0,no,0,0,0,none,0, RG_TUNNELDRIVE,Stalk
-214,0,6,4,-1,0x2,1,5,1,no,0,0,0,weapon,0, RG_RAID,Sightless Mind
-215,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPWEAPON,Divest Weapon
-216,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPSHIELD,Divest Shield
-217,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPARMOR,Divest Armor
-218,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPHELM,Divest Helm
-219,1,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, RG_INTIMIDATE,Snatch
-220,1,6,2,0,0x1,0,1,1,no,0,0,0,none,0, RG_GRAFFITI,Scribble
-221,0,6,2,0,0x1,0,5,1,no,0,0,0,none,0, RG_FLAGGRAFFITI,Piece
-222,1,6,2,0,0x3,5,1,1,no,0,0,0,none,0, RG_CLEANER,Remover
-223,0,0,0,0,0,1,1,0,no,0,0,0,none,0, RG_GANGSTER,Slyness
-224,0,0,0,0,0,0,5,0,no,0,0,0,none,0, RG_COMPULSION,Haggle
-225,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RG_PLAGIARISM,Intimidate
-226,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AM_AXEMASTERY,Axe Mastery
-227,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_LEARNINGPOTION,Potion Research
-228,0,6,4,0,0x1,0,10,0,no,0,0,0,none,0, AM_PHARMACY,Prepare Potion
-229,9,6,2,3,0x9,0,5,1,yes,0,0,0,weapon,0, AM_DEMONSTRATION,Bomb
-230,9,6,1,0,0x48,0,5,1,yes,0,0,0,weapon,0, AM_ACIDTERROR,Acid Terror
-231,9,6,16,0,0x1,0,5,1,yes,0,0xC00,0,none,0, AM_POTIONPITCHER,Aid Potion
-232,4,6,2,0,0x1,0,5,1,no,0,0,5,none,0, AM_CANNIBALIZE,Summon Flora
-233,1,6,2,0,0x1,0,5,1,no,0,0,3,none,0, AM_SPHEREMINE,Summon Marine Sphere
-234,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_WEAPON,Alchemical Weapon
-235,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_SHIELD,Synthesized Shield
-236,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_ARMOR,Synthetic Armor
-237,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_HELM,Biochemical Helm
-238,0,0,0,0,0,0,1,0,no,0,0x1,0,none,0, AM_BIOETHICS,Bioethics
-//239,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_BIOTECHNOLOGY,Biotechnology
-//240,0,0,0,0,0,0,5,0,no,0,0,0,none,0, AM_CREATECREATURE,Life Creation
-//241,0,0,0,0,0,0,5,0,no,0,0,0,none,0, AM_CULTIVATION,Cultivation
-//242,0,0,0,0,0,0,5,0,no,0,0,0,none,0, AM_FLAMECONTROL,Flame Control
-243,0,0,4,0,0x1,1,1,0,no,0,0,0,none,0, AM_CALLHOMUN,Call Homunculus
-244,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, AM_REST,Vaporize
-//245,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_DRILLMASTER,Drillmaster
-//246,9,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_HEALHOMUN,Heal Homunculus
-247,9,6,4,0,0x1,1,5,0,no,0,0,0,none,0, AM_RESURRECTHOMUN,Homunculus Resurrection
-248,0,0,0,0,0,0,10,0,no,0,0,0,none,0, CR_TRUST,Faith
-249,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, CR_AUTOGUARD,Guard
-250,3,6,1,0,0,0,5,1,no,0,0,0,weapon,5:6:7:8:9, CR_SHIELDCHARGE,Smite
-251,3:5:7:9:11,6,1,0,0,0,5,1,no,0,0,0,weapon,0, CR_SHIELDBOOMERANG,Shield Boomerang
-252,0,6,4,0,0,0,10,1,no,0,0,0,weapon,0, CR_REFLECTSHIELD,Shield Reflect
-253,-2,8,1,6,0,0,10,-2,no,0,0,0,weapon,0, CR_HOLYCROSS,Holy Cross
-254,5,6,4,6,0x48,0,10,1,no,33,0x100,0,magic,0, CR_GRANDCROSS,Grand Cross
-255,7:8:9:10:11,6,16,0,0x1,0,5,1,yes,0,0x600,0,none,0, CR_DEVOTION,Sacrifice
-256,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,none,0, CR_PROVIDENCE,Resistant Souls
-257,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, CR_DEFENDER,Defending Aura
-258,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, CR_SPEARQUICKEN,Spear Quicken
-259,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, MO_IRONHAND,Iron Fists
-260,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, MO_SPIRITSRECOVERY,Spiritual Cadence
-261,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MO_CALLSPIRITS,Summon Spirit Sphere
-262,9,6,16,0,0x1,0,1,1,yes,0,0,0,weapon,0, MO_ABSORBSPIRITS,Absorb Spirit Sphere
-263,-1,8,0,-1,0,0,10,-3,no,0,0,0,weapon,0, MO_TRIPLEATTACK,Raging Trifecta Blow
-264,18,6,2,0,0x1,0,1,1,no,0,0,0,none,0, MO_BODYRELOCATION,Snap
-265,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, MO_DODGE,Dodge
-266,2,6,1,0,0x40,0,5,1,no,0,0,0,weapon,0, MO_INVESTIGATE,Occult Impaction
-267,9,8,1,-1,0,0,5,1:2:3:4:5,no,0,0,0,weapon,0, MO_FINGEROFFENSIVE,Throw Spirit Sphere
-268,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, MO_STEELBODY,Mental Strength
-269,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, MO_BLADESTOP,Root
-270,0,6,4,0,0x1,0,5,0,no,0,0,0,weapon,0, MO_EXPLOSIONSPIRITS,Fury
-271,-2,6,1,0,0x60,0,5,1,yes,0,0,0,weapon,0, MO_EXTREMITYFIST,Asura Strike
-272,-2,8,4,-1,0,0,5,-4,no,0,0x200,0,weapon,0, MO_CHAINCOMBO,Raging Quadruple Blow
-273,-2,6,4,-1,0x2,2,5,1,no,0,0x200,0,weapon,0, MO_COMBOFINISH,Raging Thrust
-274,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, SA_ADVANCEDBOOK,Study
-275,0,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, SA_CASTCANCEL,Cast Cancel
-276,0,6,4,0,0x1,0,5,1,yes,0,0,0,magic,0, SA_MAGICROD,Magic Rod
-277,9,6,1,0,0x1,0,5,1,yes,0,0,0,magic,0, SA_SPELLBREAKER,Spell Breaker
-278,0,0,0,0,0,0,10,0,no,0,0,0,magic,0, SA_FREECAST,Free Cast
-279,0,6,4,0,0x1,0,10,1,yes,0,0,0,magic,0, SA_AUTOSPELL,Hindsight
-280,9,6,16,3,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_FLAMELAUNCHER,Endow Blaze
-281,9,6,16,1,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_FROSTWEAPON,Endow Tsunami
-282,9,6,16,4,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_LIGHTNINGLOADER,Endow Tornado
-283,9,6,16,2,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_SEISMICWEAPON,Endow Quake
-284,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, SA_DRAGONOLOGY,Dragonology
-285,2,6,2,3,0x1,0,5,1,yes,0,0,0,magic,0, SA_VOLCANO,Volcano
-286,2,6,2,1,0x1,0,5,1,yes,0,0,0,magic,0, SA_DELUGE,Deluge
-287,2,6,2,4,0x1,0,5,1,yes,0,0,0,magic,0, SA_VIOLENTGALE,Whirlwind
-288,2,6,2,0,0x1,0,5,1,yes,0,0,0,magic,0, SA_LANDPROTECTOR,Magnetic Earth
-289,9,6,1,0,0x1,0:0:0:0:0:-1,5,1,yes,0,0xE00,0,magic,0, SA_DISPELL,Dispell
-290,0,6,4,0,0x1,0,10,1,yes,0,0,0,magic,0, SA_ABRACADABRA,Hocus-pocus
-291,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_MONOCELL,Monocell
-292,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_CLASSCHANGE,Class Change
-293,0,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_SUMMONMONSTER,Monster Chant
-294,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_REVERSEORCISH,Grampus Morph
-295,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_DEATH,Grim Reaper
-296,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_FORTUNE,Gold Digger
-297,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_TAMINGMONSTER,Beastly Hypnosis
-298,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_QUESTION,Questioning
-299,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_GRAVITY,Gravity
-300,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_LEVELUP,Leveling
-301,9,6,4,0,0,0,1,1,yes,0,0x2,0,magic,0, SA_INSTANTDEATH,Suicide
-302,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_FULLRECOVERY,Rejuvenation
-303,9,6,4,0,0,0,1,1,yes,0,0x2,0,magic,0, SA_COMA,Coma
-304,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, BD_ADAPTATION,Amp
-305,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, BD_ENCORE,Encore
-306,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_LULLABY,Lullaby
-307,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_RICHMANKIM,Mental Sensing
-308,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_ETERNALCHAOS,Down Tempo
-309,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_DRUMBATTLEFIELD,Battle Theme
-310,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_RINGNIBELUNGEN,Harmonic Lick
-311,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_ROKISWEIL,Classical Pluck
-312,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_INTOABYSS,Power Chord
-313,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_SIEGFRIED,Acoustic Rhythm
-//314,0,0,0,0,0,0,1,1,no,0,0x40,0,misc,0, BD_RAGNAROK,Ragnarok
-315,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, BA_MUSICALLESSON,Music Lessons
-316,9,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, BA_MUSICALSTRIKE,Melody Strike
-317,0,8,4,0,0x41,0,5,1,no,0,0x20,0,misc,0, BA_DISSONANCE,Unchained Serenade
-318,0,6,4,0,0x3,-1,5,1,no,0,0,0,misc,0, BA_FROSTJOKER,Unbarring Octave
-319,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_WHISTLE,Perfect Tablature
-320,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_ASSASSINCROSS,Impressive Riff
-321,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_POEMBRAGI,Magic Strings
-322,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_APPLEIDUN,Song of Lutie
-323,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, DC_DANCINGLESSON,Dance Lessons
-324,9,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, DC_THROWARROW,Slinging Arrow
-325,0,8,4,0,0x1,0,5,1,no,0,0x20,0,misc,0, DC_UGLYDANCE,Hip Shaker
-326,0,6,4,0,0x3,-1,5,1,no,0,0,0,misc,0, DC_SCREAM,Dazzler
-327,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_HUMMING,Focus Ballet
-328,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_DONTFORGETME,Slow Grace
-329,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_FORTUNEKISS,Lady Luck
-330,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_SERVICEFORYOU,Gypsy's Kiss
-331,0,6,4,0,0x1,0,10,0,no,0,0x2,0,none,0, NPC_RANDOMMOVE,Random Move
-332,0,6,4,0,0x1,0,10,0,no,0,0x2,0,none,0, NPC_SPEEDUP,Speed UP
-333,0,6,4,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_REVENGE,Revenge
-334,9,6,4,0,0x1,0,1,1,yes,0,0x4,0,none,0, WE_MALE,I Will Protect You
-335,9,6,4,0,0x1,0,1,1,yes,0,0x4,0,none,0, WE_FEMALE,I Look up to You
-336,9,6,4,0,0x1,3,1,1,yes,0,0x4,1,none,0, WE_CALLPARTNER,I miss You
-337,9,6,1,-1,0,0,1,1,no,0,0x2,0,weapon,0, ITM_TOMAHAWK,Throw Tomahawk
-338,-1,8,1,7,0,0,10,-2,no,0,0x2,0,weapon,0, NPC_DARKCROSS,Cross of Darkness
-339,5,6,4,7,0x48,0,10,1,no,33,0x102,0,magic,0, NPC_GRANDDARKNESS,Grand cross of Darkness
-340,9,8,1,7,0,0,10,1:1:2:2:3:3:4:4:5:5,yes,0,0x2,0,magic,0, NPC_DARKSTRIKE,Soul Strike of Darkness
-341,9,8,1,7,0,0,10,3:4:5:6:7:8:9:10:11:12,yes,0,0x2,0,magic,2:3:3:4:4:5:5:6:6:7, NPC_DARKTHUNDER,Darkness Jupitel
-342,9,6,1,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_STOP,Stop
-343,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_WEAPONBRAKER,Break weapon
-344,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_ARMORBRAKE,Break armor
-345,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_HELMBRAKE,Break helm
-346,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_SHIELDBRAKE,Break shield
-347,-9,6,1,9,0,0,10,1,no,0,0x2,0,weapon,0, NPC_UNDEADATTACK,Undead Element Attack
-348,9,0,1,9,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_CHANGEUNDEAD,Undead Attribute Change
-349,0,6,4,0,0x1,0,10,0,no,0,0x2,0,weapon,0, NPC_POWERUP,Power Up
-350,0,6,4,0,0x1,0,10,0,no,0,0x2,0,none,0, NPC_AGIUP,Agility UP
-351,0,0,0,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_SIEGEMODE,Siege Mode
-352,2,0,4,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_CALLSLAVE,Recall Slaves
-353,0,0,0,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_INVISIBLE,Invisible
-354,2,6,4,0,0x1,0,20,0,no,0,0x2,0,misc,0, NPC_RUN,Run
-355,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, LK_AURABLADE,Aura Blade
-356,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, LK_PARRYING,Parrying
-357,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, LK_CONCENTRATION,Concentration
-358,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, LK_TENSIONRELAX,Relax
-359,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, LK_BERSERK,Frenzy
-//360,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, LK_FURY,Fury
-361,9,6,16,0,0x1,1,5,1,yes,0,0,0,magic,0, HP_ASSUMPTIO,Assumptio
-362,4,6,4,0,0x1,0,5,1,yes,0,0,0,magic,2, HP_BASILICA,Basilica
-363,0,0,0,0,0,0,10,0,no,0,0,0,magic,0, HP_MEDITATIO,Meditatio
-364,0,0,0,0,0,0,10,1,no,0,0,0,magic,0, HW_SOULDRAIN,Soul Drain
-365,9,8,1,-1,0,0,1,1,yes,0,0,0,weapon,0, HW_MAGICCRASHER,Stave Crasher
-366,0,6,4,0,0x1,0,10,1,no,0,0,0,magic,0, HW_MAGICPOWER,Mystical Amplification
-367,9,8,1,0,0xD0,0,5,1,no,0,0,0,misc,0, PA_PRESSURE,Gloria Domini
-368,0,6,4,0,0x69,0,5,1,yes,0,0,0,weapon,0, PA_SACRIFICE, Martyr's Reckoning
-369,0,6,4,0,0x41,0,10,1,yes,0,0,0,misc,0, PA_GOSPEL,Battle Chant
-370,-2,6,1,-1,0,0,5,1,yes,0,0,0,weapon,3, CH_PALMSTRIKE,Raging Palm Strike
-371,-2,8,4,-1,0,0,5,1,no,0,0x200,0,weapon,0, CH_TIGERFIST,Glacier Fist
-372,-2,8,4,-1,0,0,10,-1:-1:-2:-2:-3:-3:-4:-4:-5:-5,no,0,0x200,0,weapon,0, CH_CHAINCRUSH,Chain Crush Combo
-373,0,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, PF_HPCONVERSION,Indulge
-374,9,6,1,0,0x1,0,1,1,yes,0,0xE00,0,none,0, PF_SOULCHANGE,Soul Exhale
-375,9,6,1,0,0x98,0,5,1,yes,0,0,0,magic,0, PF_SOULBURN,Soul Siphon
-376,0,0,0,0,0x1,0,5,1,no,0,0,0,weapon,0, ASC_KATAR,Advanced Katar Mastery
-//377,0,0,4,0,0x1,0,10,1,no,0,0,0,misc,0, ASC_HALLUCINATION,Hallucination Walk
-378,0,6,4,5,0x1,0,5,1,no,0,0,0,weapon,0, ASC_EDP,Enchant Deadly Poison
-379,7,6,1,-1,0x8,0,10,1,yes,0,0,0,weapon,0, ASC_BREAKER,Soul Destroyer
-380,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, SN_SIGHT,Falcon Eyes
-381,5,8,1,0,0x40,0,5,1,yes,0,0,0,misc,0, SN_FALCONASSAULT,Falcon Assault
-382,9,8,1,-1,0,2,5,1,yes,0,0,13,weapon,0, SN_SHARPSHOOTING,Focused Arrow Strike
-383,0,6,4,0,0x3,-1,10,1,yes,0,0,0,weapon,0, SN_WINDWALK,Wind Walker
-384,0,0,4,0,0x1,0,10,1,yes,0,0,0,weapon,0, WS_MELTDOWN,Shattering Strike
-//385,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, WS_CREATECOIN,Create Coins
-//386,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, WS_CREATENUGGET,Create Nuggets
-387,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, WS_CARTBOOST,Cart Boost
-//388,9,6,2,0,0x1,0,5,1,no,0,0,0,none,0, WS_SYSTEMCREATE,Auto Attack System
-389,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, ST_CHASEWALK,Stealth
-390,0,0,4,0,0,0,5,1,yes,0,0,0,weapon,0, ST_REJECTSWORD,Counter Instinct
-//391,0,0,4,0,1,0,1,1,yes,0,0,0,magic,0, ST_STEALBACKPACK,Steal Backpack
-392,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, CR_ALCHEMY,Alchemy
-393,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, CR_SYNTHESISPOTION,Potion Synthesis
-394,9,8,1,-1,0,0,10,-9,yes,0,0,0,weapon,0, CG_ARROWVULCAN,Vulcan Arrow
-395,0,0,4,0,0x1,3,1,1,yes,0,0x40,0,misc,2, CG_MOONLIT,Sheltering Bliss
-396,1,6,16,0,0x1,0,1,1,yes,0,0x600,0,none,0, CG_MARIONETTE,Marionette Control
-397,5,8,1,-1,0x20,0,5,5,no,0,0,0,weapon,0, LK_SPIRALPIERCE,Spiral Pierce
-398,4,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, LK_HEADCRUSH,Traumatic Blow
-399,4,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, LK_JOINTBEAT,Vital Strike
-400,9,8,1,8,0x6,1,5,1:2:3:4:5,yes,0,0,0,magic,0, HW_NAPALMVULCAN,Napalm Vulcan
-401,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, CH_SOULCOLLECT,Zen
-402,9,6,1,0,0x1,0,5,1,no,0,0,0,none,0, PF_MINDBREAKER,Mind Breaker
-403,0,0,4,0,0x1,0,1,1,yes,0,0,0,magic,0, PF_MEMORIZE,Foresight
-404,9,6,2,2,0x1,0,5,1,yes,0,0x100,2,magic,0, PF_FOGWALL,Blinding Mist
-405,7,6,1,0,0x1,0,1,1,no,0,0,3,magic,0, PF_SPIDERWEB,Fiber Lock
-406,0,6,4,-1,0xA,2,10,1,no,33,0,0,weapon,0, ASC_METEORASSAULT,Meteor Assault
-407,0,6,4,0,0x1,0,1,0,no,0,0,0,none,0, ASC_CDP,Create Deadly Poison
-408,9,6,4,0,0x1,0,1,1,yes,0,0x4,0,none,0, WE_BABY,Baby
-409,9,6,4,0,0x1,3,1,1,yes,0,0x4,1,none,0, WE_CALLPARENT,Call Parent
-410,9,6,4,0,0x1,3,1,1,yes,0,0x4,1,none,0, WE_CALLBABY,Call Baby
-411,0,6,4,0,0x1,0,10,1,yes,0,0,0,misc,4, TK_RUN,Running
-412,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYSTORM,Tornado Stance
-413,-2,8,4,-1,0x2,2,7,-3,no,0,0x200,0,weapon,0, TK_STORMKICK,Tornado Kick
-414,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYDOWN,Heel Drop Stance
-415,-2,8,4,-1,0,0,7,-3,no,0,0x200,0,weapon,0, TK_DOWNKICK,Heel Drop
-416,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYTURN,Roundhouse Stance
-417,-2,8,4,-1,0x2,1,7,-3,no,0,0x200,0,weapon,2, TK_TURNKICK,Roundhouse Kick
-418,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYCOUNTER,Counter Kick Stance
-419,-2,8,4,-1,0x40,0,7,-3,no,0,0x200,0,weapon,0, TK_COUNTER,Counter Kick
-420,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_DODGE,Tumbling
-421,9,8,16,-1,0x1,0,7,-3,no,0,0,0,weapon,0, TK_JUMPKICK,Flying Kick
-422,0,0,0,0,0,1,10,0,no,0,0,0,none,0, TK_HPTIME,Peaceful Break
-423,0,0,0,0,0,1,10,0,no,0,0,0,none,0, TK_SPTIME,Happy Break
-424,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, TK_POWER,Kihop
-425,0,6,4,2:4:1:3:8:7:6,0x1,0,7,1,no,0,0,0,weapon,0, TK_SEVENWIND,Mild Wind
-426,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, TK_HIGHJUMP,Taekwon Jump
-427,0,6,4,0,0x1,0,3,1,yes,0,0,0,magic,0, SG_FEEL,Feeling the Sun Moon and Stars
-428,1,6,4,-1,0x2,1,3,1,yes,0,0,0,weapon,2, SG_SUN_WARM,Warmth of the Sun
-429,1,6,4,-1,0x2,1,3,1,yes,0,0,0,weapon,2, SG_MOON_WARM,Warmth of the Moon
-430,1,6,4,-1,0x2,1,3,1,yes,0,0,0,weapon,2, SG_STAR_WARM,Warmth of the Stars
-431,0,0,4,0,0x1,0,4,1,yes,0,0,0,magic,0, SG_SUN_COMFORT,Comfort of the Sun
-432,0,0,4,0,0x1,0,4,1,yes,0,0,0,magic,0, SG_MOON_COMFORT,Comfort of the Moon
-433,0,0,4,0,0x1,0,4,1,yes,0,0,0,magic,0, SG_STAR_COMFORT,Comfort of the Stars
-434,10,6,1,0,0x1,0,3,1,yes,0,0,0,magic,0, SG_HATE,Hatred of the Sun Moon and Stars
-435,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_SUN_ANGER,Anger of the Sun
-436,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_MOON_ANGER,Anger of the Moon
-437,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_STAR_ANGER,Anger of the Stars
-438,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SG_SUN_BLESS,Blessing of the Sun
-439,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SG_MOON_BLESS,Blessing of the Moon
-440,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SG_STAR_BLESS,Blessing of the Stars
-441,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SG_DEVIL,Demon of the Sun Moon and Stars
-442,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_FRIEND,Friend of the Sun Moon and Stars
-443,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SG_KNOWLEDGE,Knowledge of the Sun Moon and Stars
-444,0,6,4,0,0x1,0,1,1,no,0,0,0,misc,0, SG_FUSION,Union of the Sun Moon and Stars
-445,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_ALCHEMIST,Spirit of the Alchemist
-446,9,6,16,0,0x1,0,1,1,yes,0,0xC08,0,none,0, AM_BERSERKPITCHER,Aid Berserk Potion
-447,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_MONK,Spirit of the Monk
-448,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_STAR,Spirit of the Star Gladiator
-449,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_SAGE,Spirit of the Sage
-450,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_CRUSADER,Spirit of the Crusader
-451,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_SUPERNOVICE,Spirit of the Supernovice
-452,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_KNIGHT,Spirit of the Knight
-453,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_WIZARD,Spirit of the Wizard
-454,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_PRIEST,Spirit of the Priest
-455,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_BARDDANCER,Spirit of the Artist
-456,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_ROGUE,Spirit of the Rogue
-457,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_ASSASIN,Spirit of the Assasin
-458,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_BLACKSMITH,Spirit of the Blacksmith
-459,0,6,4,0,0x3,-1,1,1,no,0,0x8,0,weapon,0 , BS_ADRENALINE2,Advanced Adrenaline Rush
-460,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_HUNTER,Spirit of the Hunter
-461,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_SOULLINKER,Spirit of the Soul Linker
-462,9,6,16,0,0x1,0,7,1,yes,0,0,0,magic,0, SL_KAIZEL,Kaizel
-463,9,6,16,0,0x1,0,7,1,yes,0,0,0,magic,0, SL_KAAHI,Kaahi
-464,9,6,16,0,0x1,0,3,1,yes,0,0,0,magic,0, SL_KAUPE,Kaupe
-465,9,6,16,0,0x1,0,7,1,yes,0,0,0,magic,0, SL_KAITE,Kaite
-466,0,0,0,0,0,0,7,0,yes,0,0,0,magic,0, SL_KAINA,Kaina
-467,9,6,1,-2,0,0,7,1,no,0,0,0,magic,2, SL_STIN,Estin
-468,9,6,1,-2,0,0,7,1,no,0,0,0,magic,0, SL_STUN,Estun
-469,9,8,1,-2,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, SL_SMA,Esma
-470,9,6,1,0,0x1,0,7,1,no,0,0,0,magic,0, SL_SWOO,Eswoo
-471,9,6,1,0,0x1,0,3,1,no,0,0,0,magic,0, SL_SKE,Eske
-472,9,6,1,0,0x1,0,3,1,no,0,0,0,magic,0, SL_SKA,Eska
-473,0,6,4,0,0,0,1,1,no,0,0,0,none,0, SM_SELFPROVOKE,Provoke Self
-474,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_EMOTION_ON,Emotion ON
-475,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, ST_PRESERVE,Preserve
-476,1,6,1,0,0x1,0,5,1,yes,0,0,0,weapon,0, ST_FULLSTRIP,Divest All
-477,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, WS_WEAPONREFINE,Upgrade Weapon
-478,3,6,2,0,0x3,3,10,1,no,0,0,0,none,0, CR_SLIMPITCHER,Aid Condensed Potion
-479,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, CR_FULLPROTECTION,Full Protection
-480,5,8,1,-1,0,0,5,5,no,0,0,0,weapon,0, PA_SHIELDCHAIN,Shield Chain
-481,0,0,0,0,0,0,5,0,no,0,0,0,none,0, HP_MANARECHARGE,Mana Recharge
-482,0,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, PF_DOUBLECASTING,Double Casting
-483,16,6,2,0,0x1,1:2:3:4:5,1,1,no,0,0,0,none,0, HW_GANBANTEIN,Ganbantein
-484,9,6,2,2,0x91,0,5,1,yes,0,0,0,misc,0, HW_GRAVITATION,Gravitation Field
-485,-2,6,1,-1,0x8,0,10,1,no,0,0,0,weapon,0, WS_CARTTERMINATION,Cart Termination
-486,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, WS_OVERTHRUSTMAX,Maximum Power Thrust
-487,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, CG_LONGINGFREEDOM,Longing for Freedom
-488,0,6,4,0,0x1,1,5,1,no,0,0x40,0,misc,0, CG_HERMODE,Wand of Hermode
-489,9,6,1,0,0x41,0,5,1,no,0,0,0,misc,0, CG_TAROTCARD,Tarot Card of Fate
-490,9,8,1,0,0x40,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,misc,0, CR_ACIDDEMONSTRATION,Acid Demonstration
-491,1,6,2,0,0x1,0,2,1,no,0,0,0,none,0, CR_CULTIVATION,Plant Cultivation
-492,0,6,4,0:1:2:3:4:5:6:7:8:9,0x1,0,10,1,no,0,0x2,0,none,0, ITEM_ENCHANTARMS,Weapon Enchantment
-493,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, TK_MISSION,Taekwon Mission
-494,9,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, SL_HIGH,Spirit of Rebirth
-495,0,6,4,0,0x1,0,1,1,no,0,0x8,0,weapon,0, KN_ONEHAND,Onehand Quicken
-496,0,6,4,0,0x1,0,1,0,no,0,0x8,0,none,0, AM_TWILIGHT1,Twilight Alchemy 1
-497,0,6,4,0,0x1,0,1,0,no,0,0x8,0,none,0, AM_TWILIGHT2,Twilight Alchemy 2
-498,0,6,4,0,0x1,0,1,0,no,0,0x8,0,none,0, AM_TWILIGHT3,Twilight Alchemy 3
-499,-9,8,4,-1,0,0,1,2,no,0,0x208,0,weapon,0, HT_POWER,Beast Strafing
-500,0,6,4,0,0x40,0,5,1,no,0,0,0,misc,0, GS_GLITTERING,Flip the Coin
-501,9,6,1,-1,0x50,0,1,1,no,0,0,0,misc,0, GS_FLING,Fling
-502,-9,8,1,-1,0,0,1,3,no,0,0,0,weapon,0, GS_TRIPLEACTION,Triple Action
-503,-9,6,1,-1,0x8,0,1,1,no,0,0,0,weapon,0, GS_BULLSEYE,Bulls Eye
-504,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, GS_MADNESSCANCEL,Madness Canceller
-505,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, GS_ADJUSTMENT,AdJustment
-506,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, GS_INCREASING,Increasing Accuracy
-507,-9,6,1,8,0,0,1,1,no,0,0,0,weapon,0, GS_MAGICALBULLET,Magical Bullet
-508,-9,6,1,-1,0x1,0,1,1,no,0,0,0,weapon,0, GS_CRACKER,Cracker
-509,0,0,0,0,0,0,10,0,no,0,0,0,none,0, GS_SINGLEACTION,Single Action
-510,0,0,0,0,0,0,10,0,no,0,0,0,none,0, GS_SNAKEEYE,Snake Eye
-511,-9,8,0,-1,0,0,10,2,no,0,0,0,weapon,0, GS_CHAINACTION,Chain Action
-512,-9,6,1,-1,0,0,10,1,yes,0,0,0,weapon,0, GS_TRACKING,Tracking
-513,-9,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, GS_DISARM,Disarm
-514,-9,6,1,-1,0x20,0,5,1,no,0,0,0,weapon,0, GS_PIERCINGSHOT,Piercing Shot
-515,-9,8,1,-1,0,0,10,5,no,0,0,0,weapon,0, GS_RAPIDSHOWER,Rapid Shower
-516,0,8,4,-1,0x2,3,10,1,no,0,0,0,weapon,0, GS_DESPERADO,Desperado
-517,0,6,4,-1,0x1,0,10,1,no,0,0,0,weapon,0, GS_GATLINGFEVER,Gatling Fever
-518,2,6,1,-1,0,0,10,1,no,0,0,0,weapon,5, GS_DUST,Dust
-519,-9,6,1,-1,0,0,10,1,yes,0,0,0,weapon,0, GS_FULLBUSTER,Full Buster
-520,-9,6,1,-1,0x2,1:1:1:2:2:2:3:3:3:4,10,1,no,0,0,0,weapon,0, GS_SPREADATTACK,Spread Attack
-521,-9,6,2,-1,0x40,1,10,1,no,0,0,0,weapon,3, GS_GROUNDDRIFT,Ground Drift
-522,0,0,0,0,0,0,10,1,no,0,0,0,weapon,0, NJ_TOBIDOUGU,Shuriken Training
-523,9,6,1,-1,0x40,0,10,1,no,0,0,0,weapon,0, NJ_SYURIKEN,Throw Shuriken
-524,9,8,1,-1,0x40,0,5,3,no,0,0,0,weapon,0, NJ_KUNAI,Throw Kunai
-525,9,8,2,-1,0x2,0,5,-3:-3:-4:-4:-5,no,0,0,0,weapon,0, NJ_HUUMA,Throw Huuma Shuriken
-526,9,6,1,0,0x50,0,10,1,no,0,0,0,misc,0, NJ_ZENYNAGE,Throw Zeny
-527,0,6,4,-1,0,0,5,1,no,0,0,0,weapon,4, NJ_TATAMIGAESHI,Improvised Defense
-528,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, NJ_KASUMIKIRI,Vanishing Slash
-529,7:9:11:13:15,6,2,0,0x1,0,5,1,no,0,0,0,none,0, NJ_SHADOWJUMP,Shadow Leap
-530,7:9:11:13:15,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, NJ_KIRIKAGE,Shadow Slash
-531,0,6,4,0,0x1,0,5,1,no,0,0,0,none,7, NJ_UTSUSEMI,Cicada Skin Sheeding
-532,0,6,4,0,0x1,0,10,1,yes,0,0,0,magic,0, NJ_BUNSINJYUTSU,Mirror Image
-533,0,0,0,0,0,0,10,0,no,0,0,0,none,0, NJ_NINPOU,Spirit of the Blade
-534,9,8,1,3,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, NJ_KOUENKA,Crimson Fire Petal
-535,0,8,4,3,0,0,10,1,yes,0,0,0,magic,1, NJ_KAENSIN,Crimson Fire Formation
-536,9,8,1,3,0x2,2,5,3,yes,0,0,0,magic,0, NJ_BAKUENRYU,Raging Fire Dragon
-537,9,8,1,1,0,0,10,3:4:5:6:7:8:9:10:11:12,yes,0,0,0,magic,0, NJ_HYOUSENSOU,Spear of Ice
-538,9,6,2,1,0x1,0,10,1,yes,0,0,0,magic,0, NJ_SUITON,Hidden Water
-539,0,6,4,1,0x2,3,5,1,yes,0,0,0,magic,0, NJ_HYOUSYOURAKU,Ice Meteor
-540,9,8,1,4,0,0,10,1:2:2:3:3:4:4:5:5:6,yes,0,0,0,magic,0, NJ_HUUJIN,Wind Blade
-541,9,6,2,4,0x2,2:2:3:3:4,5,1,yes,0,0,0,magic,0, NJ_RAIGEKISAI,Lightning Strike of Destruction
-542,9,8,1,4,0,3,5,1,yes,0,0,5:6:7:8:9,magic,0, NJ_KAMAITACHI,Kamaitachi
-543,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, NJ_NEN,Soul
-544,-5,8,1,0,0x40,0,10,1,no,0,0,0,weapon,0, NJ_ISSEN,Final Strike
-
-// Additional NPC Skills (Episode 11.3)
-653,0,8,4,0,0x6,5:7:9:11:13:5:7:9:11:13,10,1,no,0,0x2,0,magic,0, NPC_EARTHQUAKE,Earthquake
-654,9,6,1,3,0,5,10,1,no,0,0x2,14,weapon,0, NPC_FIREBREATH,Fire Breath
-655,9,6,1,1,0,5,10,1,no,0,0x2,14,weapon,0, NPC_ICEBREATH,Ice Breath
-656,9,6,1,4,0,5,10,1,no,0,0x2,14,weapon,0, NPC_THUNDERBREATH,Thunder Breath
-657,9,6,1,5,0,5,10,1,no,0,0x2,14,weapon,0, NPC_ACIDBREATH,Acid Breath
-658,9,6,1,7,0,5,10,1,no,0,0x2,14,weapon,0, NPC_DARKNESSBREATH,Darkness Breath
-659,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_DRAGONFEAR,Dragon Fear
-660,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_BLEEDING,Bleeding
-661,0,6,4,0,0x2,7,5,1,no,0,0x2,0,weapon,7, NPC_PULSESTRIKE,Pulse Strike
-662,0,6,4,0,0x2,14,10,1,no,0,0x2,0,weapon,0, NPC_HELLJUDGEMENT,Hell's Judgement
-663,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESILENCE,Wide Silence
-664,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDEFREEZE,Wide Freeze
-665,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDEBLEEDING,Wide Bleeding
-666,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESTONE,Wide Petrify
-667,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDECONFUSE,Wide Confusion
-668,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESLEEP,Wide Sleep
-669,0,6,4,3,0x3,5,1,1,no,0,0x2,0,magic,0, NPC_WIDESIGHT,Wide Sight
-670,9,6,2,7,0x91,0,10,1,no,0,0x2,0,magic,0, NPC_EVILLAND,Evil Land
-671,0,6,4,0,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_MAGICMIRROR,Magic Mirror
-672,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_SLOWCAST,Slow Cast
-673,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_CRITICALWOUND,Critical Wounds
-674,-9,6,1,-1,0x1,0,1,1,no,0,0x2,0,none,0, NPC_EXPULSION,Expulsion
-675,0,6,4,0,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_STONESKIN,Stone Skin
-676,0,6,4,0,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_ANTIMAGIC,Anti Magic
-677,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDECURSE,Wide Curse
-678,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESTUN,Wide Stun
-679,0,6,4,0,0x2,5:7:9:11:13:13:13:13:13:13,10,1,no,0,0x2,0,weapon,0, NPC_VAMPIRE_GIFT,Vampire Gift
-680,0,6,4,0,0x3,5:7:9:11:13:13:13:13:13:13,10,1,no,0,0x2,0,none,0, NPC_WIDESOULDRAIN,Wide Soul Drain
-
-// Cash Shop Skill
-681,0,0,0,0,0,0,10,0,no,0,0x1,0,none,0, ALL_INCCARRY,Increase Weight Limit R
-
-// Additional NPC skill (Episode 12)
-682,0,0,4,0,0x1,0,1,1,no,0,0x2,0,none,0, NPC_TALK,Talk
-683,-9,6,1,-1,0,0,1,1,no,0,0x2,0,none,0, NPC_HELLPOWER,Hell Power
-684,0,6,4,0,0x3,-1,1,1,no,0,0x2,0,none,0, NPC_WIDEHELLDIGNITY,Hell Dignity
-685,0,0,4,0,0x1,0,1,1,no,0,0x2,0,none,0, NPC_INVINCIBLE,Invincible
-686,0,0,4,0,0x1,0,1,1,no,0,0x2,0,none,0, NPC_INVINCIBLEOFF,Invincible off
-687,0,6,4,0,0x1,0,1,1,yes,0,0x2,0,none,0, NPC_ALLHEAL,Full Heal
-
-// Additional Skill (??)
-688,9,6,16,0,0x1,0,10,0,no,0,0x200,0,none,0, GM_SANDMAN,GM Sandman
-689,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0, CASH_BLESSING,Party Blessing
-690,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0, CASH_INCAGI,Party Increase AGI
-691,0,6,4,0,0x3,-1,5,1,yes,0,0x2,0,magic,0, CASH_ASSUMPTIO,Party Assumptio
-//692,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_CATCRY,Cat Cry
-693,0,6,4,0,0x3,-1,1,1,yes,0,0x2,0,magic,0, ALL_PARTYFLEE,Party Flee
-//694,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_ANGEL_PROTECT,Angel's Protection
-//695,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_DREAM_SUMMERNIGHT,Summer Night Dream
-//696,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, NPC_CHANGEUNDEAD2,Change Undead
-//697,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, ALL_REVERSEORCISH,Reverse Orcish
-698,0,6,4,0,0x01,0,1,1,no,0,0x2,0,none,0, ALL_WEWISH,Christmas Carol
-//699,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_SONKRAN,ALL_SONKRAN
-
-// New NPC Wide Status AoE Skills And Others
-//700,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDEHEALTHFEAR,Wide Health Fear
-//701,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDEBODYBURNNING,Wide Body Burnning
-//702,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDEFROSTMISTY,Wide Freezing
-//703,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDECOLD,Wide Crystalize
-//704,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDE_DEEP_SLEEP,Wide Deep Sleep
-//705,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDESIREN,Wide Siren's Voice
-//706,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_VENOMFOG,Venom Fog
-//707,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_MILLENNIUMSHIELD,Millenium Shield 2
-//708,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_COMET,Comet 2
-
-1001,9,6,1,-1,0,0,1,1,no,0,0x1,0,weapon,0, KN_CHARGEATK,Charge Attack
-1002,0,6,4,0,0x1,0,1,0,no,0,0x1,0,weapon,2, CR_SHRINK,Shrink
-1003,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0, AS_SONICACCEL,Sonic Acceleration
-1004,9,8,1,0,0x8,0,1,1,no,0,0x1,0,weapon,0, AS_VENOMKNIFE,Throw Venom Knife
-1005,1,6,1,0,0x1,0,1,1,no,0,0x1,0,weapon,0, RG_CLOSECONFINE,Close Confine
-1006,0,6,4,3,0,2,1,1,yes,0,0x1,0,magic,3, WZ_SIGHTBLASTER,Sight Blaster
-1007,0,6,4,0,0x1,0,1,0,no,0,0x1,0,none,0, SA_CREATECON,Create Elemental Converter
-1008,9,6,1,1,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTWATER,Elemental Change Water
-1009,-9,6,1,0,0,0,1,1,no,0,0x1,0,weapon,3, HT_PHANTASMIC,Phantasmic Arrow
-1010,9,6,1,0,0x1,0,1,0,no,0,0x1,0,misc,0, BA_PANGVOICE,Pang Voice
-1011,9,6,1,0,0x1,0,1,0,no,0,0x1,0,misc,0, DC_WINKCHARM,Wink of Charm
-1012,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0, BS_UNFAIRLYTRICK,Unfair Trick
-1013,0,6,4,0,0x3,2,1,0,no,0,0x1,0,weapon,0, BS_GREED,Greed
-1014,0,6,4,6,0x3,14,1,0,yes,0,0x1,0,magic,0, PR_REDEMPTIO,Redemptio
-1015,9,6,16,0,0x1,0,1,1,no,0,0x401,0,weapon,0, MO_KITRANSLATION,Ki Translation
-1016,-1,6,1,-1,0x2,1,1,1,no,0,0x1,0,weapon,5, MO_BALKYOUNG,Ki Explosion
-1017,9,6,1,2,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTGROUND,Elemental Change Earth
-1018,9,6,1,3,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTFIRE,Elemental Change Fire
-1019,9,6,1,4,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTWIND,Elemental Change Wind
-
-//****
-// RK Rune Knight
-//****
-2001,1,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, RK_ENCHANTBLADE,Enchant Blade
-2002,7:8:9:10:11,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, RK_SONICWAVE,Sonic Wave
-2003,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, RK_DEATHBOUND,Death Bound
-2004,1,8,1,-1,0,0,10,-5,no,0,0,0,weapon,0, RK_HUNDREDSPEAR,Hundred Spear
-2005,1,6,2,4,0x2,2,5,1,no,0,0,0,weapon,3, RK_WINDCUTTER,Wind Cutter
-2006,0,6,4,-1,0x2,5,5,1,no,0,0,0,weapon,0, RK_IGNITIONBREAK,Ignition Break
-2007,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, RK_DRAGONTRAINING,Dragon Training
-2008,9,6,2,3,0xC2,1:1:1:2:2:2:3:3:4:4,10,1,no,0,0,0,misc,0, RK_DRAGONBREATH,Dragon Breath //CHECK May have to change this back to a weapon type attack.
-2009,0,6,4,0,0x3,3:4:5:6:7,5,1,yes,0,0,0,weapon,0, RK_DRAGONHOWLING,Dragon Howling
-2010,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RK_RUNEMASTERY,Rune Mastery
-2011,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_MILLENNIUMSHIELD,Millenium Shield
-2012,1,6,4,-1,0,0x8,1,1,yes,0,0,0,weapon,0, RK_CRUSHSTRIKE,Crush Strike
-2013,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_REFRESH,Refresh
-2014,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_GIANTGROWTH,Giant Growth
-2015,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_STONEHARDSKIN,Stone Hard Skin
-2016,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_VITALITYACTIVATION,Vitality Activation
-2017,0,6,4,-1,0x2,3,1,1,no,0,0,0,weapon,7, RK_STORMBLAST,Storm Blast
-2018,0,6,4,0,0x3,-1,1,1,yes,0,0,0,none,0, RK_FIGHTINGSPIRIT,Fighting Spirit //CHECK Is this splash needed?
-2019,9,6,4,6,0x1,0,1,1,yes,0,0,0,none,0, RK_ABUNDANCE,Abundance
-2020,5:6:7:8:9,6,1,-1,0,0,5,1,yes,0,0,0,weapon,0, RK_PHANTOMTHRUST,Phantom Thrust
-
-//****
-// WL Warlock
-//****
-2201,11,6,16,0,0,0,5,1,yes,0,0,0,magic,0, WL_WHITEIMPRISON,White Imprison
-2202,11,8,1,8,0x2,1:1:1:2:2,5,-2,yes,0,0,0,magic,0, WL_SOULEXPANSION,Soul Expansion
-2203,0,8,4,1,0x2,13,5,-3:-4:-5:-6:-7,yes,0,0,0,magic,0, WL_FROSTMISTY,Frosty Misty
-2204,0,8,4,1,0x2,13,5,-5,yes,0,0,0,magic,0, WL_JACKFROST,Jack Frost
-2205,11,6,1,0,0x1,0,5,1,yes,0,0,0,magic,0, WL_MARSHOFABYSS,Marsh of Abyss
-2206,0,6,4,0,0x1,0,5,1,yes,0,0,0,magic,0, WL_RECOGNIZEDSPELL,Recognized Spell
-2207,7,6,1,2,0x3,1:2:2:3:3,5,1,yes,0,0,0,magic,0, WL_SIENNAEXECRATE,Sienna Execrate
-2208,0,0,0,0,0,0,3,0,no,0,0,0,none,0, WL_RADIUS,Radius
-2209,0,6,4,0,0x3,9:10:11:12:13,5,1,yes,0,0,0,magic,0, WL_STASIS,Stasis
-2210,11,6,1,0,0,0,5,1,yes,0,0,0,magic,0, WL_DRAINLIFE,Drain Life
-2211,11,8,1,3,0x2,3,5,-7,yes,0,0,0,magic,3, WL_CRIMSONROCK,Crimson Rock
-2212,11,6,1,3,0,0,5,1,yes,0,0,0,magic,0, WL_HELLINFERNO,Hell Inferno
-2213,11,8,2,0,0x2,15,5,-20,yes,0,0,0,magic,2, WL_COMET,Comet //CHECK AoE in official code appears to be 15 x 15, yet casting circle is much bigger then that.
-2214,11,6,1,0,0,3,5,1,yes,0,0,0,magic,0, WL_CHAINLIGHTNING,Chain Lightning //CHECK Is the splash being used for the target search?
-2215,11,6,1,4,0,0,5,1,no,0,0,0,magic,0, WL_CHAINLIGHTNING_ATK,Chain Lightning Attack
-2216,3,8,2,2,0,0,5,-6:-7:-8:-9:-10,yes,0,0,0,magic,0, WL_EARTHSTRAIN,Earth Strain
-2217,11,6,1,0,0,0,5,1,yes,0,0,0,magic,0, WL_TETRAVORTEX,Tetra Vortex
-2218,11,6,1,3,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_FIRE,Tetra Vortex Fire
-2219,11,6,1,1,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_WATER,Tetra Vortex Water
-2220,11,6,1,4,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_WIND,Tetra Vortex Wind
-2221,11,6,1,2,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_GROUND,Tetra Vortex Earth
-2222,0,6,4,3,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONFB,Summon Fire Ball
-2223,0,6,4,4,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONBL,Summon Lightning Ball
-2224,0,6,4,1,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONWB,Summon Water Ball
-2225,11,6,1,3,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_FIRE,Summon Attack Fire //CHECK Summon attack ID's dont appear to have a range.
-2226,11,6,1,4,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_WIND,Summon Attack Wind
-2227,11,6,1,1,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_WATER,Summon Attack Water
-2228,11,6,1,2,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_GROUND,Summon Attack Earth
-2229,0,6,4,2,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONSTONE,Summon Stone
-2230,11,8,1,0,0,0,2,1,yes,0,0,0,magic,0, WL_RELEASE,Release //CHECK Should it be left to do multi hit or single hit?
-2231,0,6,4,0,0x1,0,1,1,yes,0,0,0,magic,0, WL_READING_SB,Reading Spellbook
-2232,0,0,0,0,0,0,5,0,no,0,0,0,none,0, WL_FREEZE_SP,Freeze Spell
-
-
-//****
-// GC Guillotine Cross
-//****
-2021,5,6,1,0,0x1,0,5,1,no,0,0,0,none,0, GC_VENOMIMPRESS,Venom Impress
-2022,3,8,1,-1,0,0,5,-7,no,0,0,0,weapon,0, GC_CROSSIMPACT,Cross Impact
-2023,3:4:5:6:7,6,1,-1,0,0,5,1,no,0,0,0,weapon,0,GC_DARKILLUSION,Dark Illusion
-2024,0,0,0,0,0,0,10,0,no,0,0,0,none,0, GC_RESEARCHNEWPOISON,Research New Poison
-2025,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, GC_CREATENEWPOISON,Create New Poison
-2026,5,6,16,0,0x1,0,1,1,no,0,0,0,none,0, GC_ANTIDOTE,Antidote
-2027,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, GC_POISONINGWEAPON,Poisoning Weapon
-2028,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, GC_WEAPONBLOCKING,Weapon Blocking
-2029,-2,6,4,-1,0x2,1,5,1,no,0,0,0,weapon,3, GC_COUNTERSLASH,Counter Slash
-2030,-2,6,4,-1,0x1,0,5,1,no,0,0x200,0,weapon,0, GC_WEAPONCRUSH,Weapon Crush //CHECK SHould this and the above skill have INF2 0x200?
-2031,1,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, GC_VENOMPRESSURE,Venom Pressure
-2032,5,6,2,0,0x1,0,5,1,yes,0,0,1,none,0, GC_POISONSMOKE,Poison Smoke
-2033,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, GC_CLOAKINGEXCEED,Cloaking Exceed
-2034,0,6,4,-1,0x2,3,1,1,no,0,0,0,weapon,0, GC_PHANTOMMENACE,Phantom Menace
-2035,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, GC_HALLUCINATIONWALK,Hallucination Walk
-2036,0,6,4,-1,0x2,1:1:1:1:2,5,1,no,0,0,0,weapon,0, GC_ROLLINGCUTTER,Rolling Cutter
-2037,9:10:11:12:13,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, GC_CROSSRIPPERSLASHER,Cross Ripper Slasher
-
-//****
-// AB Arch Bishop
-//****
-2038,11,8,1,6,0x2,3,5,-3,yes,0,0,0,magic,0, AB_JUDEX,Judex
-2039,0,6,4,0,0x1,0,1,1,yes,0,0,0,magic,0, AB_ANCILLA,Ancilla
-2040,11,8,1,6,0,0,10,-10,yes,0,0,0,magic,0, AB_ADORAMUS,Adoramus
-2041,0,6,4,6,0x3,3:7:15,3,1,yes,0,0,0,magic,0, AB_CLEMENTIA,Crementia
-2042,0,6,4,6,0x3,3:7:15,3,1,yes,0,0,0,magic,0, AB_CANTO,Canto Candidus
-2043,0,6,4,6,0x3,3:7:15,3,1,yes,0,0,0,magic,0, AB_CHEAL,Coluceo Heal
-2044,11,6,2,6,0x1,0,5,1,yes,0,0,1,magic,0, AB_EPICLESIS,Epiclesis
-2045,0,6,4,6,0x3,15,10,1,yes,0,0,0,magic,0, AB_PRAEFATIO,Praefatio
-2046,0,6,4,6,0x3,15,10,1,yes,0,0,0,magic,0, AB_ORATIO,Oratio
-2047,0,6,4,6,0x3,15,4,1,yes,0,0,0,magic,0, AB_LAUDAAGNUS,Lauda Agnus
-2048,0,6,4,6,0x3,15,4,1,yes,0,0,0,magic,0, AB_LAUDARAMUS,Lauda Ramus
-2049,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AB_EUCHARISTICA,Eucharistica
-2050,11,6,16,6,0x1,0,1,1,yes,0,0,0,magic,0, AB_RENOVATIO,Renovatio
-2051,11,6,16,6,0x21,0,5,1,yes,0,0,0,magic,0, AB_HIGHNESSHEAL,Highness Heal //CHECK Info shows this has magic attack.
-2052,11,6,1,0,0x1,0,5,1,yes,0,0xA00,0,magic,0, AB_CLEARANCE,Clearance //CHECK Also shows this as a magic attack. Why?
-2053,0,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, AB_EXPIATIO,Expiatio //CHECK Does this also give the buff to party members?
-2054,0,6,4,6,0x1,0,10,1,yes,0,0,0,none,0, AB_DUPLELIGHT,Duple Light //CHECK Had issues adding a skill level check to make the % go higher with the skills level. Will do later.
-2055,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, AB_DUPLELIGHT_MELEE,Duple Light Melee
-2056,-1,6,1,0,0,0,10,1,no,0,0,0,magic,0, AB_DUPLELIGHT_MAGIC,Duple Light Magic
-2057,0,6,4,6,0x3,4:5:6:7:8,5,1,yes,0,0,0,magic,0, AB_SILENTIUM,Silentium //CHECk Marked magic attack as well. Hmmmm....
-
-2515,11,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, AB_SECRAMENT,Secrament
-
-//****
-// RA Ranger
-//****
-2233,9,8,1,-1,0x2,3:3:3:3:3:4:4:4:4:5,10,-3,yes,0,0,0,weapon,0, RA_ARROWSTORM,Arrow Storm
-2234,0,6,4,0,0,0,5,1,yes,0,0,0,none,0, RA_FEARBREEZE,Fear Breeze
-2235,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RA_RANGERMAIN,Ranger Main
-2236,9,8,1,-1,0,0,10,1,yes,0,0,0,weapon,0, RA_AIMEDBOLT,Aimed Bolt
-2237,9,6,2,0,0x3,3,1,1,no,0,0,0,none,0, RA_DETONATOR,Detonator
-2238,3,6,2,0,0x3,2,5,1,no,0,0x80,3,misc,0, RA_ELECTRICSHOCKER,Electric Shocker
-2239,3,6,2,0,0x42,3,5,1,no,0,0x80,3,misc,0, RA_CLUSTERBOMB,Cluster Bomb
-2240,0,6,4,0,0,0,1,1,no,0,0,0,none,0, RA_WUGMASTERY,Warg Mastery
-2241,0,6,4,0,0,0,3,1,no,0,0,0,none,0, RA_WUGRIDER,Warg Rider
-2242,0,6,4,-1,0x2,1,1,0,no,0,0,0,weapon,0, RA_WUGDASH,Warg Dash
-2243,9,6,1,0,0,0,5,1,no,0,0,0,weapon,0, RA_WUGSTRIKE,Warg Strike
-2244,9,6,1,0,0,0,5,1,no,0,0,0,weapon,0, RA_WUGBITE,Warg Bite
-2245,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RA_TOOTHOFWUG,Tooth of Warg
-2246,0,6,4,0,0x2,3:4:5:6:7,5,1,no,0,0,0,weapon,0, RA_SENSITIVEKEEN,Sensitive Keen
-2247,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, RA_CAMOUFLAGE,Camouflage
-2248,0,0,0,0,0,0,5,0,no,0,0,0,none,0, RA_RESEARCHTRAP,Research Trap
-2249,3,6,2,3,0x43,2,1,1,no,0,0x80,1,misc,0, RA_MAGENTATRAP,Magenta Trap
-2250,3,6,2,1,0x43,2,1,1,no,0,0x80,1,misc,0, RA_COBALTTRAP,Cobalt Trap
-2251,3,6,2,2,0x43,2,1,1,no,0,0x80,1,misc,0, RA_MAIZETRAP,Maize Trap
-2252,3,6,2,4,0x43,2,1,1,no,0,0x80,1,misc,0, RA_VERDURETRAP,Verdure Trap
-2253,3,6,2,0,0x42,2,5,1,no,0,0x80,2,misc,0, RA_FIRINGTRAP,Firing Trap
-2254,3,6,2,0,0x42,2,5,1,no,0,0x80,2,misc,0, RA_ICEBOUNDTRAP,Icebound Trap
-
-//****
-// NC Mechanic
-2255,0,0,0,0,0,0,5,0,no,0,0,0,none,0, NC_MADOLICENCE,Mado License
-2256,11,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, NC_BOOSTKNUCKLE,Boost Knuckle
-2257,3,6,1,-1,0,0,3,1,no,0,0,0,weapon,0, NC_PILEBUNKER,Pile Bunker
-2258,13,6,1,-1,0,0,3,1,no,0,0,0,weapon,0, NC_VULCANARM,Vulcan Arm
-2259,5,6,1,3,0,2,3,1,no,0,0,5,weapon,0, NC_FLAMELAUNCHER,Flame Launcher
-2260,7,6,2,1,0x2,2:3:4,3,1,no,0,0,0,weapon,0, NC_COLDSLOWER,Cold Slower
-2261,7,6,2,-1,0x42,3:2:1,3,1,no,0,0,0,weapon,0, NC_ARMSCANNON,Arm Cannon
-2262,0,6,4,0,0x1,0,3,1,no,0,0,0,none,0, NC_ACCELERATION,Acceleration
-2263,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, NC_HOVERING,Hovering
-2264,0,6,4,0,0x1,0,1,1,no,0,0,0,none,7, NC_F_SIDESLIDE,Front-Side Slide
-2265,0,6,4,0,0x1,0,1,1,no,0,0,0,none,7, NC_B_SIDESLIDE,Back-Side Slide
-2266,0,0,0,0,0,0,4,0,no,0,0,0,none,0, NC_MAINFRAME,Mainframe Restructure // Check me. Part of the code notes translated to "The amount of fuel have".
-2267,0,6,4,-1,0x42,2:3:4,3,1,no,0,0,0,misc,5, NC_SELFDESTRUCTION,Self Destruction
-2268,0,6,4,0,0x1,0,4,1,yes,0,0,0,none,0, NC_SHAPESHIFT,Shape Shift
-2269,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, NC_EMERGENCYCOOL,Emergency Cool
-2270,0,6,4,0,0x3,7,1,1,yes,0,0,0,none,0, NC_INFRAREDSCAN,Infrared Scan
-2271,9,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, NC_ANALYZE,Analyze
-2272,0,6,4,0,0x3,1:2:3,3,1,yes,0,0,0,none,0, NC_MAGNETICFIELD,Magnetic Field
-2273,0,6,4,0,0x1,0,3,1,yes,0,0,0,none,0, NC_NEUTRALBARRIER,Neutral Barrier
-2274,0,6,4,0,0x1,0,3,1,yes,0,0,0,none,0, NC_STEALTHFIELD,Stealth Field
-2275,5,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, NC_REPAIR,Repair
-2276,0,0,0,0,0,0,10,0,no,0,0,0,none,0, NC_TRAININGAXE,Axe Training
-2277,0,0,0,0,0,0,5,0,no,0,0,0,none,0, NC_RESEARCHFE,Research Fire/Earth
-2278,4:5:6:7:8,6,1,-1,0,0,5,1,no,0,0,0,weapon,2:3:4:5:6, NC_AXEBOOMERANG,Axe Boomerang
-2279,1,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, NC_POWERSWING,Power Swing
-2280,0,8,4,-1,0x2,2:2:3:3:3,5,-6,no,0,0,0,weapon,0, NC_AXETORNADO,Axe Tornado // Check me. Takes 20 * Skill LV amount of HP each use.
-2281,2,6,2,0,0x1,0,5,1,yes,0,0,2,none,0, NC_SILVERSNIPER,FAW - Silver Sniper
-2282,2,6,2,0,0x1,0,5,1,yes,0,0,2,none,0, NC_MAGICDECOY,FAW - Magic Decoy //CHECK FIX ME!!!! Wind and Earth stones spawning opposite decoys.
-2283,2,6,1,0,0x1,0,1,1,no,0,0,0,none,0, NC_DISJOINT,FAW Removal
-
-//****
-// SC Shadow Chaser
-2284,1,6,1,-1,0x2,1,5,1,no,0,0,0,weapon,0, SC_FATALMENACE,Fatal Menace
-2285,0,6,4,0,0x1,0,10,1,no,0,0,0,none,0, SC_REPRODUCE,Reproduce
-2286,0,6,4,0,0x1,0,10,1,yes,0,0,0,none,0, SC_AUTOSHADOWSPELL,Auto Shadow Spell
-2287,5,6,1,0,0x1,0,5,1,no,0,0,0,none,0, SC_SHADOWFORM,Shadow Form
-2288,7:7:7:9:9:9:9:11:11:11,8,1,-1,0,0,10,-3,yes,0,0,0,weapon,3, SC_TRIANGLESHOT,Triangle Shot
-2289,0,6,4,0,0x3,2,5,1,no,0,0,0,none,0, SC_BODYPAINT,Body Painting
-2290,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, SC_INVISIBILITY,Invisibility
-2291,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SC_DEADLYINFECT,Deadly Infect
-2292,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_ENERVATION,Masquerade - Enervation
-2293,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_GROOMY,Masquerade - Gloomy
-2294,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_IGNORANCE,Masquerade - Ignorance
-2295,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_LAZINESS,Masquerade - Laziness
-2296,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_UNLUCKY,Masquerade - Unlucky
-2297,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_WEAKNESS,Masquerade - Weakness
-2298,3,6,1,0,0x1,0,5,1,yes,0,0,0,weapon,0, SC_STRIPACCESSARY,Strip Accessory //CHECK Is weapon attack type needed?
-2299,7,6,2,0,0x1,0,3,1,yes,0,0,3,none,0, SC_MANHOLE,Man Hole
-2300,7,6,2,0,0x1,0,3,1,yes,0,0,1,none,0, SC_DIMENSIONDOOR,Dimension Door
-2301,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0, SC_CHAOSPANIC,Chaos Panic
-2302,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0, SC_MAELSTROM,Maelstrom
-2303,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0, SC_BLOODYLUST,Bloody Lust
-2304,0,6,4,-1,0,0,3,1,no,0,0,0,weapon,0, SC_FEINTBOMB,Feint Bomb
-
-//****
-// LG Royal Guard
-2307,11,8,1,-1,0,2,5,1,no,0,0,10,weapon,0, LG_CANNONSPEAR,Cannon Spear
-2308,7,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, LG_BANISHINGPOINT,Banishing Point
-2309,0,6,4,0,0x3,2,3,1,no,0,0,0,none,0, LG_TRAMPLE,Trample
-2310,1,6,1,0,0,0,5,1,no,0,0,0,weapon,0, LG_SHIELDPRESS,Shield Press
-2311,0,6,4,0,0x3,3,5,1,no,0,0,0,none,0, LG_REFLECTDAMAGE,Reflect Damage
-2312,5,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, LG_PINPOINTATTACK,Pinpoint Attack
-2313,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_FORCEOFVANGUARD,Force of Vanguard
-2314,1,6,1,-1,0,0,1,1,no,0,0,0,weapon,0, LG_RAGEBURST,Rage Burst
-2315,0,6,4,0,0x2,3,3,1,yes,0,0,0,none,2, LG_SHIELDSPELL,Shield Spell
-2316,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_EXEEDBREAK,Exceed Break
-2317,1,6,2,-1,0x2,5,5,1,yes,0,0,0,none,3:4:5:6:7, LG_OVERBRAND,Over Brand //CHECK I know the splash is needed somehow for the strange AoE it gives.
-2318,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_PRESTIGE,Prestige
-2319,0,6,4,0,0x1,3,5,1,no,0,0,0,weapon,0, LG_BANDING,Banding //CHECK Splash isnt needed right? Banding has its own UNIT ID.
-2320,0,6,4,-1,0x2,3,5,1,yes,0,0,0,weapon,0, LG_MOONSLASHER,Moon Slasher
-2321,1,8,2,6,0x2,5,5,-7,yes,0,0,0,weapon,0, LG_RAYOFGENESIS,Ray of Genesis
-2322,0,6,16,0,0x3,1,5,1,yes,0,0,0,none,0, LG_PIETY,Piety
-2323,0,8,4,2,0x2,1:1:2:2:3,5,-5,yes,0,0,0,weapon,0, LG_EARTHDRIVE,Earth Drive
-2324,3,8,1,-1,0,0,5,3,yes,0,0,0,weapon,0, LG_HESPERUSLIT,Hesperus Lit
-2325,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_INSPIRATION,Inspiration
-
-//****
-// SR Sura
-2326,-2,8,1,-1,0,0,10,2,no,0,0,0,weapon,0, SR_DRAGONCOMBO,Dragon Combo //CHECK Is this 2 regular hits or sub hits? Yes its 2 sub hits.
-2327,0,8,4,-1,0x2,2,5,-3,no,0,0,0,weapon,3, SR_SKYNETBLOW,Sky Net Blow //CHECK Video shows 3 hits. Its sub hits right? Data check shows no sub, one source shows 3 hits, another shows 5.
-2328,0,6,4,-1,0x2,1:2:3:4:5,5,1,no,0,0,0,weapon,0, SR_EARTHSHAKER,Earth Shaker //CHECK Must add a check in battle.c to triple damage if hitting a hidden target.
-2329,-2,8,4,-1,0,0,5,-2,no,0,0x200,0,weapon,0, SR_FALLENEMPIRE,Fallen Empire //CHECK Video shows 2 hits. Is it sub hits? Yes its divided between 2 hits.
-2330,-2,6,1,-1,0x42,1:1:1:1:1:2:2:2:2:2,10,1,yes,0,0,0,weapon,0, SR_TIGERCANNON,Tiger Cannon //CHECK Need to fix to be enemy targeted and also combo after Fallen Empire.
-2331,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SR_HELLGATE,Hell Gate
-2332,5,6,4,-1,0x2,3,5,1,no,0,0,0,weapon,0, SR_RAMPAGEBLASTER,Rampage Blaster
-2333,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SR_CRESCENTELBOW,Crescent Elbow //CHECK Check the autospell ID.
-2334,0,6,4,0,0x3,1:1:2:2:3,5,1,no,0,0,0,none,0, SR_CURSEDCIRCLE,Cursed Circle //CHECK Code shows it takes up to 5% of your HP upon use?
-2335,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SR_LIGHTNINGWALK,Lightning Walk
-2336,7:8:9:10:11,6,1,-1,0,0,5,1,no,0,0,0,weapon,2:3:4:5:6, SR_KNUCKLEARROW,Knuckle Arrow
-2337,0,6,4,-1,0x2,2,1,1,yes,0,0,0,weapon,0, SR_WINDMILL,Windmill
-2338,0,6,4,0,0x1,0,10,1,no,0,0,0,none,0, SR_RAISINGDRAGON,Raising Dragon
-2339,0,0,0,0,0,0,5,1,no,0,0,0,none,0, SR_GENTLETOUCH,Gentle Touch
-2340,0,6,4,0,0x3,2,1,1,no,0,0,0,none,0, SR_ASSIMILATEPOWER,Assimilate Power
-2341,3,6,16,0,0x1,0,1,1,yes,0,0x200,0,none,0, SR_POWERVELOCITY,Power Velocity
-2342,1,6,1,-1,0x20,0,5,1,no,0,0,0,weapon,3, SR_CRESCENTELBOW_AUTOSPELL,Crescent Elbow Autospell //CHECK Does this ignore defense?
-2343,1:2:3:3:4:4:5:5:6:7,8,1,0,0,0,10,-7,yes,0,0,0,weapon,0, SR_GATEOFHELL,Gate of Hell //CHECK Need to fix to be enemy targeted and also combo after Fallen Empire
-2344,2,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, SR_GENTLETOUCH_QUIET,Gentle Touch - Quiet
-2345,2,6,16,0,0x1,0,5,1,no,0,0,0,magic,0, SR_GENTLETOUCH_CURE,Gentle Touch - Cure //CHECK Its a healing skill. Guessing it has to be magic type? Healing isnt working.
-2346,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, SR_GENTLETOUCH_ENERGYGAIN,Gentle Touch - Energy Gain
-2347,2,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, SR_GENTLETOUCH_CHANGE,Gentle Touch - Change
-2348,2,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, SR_GENTLETOUCH_REVITALIZE,Gentle Touch - Revitalize
-//More from Sura but not following ID order
-2517,0,6,4,-1,0x2,3:4:5:6:7,5,1,no,0,0,0,weapon,0, SR_HOWLINGOFLION,Howling of Lion
-2518,11,6,2,-1,0x2,2:2:3:3:4,5,1,no,0,0,0,weapon,0, SR_RIDEINLIGHTNING,Ride In Lightening
-
-//****
-// WA Wanderer
-2350,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, WA_SWING_DANCE,Swing Dance
-2351,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, WA_SYMPHONY_OF_LOVER,Symphony of Lovers
-2352,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, WA_MOONLIT_SERENADE,Moonlit Serenade
-
-//****
-// MI Minstrel
-2381,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, MI_RUSH_WINDMILL,Windmill Rush Attack
-2382,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, MI_ECHOSONG,Echo Song
-2383,9,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, MI_HARMONIZE,Harmonize
-
-//****
-// WM Wanderer/Minstrel
-2412,0,0,0,0,0,0,10,0,no,0,0,0,none,0, WM_LESSON,Lesson
-2413,9,8,1,-1,0,0,5,-2:-2:-3:-3:-4,yes,0,0,0,magic,0, WM_METALICSOUND,Metallic Sound
-2414,9,6,2,-1,0x3,1,5,1,yes,0,0x80,3,none,0, WM_REVERBERATION,Reverberation
-2415,0,6,1,-1,0x6,1,5,1,no,0,0,0,weapon,0, WM_REVERBERATION_MELEE,Reverberation Melee
-2416,0,6,1,0,0x6,1,5,1,no,0,0,0,magic,0, WM_REVERBERATION_MAGIC,Reverberation Magic
-2417,11,6,2,0,0x3,5,1,1,no,0,0,0,none,0, WM_DOMINION_IMPULSE,Dominion Impulse
-2418,9,6,2,-1,0x1,0,5,1,yes,0,0,0,none,0, WM_SEVERE_RAINSTORM,Severe Rainstorm
-2419,9,6,2,0,0x3,1,5,1,yes,0,0x80,5,none,0, WM_POEMOFNETHERWORLD,Poem of The Netherworld //CHECK May need to recode too.
-2420,0,6,4,0,0x2,2:3:4:5:6,5,1,yes,0,0,0,none,0, WM_VOICEOFSIREN,Voice of Siren
-2421,7,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, WM_DEADHILLHERE,Valley of Death
-2422,7,6,2,0,0x3,5:6:7:8:9,5,1,yes,0,0,0,none,0, WM_LULLABY_DEEPSLEEP,Deep Sleep Lullaby
-2423,0,6,4,0,0x3,3:4:5:6:7,5,1,yes,0,0,0,none,0, WM_SIRCLEOFNATURE,Circle of Nature's Sound
-2424,9,6,4,0,0x1,0,5,1,yes,0,0,0,magic,0, WM_RANDOMIZESPELL,Improvised Song
-2425,9,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, WM_GLOOMYDAY,Gloomy Day
-2426,9,6,2,0,0x2,2:3:3:4:4,5,1,yes,0,0x4000,0,weapon,0, WM_GREAT_ECHO,Great Echo
-2427,0,6,4,0,0x3,5:6:7:8:9,5,1,yes,0,0x4000,0,none,0, WM_SONG_OF_MANA,Song of Mana
-2428,0,6,4,0,0x3,5:6:7:8:9,5,1,yes,0,0x4000,0,none,0, WM_DANCE_WITH_WUG,Dance With A Warg
-2429,9,6,1,0,0x2,2:2:3:3:4,5,1,yes,0,0x4000,0,weapon,0, WM_SOUND_OF_DESTRUCTION,Sound of Destruction //CHECK Source shows its magic attack. Need to confirm before changing.
-2430,0,6,4,0,0x3,3:4:5:6:7,5,1,yes,0,0x4000,0,none,0, WM_SATURDAY_NIGHT_FEVER,Saturday Night Fever
-2431,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,magic,0, WM_LERADS_DEW,Lerad's Dew
-2432,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,none,0, WM_MELODYOFSINK,Melody of Sink
-2433,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,none,0, WM_BEYOND_OF_WARCRY,Warcry of Beyond
-2434,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,none,0, WM_UNLIMITED_HUMMING_VOICE,Unlimited Humming Voice
-2516,11,6,1,-1,0x2,5,5,1,no,0,0,0,weapon,0, WM_SEVERE_RAINSTORM_MELEE,Severe Rainstorm Melee
-
-//****
-// SO Sorcerer
-2443,0,6,4,3,0,0,5,1,yes,0,0,8:10:12:14:16,magic,0, SO_FIREWALK,Fire Walk //CHECK Video and data shows each cell only hits once.
-2444,0,6,4,4,0,0,5,1,yes,0,0,8:10:12:14:16,magic,0, SO_ELECTRICWALK,Electric Walk
-2445,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SO_SPELLFIST,Spell Fist
-2446,9,6,2,2,0,0,5,-3,yes,0,0,0,magic,0, SO_EARTHGRAVE,Earth Grave
-2447,9,6,2,1,0,0,5,-5,yes,0,0,0,magic,0, SO_DIAMONDDUST,Diamond Dust
-2448,9,6,1,5,0x2,1:1:1:1:2,5,1,yes,0,0,0,magic,0, SO_POISON_BUSTER,Poison Buster
-2449,9,6,2,0,0,0,5,1,yes,0,0,0,magic,0, SO_PSYCHIC_WAVE,Psychic Wave
-2450,9,6,2,5,0,0,5,1,yes,0,0,0,magic,0, SO_CLOUD_KILL,Cloud Kill
-2451,9,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, SO_STRIKING,Striking //CHECK Data shows a % for increased successful refine rate. Is this true?
-2452,9,6,2,3,0x1,0,5,1,yes,0,0,0,magic,0, SO_WARMER,Warmer
-2453,9,6,2,0,0x1,0,5,1,yes,0,0,0,magic,0, SO_VACUUM_EXTREME,Vacuum Extreme
-2454,9,6,1,4,0x2,1:1:2:2:3,5,1,yes,0,0,0,magic,0, SO_VARETYR_SPEAR,Varetyr Spear
-2455,9,6,1,0,0x3,1:1:2:2:3,5,1,yes,0,0,0,magic,0, SO_ARRULLO,Arrullo
-2456,0,6,4,0,0x1,0,4,1,yes,0,0,0,none,0, SO_EL_CONTROL,Spirit Control
-2457,0,6,4,3,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_AGNI,Summon Fire Spirit Agni
-2458,0,6,4,1,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_AQUA,Summon Water Spirit Aqua
-2459,0,6,4,4,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_VENTUS,Summon Wind Spirit Ventus
-2460,0,6,4,2,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_TERA,Summon Earth Spirit Tera
-2461,5,6,1,0,0x1,0,1,1,no,0,0,0,none,0, SO_EL_ACTION,Elemental Action
-2462,0,6,4,0,0x1,0,2,1,yes,0,0,0,none,0, SO_EL_ANALYSIS,Four Spirit Analysis
-2463,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SO_EL_SYMPATHY,Spirit Sympathy
-2464,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, SO_EL_CURE,Spirit Recovery
-2465,9,6,2,3,0x1,0,3,1,yes,0,0,1,magic,0, SO_FIRE_INSIGNIA,Fire Insignia //CHECK All 4 insignia skills can be targeted and animations work
-2466,9,6,2,1,0x1,0,3,1,yes,0,0,1,magic,0, SO_WATER_INSIGNIA,Water Insignia // but its effects havent been coded yet.
-2467,9,6,2,4,0x1,0,3,1,yes,0,0,1,magic,0, SO_WIND_INSIGNIA,Wind Insignia
-2468,9,6,2,2,0x1,0,3,1,yes,0,0,1,magic,0, SO_EARTH_INSIGNIA,Earth Insignia
-
-//****
-// GN Genetic
-2474,0,0,0,0,0,0,5,0,no,0,0,0,none,0, GN_TRAINING_SWORD,Sword Training
-2475,0,0,0,0,0,0,5,0,no,0,0,0,none,0, GN_REMODELING_CART,Cart Remodeling
-2476,0,6,4,-1,0x2,2,5,1,no,0,0,0,weapon,2, GN_CART_TORNADO,Cart Tornado
-2477,7:8:9:10:11,6,1,-1,0x2,1:1:2:2:3,5,1,yes,0,0,0,weapon,0, GN_CARTCANNON,Cart Cannon
-2478,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, GN_CARTBOOST,Cart Boost
-2479,9,6,2,0,0,0,5,1,yes,0,0x80,5,misc,0, GN_THORNS_TRAP,Thorn Trap
-2480,11,6,1,0,0x1,0,5,1,yes,0,0,3,misc,0, GN_BLOOD_SUCKER,Blood Sucker //CHECK Data says its a magic attack. Hmmmm....
-2481,11,6,1,-1,0x2,1:2:3:4:5,5,1,yes,0,0,0,weapon,0, GN_SPORE_EXPLOSION,Spore Explosion //CHECK Data says its element is set to neutral. Need to confirm.
-2482,11,6,16,0,0,0,5,1,yes,0,0,1,weapon,2, GN_WALLOFTHORN,Wall of Thorns
-2483,11,6,2,0,0x3,4,10,1,yes,0,0x2000,0,weapon,0, GN_CRAZYWEED,Crazy Weed
-2484,0,6,2,2,0x2,3,10,1,no,0,0x2000,0,weapon,0, GN_CRAZYWEED_ATK,Crazy Weed Attack
-2485,9,6,2,3,0,0,5,1,yes,0,0,0,magic,0, GN_DEMONIC_FIRE,Demonic Fire
-2486,9,6,2,0,0,0,5,1,yes,0,0,0,none,0, GN_FIRE_EXPANSION,Fire Expansion //CHECK FIX ME!!!! Level 1 is reducing the damage. Should increase it by 50%
-2487,9,6,2,0,0,0,1,1,no,0,0,0,none,0, GN_FIRE_EXPANSION_SMOKE_POWDER,Fire Expansion Smoke Powder
-2488,9,6,2,0,0,0,1,1,no,0,0,0,none,0, GN_FIRE_EXPANSION_TEAR_GAS,Fire Expansion Tear Gas
-2489,11,6,1,0,0,0,10,1:2:3:4:5:6:7:8:9:10,no,0,0,0,weapon,0, GN_FIRE_EXPANSION_ACID,Fire Expansion Acid
-2490,9,6,2,0,0x3,1,5,1,yes,0,0x80,2:3:4:5:6,none,0, GN_HELLS_PLANT,Hell's Plant
-2491,0,6,1,0,0xC0,0,5,1,no,0,0,0,misc,0, GN_HELLS_PLANT_ATK,Hell's Plant Attack
-2492,0,6,4,0,0x3,6:7:8:9:10,5,1,yes,0,0,0,none,0, GN_MANDRAGORA,Howling of Mandragora
-2493,11,6,16,0,0x1,0,1,1,yes,0,0,0,none,0, GN_SLINGITEM,Sling Item
-2494,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, GN_CHANGEMATERIAL,Change Material
-2495,0,6,4,0,0x1,0,2,1,no,0,0,0,none,0, GN_MIX_COOKING,Mix Cooking
-2496,0,6,4,0,0x1,0,2,1,no,0,0,0,none,0, GN_MAKEBOMB,Create Bomb
-2497,0,6,4,0,0x1,0,10,1,no,0,0,0,none,0, GN_S_PHARMACY,Special Pharmacy
-2498,11,6,1,0,0,0,1,1,no,0,0,0,weapon,0, GN_SLINGITEM_RANGEMELEEATK,Sling Item Attack
-
-// Episode 13.3
-//2533,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, ALL_ODINS_RECALL,Odin's Recall
-2534,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, RETURN_TO_ELDICASTES,Return To Eldicastes
-2535,0,0,4,0,0x1,0,1,0,no,0,0x1,0,none,0, ALL_BUYING_STORE,Open Buying Store
-2536,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, ALL_GUARDIAN_RECALL,Guardian's Recall
-2537,9,6,16,0,0x1,0,2,1,yes,0,0,0,magic,0, ALL_ODINS_POWER,Odin's Power
-//2538,0,0,0,0,0,0,??,0,no,0,0,0,none,0, BEER_BOTTLE_CAP,Beer Bottle Cap
-//2539,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_ASSASSINCROSS,Assassin Cross of Sunset 2
-//2540,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_DISSONANCE,Dissonance 2
-//2541,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_UGLYDANCE,Ugly Dance 2
-//2542,0,0,0,0,0,0,??,0,no,0,0,0,none,0, ALL_TETANY,Tetany
-//2543,0,0,0,0,0,0,??,0,no,0,0,0,none,0, ALL_RAY_OF_PROTECTION,Ray of Protection
-//2544,0,0,0,0,0,0,??,0,no,0,0,0,none,0, MC_CARTDECORATE,Decorate Cart
-
-//****
-// Kagerou & Oboro
-3001,0,6,4,0,0,0,1,1,no,0,0,0,none,0, KO_YAMIKUMO,Yamikumo
-3002,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, KO_RIGHT,Right Hand Mastery
-3003,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, KO_LEFT,Left Hand Mastery
-3004,3:4:5:6:7,8,1,-1,0,0,5,-2,no,0,0,0,weapon,0, KO_JYUMONJIKIRI,Cross Strike
-3005,2,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, KO_SETSUDAN,Soul Sever
-3006,7:8:9:10:11,6,2,0,0x2,2,5,0,no,0,0,0,weapon,0, KO_BAKURETSU,Bakuretsu Kunai
-3007,0,6,4,-1,0x42,4:4:4:4:5,5,0,no,0,0,0,misc,0, KO_HAPPOKUNAI,Happo Kunai
-3008,9,8,2,0,0x52,2,10,-10,no,0,0,0,misc,0, KO_MUCHANAGE,Mucha Nage
-3009,9:10:11:12:13,8,2,-1,0x2,3,5,2,no,0,0,0,weapon,0, KO_HUUMARANKA,Huuma Shuriken Ranka
-3010,3,6,4,0,0x43,0,5,1,no,0,0x80,0,misc,0, KO_MAKIBISHI,Makibishi
-3011,0,6,4,0,0x1,0,5,0,yes,0,0,0,none,0, KO_MEIKYOUSISUI,Meikyo Shisui
-3012,0,6,4,0,0x1,0,5,0,no,0,0,1,none,7, KO_ZANZOU,Zanzou
-3013,5,6,1,0,0x1,0,5,0,no,0,0,0,none,0, KO_KYOUGAKU,Kyougaku
-3014,5,6,1,0,0x1,0,5,0,no,0,0,0,none,0, KO_JYUSATSU,Jyusatsu
-3015,0,6,4,3,0x1,0,1,1,no,0,0,0,none,0, KO_KAHU_ENTEN,Kahu Enten
-3016,0,6,4,1,0x1,0,1,1,no,0,0,0,none,0, KO_HYOUHU_HUBUKI,Hyouhu Hubuki
-3017,0,6,4,4,0x1,0,1,1,no,0,0,0,none,0, KO_KAZEHU_SEIRAN,Kazehu Seiran
-3018,0,6,4,2,0x1,0,1,1,no,0,0,0,none,0, KO_DOHU_KOUKAI,Dohu Koukai
-3019,11,6,1,0,0,0,5,0,no,0,0,0,weapon,0, KO_KAIHOU,Technique Kaihou
-3020,7,6,2,0,0,0,1,3,yes,0,0,0,magic,0, KO_ZENKAI,Zenkai
-3021,5:6:7:8:9,6,16,0,0x1,0,5,1,no,0,0,0,none,0, KO_GENWAKU,Genwaku
-3022,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, KO_IZAYOI,Izayoi
-3023,0,6,4,0,0x3,2:3:4:5:6,5,0,no,0,0,0,none,0, KG_KAGEHUMI,Kagehumi
-3024,7,6,1,0,0x1,0,5,1,no,0,0,0,none,0, KG_KYOMU,Kyomu
-3025,7,6,16,0,0x1,0,5,1,no,0,0,0,none,0, KG_KAGEMUSYA,Kagemusha
-3026,7,6,16,0,0x1,0,5,1,no,0,0,0,none,0, OB_ZANGETSU,Zangetsu
-3027,7,6,16,0,0x1,0,5,1,no,0,0,0,none,0, OB_OBOROGENSOU,Oboro Gensou
-3028,1,6,4,0,0x2,3,1,1,no,0,0,0,weapon,0, OB_OBOROGENSOU_TRANSITION_ATK,
-3029,7,6,1,0,0x1,0,5,0,no,0,0,0,none,0, OB_AKAITSUKI,Akaitsuki
-
-8001,9,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, HLIF_HEAL,Healing Touch
-8002,0,6,4,0,0x3,-1,5,1,no,0,0,0,none,0, HLIF_AVOID,Avoid
-8003,0,0,0,0,0,1,5,0,no,0,0,0,none,0, HLIF_BRAIN,Brain Surgery
-8004,0,6,4,0,0x1,0,3,0,no,0,0,0,none,0, HLIF_CHANGE,Change
-8005,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HAMI_CASTLE,Castling
-8006,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HAMI_DEFENCE,Defense
-8007,0,0,0,0,0x1,0,5,0,no,0,0,0,none,0, HAMI_SKIN,Adamantium Skin
-8008,0,6,4,0,0x1,0,3,0,no,0,0,0,none,0, HAMI_BLOODLUST,Bloodlust
-8009,1,8,1,0,0,0,5,-1:-2:-2:-2:-3,no,0,0,0,weapon,0, HFLI_MOON,Moonlight
-8010,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HFLI_FLEET,Fleeting Move
-8011,0,6,4,0,0x1,0,5,0,yes,0,0,0,misc,0, HFLI_SPEED,Speed
-8012,1,6,1,0,0,0,3,0,no,0,0,0,none,0, HFLI_SBR44,S.B.R.44
-8013,9,6,1,0,0,0,5,1:2:3:4:5,no,0,0,0,magic,0, HVAN_CAPRICE,Caprice
-8014,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HVAN_CHAOTIC,Benediction of Chaos
-8015,0,0,0,0,0x1,0,5,0,no,0,0,0,none,0, HVAN_INSTRUCT,Instruct
-8016,4,6,4,-1,0xD2,4,3,1,no,0,0,0,misc,0, HVAN_EXPLOSION,Bio Explosion
-//
-8018,9,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_SUMMON_LEGION,Summon Legion
-8019,5,6,1,5,0,0,5,1,no,0,0,0,weapon,0, MH_NEEDLE_OF_PARALYZE,Needle of Paralyze
-8020,5,6,2,5,0,0,5,1,no,0,0,1,weapon,0, MH_POISON_MIST,Poison Mist
-8021,1,6,1,0,0x1,0,5,1,no,0,0,0,none,0, MH_PAIN_KILLER,Pain Killer
-8022,0,6,4,0,0,0x1,5,1,no,0,0,0,none,0, MH_LIGHT_OF_REGENE,Light of Regene
-8023,0,6,4,0,0,0x1,5,1,no,0,0,0,none,0, MH_OVERED_BOOST,Overed Boost
-8024,7,6,1,4:0:4:0:4,0,0,5,1,no,0,0,0,magic,0, MH_ERASER_CUTTER,Eraser Cutter
-8025,7,6,2,4:0:4:0:4,0,0,5,1,no,0,0,0,magic,0, MH_XENO_SLASHER,Xeno Slasher
-8026,5:5:7:7:9,6,16,0,0x1,0,5,1,no,0,0,0,magic,0, MH_SILENT_BREEZE,Silent Breeze
-8027,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, MH_STYLE_CHANGE,Style Change
-8028,1,8,1,0,0,0,5,1,no,0,0,0,weapon,0, MH_SONIC_CRAW,Sonic Claw
-8029,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_SILVERVEIN_RUSH,Silver Bain Rush
-8030,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_MIDNIGHT_FRENZY,Midnight Frenzy
-8031,5:6:7:8:9,6,1,0,0,0,5,1,no,0,0,0,weapon,3, MH_STAHL_HORN,Steel Horn
-8032,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_GOLDENE_FERSE,Golden Heel
-8033,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_STEINWAND,Stone Wall
-8034,9,6,1,6,0x2,1:1:1:1:2,5,1,no,0,0,0,magic,0, MH_HEILIGE_STANGE,Holy Pole
-8035,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_ANGRIFFS_MODUS,Attack Mode
-8036,3:4:5:6:7,6,1,0,0,0,5,1,no,0,0,0,weapon,0, MH_TINDER_BREAKER,Tinder Breaker
-8037,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_CBC,Continual Break Combo
-8038,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_EQC,Eternal Quick Combo
-8039,0,6,4,3,0x2,1:1:1:2:2,5,1,no,0,0,0,weapon,0, MH_MAGMA_FLOW,Magma Flow
-8040,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_GRANITIC_ARMOR,Granitic Armor
-8041,7,6,2,3,0x2,0,5,1,no,0,0,1,weapon,0, MH_LAVA_SLIDE,Lava Slide
-8042,0,6,4,3,0x1,0,5,1,no,0,0,0,none,0, MH_PYROCLASTIC,Pyroclastic
-8043,7,6,2,0,0x1,0,5,1,no,0,0,3,none,0, MH_VOLCANIC_ASH,Volcanic Ash
-
-// Mercenary Skill Place holders
-8201,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, MS_BASH,Bash
-8202,0,6,4,3,0x2,2,10,1,no,0,0,0,weapon,2, MS_MAGNUM,Magnum_Break
-8203,-2,6,1,-1,0x2,1,10,1,no,33,0,0,weapon,1, MS_BOWLINGBASH,Bowling_Bash
-8204,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, MS_PARRYING,Parry
-8205,0,6,4,0,0,0,10,1,no,0,0,0,weapon,0, MS_REFLECTSHIELD,Shield_Reflect
-8206,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, MS_BERSERK,Frenzy
-8207,-9,8,1,-1,0,0,10,2,no,0,0,0,weapon,0, MA_DOUBLE,Double_Strafe
-8208,-9,6,2,-1,0x2,2,10,1,no,0,0x2000,0,weapon,2, MA_SHOWER,Arrow_Shower
-8209,3,6,2,0,0x1,0,5,1,no,0,0x80,0,misc,6:7:8:9:10, MA_SKIDTRAP,Skid_Trap
-8210,3,6,2,2,0x40,0,5,1,no,0,0x80,0,misc,0, MA_LANDMINE,Land_Mine
-8211,3,6,2,0,0x3,2,5,1,no,0,0x80,0,misc,0, MA_SANDMAN,Sandman
-8212,3,6,2,1,0x42,1,5,1,no,0,0x80,0,weapon,0, MA_FREEZINGTRAP,Freezing_Trap
-8213,2,6,32,0,0x1,0,1,1,no,0,0,0,misc,0, MA_REMOVETRAP,Remove_Trap
-8214,-9,6,1,-1,0x2,0,1,1,no,0,0x1,0,weapon,6, MA_CHARGEARROW,Arrow_Repel
-8215,9,8,1,-1,0,2,5,1,yes,0,0,13,weapon,0, MA_SHARPSHOOTING,Focused_Arrow_Strike
-8216,-2,8,1,-1,0,0,10,3,no,0,0,0,weapon,0, ML_PIERCE,Pierce
-8217,-2,6,1,-1,0x1,0,10,1,no,33,0,0,weapon,3, ML_BRANDISH,Brandish_Spear
-8218,5,8,1,-1,0x20,0,5,5,no,0,0,0,weapon,0, ML_SPIRALPIERCE,Spiral_Pierce
-8219,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, ML_DEFENDER,Defending_Aura
-8220,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, ML_AUTOGUARD,Guard
-8221,7:8:9:10:11,6,16,0,0x1,0,5,1,yes,0,0x600,0,none,0, ML_DEVOTION,Sacrifice
-8222,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0, MER_MAGNIFICAT,Magnificat
-8223,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, MER_QUICKEN,Two-Hand_Quicken
-8224,0,6,4,3,0x3,3,1,1,yes,0,0,0,magic,0, MER_SIGHT,Sight
-8225,1,8,1,-1,0,0,5,3,no,0,0,0,weapon,0, MER_CRASH,Crash
-8226,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_REGAIN,Regain
-8227,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_TENDER,Tender
-8228,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_BENEDICTION,Benediction
-8229,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_RECUPERATE,Recuperate
-8230,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_MENTALCURE,Mental_Cure
-8231,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_COMPRESS,Compress
-8232,9,6,1,0,0x1,0,10,1,no,0,0,0,none,0, MER_PROVOKE,Provoke
-8233,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, MER_AUTOBERSERK,Berserk
-8234,9,6,1,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_DECAGI,Decrease_AGI
-8235,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, MER_SCAPEGOAT,Scapegoat
-8236,5,6,1,0,0x1,0,10,0,yes,0,0,0,magic,0, MER_LEXDIVINA,Lex_Divina
-8237,9,6,1,0,0x1,0,1,1,yes,0,0,0,magic,0, MER_ESTIMATION,Sense
-8238,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_KYRIE,Kyrie Eleison
-8239,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_BLESSING,Blessing
-8240,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_INCAGI,Increase Agility
-
-// Elemental Spirits Skills
-8401,0,6,4,3,0,0,1,1,no,0,0,0,weapon,2, EL_CIRCLE_OF_FIRE,Circle of Fire
-8402,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_FIRE_CLOAK,Fire Cloak
-8403,0,6,4,3,0,0,1,1,no,0,0,3,magic,2, EL_FIRE_MANTLE,Fire Mantle
-8404,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WATER_SCREEN,Water Screen
-8405,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WATER_DROP,Water Drop
-8406,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WATER_BARRIER,Water Barrier
-8407,0,6,4,0,0x1,0,1,1,no,0,0,0,none,5, EL_WIND_STEP,Wind Step
-8408,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WIND_CURTAIN,Wind Curtain
-8409,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_ZEPHYR,Zephyr
-8410,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_SOLID_SKIN,Solid Skin
-8411,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_STONE_SHIELD,Stone Shield
-8412,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_POWER_OF_GAIA,Power of Gaia
-8413,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_PYROTECHNIC,Pyrotechnic
-8414,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_HEATER,Heater
-8415,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_TROPIC,Tropic
-8416,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_AQUAPLAY,Aqua Play
-8417,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_COOLER,Cooler
-8418,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_CHILLY_AIR,Cool Air
-8419,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_GUST,Gust
-8420,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_BLAST,Blast
-8421,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WILD_STORM,Wild Storm
-8422,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_PETROLOGY,Petrology
-8423,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_CURSED_SOIL,Cursed Soil
-8424,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_UPHEAVAL,Upheaval
-8425,6,6,1,3,0,0,1,1,no,0,0,0,magic,0, EL_FIRE_ARROW,Fire Arrow
-8426,6,6,1,3,0,1,1,1,no,0,0,0,magic,0, EL_FIRE_BOMB,Fire Bomb
-8427,6,6,1,3,0,1,1,1,no,0,0,0,weapon,0, EL_FIRE_BOMB_ATK,Fire Bomb Attack
-8428,6,6,1,3,0,1,1,1,no,0,0,0,magic,0, EL_FIRE_WAVE,Fire Wave
-8429,6,6,1,3,0,1,1,1,no,0,0,0,weapon,0, EL_FIRE_WAVE_ATK,Fire Wave Attack
-8430,9,6,1,1,0,0,1,1,no,0,0,0,magic,0, EL_ICE_NEEDLE,Ice Needle
-8431,9,6,1,1,0,1,1,1,no,0,0,0,magic,0, EL_WATER_SCREW,Water Screw
-8432,9,6,1,1,0,1,1,1,no,0,0,0,weapon,0, EL_WATER_SCREW_ATK,Water Screw Attack
-8433,9,6,1,1,0,1,1,1,no,0,0,0,weapon,0, EL_TIDAL_WEAPON,Tidal Weapon
-8434,11,6,1,4,0,0,1,1,no,0,0,0,weapon,0, EL_WIND_SLASH,Wind Slasher
-8435,11,6,1,4,0,1,1,1,no,0,0,0,weapon,0, EL_HURRICANE,Hurricane Rage
-8436,7,6,1,4,0,0,1,1,no,0,0,0,magic,0, EL_HURRICANE_ATK,Hurricane Rage Attack
-8437,11,8,1,4,0,1,1,-3,no,0,0,0,weapon,0, EL_TYPOON_MIS,Typhoon Missile
-8438,11,8,1,4,0,1,1,-3,no,0,0,0,magic,0, EL_TYPOON_MIS_ATK,Typhoon Missile Attack
-8439,5,6,1,2,0,0,1,1,no,0,0,0,weapon,0, EL_STONE_HAMMER,Stone Hammer
-8440,3,6,1,2,0,1,1,1,no,0,0,0,weapon,0, EL_ROCK_CRUSHER,Rock Launcher
-8441,5,6,1,2,0,1,1,1,no,0,0,0,magic,0, EL_ROCK_CRUSHER_ATK,Rock Launcher Attack
-8442,9,6,1,2,0,1,1,-5,no,0,0,0,weapon,0, EL_STONE_RAIN,Stone Rain
-
-10000,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_APPROVAL,Official Guild Approval
-10001,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_KAFRACONTRACT,Kafra Contract
-10002,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_GUARDRESEARCH,Guardian Research
-10003,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_GUARDUP,Strengthen Guardians
-10004,0,0,0,0,0,0,10,0,no,0,0x10,0,none,0, GD_EXTENSION,Guild Extension
-10005,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_GLORYGUILD,Guild's Glory
-10006,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_LEADERSHIP,Great Leadership
-10007,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_GLORYWOUNDS,Glorious Wounds
-10008,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_SOULCOLD,Cold Heart
-10009,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_HAWKEYES,Sharp Gaze
-10010,0,0,4,0,0x3,15,1,0,yes,0,0x10,0,none,0, GD_BATTLEORDER,Battle Orders
-10011,0,0,4,0,0x3,15,3,0,yes,0,0x10,0,none,0, GD_REGENERATION,Regeneration
-10012,0,0,4,0,0x3,15,1,0,yes,0,0x10,0,none,0, GD_RESTORE,Restoration
-10013,0,0,4,0,0x3,0,1,0,yes,0,0x10,0,none,0, GD_EMERGENCYCALL,Urgent Call
-10014,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_DEVELOPMENT,Permanent Development
-//10015,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_ITEMEMERGENCYCALL,Unknown Skill
+//id,range,hit,inf,element,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count,name,description
+// 01 ID
+// 02 range (combo skills do not check for range when used,
+// if range is < 5, the skill is considered melee-range)
+// 03 hit (8- repeated hitting, 6- single-hit)
+// 04 inf (0- passive, 1- enemy, 2- place, 4- self, 16- friend, 32- trap)
+// 05 element (0 - neutral, 1 - water, 2 - earth, 3 - fire, 4 - wind, 5 - poison,
+// 6 - holy, 7 - dark, 8 - ghost, 9 - undead, -1 - use weapon element
+// -2 - use endowed element, -3 - use random element.)
+// 06 nk (skill damage properties):
+// 0x01 - No damage skill
+// 0x02 - Has splash area
+// 0x04 - Damage should be split among targets
+// 0x08 - Skill ignores caster's % damage cards (misc type always ignores)
+// 0x10 - Skill ignores elemental adjustments
+// 0x20 - Skill ignores target's defense (misc type always ignores)
+// 0x40 - Skill ignores target's flee (magic type always ignores)
+// 0x80 - Skill ignores target's def cards
+// 07 splash/effect range (-1 for screen-wide)
+// 08 MaxLv
+// 09 Number of hits (when positive, damage is increased by hits,
+// negative values just show number of hits without increasing total damage)
+// 10 Cast interrupted when hit?
+// 11 defense-reduction rate during cast.
+// 12 inf2 (skill information 2):
+// 0x0001- quest skill
+// 0x0002- npc skill
+// 0x0004- wedding skill
+// 0x0008- spirit skill
+// 0x0010- guild skill
+// 0x0020- song/dance
+// 0x0040- ensemble skill
+// 0x0080- trap
+// 0x0100- skill that damages/targets yourself
+// 0x0200- cannot be casted on self (if inf = 4, auto-select target skill)
+// 0x0400- usable only on party-members (and enemies if skill is offensive)
+// 0x0800- usable only on guild-mates (and enemies if skill is offensive)
+// 0x1000- disable usage on enemies (for non-offensive skills).
+// 0x2000- skill ignores land protector (e.g. arrow shower)
+// 0x4000- chorus skill
+// 13 maxcount: max amount of skill instances to place on the ground when
+// player_land_skill_limit/monster_land_skill_limit is enabled. For skills
+// that attack using a path, this is the path length to be used.
+// 14 attack type (none, weapon, magic, misc)
+// 15 Blowcount (amount of tiles skill knockbacks)
+// 16 Name
+// 17 Description
+1,0,0,0,0,0,0,9,0,no,0,0,0,none,0, NV_BASIC,Basic Skill
+2,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, SM_SWORD,Sword Mastery
+3,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, SM_TWOHAND,Two-Handed Sword Mastery
+4,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SM_RECOVERY,Increase HP Recovery
+5,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, SM_BASH,Bash
+6,9,6,1,0,1,0,10,1,no,0,0,0,none,0, SM_PROVOKE,Provoke
+7,0,6,4,3,0x2,2,10,1,no,0,0,0,weapon,2, SM_MAGNUM,Magnum Break
+8,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, SM_ENDURE,Endure
+9,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MG_SRECOVERY,Increase SP Recovery
+10,0,6,4,3,0x3,3,1,1,yes,0,0,0,magic,0, MG_SIGHT,Sight
+11,9,6,1,8,0x6,1,10,1,yes,0,0,0,magic,0, MG_NAPALMBEAT,Napalm Beat
+12,9,8,2,8,0x1,0,10,1,yes,0,0,0,magic,0, MG_SAFETYWALL,Safety Wall
+13,9,8,1,8,0,0,10,1:1:2:2:3:3:4:4:5:5,yes,0,0,0,magic,0, MG_SOULSTRIKE,Soul Strike
+14,9,8,1,1,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_COLDBOLT,Cold Bolt
+15,9,6,1,1,0,0,10,1,yes,0,0,0,magic,0, MG_FROSTDIVER,Frost Diver
+16,2,6,1,2,0x1,0,10,1,yes,0,0,0,magic,0, MG_STONECURSE,Stone Curse
+17,9,6,1,3,0x2,2,10,1,yes,0,0,0,magic,0, MG_FIREBALL,Fire Ball
+18,9,6,2,3,0,0,10,1,yes,0,0,3,magic,2, MG_FIREWALL,Fire Wall
+19,9,8,1,3,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_FIREBOLT,Fire Bolt
+20,9,8,1,4,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_LIGHTNINGBOLT,Lightning Bolt
+21,9,8,2,4,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, MG_THUNDERSTORM,Thunderstorm
+22,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AL_DP,Divine Protection
+23,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AL_DEMONBANE,Demon Bane
+24,0,6,4,6,0x3,2,1,1,yes,0,0,0,magic,0, AL_RUWACH,Ruwach
+25,9,6,2,0,0x1,0,1,1,yes,0,0,0,magic,0, AL_PNEUMA,Pneuma
+26,0,6,4,0,0x1,0,2,1,yes,0,0,0,magic,0, AL_TELEPORT,Teleport
+27,9,6,2,0,0x1,0,4,1,yes,0,0,3,magic,0, AL_WARP,Warp Portal
+28,9,6,16,6,0x21,0,10,1,yes,0,0,0,magic,0, AL_HEAL,Heal
+29,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, AL_INCAGI,Increase AGI
+30,9,6,1,0,0x1,0,10,1,yes,0,0,0,magic,0, AL_DECAGI,Decrease AGI
+31,0,6,4,0,0x1,0,1,1,yes,0,0,0,magic,0, AL_HOLYWATER,Aqua Benedicta
+32,0,6,4,0,0x3,15,10,1,yes,0,0,0,magic,0, AL_CRUCIS,Signum Crucis
+33,0,6,4,0,0x3,-1,10,1,yes,0,0,0,magic,0, AL_ANGELUS,Angelus
+34,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, AL_BLESSING,Blessing
+35,9,6,16,0,0x1,0,1,1,yes,0,0,0,magic,0, AL_CURE,Cure
+36,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_INCCARRY,Enlarge Weight Limit
+37,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_DISCOUNT,Discount
+38,0,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_OVERCHARGE,Overcharge
+39,1,0,0,0,0,0,10,0,no,0,0,0,none,0, MC_PUSHCART,Pushcart
+40,1,6,4,0,0x1,0,1,1,no,0,0,0,none,0, MC_IDENTIFY,Item Appraisal
+41,1,6,4,0,0x1,0,10,1,no,0,0,0,none,0, MC_VENDING,Vending
+42,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, MC_MAMMONITE,Mammonite
+43,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AC_OWL,Owl's Eye
+44,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AC_VULTURE,Vulture's Eye
+45,0,6,4,0,0x3,3,10,1,no,0,0,0,weapon,0, AC_CONCENTRATION,Improve Concentration
+46,-9,8,1,-1,0,0,10,2,no,0,0,0,weapon,0, AC_DOUBLE,Double Strafe
+47,-9,6,2,-1,0x2,2,10,1,no,0,0x2000,0,weapon,2, AC_SHOWER,Arrow Shower
+48,-1,8,0,-1,0,0,10,2,no,0,0,0,weapon,0, TF_DOUBLE,Double Attack
+49,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, TF_MISS,Improve Dodge
+50,1,6,1,0,1,0,10,1,no,0,0,0,weapon,0, TF_STEAL,Steal
+51,1,6,4,0,1,0,10,1,no,0,0,0,none,0, TF_HIDING,Hiding
+52,-2,6,1,5,0,0,10,1,no,0,0,0,weapon,0, TF_POISON,Envenom
+53,9,6,16,5,0x1,0,1,1,no,0,0,0,weapon,0, TF_DETOXIFY,Detoxify
+54,9,6,16,6,0x1,0,4,1,yes,0,0,0,magic,0, ALL_RESURRECTION,Resurrection
+55,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, KN_SPEARMASTERY,Spear Mastery
+56,-2,8,1,-1,0,0,10,3,no,0,0,0,weapon,0, KN_PIERCE,Pierce
+57,-2,6,1,-1,0x1,0,10,1,no,33,0,0,weapon,3, KN_BRANDISHSPEAR,Brandish Spear
+58,-4,6,1,-1,0x2,0,10,1,no,0,0,0,weapon,6, KN_SPEARSTAB,Spear Stab
+59,3:5:7:9:11,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, KN_SPEARBOOMERANG,Spear Boomerang
+60,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, KN_TWOHANDQUICKEN,Twohand Quicken
+61,0,6,4,-1,0x20,0,5,1,no,0,0,0,weapon,0, KN_AUTOCOUNTER,Counter Attack
+62,-2,6,1,-1,0x2,1,10,1,no,33,0,0,weapon,1, KN_BOWLINGBASH,Bowling Bash
+63,0,0,0,0,0,0,1,0,no,0,0,0,weapon,0, KN_RIDING,Peco Peco Riding
+64,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, KN_CAVALIERMASTERY,Cavalier Mastery
+65,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, PR_MACEMASTERY,Mace Mastery
+66,9,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, PR_IMPOSITIO,Impositio Manus
+67,9,6,16,0,0x1,0,3,1,yes,0,0x200,0,magic,0, PR_SUFFRAGIUM,Suffragium
+68,9,6,16,6,0x31,0,5,1,yes,0,0,0,magic,0, PR_ASPERSIO,Aspersio
+69,9,6,2,0,0x23,1,5,1,yes,0,0x40,0,magic,0, PR_BENEDICTIO,B.S. Sacramenti
+70,9,6,2,6,0x21,0,10,1,yes,0,0,0,magic,1, PR_SANCTUARY,Sanctuary
+71,9,6,16,0,0x1,0,4,1,yes,0,0,0,magic,0, PR_SLOWPOISON,Slow Poison
+72,9,6,16,0,0x1,0,1,1,yes,0,0,0,magic,0, PR_STRECOVERY,Status Recovery
+73,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, PR_KYRIE,Kyrie Eleison
+74,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0, PR_MAGNIFICAT,Magnificat
+75,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0, PR_GLORIA,Gloria
+76,5,6,1,0,0x1,0,10,0,yes,0,0,0,magic,0, PR_LEXDIVINA,Lex Divina
+77,5,6,1,6,0x28,0,10,1,yes,0,0,0,magic,0, PR_TURNUNDEAD,Turn Undead
+78,9,6,1,0,0x1,0,1,0,yes,0,0,0,magic,0, PR_LEXAETERNA,Lex Aeterna
+79,9,8,2,6,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, PR_MAGNUS,Magnus Exorcismus
+80,9,8,2,3,0x20,1:1:1:1:1:2:2:2:2:2:2,10,3:4:5:6:7:8:9:10:11:12:12,yes,0,0x80,5,magic,0, WZ_FIREPILLAR,Fire Pillar
+81,0,6,4,3,0,3,10,1,yes,0,0,0,magic,5, WZ_SIGHTRASHER,Sightrasher
+83,9,8,2,3,0,3:3:3:3:3:3:3:3:3:3:14,10,1:1:2:2:3:3:4:4:5:5:15,yes,0,0,0,magic,0, WZ_METEOR,Meteor Storm
+84,9,8,1,4,0,0,10,3:4:5:6:7:8:9:10:11:12,yes,0,0,0,magic,2:3:3:4:4:5:5:6:6:7, WZ_JUPITEL,Jupitel Thunder
+85,9,8,2,4,0,0,10,-10,yes,0,0,0,magic,0, WZ_VERMILION,Lord of Vermilion
+86,9,8,1,1,0,0,5,1,yes,0,0,0,magic,0, WZ_WATERBALL,Water Ball
+87,9,6,2,1,0x1,0,10,1,yes,0,0,0,magic,0, WZ_ICEWALL,Ice Wall
+88,0,6,4,1,0x2,2,10,1,yes,0,0,0,magic,0, WZ_FROSTNOVA,Frost Nova
+89,9,6,2,1,0,0,10,1,yes,0,0,0,magic,2, WZ_STORMGUST,Storm Gust
+90,9,8,1,2,0,0,5,1:2:3:4:5,yes,0,0,0,magic,0, WZ_EARTHSPIKE,Earth Spike
+91,9,8,2,2,0,0,5,1:2:3:4:5,yes,0,0,0,magic,0, WZ_HEAVENDRIVE,Heaven's Drive
+92,9,6,2,2,0x1,0,5,1,yes,0,0,3,magic,0, WZ_QUAGMIRE,Quagmire
+93,9,6,1,0,0x1,0,1,1,yes,0,0,0,magic,0, WZ_ESTIMATION,Sense
+94,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_IRON,Iron Tempering
+95,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_STEEL,Steel Tempering
+96,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_ENCHANTEDSTONE,Enchanted Stone Craft
+97,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_ORIDEOCON,Oridecon Research
+98,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_DAGGER,Smith Dagger
+99,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_SWORD,Smith Sword
+100,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_TWOHANDSWORD,Smith Two-handed Sword
+101,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_AXE,Smith Axe
+102,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_MACE,Smith Mace
+103,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_KNUCKLE,Smith Knucklebrace
+104,0,0,0,0,0,0,3,0,no,0,0,0,weapon,0, BS_SPEAR,Smith Spear
+105,0,0,0,0,0,0,1,0,no,0,0,0,weapon,0, BS_HILTBINDING,Hilt Binding
+106,0,0,0,0,0,0,1,0,no,0,0,0,weapon,0, BS_FINDINGORE,Ore Discovery
+107,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, BS_WEAPONRESEARCH,Weaponry Research
+108,2,6,16,0,0x1,0,1,1,yes,0,0,0,weapon,0, BS_REPAIRWEAPON,Weapon Repair
+109,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, BS_SKINTEMPER,Skin Tempering
+110,1,6,2,0,0x3,2:2:2:2:2:14,5,1,no,0,0,0,weapon,0, BS_HAMMERFALL,Hammer Fall
+111,0,6,4,0,0x3,-1,5,1,no,0,0,0,weapon,0, BS_ADRENALINE,Adrenaline Rush
+112,0,6,4,0,0x3,-1,5,1,no,0,0,0,weapon,0, BS_WEAPONPERFECT,Weapon Perfection
+113,0,6,4,0,0x3,-1,5,1,no,0,0,0,weapon,0, BS_OVERTHRUST,Power-Thrust
+114,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, BS_MAXIMIZE,Maximize Power
+115,3,6,2,0,0x1,0,5,1,no,0,0x80,0,misc,6:7:8:9:10, HT_SKIDTRAP,Skid Trap
+116,3,6,2,2,0x42,1,5,1,no,0,0x80,0,misc,0, HT_LANDMINE,Land Mine
+117,3,6,2,0,0x1,0,5,1,no,0,0x80,0,misc,0, HT_ANKLESNARE,Ankle Snare
+118,3,6,2,0,0x2,1,5,1,no,0,0x80,0,misc,0, HT_SHOCKWAVE,Shockwave Trap
+119,3,6,2,0,0x3,2,5,1,no,0,0x80,0,misc,0, HT_SANDMAN,Sandman
+120,3,6,2,0,0x3,1,5,1,no,0,0x80,0,misc,0, HT_FLASHER,Flasher
+121,3,6,2,1,0x42,1,5,1,no,0,0x80,0,weapon,0, HT_FREEZINGTRAP,Freezing Trap
+122,3,6,2,4,0x42,1,5,1,no,0,0x80,0,misc,0, HT_BLASTMINE,Blast Mine
+123,3,6,2,3,0x42,2,5,1,no,0,0x80,0,misc,0, HT_CLAYMORETRAP,Claymore Trap
+124,2,6,32,0,0x1,0,1,1,no,0,0,0,misc,0, HT_REMOVETRAP,Remove Trap
+125,3,6,2,0,0x1,0,1,1,no,0,0x80,0,misc,0, HT_TALKIEBOX,Talkie Box
+126,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, HT_BEASTBANE,Beast Bane
+127,0,0,0,0,0,0,1,0,no,0,0,0,misc,0, HT_FALCON,Falconry Mastery
+128,0,0,0,0,0,0,10,0,no,0,0,0,misc,0, HT_STEELCROW,Steel Crow
+129,5,8,1,0,0x42,1,5,1:2:3:4:5,yes,0,0,0,misc,0, HT_BLITZBEAT,Blitz Beat
+130,3:5:7:9,6,2,0,0x3,3,4,1,no,0,0,0,misc,0, HT_DETECTING,Detect
+131,4:5:6:7:8,6,32,0,0x1,0,5,1,no,0,0,0,misc,0, HT_SPRINGTRAP,Spring Trap
+132,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, AS_RIGHT,Righthand Mastery
+133,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, AS_LEFT,Lefthand Mastery
+134,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AS_KATAR,Katar Mastery
+135,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, AS_CLOAKING,Cloaking
+136,-1,8,1,-1,0,0,10,8,no,0,0,0,weapon,0, AS_SONICBLOW,Sonic Blow
+137,3:4:5:6:7,6,1,-1,0x2,1,5,1,no,0,0,0,weapon,0,AS_GRIMTOOTH,Grimtooth
+138,1,6,16,5,0x1,0,10,1,no,0,0x400,0,weapon,0, AS_ENCHANTPOISON,Enchant Poison
+139,0,6,4,0,0,0,10,1,no,0,0,0,weapon,0, AS_POISONREACT,Poison React
+140,2,6,2,5,0x1,0,10,1,no,0,0,0,weapon,0, AS_VENOMDUST,Venom Dust
+141,1,6,1,-1,0x51,2,10,1,yes,0,0,0,weapon,0, AS_SPLASHER,Venom Splasher
+142,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, NV_FIRSTAID,First Aid
+143,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, NV_TRICKDEAD,Play Dead
+144,0,0,0,0,0,0,1,0,no,0,0x1,0,none,0, SM_MOVINGRECOVERY,Moving HP-Recovery
+145,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0, SM_FATALBLOW,Fatal Blow
+146,0,6,4,0,0x1,0,1,1,no,0,0x1,0,weapon,0, SM_AUTOBERSERK,Auto Berserk
+147,0,0,4,0,0x1,0,1,0,no,0,0x1,0,weapon,0, AC_MAKINGARROW,Arrow Crafting
+148,-9,6,1,-1,0x2,0,1,1,no,0,0x1,0,weapon,6, AC_CHARGEARROW,Arrow Repel
+149,1,6,1,2,0,0,1,1,no,0,0x1,0,weapon,0, TF_SPRINKLESAND,Sand Attack
+150,0,6,4,0,0x1,0,1,1,no,0,0x1,0,weapon,5, TF_BACKSLIDING,Back Slide
+151,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, TF_PICKSTONE,Find Stone
+152,7,6,1,0,0x40,0,1,1,no,0,0x1,0,misc,0, TF_THROWSTONE,Stone Fling
+153,1,6,1,-1,0x2,1,1,1,no,0,0x1,0,weapon,2, MC_CARTREVOLUTION,Cart Revolution
+154,0,6,4,0,0x1,0,1,1,no,0,0x1,0,none,0, MC_CHANGECART,Change Cart
+155,0,6,4,0,0x1,0,1,1,no,0,0x1,0,weapon,0, MC_LOUD,Crazy Uproar
+156,9,6,1,6,0,0,1,1,yes,0,0x1,0,magic,0, AL_HOLYLIGHT,Holy Light
+157,0,6,4,0,0x1,0,1,1,yes,0,0x1,0,magic,0, MG_ENERGYCOAT,Energy Coat
+158,3,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_PIERCINGATT,Piercing Attack
+159,-1,6,1,-1,0x40,0,5,1,no,0,0x2,0,weapon,0, NPC_MENTALBREAKER,Spirit Destruction
+160,9,6,1,0,0,0,10,1,no,0,0x2,0,weapon,0, NPC_RANGEATTACK,Stand off attack
+161,0,0,4,0,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_ATTRICHANGE,Attribute Change
+162,0,0,4,1,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEWATER,Water Attribute Change
+163,0,0,4,2,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEGROUND,Earth Attribute Change
+164,0,0,4,3,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEFIRE,Fire Attribute Change
+165,0,0,4,4,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEWIND,Wind Attribute Change
+166,0,0,4,5,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEPOISON,Poison Attribute Change
+167,0,0,4,6,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEHOLY,Holy Attribute Change
+168,0,0,4,7,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGEDARKNESS,Shadow Attribute Change
+169,0,0,4,8,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_CHANGETELEKINESIS,Ghost Attribute Change
+170,-9,6,1,-1,0x20,0,10,1,no,0,0x2,0,weapon,0, NPC_CRITICALSLASH,Defense disregard attack
+171,-9,8,1,-1,0,0,10,-2:-3:-4:-5:-6:-7:-8:-9:-10:-11,no,0,0x2,0,weapon,0, NPC_COMBOATTACK,Multi-stage Attack
+172,-9,6,1,-1,0x40,0,10,1,no,0,0x2,0,weapon,0, NPC_GUIDEDATTACK,Guided Attack
+173,5,6,4,3,0xE2,5,10,1,no,0,0x2,0,misc,3, NPC_SELFDESTRUCTION,Suicide bombing
+174,-9,6,1,-1,0x2,3,1,1,no,0,0x2,0,weapon,0, NPC_SPLASHATTACK,Splash attack
+175,0,0,4,0,0x41,0,10,1,no,0,0x2,0,misc,0, NPC_SUICIDE,Suicide
+176,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_POISON,Poison Attack
+177,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_BLINDATTACK,Blind Attack
+178,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_SILENCEATTACK,Silence Attack
+179,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_STUNATTACK,Stun Attack
+180,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_PETRIFYATTACK,Petrify Attack
+181,-9,6,1,7,0,0,5,1,no,0,0x2,0,weapon,0, NPC_CURSEATTACK,Curse Attack
+182,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_SLEEPATTACK,Sleep attack
+183,-9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_RANDOMATTACK,Random Attack
+184,-9,6,1,1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_WATERATTACK,Water Attribute Attack
+185,-9,6,1,2,0,0,10,1,no,0,0x2,0,weapon,0, NPC_GROUNDATTACK,Earth Attribute Attack
+186,-9,6,1,3,0,0,10,1,no,0,0x2,0,weapon,0, NPC_FIREATTACK,Fire Attribute Attack
+187,-9,6,1,4,0,0,10,1,no,0,0x2,0,weapon,0, NPC_WINDATTACK,Wind Attribute Attack
+188,-9,6,1,5,0,0,10,1,no,0,0x2,0,weapon,0, NPC_POISONATTACK,Poison Attribute Attack
+189,-9,6,1,6,0,0,10,1,no,0,0x2,0,weapon,0, NPC_HOLYATTACK,Holy Attribute Attack
+190,-9,6,1,7,0,0,10,1,no,0,0x2,0,weapon,0, NPC_DARKNESSATTACK,Shadow Attribute Attack
+191,-9,6,1,8,0,0,10,1,no,0,0x2,0,weapon,0, NPC_TELEKINESISATTACK,Ghost Attribute Attack
+192,-9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_MAGICALATTACK,Demon Shock Attack
+193,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_METAMORPHOSIS,Metamorphosis
+194,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_PROVOCATION,Provocation
+195,0,6,4,0,0x50,0,10,1,no,0,0x2,0,misc,0, NPC_SMOKING,Smoking
+196,0,0,4,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_SUMMONSLAVE,Follower Summons
+197,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_EMOTION,Emotion
+198,0,0,4,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_TRANSFORMATION,Transformation
+199,9,6,1,7,0x40,0,1,1,no,0,0x2,0,weapon,0, NPC_BLOODDRAIN,Sucking Blood
+200,9,6,1,7,0,0,1,1,no,0,0x2,0,magic,0, NPC_ENERGYDRAIN,Energy Drain
+201,0,0,4,0,0x1,0,1,1,no,0,0x2,0,weapon,0, NPC_KEEPING,Keeping
+202,9,6,1,7,0,0,5,1,no,0,0x2,0,misc,0, NPC_DARKBREATH,Dark Breath
+203,9,6,1,7,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_DARKBLESSING,Dark Blessing
+204,0,0,4,0,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_BARRIER,Barrier
+205,0,0,4,0,0x1,0,1,1,no,0,0x2,0,weapon,0, NPC_DEFENDER,Defender
+206,1,6,1,-1,0x1,0,5,1,no,0,0x2,0,weapon,0, NPC_LICK,Lick
+207,9,0,1,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_HALLUCINATION,Hallucination
+208,0,0,4,0,0x1,0,1,1,no,0,0x2,0,magic,0, NPC_REBIRTH,Rebirth
+209,0,0,4,0,0x1,0,10,1,no,0,0x2,0,magic,0, NPC_SUMMONMONSTER,Monster Summons
+210,0,0,0,-1,0,0,10,0,no,0,0,0,weapon,0, RG_SNATCHER,Gank
+211,1,6,1,0,0x1,0,10,1,no,0,0,0,weapon,0, RG_STEALCOIN,Mug
+212,-1,6,1,-1,0x40,0,10,1,no,0,0,0,weapon,0, RG_BACKSTAP,Back Stab
+213,0,0,0,0,0,0,5,0,no,0,0,0,none,0, RG_TUNNELDRIVE,Stalk
+214,0,6,4,-1,0x2,1,5,1,no,0,0,0,weapon,0, RG_RAID,Sightless Mind
+215,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPWEAPON,Divest Weapon
+216,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPSHIELD,Divest Shield
+217,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPARMOR,Divest Armor
+218,1,6,1,0,0x1,0,5,1,no,0,0,0,weapon,0, RG_STRIPHELM,Divest Helm
+219,1,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, RG_INTIMIDATE,Snatch
+220,1,6,2,0,0x1,0,1,1,no,0,0,0,none,0, RG_GRAFFITI,Scribble
+221,0,6,2,0,0x1,0,5,1,no,0,0,0,none,0, RG_FLAGGRAFFITI,Piece
+222,1,6,2,0,0x3,5,1,1,no,0,0,0,none,0, RG_CLEANER,Remover
+223,0,0,0,0,0,1,1,0,no,0,0,0,none,0, RG_GANGSTER,Slyness
+224,0,0,0,0,0,0,5,0,no,0,0,0,none,0, RG_COMPULSION,Haggle
+225,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RG_PLAGIARISM,Intimidate
+226,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, AM_AXEMASTERY,Axe Mastery
+227,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_LEARNINGPOTION,Potion Research
+228,0,6,4,0,0x1,0,10,0,no,0,0,0,none,0, AM_PHARMACY,Prepare Potion
+229,9,6,2,3,0x9,0,5,1,yes,0,0,0,weapon,0, AM_DEMONSTRATION,Bomb
+230,9,6,1,0,0x48,0,5,1,yes,0,0,0,weapon,0, AM_ACIDTERROR,Acid Terror
+231,9,6,16,0,0x1,0,5,1,yes,0,0xC00,0,none,0, AM_POTIONPITCHER,Aid Potion
+232,4,6,2,0,0x1,0,5,1,no,0,0,5,none,0, AM_CANNIBALIZE,Summon Flora
+233,1,6,2,0,0x1,0,5,1,no,0,0,3,none,0, AM_SPHEREMINE,Summon Marine Sphere
+234,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_WEAPON,Alchemical Weapon
+235,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_SHIELD,Synthesized Shield
+236,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_ARMOR,Synthetic Armor
+237,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, AM_CP_HELM,Biochemical Helm
+238,0,0,0,0,0,0,1,0,no,0,0x1,0,none,0, AM_BIOETHICS,Bioethics
+//239,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_BIOTECHNOLOGY,Biotechnology
+//240,0,0,0,0,0,0,5,0,no,0,0,0,none,0, AM_CREATECREATURE,Life Creation
+//241,0,0,0,0,0,0,5,0,no,0,0,0,none,0, AM_CULTIVATION,Cultivation
+//242,0,0,0,0,0,0,5,0,no,0,0,0,none,0, AM_FLAMECONTROL,Flame Control
+243,0,0,4,0,0x1,1,1,0,no,0,0,0,none,0, AM_CALLHOMUN,Call Homunculus
+244,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, AM_REST,Vaporize
+//245,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_DRILLMASTER,Drillmaster
+//246,9,0,0,0,0,0,10,0,no,0,0,0,none,0, AM_HEALHOMUN,Heal Homunculus
+247,9,6,4,0,0x1,1,5,0,no,0,0,0,none,0, AM_RESURRECTHOMUN,Homunculus Resurrection
+248,0,0,0,0,0,0,10,0,no,0,0,0,none,0, CR_TRUST,Faith
+249,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, CR_AUTOGUARD,Guard
+250,3,6,1,0,0,0,5,1,no,0,0,0,weapon,5:6:7:8:9, CR_SHIELDCHARGE,Smite
+251,3:5:7:9:11,6,1,0,0,0,5,1,no,0,0,0,weapon,0, CR_SHIELDBOOMERANG,Shield Boomerang
+252,0,6,4,0,0,0,10,1,no,0,0,0,weapon,0, CR_REFLECTSHIELD,Shield Reflect
+253,-2,8,1,6,0,0,10,-2,no,0,0,0,weapon,0, CR_HOLYCROSS,Holy Cross
+254,5,6,4,6,0x48,0,10,1,no,33,0x100,0,magic,0, CR_GRANDCROSS,Grand Cross
+255,7:8:9:10:11,6,16,0,0x1,0,5,1,yes,0,0x600,0,none,0, CR_DEVOTION,Sacrifice
+256,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,none,0, CR_PROVIDENCE,Resistant Souls
+257,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, CR_DEFENDER,Defending Aura
+258,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, CR_SPEARQUICKEN,Spear Quicken
+259,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, MO_IRONHAND,Iron Fists
+260,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, MO_SPIRITSRECOVERY,Spiritual Cadence
+261,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MO_CALLSPIRITS,Summon Spirit Sphere
+262,9,6,16,0,0x1,0,1,1,yes,0,0,0,weapon,0, MO_ABSORBSPIRITS,Absorb Spirit Sphere
+263,-1,8,0,-1,0,0,10,-3,no,0,0,0,weapon,0, MO_TRIPLEATTACK,Raging Trifecta Blow
+264,18,6,2,0,0x1,0,1,1,no,0,0,0,none,0, MO_BODYRELOCATION,Snap
+265,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, MO_DODGE,Dodge
+266,2,6,1,0,0x40,0,5,1,no,0,0,0,weapon,0, MO_INVESTIGATE,Occult Impaction
+267,9,8,1,-1,0,0,5,1:2:3:4:5,no,0,0,0,weapon,0, MO_FINGEROFFENSIVE,Throw Spirit Sphere
+268,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, MO_STEELBODY,Mental Strength
+269,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, MO_BLADESTOP,Root
+270,0,6,4,0,0x1,0,5,0,no,0,0,0,weapon,0, MO_EXPLOSIONSPIRITS,Fury
+271,-2,6,1,0,0x60,0,5,1,yes,0,0,0,weapon,0, MO_EXTREMITYFIST,Asura Strike
+272,-2,8,4,-1,0,0,5,-4,no,0,0x200,0,weapon,0, MO_CHAINCOMBO,Raging Quadruple Blow
+273,-2,6,4,-1,0x2,2,5,1,no,0,0x200,0,weapon,0, MO_COMBOFINISH,Raging Thrust
+274,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, SA_ADVANCEDBOOK,Study
+275,0,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, SA_CASTCANCEL,Cast Cancel
+276,0,6,4,0,0x1,0,5,1,yes,0,0,0,magic,0, SA_MAGICROD,Magic Rod
+277,9,6,1,0,0x1,0,5,1,yes,0,0,0,magic,0, SA_SPELLBREAKER,Spell Breaker
+278,0,0,0,0,0,0,10,0,no,0,0,0,magic,0, SA_FREECAST,Free Cast
+279,0,6,4,0,0x1,0,10,1,yes,0,0,0,magic,0, SA_AUTOSPELL,Hindsight
+280,9,6,16,3,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_FLAMELAUNCHER,Endow Blaze
+281,9,6,16,1,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_FROSTWEAPON,Endow Tsunami
+282,9,6,16,4,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_LIGHTNINGLOADER,Endow Tornado
+283,9,6,16,2,0x1,0,5,1,yes,0,0xC00,0,magic,0, SA_SEISMICWEAPON,Endow Quake
+284,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, SA_DRAGONOLOGY,Dragonology
+285,2,6,2,3,0x1,0,5,1,yes,0,0,0,magic,0, SA_VOLCANO,Volcano
+286,2,6,2,1,0x1,0,5,1,yes,0,0,0,magic,0, SA_DELUGE,Deluge
+287,2,6,2,4,0x1,0,5,1,yes,0,0,0,magic,0, SA_VIOLENTGALE,Whirlwind
+288,2,6,2,0,0x1,0,5,1,yes,0,0,0,magic,0, SA_LANDPROTECTOR,Magnetic Earth
+289,9,6,1,0,0x1,0:0:0:0:0:-1,5,1,yes,0,0xE00,0,magic,0, SA_DISPELL,Dispell
+290,0,6,4,0,0x1,0,10,1,yes,0,0,0,magic,0, SA_ABRACADABRA,Hocus-pocus
+291,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_MONOCELL,Monocell
+292,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_CLASSCHANGE,Class Change
+293,0,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_SUMMONMONSTER,Monster Chant
+294,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_REVERSEORCISH,Grampus Morph
+295,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_DEATH,Grim Reaper
+296,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_FORTUNE,Gold Digger
+297,9,6,1,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_TAMINGMONSTER,Beastly Hypnosis
+298,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_QUESTION,Questioning
+299,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_GRAVITY,Gravity
+300,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_LEVELUP,Leveling
+301,9,6,4,0,0,0,1,1,yes,0,0x2,0,magic,0, SA_INSTANTDEATH,Suicide
+302,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, SA_FULLRECOVERY,Rejuvenation
+303,9,6,4,0,0,0,1,1,yes,0,0x2,0,magic,0, SA_COMA,Coma
+304,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, BD_ADAPTATION,Amp
+305,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, BD_ENCORE,Encore
+306,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_LULLABY,Lullaby
+307,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_RICHMANKIM,Mental Sensing
+308,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_ETERNALCHAOS,Down Tempo
+309,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_DRUMBATTLEFIELD,Battle Theme
+310,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_RINGNIBELUNGEN,Harmonic Lick
+311,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_ROKISWEIL,Classical Pluck
+312,0,6,4,0,0x1,0,1,1,no,0,0x40,0,misc,0, BD_INTOABYSS,Power Chord
+313,0,6,4,0,0x1,0,5,1,no,0,0x40,0,misc,0, BD_SIEGFRIED,Acoustic Rhythm
+//314,0,0,0,0,0,0,1,1,no,0,0x40,0,misc,0, BD_RAGNAROK,Ragnarok
+315,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, BA_MUSICALLESSON,Music Lessons
+316,9,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, BA_MUSICALSTRIKE,Melody Strike
+317,0,8,4,0,0x41,0,5,1,no,0,0x20,0,misc,0, BA_DISSONANCE,Unchained Serenade
+318,0,6,4,0,0x3,-1,5,1,no,0,0,0,misc,0, BA_FROSTJOKER,Unbarring Octave
+319,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_WHISTLE,Perfect Tablature
+320,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_ASSASSINCROSS,Impressive Riff
+321,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_POEMBRAGI,Magic Strings
+322,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, BA_APPLEIDUN,Song of Lutie
+323,0,0,0,0,0,0,10,0,no,0,0,0,weapon,0, DC_DANCINGLESSON,Dance Lessons
+324,9,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, DC_THROWARROW,Slinging Arrow
+325,0,8,4,0,0x1,0,5,1,no,0,0x20,0,misc,0, DC_UGLYDANCE,Hip Shaker
+326,0,6,4,0,0x3,-1,5,1,no,0,0,0,misc,0, DC_SCREAM,Dazzler
+327,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_HUMMING,Focus Ballet
+328,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_DONTFORGETME,Slow Grace
+329,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_FORTUNEKISS,Lady Luck
+330,0,6,4,0,0x1,0,10,1,no,0,0x20,0,misc,0, DC_SERVICEFORYOU,Gypsy's Kiss
+331,0,6,4,0,0x1,0,10,0,no,0,0x2,0,none,0, NPC_RANDOMMOVE,Random Move
+332,0,6,4,0,0x1,0,10,0,no,0,0x2,0,none,0, NPC_SPEEDUP,Speed UP
+333,0,6,4,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_REVENGE,Revenge
+334,9,6,4,0,0x1,0,1,1,yes,0,0x4,0,none,0, WE_MALE,I Will Protect You
+335,9,6,4,0,0x1,0,1,1,yes,0,0x4,0,none,0, WE_FEMALE,I Look up to You
+336,9,6,4,0,0x1,3,1,1,yes,0,0x4,1,none,0, WE_CALLPARTNER,I miss You
+337,9,6,1,-1,0,0,1,1,no,0,0x2,0,weapon,0, ITM_TOMAHAWK,Throw Tomahawk
+338,-1,8,1,7,0,0,10,-2,no,0,0x2,0,weapon,0, NPC_DARKCROSS,Cross of Darkness
+339,5,6,4,7,0x48,0,10,1,no,33,0x102,0,magic,0, NPC_GRANDDARKNESS,Grand cross of Darkness
+340,9,8,1,7,0,0,10,1:1:2:2:3:3:4:4:5:5,yes,0,0x2,0,magic,0, NPC_DARKSTRIKE,Soul Strike of Darkness
+341,9,8,1,7,0,0,10,3:4:5:6:7:8:9:10:11:12,yes,0,0x2,0,magic,2:3:3:4:4:5:5:6:6:7, NPC_DARKTHUNDER,Darkness Jupitel
+342,9,6,1,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_STOP,Stop
+343,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_WEAPONBRAKER,Break weapon
+344,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_ARMORBRAKE,Break armor
+345,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_HELMBRAKE,Break helm
+346,9,6,1,-1,0,0,10,1,no,0,0x2,0,weapon,0, NPC_SHIELDBRAKE,Break shield
+347,-9,6,1,9,0,0,10,1,no,0,0x2,0,weapon,0, NPC_UNDEADATTACK,Undead Element Attack
+348,9,0,1,9,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_CHANGEUNDEAD,Undead Attribute Change
+349,0,6,4,0,0x1,0,10,0,no,0,0x2,0,weapon,0, NPC_POWERUP,Power Up
+350,0,6,4,0,0x1,0,10,0,no,0,0x2,0,none,0, NPC_AGIUP,Agility UP
+351,0,0,0,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_SIEGEMODE,Siege Mode
+352,2,0,4,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_CALLSLAVE,Recall Slaves
+353,0,0,0,0,0x1,0,1,0,no,0,0x2,0,none,0, NPC_INVISIBLE,Invisible
+354,2,6,4,0,0x1,0,20,0,no,0,0x2,0,misc,0, NPC_RUN,Run
+355,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, LK_AURABLADE,Aura Blade
+356,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, LK_PARRYING,Parrying
+357,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, LK_CONCENTRATION,Concentration
+358,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, LK_TENSIONRELAX,Relax
+359,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, LK_BERSERK,Frenzy
+//360,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, LK_FURY,Fury
+361,9,6,16,0,0x1,1,5,1,yes,0,0,0,magic,0, HP_ASSUMPTIO,Assumptio
+362,4,6,4,0,0x1,0,5,1,yes,0,0,0,magic,2, HP_BASILICA,Basilica
+363,0,0,0,0,0,0,10,0,no,0,0,0,magic,0, HP_MEDITATIO,Meditatio
+364,0,0,0,0,0,0,10,1,no,0,0,0,magic,0, HW_SOULDRAIN,Soul Drain
+365,9,8,1,-1,0,0,1,1,yes,0,0,0,weapon,0, HW_MAGICCRASHER,Stave Crasher
+366,0,6,4,0,0x1,0,10,1,no,0,0,0,magic,0, HW_MAGICPOWER,Mystical Amplification
+367,9,8,1,0,0xD0,0,5,1,no,0,0,0,misc,0, PA_PRESSURE,Gloria Domini
+368,0,6,4,0,0x69,0,5,1,yes,0,0,0,weapon,0, PA_SACRIFICE, Martyr's Reckoning
+369,0,6,4,0,0x41,0,10,1,yes,0,0,0,misc,0, PA_GOSPEL,Battle Chant
+370,-2,6,1,-1,0,0,5,1,yes,0,0,0,weapon,3, CH_PALMSTRIKE,Raging Palm Strike
+371,-2,8,4,-1,0,0,5,1,no,0,0x200,0,weapon,0, CH_TIGERFIST,Glacier Fist
+372,-2,8,4,-1,0,0,10,-1:-1:-2:-2:-3:-3:-4:-4:-5:-5,no,0,0x200,0,weapon,0, CH_CHAINCRUSH,Chain Crush Combo
+373,0,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, PF_HPCONVERSION,Indulge
+374,9,6,1,0,0x1,0,1,1,yes,0,0xE00,0,none,0, PF_SOULCHANGE,Soul Exhale
+375,9,6,1,0,0x98,0,5,1,yes,0,0,0,magic,0, PF_SOULBURN,Soul Siphon
+376,0,0,0,0,0x1,0,5,1,no,0,0,0,weapon,0, ASC_KATAR,Advanced Katar Mastery
+//377,0,0,4,0,0x1,0,10,1,no,0,0,0,misc,0, ASC_HALLUCINATION,Hallucination Walk
+378,0,6,4,5,0x1,0,5,1,no,0,0,0,weapon,0, ASC_EDP,Enchant Deadly Poison
+379,7,6,1,-1,0x8,0,10,1,yes,0,0,0,weapon,0, ASC_BREAKER,Soul Destroyer
+380,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, SN_SIGHT,Falcon Eyes
+381,5,8,1,0,0x40,0,5,1,yes,0,0,0,misc,0, SN_FALCONASSAULT,Falcon Assault
+382,9,8,1,-1,0,2,5,1,yes,0,0,13,weapon,0, SN_SHARPSHOOTING,Focused Arrow Strike
+383,0,6,4,0,0x3,-1,10,1,yes,0,0,0,weapon,0, SN_WINDWALK,Wind Walker
+384,0,0,4,0,0x1,0,10,1,yes,0,0,0,weapon,0, WS_MELTDOWN,Shattering Strike
+//385,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, WS_CREATECOIN,Create Coins
+//386,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, WS_CREATENUGGET,Create Nuggets
+387,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, WS_CARTBOOST,Cart Boost
+//388,9,6,2,0,0x1,0,5,1,no,0,0,0,none,0, WS_SYSTEMCREATE,Auto Attack System
+389,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, ST_CHASEWALK,Stealth
+390,0,0,4,0,0,0,5,1,yes,0,0,0,weapon,0, ST_REJECTSWORD,Counter Instinct
+//391,0,0,4,0,1,0,1,1,yes,0,0,0,magic,0, ST_STEALBACKPACK,Steal Backpack
+392,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, CR_ALCHEMY,Alchemy
+393,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, CR_SYNTHESISPOTION,Potion Synthesis
+394,9,8,1,-1,0,0,10,-9,yes,0,0,0,weapon,0, CG_ARROWVULCAN,Vulcan Arrow
+395,0,0,4,0,0x1,3,1,1,yes,0,0x40,0,misc,2, CG_MOONLIT,Sheltering Bliss
+396,1,6,16,0,0x1,0,1,1,yes,0,0x600,0,none,0, CG_MARIONETTE,Marionette Control
+397,5,8,1,-1,0x20,0,5,5,no,0,0,0,weapon,0, LK_SPIRALPIERCE,Spiral Pierce
+398,4,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, LK_HEADCRUSH,Traumatic Blow
+399,4,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, LK_JOINTBEAT,Vital Strike
+400,9,8,1,8,0x6,1,5,1:2:3:4:5,yes,0,0,0,magic,0, HW_NAPALMVULCAN,Napalm Vulcan
+401,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, CH_SOULCOLLECT,Zen
+402,9,6,1,0,0x1,0,5,1,no,0,0,0,none,0, PF_MINDBREAKER,Mind Breaker
+403,0,0,4,0,0x1,0,1,1,yes,0,0,0,magic,0, PF_MEMORIZE,Foresight
+404,9,6,2,2,0x1,0,5,1,yes,0,0x100,2,magic,0, PF_FOGWALL,Blinding Mist
+405,7,6,1,0,0x1,0,1,1,no,0,0,3,magic,0, PF_SPIDERWEB,Fiber Lock
+406,0,6,4,-1,0xA,2,10,1,no,33,0,0,weapon,0, ASC_METEORASSAULT,Meteor Assault
+407,0,6,4,0,0x1,0,1,0,no,0,0,0,none,0, ASC_CDP,Create Deadly Poison
+408,9,6,4,0,0x1,0,1,1,yes,0,0x4,0,none,0, WE_BABY,Baby
+409,9,6,4,0,0x1,3,1,1,yes,0,0x4,1,none,0, WE_CALLPARENT,Call Parent
+410,9,6,4,0,0x1,3,1,1,yes,0,0x4,1,none,0, WE_CALLBABY,Call Baby
+411,0,6,4,0,0x1,0,10,1,yes,0,0,0,misc,4, TK_RUN,Running
+412,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYSTORM,Tornado Stance
+413,-2,8,4,-1,0x2,2,7,-3,no,0,0x200,0,weapon,0, TK_STORMKICK,Tornado Kick
+414,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYDOWN,Heel Drop Stance
+415,-2,8,4,-1,0,0,7,-3,no,0,0x200,0,weapon,0, TK_DOWNKICK,Heel Drop
+416,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYTURN,Roundhouse Stance
+417,-2,8,4,-1,0x2,1,7,-3,no,0,0x200,0,weapon,2, TK_TURNKICK,Roundhouse Kick
+418,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_READYCOUNTER,Counter Kick Stance
+419,-2,8,4,-1,0x40,0,7,-3,no,0,0x200,0,weapon,0, TK_COUNTER,Counter Kick
+420,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, TK_DODGE,Tumbling
+421,9,8,16,-1,0x1,0,7,-3,no,0,0,0,weapon,0, TK_JUMPKICK,Flying Kick
+422,0,0,0,0,0,1,10,0,no,0,0,0,none,0, TK_HPTIME,Peaceful Break
+423,0,0,0,0,0,1,10,0,no,0,0,0,none,0, TK_SPTIME,Happy Break
+424,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, TK_POWER,Kihop
+425,0,6,4,2:4:1:3:8:7:6,0x1,0,7,1,no,0,0,0,weapon,0, TK_SEVENWIND,Mild Wind
+426,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, TK_HIGHJUMP,Taekwon Jump
+427,0,6,4,0,0x1,0,3,1,yes,0,0,0,magic,0, SG_FEEL,Feeling the Sun Moon and Stars
+428,1,6,4,-1,0x2,1,3,1,yes,0,0,0,weapon,2, SG_SUN_WARM,Warmth of the Sun
+429,1,6,4,-1,0x2,1,3,1,yes,0,0,0,weapon,2, SG_MOON_WARM,Warmth of the Moon
+430,1,6,4,-1,0x2,1,3,1,yes,0,0,0,weapon,2, SG_STAR_WARM,Warmth of the Stars
+431,0,0,4,0,0x1,0,4,1,yes,0,0,0,magic,0, SG_SUN_COMFORT,Comfort of the Sun
+432,0,0,4,0,0x1,0,4,1,yes,0,0,0,magic,0, SG_MOON_COMFORT,Comfort of the Moon
+433,0,0,4,0,0x1,0,4,1,yes,0,0,0,magic,0, SG_STAR_COMFORT,Comfort of the Stars
+434,10,6,1,0,0x1,0,3,1,yes,0,0,0,magic,0, SG_HATE,Hatred of the Sun Moon and Stars
+435,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_SUN_ANGER,Anger of the Sun
+436,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_MOON_ANGER,Anger of the Moon
+437,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_STAR_ANGER,Anger of the Stars
+438,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SG_SUN_BLESS,Blessing of the Sun
+439,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SG_MOON_BLESS,Blessing of the Moon
+440,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SG_STAR_BLESS,Blessing of the Stars
+441,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SG_DEVIL,Demon of the Sun Moon and Stars
+442,0,0,0,0,0,0,3,0,no,0,0,0,none,0, SG_FRIEND,Friend of the Sun Moon and Stars
+443,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SG_KNOWLEDGE,Knowledge of the Sun Moon and Stars
+444,0,6,4,0,0x1,0,1,1,no,0,0,0,misc,0, SG_FUSION,Union of the Sun Moon and Stars
+445,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_ALCHEMIST,Spirit of the Alchemist
+446,9,6,16,0,0x1,0,1,1,yes,0,0xC08,0,none,0, AM_BERSERKPITCHER,Aid Berserk Potion
+447,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_MONK,Spirit of the Monk
+448,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_STAR,Spirit of the Star Gladiator
+449,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_SAGE,Spirit of the Sage
+450,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_CRUSADER,Spirit of the Crusader
+451,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_SUPERNOVICE,Spirit of the Supernovice
+452,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_KNIGHT,Spirit of the Knight
+453,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_WIZARD,Spirit of the Wizard
+454,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_PRIEST,Spirit of the Priest
+455,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_BARDDANCER,Spirit of the Artist
+456,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_ROGUE,Spirit of the Rogue
+457,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_ASSASIN,Spirit of the Assasin
+458,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_BLACKSMITH,Spirit of the Blacksmith
+459,0,6,4,0,0x3,-1,1,1,no,0,0x8,0,weapon,0 , BS_ADRENALINE2,Advanced Adrenaline Rush
+460,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_HUNTER,Spirit of the Hunter
+461,9,6,16,0,0x1,0,5,1,yes,0,0x200,0,magic,0, SL_SOULLINKER,Spirit of the Soul Linker
+462,9,6,16,0,0x1,0,7,1,yes,0,0,0,magic,0, SL_KAIZEL,Kaizel
+463,9,6,16,0,0x1,0,7,1,yes,0,0,0,magic,0, SL_KAAHI,Kaahi
+464,9,6,16,0,0x1,0,3,1,yes,0,0,0,magic,0, SL_KAUPE,Kaupe
+465,9,6,16,0,0x1,0,7,1,yes,0,0,0,magic,0, SL_KAITE,Kaite
+466,0,0,0,0,0,0,7,0,yes,0,0,0,magic,0, SL_KAINA,Kaina
+467,9,6,1,-2,0,0,7,1,no,0,0,0,magic,2, SL_STIN,Estin
+468,9,6,1,-2,0,0,7,1,no,0,0,0,magic,0, SL_STUN,Estun
+469,9,8,1,-2,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, SL_SMA,Esma
+470,9,6,1,0,0x1,0,7,1,no,0,0,0,magic,0, SL_SWOO,Eswoo
+471,9,6,1,0,0x1,0,3,1,no,0,0,0,magic,0, SL_SKE,Eske
+472,9,6,1,0,0x1,0,3,1,no,0,0,0,magic,0, SL_SKA,Eska
+473,0,6,4,0,0,0,1,1,no,0,0,0,none,0, SM_SELFPROVOKE,Provoke Self
+474,0,0,4,0,0x1,0,10,1,no,0,0x2,0,none,0, NPC_EMOTION_ON,Emotion ON
+475,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0, ST_PRESERVE,Preserve
+476,1,6,1,0,0x1,0,5,1,yes,0,0,0,weapon,0, ST_FULLSTRIP,Divest All
+477,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, WS_WEAPONREFINE,Upgrade Weapon
+478,3,6,2,0,0x3,3,10,1,no,0,0,0,none,0, CR_SLIMPITCHER,Aid Condensed Potion
+479,1,6,16,0,0x1,0,5,1,yes,0,0,0,weapon,0, CR_FULLPROTECTION,Full Protection
+480,5,8,1,-1,0,0,5,5,no,0,0,0,weapon,0, PA_SHIELDCHAIN,Shield Chain
+481,0,0,0,0,0,0,5,0,no,0,0,0,none,0, HP_MANARECHARGE,Mana Recharge
+482,0,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, PF_DOUBLECASTING,Double Casting
+483,16,6,2,0,0x1,1:2:3:4:5,1,1,no,0,0,0,none,0, HW_GANBANTEIN,Ganbantein
+484,9,6,2,2,0x91,0,5,1,yes,0,0,0,misc,0, HW_GRAVITATION,Gravitation Field
+485,-2,6,1,-1,0x8,0,10,1,no,0,0,0,weapon,0, WS_CARTTERMINATION,Cart Termination
+486,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, WS_OVERTHRUSTMAX,Maximum Power Thrust
+487,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, CG_LONGINGFREEDOM,Longing for Freedom
+488,0,6,4,0,0x1,1,5,1,no,0,0x40,0,misc,0, CG_HERMODE,Wand of Hermode
+489,9,6,1,0,0x41,0,5,1,no,0,0,0,misc,0, CG_TAROTCARD,Tarot Card of Fate
+490,9,8,1,0,0x40,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,misc,0, CR_ACIDDEMONSTRATION,Acid Demonstration
+491,1,6,2,0,0x1,0,2,1,no,0,0,0,none,0, CR_CULTIVATION,Plant Cultivation
+492,0,6,4,0:1:2:3:4:5:6:7:8:9,0x1,0,10,1,no,0,0x2,0,none,0, ITEM_ENCHANTARMS,Weapon Enchantment
+493,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, TK_MISSION,Taekwon Mission
+494,9,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, SL_HIGH,Spirit of Rebirth
+495,0,6,4,0,0x1,0,1,1,no,0,0x8,0,weapon,0, KN_ONEHAND,Onehand Quicken
+496,0,6,4,0,0x1,0,1,0,no,0,0x8,0,none,0, AM_TWILIGHT1,Twilight Alchemy 1
+497,0,6,4,0,0x1,0,1,0,no,0,0x8,0,none,0, AM_TWILIGHT2,Twilight Alchemy 2
+498,0,6,4,0,0x1,0,1,0,no,0,0x8,0,none,0, AM_TWILIGHT3,Twilight Alchemy 3
+499,-9,8,4,-1,0,0,1,2,no,0,0x208,0,weapon,0, HT_POWER,Beast Strafing
+500,0,6,4,0,0x40,0,5,1,no,0,0,0,misc,0, GS_GLITTERING,Flip the Coin
+501,9,6,1,-1,0x50,0,1,1,no,0,0,0,misc,0, GS_FLING,Fling
+502,-9,8,1,-1,0,0,1,3,no,0,0,0,weapon,0, GS_TRIPLEACTION,Triple Action
+503,-9,6,1,-1,0x8,0,1,1,no,0,0,0,weapon,0, GS_BULLSEYE,Bulls Eye
+504,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, GS_MADNESSCANCEL,Madness Canceller
+505,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, GS_ADJUSTMENT,AdJustment
+506,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, GS_INCREASING,Increasing Accuracy
+507,-9,6,1,8,0,0,1,1,no,0,0,0,weapon,0, GS_MAGICALBULLET,Magical Bullet
+508,-9,6,1,-1,0x1,0,1,1,no,0,0,0,weapon,0, GS_CRACKER,Cracker
+509,0,0,0,0,0,0,10,0,no,0,0,0,none,0, GS_SINGLEACTION,Single Action
+510,0,0,0,0,0,0,10,0,no,0,0,0,none,0, GS_SNAKEEYE,Snake Eye
+511,-9,8,0,-1,0,0,10,2,no,0,0,0,weapon,0, GS_CHAINACTION,Chain Action
+512,-9,6,1,-1,0,0,10,1,yes,0,0,0,weapon,0, GS_TRACKING,Tracking
+513,-9,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, GS_DISARM,Disarm
+514,-9,6,1,-1,0x20,0,5,1,no,0,0,0,weapon,0, GS_PIERCINGSHOT,Piercing Shot
+515,-9,8,1,-1,0,0,10,5,no,0,0,0,weapon,0, GS_RAPIDSHOWER,Rapid Shower
+516,0,8,4,-1,0x2,3,10,1,no,0,0,0,weapon,0, GS_DESPERADO,Desperado
+517,0,6,4,-1,0x1,0,10,1,no,0,0,0,weapon,0, GS_GATLINGFEVER,Gatling Fever
+518,2,6,1,-1,0,0,10,1,no,0,0,0,weapon,5, GS_DUST,Dust
+519,-9,6,1,-1,0,0,10,1,yes,0,0,0,weapon,0, GS_FULLBUSTER,Full Buster
+520,-9,6,1,-1,0x2,1:1:1:2:2:2:3:3:3:4,10,1,no,0,0,0,weapon,0, GS_SPREADATTACK,Spread Attack
+521,-9,6,2,-1,0x40,1,10,1,no,0,0,0,weapon,3, GS_GROUNDDRIFT,Ground Drift
+522,0,0,0,0,0,0,10,1,no,0,0,0,weapon,0, NJ_TOBIDOUGU,Shuriken Training
+523,9,6,1,-1,0x40,0,10,1,no,0,0,0,weapon,0, NJ_SYURIKEN,Throw Shuriken
+524,9,8,1,-1,0x40,0,5,3,no,0,0,0,weapon,0, NJ_KUNAI,Throw Kunai
+525,9,8,2,-1,0x2,0,5,-3:-3:-4:-4:-5,no,0,0,0,weapon,0, NJ_HUUMA,Throw Huuma Shuriken
+526,9,6,1,0,0x50,0,10,1,no,0,0,0,misc,0, NJ_ZENYNAGE,Throw Zeny
+527,0,6,4,-1,0,0,5,1,no,0,0,0,weapon,4, NJ_TATAMIGAESHI,Improvised Defense
+528,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, NJ_KASUMIKIRI,Vanishing Slash
+529,7:9:11:13:15,6,2,0,0x1,0,5,1,no,0,0,0,none,0, NJ_SHADOWJUMP,Shadow Leap
+530,7:9:11:13:15,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, NJ_KIRIKAGE,Shadow Slash
+531,0,6,4,0,0x1,0,5,1,no,0,0,0,none,7, NJ_UTSUSEMI,Cicada Skin Sheeding
+532,0,6,4,0,0x1,0,10,1,yes,0,0,0,magic,0, NJ_BUNSINJYUTSU,Mirror Image
+533,0,0,0,0,0,0,10,0,no,0,0,0,none,0, NJ_NINPOU,Spirit of the Blade
+534,9,8,1,3,0,0,10,1:2:3:4:5:6:7:8:9:10,yes,0,0,0,magic,0, NJ_KOUENKA,Crimson Fire Petal
+535,0,8,4,3,0,0,10,1,yes,0,0,0,magic,1, NJ_KAENSIN,Crimson Fire Formation
+536,9,8,1,3,0x2,2,5,3,yes,0,0,0,magic,0, NJ_BAKUENRYU,Raging Fire Dragon
+537,9,8,1,1,0,0,10,3:4:5:6:7:8:9:10:11:12,yes,0,0,0,magic,0, NJ_HYOUSENSOU,Spear of Ice
+538,9,6,2,1,0x1,0,10,1,yes,0,0,0,magic,0, NJ_SUITON,Hidden Water
+539,0,6,4,1,0x2,3,5,1,yes,0,0,0,magic,0, NJ_HYOUSYOURAKU,Ice Meteor
+540,9,8,1,4,0,0,10,1:2:2:3:3:4:4:5:5:6,yes,0,0,0,magic,0, NJ_HUUJIN,Wind Blade
+541,9,6,2,4,0x2,2:2:3:3:4,5,1,yes,0,0,0,magic,0, NJ_RAIGEKISAI,Lightning Strike of Destruction
+542,9,8,1,4,0,3,5,1,yes,0,0,5:6:7:8:9,magic,0, NJ_KAMAITACHI,Kamaitachi
+543,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, NJ_NEN,Soul
+544,-5,8,1,0,0x40,0,10,1,no,0,0,0,weapon,0, NJ_ISSEN,Final Strike
+
+// Additional NPC Skills (Episode 11.3)
+653,0,8,4,0,0x6,5:7:9:11:13:5:7:9:11:13,10,1,no,0,0x2,0,magic,0, NPC_EARTHQUAKE,Earthquake
+654,9,6,1,3,0,5,10,1,no,0,0x2,14,weapon,0, NPC_FIREBREATH,Fire Breath
+655,9,6,1,1,0,5,10,1,no,0,0x2,14,weapon,0, NPC_ICEBREATH,Ice Breath
+656,9,6,1,4,0,5,10,1,no,0,0x2,14,weapon,0, NPC_THUNDERBREATH,Thunder Breath
+657,9,6,1,5,0,5,10,1,no,0,0x2,14,weapon,0, NPC_ACIDBREATH,Acid Breath
+658,9,6,1,7,0,5,10,1,no,0,0x2,14,weapon,0, NPC_DARKNESSBREATH,Darkness Breath
+659,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_DRAGONFEAR,Dragon Fear
+660,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_BLEEDING,Bleeding
+661,0,6,4,0,0x2,7,5,1,no,0,0x2,0,weapon,7, NPC_PULSESTRIKE,Pulse Strike
+662,0,6,4,0,0x2,14,10,1,no,0,0x2,0,weapon,0, NPC_HELLJUDGEMENT,Hell's Judgement
+663,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESILENCE,Wide Silence
+664,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDEFREEZE,Wide Freeze
+665,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDEBLEEDING,Wide Bleeding
+666,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESTONE,Wide Petrify
+667,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDECONFUSE,Wide Confusion
+668,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESLEEP,Wide Sleep
+669,0,6,4,3,0x3,5,1,1,no,0,0x2,0,magic,0, NPC_WIDESIGHT,Wide Sight
+670,9,6,2,7,0x91,0,10,1,no,0,0x2,0,magic,0, NPC_EVILLAND,Evil Land
+671,0,6,4,0,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_MAGICMIRROR,Magic Mirror
+672,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_SLOWCAST,Slow Cast
+673,-9,6,1,-1,0,0,5,1,no,0,0x2,0,weapon,0, NPC_CRITICALWOUND,Critical Wounds
+674,-9,6,1,-1,0x1,0,1,1,no,0,0x2,0,none,0, NPC_EXPULSION,Expulsion
+675,0,6,4,0,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_STONESKIN,Stone Skin
+676,0,6,4,0,0x1,0,5,1,no,0,0x2,0,magic,0, NPC_ANTIMAGIC,Anti Magic
+677,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDECURSE,Wide Curse
+678,0,6,4,0,0x3,2:5:8:11:14,5,1,no,0,0x2,0,none,0, NPC_WIDESTUN,Wide Stun
+679,0,6,4,0,0x2,5:7:9:11:13:13:13:13:13:13,10,1,no,0,0x2,0,weapon,0, NPC_VAMPIRE_GIFT,Vampire Gift
+680,0,6,4,0,0x3,5:7:9:11:13:13:13:13:13:13,10,1,no,0,0x2,0,none,0, NPC_WIDESOULDRAIN,Wide Soul Drain
+
+// Cash Shop Skill
+681,0,0,0,0,0,0,10,0,no,0,0x1,0,none,0, ALL_INCCARRY,Increase Weight Limit R
+
+// Additional NPC skill (Episode 12)
+682,0,0,4,0,0x1,0,1,1,no,0,0x2,0,none,0, NPC_TALK,Talk
+683,-9,6,1,-1,0,0,1,1,no,0,0x2,0,none,0, NPC_HELLPOWER,Hell Power
+684,0,6,4,0,0x3,-1,1,1,no,0,0x2,0,none,0, NPC_WIDEHELLDIGNITY,Hell Dignity
+685,0,0,4,0,0x1,0,1,1,no,0,0x2,0,none,0, NPC_INVINCIBLE,Invincible
+686,0,0,4,0,0x1,0,1,1,no,0,0x2,0,none,0, NPC_INVINCIBLEOFF,Invincible off
+687,0,6,4,0,0x1,0,1,1,yes,0,0x2,0,none,0, NPC_ALLHEAL,Full Heal
+
+// Additional Skill (??)
+688,9,6,16,0,0x1,0,10,0,no,0,0x200,0,none,0, GM_SANDMAN,GM Sandman
+689,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0, CASH_BLESSING,Party Blessing
+690,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0, CASH_INCAGI,Party Increase AGI
+691,0,6,4,0,0x3,-1,5,1,yes,0,0x2,0,magic,0, CASH_ASSUMPTIO,Party Assumptio
+//692,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_CATCRY,Cat Cry
+693,0,6,4,0,0x3,-1,1,1,yes,0,0x2,0,magic,0, ALL_PARTYFLEE,Party Flee
+//694,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_ANGEL_PROTECT,Angel's Protection
+//695,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_DREAM_SUMMERNIGHT,Summer Night Dream
+//696,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, NPC_CHANGEUNDEAD2,Change Undead
+//697,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0, ALL_REVERSEORCISH,Reverse Orcish
+698,0,6,4,0,0x01,0,1,1,no,0,0x2,0,none,0, ALL_WEWISH,Christmas Carol
+//699,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0, ALL_SONKRAN,ALL_SONKRAN
+
+// New NPC Wide Status AoE Skills And Others
+//700,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDEHEALTHFEAR,Wide Health Fear
+//701,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDEBODYBURNNING,Wide Body Burnning
+//702,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDEFROSTMISTY,Wide Freezing
+//703,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDECOLD,Wide Crystalize
+//704,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDE_DEEP_SLEEP,Wide Deep Sleep
+//705,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_WIDESIREN,Wide Siren's Voice
+//706,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_VENOMFOG,Venom Fog
+//707,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_MILLENNIUMSHIELD,Millenium Shield 2
+//708,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_COMET,Comet 2
+
+1001,9,6,1,-1,0,0,1,1,no,0,0x1,0,weapon,0, KN_CHARGEATK,Charge Attack
+1002,0,6,4,0,0x1,0,1,0,no,0,0x1,0,weapon,2, CR_SHRINK,Shrink
+1003,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0, AS_SONICACCEL,Sonic Acceleration
+1004,9,8,1,0,0x8,0,1,1,no,0,0x1,0,weapon,0, AS_VENOMKNIFE,Throw Venom Knife
+1005,1,6,1,0,0x1,0,1,1,no,0,0x1,0,weapon,0, RG_CLOSECONFINE,Close Confine
+1006,0,6,4,3,0,2,1,1,yes,0,0x1,0,magic,3, WZ_SIGHTBLASTER,Sight Blaster
+1007,0,6,4,0,0x1,0,1,0,no,0,0x1,0,none,0, SA_CREATECON,Create Elemental Converter
+1008,9,6,1,1,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTWATER,Elemental Change Water
+1009,-9,6,1,0,0,0,1,1,no,0,0x1,0,weapon,3, HT_PHANTASMIC,Phantasmic Arrow
+1010,9,6,1,0,0x1,0,1,0,no,0,0x1,0,misc,0, BA_PANGVOICE,Pang Voice
+1011,9,6,1,0,0x1,0,1,0,no,0,0x1,0,misc,0, DC_WINKCHARM,Wink of Charm
+1012,0,0,0,0,0,0,1,0,no,0,0x1,0,weapon,0, BS_UNFAIRLYTRICK,Unfair Trick
+1013,0,6,4,0,0x3,2,1,0,no,0,0x1,0,weapon,0, BS_GREED,Greed
+1014,0,6,4,6,0x3,14,1,0,yes,0,0x1,0,magic,0, PR_REDEMPTIO,Redemptio
+1015,9,6,16,0,0x1,0,1,1,no,0,0x401,0,weapon,0, MO_KITRANSLATION,Ki Translation
+1016,-1,6,1,-1,0x2,1,1,1,no,0,0x1,0,weapon,5, MO_BALKYOUNG,Ki Explosion
+1017,9,6,1,2,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTGROUND,Elemental Change Earth
+1018,9,6,1,3,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTFIRE,Elemental Change Fire
+1019,9,6,1,4,0x1,0,1,1,yes,0,0x1,0,magic,0, SA_ELEMENTWIND,Elemental Change Wind
+
+//****
+// RK Rune Knight
+//****
+2001,1,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, RK_ENCHANTBLADE,Enchant Blade
+2002,7:8:9:10:11,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, RK_SONICWAVE,Sonic Wave
+2003,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, RK_DEATHBOUND,Death Bound
+2004,1,8,1,-1,0,0,10,-5,no,0,0,0,weapon,0, RK_HUNDREDSPEAR,Hundred Spear
+2005,1,6,2,4,0x2,2,5,1,no,0,0,0,weapon,3, RK_WINDCUTTER,Wind Cutter
+2006,0,6,4,-1,0x2,5,5,1,no,0,0,0,weapon,0, RK_IGNITIONBREAK,Ignition Break
+2007,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, RK_DRAGONTRAINING,Dragon Training
+2008,9,6,2,3,0xC2,1:1:1:2:2:2:3:3:4:4,10,1,no,0,0,0,misc,0, RK_DRAGONBREATH,Dragon Breath //CHECK May have to change this back to a weapon type attack.
+2009,0,6,4,0,0x3,3:4:5:6:7,5,1,yes,0,0,0,weapon,0, RK_DRAGONHOWLING,Dragon Howling
+2010,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RK_RUNEMASTERY,Rune Mastery
+2011,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_MILLENNIUMSHIELD,Millenium Shield
+2012,1,6,4,-1,0,0x8,1,1,yes,0,0,0,weapon,0, RK_CRUSHSTRIKE,Crush Strike
+2013,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_REFRESH,Refresh
+2014,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_GIANTGROWTH,Giant Growth
+2015,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_STONEHARDSKIN,Stone Hard Skin
+2016,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, RK_VITALITYACTIVATION,Vitality Activation
+2017,0,6,4,-1,0x2,3,1,1,no,0,0,0,weapon,7, RK_STORMBLAST,Storm Blast
+2018,0,6,4,0,0x3,-1,1,1,yes,0,0,0,none,0, RK_FIGHTINGSPIRIT,Fighting Spirit //CHECK Is this splash needed?
+2019,9,6,4,6,0x1,0,1,1,yes,0,0,0,none,0, RK_ABUNDANCE,Abundance
+2020,5:6:7:8:9,6,1,-1,0,0,5,1,yes,0,0,0,weapon,0, RK_PHANTOMTHRUST,Phantom Thrust
+
+//****
+// WL Warlock
+//****
+2201,11,6,16,0,0,0,5,1,yes,0,0,0,magic,0, WL_WHITEIMPRISON,White Imprison
+2202,11,8,1,8,0x2,1:1:1:2:2,5,-2,yes,0,0,0,magic,0, WL_SOULEXPANSION,Soul Expansion
+2203,0,8,4,1,0x2,13,5,-3:-4:-5:-6:-7,yes,0,0,0,magic,0, WL_FROSTMISTY,Frosty Misty
+2204,0,8,4,1,0x2,13,5,-5,yes,0,0,0,magic,0, WL_JACKFROST,Jack Frost
+2205,11,6,1,0,0x1,0,5,1,yes,0,0,0,magic,0, WL_MARSHOFABYSS,Marsh of Abyss
+2206,0,6,4,0,0x1,0,5,1,yes,0,0,0,magic,0, WL_RECOGNIZEDSPELL,Recognized Spell
+2207,7,6,1,2,0x3,1:2:2:3:3,5,1,yes,0,0,0,magic,0, WL_SIENNAEXECRATE,Sienna Execrate
+2208,0,0,0,0,0,0,3,0,no,0,0,0,none,0, WL_RADIUS,Radius
+2209,0,6,4,0,0x3,9:10:11:12:13,5,1,yes,0,0,0,magic,0, WL_STASIS,Stasis
+2210,11,6,1,0,0,0,5,1,yes,0,0,0,magic,0, WL_DRAINLIFE,Drain Life
+2211,11,8,1,3,0x2,3,5,-7,yes,0,0,0,magic,3, WL_CRIMSONROCK,Crimson Rock
+2212,11,6,1,3,0,0,5,1,yes,0,0,0,magic,0, WL_HELLINFERNO,Hell Inferno
+2213,11,8,2,0,0x2,15,5,-20,yes,0,0,0,magic,2, WL_COMET,Comet //CHECK AoE in official code appears to be 15 x 15, yet casting circle is much bigger then that.
+2214,11,6,1,0,0,3,5,1,yes,0,0,0,magic,0, WL_CHAINLIGHTNING,Chain Lightning //CHECK Is the splash being used for the target search?
+2215,11,6,1,4,0,0,5,1,no,0,0,0,magic,0, WL_CHAINLIGHTNING_ATK,Chain Lightning Attack
+2216,3,8,2,2,0,0,5,-6:-7:-8:-9:-10,yes,0,0,0,magic,0, WL_EARTHSTRAIN,Earth Strain
+2217,11,6,1,0,0,0,5,1,yes,0,0,0,magic,0, WL_TETRAVORTEX,Tetra Vortex
+2218,11,6,1,3,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_FIRE,Tetra Vortex Fire
+2219,11,6,1,1,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_WATER,Tetra Vortex Water
+2220,11,6,1,4,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_WIND,Tetra Vortex Wind
+2221,11,6,1,2,0,0,5,1,no,0,0,0,magic,0, WL_TETRAVORTEX_GROUND,Tetra Vortex Earth
+2222,0,6,4,3,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONFB,Summon Fire Ball
+2223,0,6,4,4,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONBL,Summon Lightning Ball
+2224,0,6,4,1,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONWB,Summon Water Ball
+2225,11,6,1,3,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_FIRE,Summon Attack Fire //CHECK Summon attack ID's dont appear to have a range.
+2226,11,6,1,4,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_WIND,Summon Attack Wind
+2227,11,6,1,1,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_WATER,Summon Attack Water
+2228,11,6,1,2,0,0,5,1,no,0,0,1,magic,0, WL_SUMMON_ATK_GROUND,Summon Attack Earth
+2229,0,6,4,2,0x1,0,5,1,yes,0,0,0,magic,0, WL_SUMMONSTONE,Summon Stone
+2230,11,8,1,0,0,0,2,1,yes,0,0,0,magic,0, WL_RELEASE,Release //CHECK Should it be left to do multi hit or single hit?
+2231,0,6,4,0,0x1,0,1,1,yes,0,0,0,magic,0, WL_READING_SB,Reading Spellbook
+2232,0,0,0,0,0,0,5,0,no,0,0,0,none,0, WL_FREEZE_SP,Freeze Spell
+
+
+//****
+// GC Guillotine Cross
+//****
+2021,10,6,1,0,0x1,0,5,1,no,0,0,0,none,0, GC_VENOMIMPRESS,Venom Impress
+2022,3,8,1,-1,0,0,5,-7,no,0,0,0,weapon,0, GC_CROSSIMPACT,Cross Impact
+2023,3:4:5:6:7,6,1,-1,0,0,5,1,no,0,0,0,weapon,0,GC_DARKILLUSION,Dark Illusion
+2024,0,0,0,0,0,0,10,0,no,0,0,0,none,0, GC_RESEARCHNEWPOISON,Research New Poison
+2025,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, GC_CREATENEWPOISON,Create New Poison
+2026,5,6,16,0,0x1,0,1,1,no,0,0,0,none,0, GC_ANTIDOTE,Antidote
+2027,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, GC_POISONINGWEAPON,Poisoning Weapon
+2028,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, GC_WEAPONBLOCKING,Weapon Blocking
+2029,-2,6,4,-1,0x2,1,5,1,no,0,0,0,weapon,3, GC_COUNTERSLASH,Counter Slash
+2030,-2,6,4,-1,0x1,0,5,1,no,0,0x200,0,weapon,0, GC_WEAPONCRUSH,Weapon Crush //CHECK SHould this and the above skill have INF2 0x200?
+2031,1,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, GC_VENOMPRESSURE,Venom Pressure
+2032,5,6,2,0,0x1,0,5,1,yes,0,0,1,none,0, GC_POISONSMOKE,Poison Smoke
+2033,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, GC_CLOAKINGEXCEED,Cloaking Exceed
+2034,0,6,4,-1,0x2,3,1,1,no,0,0,0,weapon,0, GC_PHANTOMMENACE,Phantom Menace
+2035,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, GC_HALLUCINATIONWALK,Hallucination Walk
+2036,0,6,4,-1,0x2,1:1:1:1:2,5,1,no,0,0,0,weapon,0, GC_ROLLINGCUTTER,Rolling Cutter
+2037,9:10:11:12:13,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, GC_CROSSRIPPERSLASHER,Cross Ripper Slasher
+
+//****
+// AB Arch Bishop
+//****
+2038,11,8,1,6,0x2,3,5,-3,yes,0,0,0,magic,0, AB_JUDEX,Judex
+2039,0,6,4,0,0x1,0,1,1,yes,0,0,0,magic,0, AB_ANCILLA,Ancilla
+2040,11,8,1,6,0,0,10,-10,yes,0,0,0,magic,0, AB_ADORAMUS,Adoramus
+2041,0,6,4,6,0x3,3:7:15,3,1,yes,0,0,0,magic,0, AB_CLEMENTIA,Crementia
+2042,0,6,4,6,0x3,3:7:15,3,1,yes,0,0,0,magic,0, AB_CANTO,Canto Candidus
+2043,0,6,4,6,0x3,3:7:15,3,1,yes,0,0,0,magic,0, AB_CHEAL,Coluceo Heal
+2044,11,6,2,6,0x1,0,5,1,yes,0,0,1,magic,0, AB_EPICLESIS,Epiclesis
+2045,0,6,4,6,0x3,15,10,1,yes,0,0,0,magic,0, AB_PRAEFATIO,Praefatio
+2046,0,6,4,6,0x3,15,10,1,yes,0,0,0,magic,0, AB_ORATIO,Oratio
+2047,0,6,4,6,0x3,15,4,1,yes,0,0,0,magic,0, AB_LAUDAAGNUS,Lauda Agnus
+2048,0,6,4,6,0x3,15,4,1,yes,0,0,0,magic,0, AB_LAUDARAMUS,Lauda Ramus
+2049,0,0,0,0,0,0,10,0,no,0,0,0,none,0, AB_EUCHARISTICA,Eucharistica
+2050,11,6,16,6,0x1,0,1,1,yes,0,0,0,magic,0, AB_RENOVATIO,Renovatio
+2051,11,6,16,6,0x21,0,5,1,yes,0,0,0,magic,0, AB_HIGHNESSHEAL,Highness Heal //CHECK Info shows this has magic attack.
+2052,11,6,1,0,0x1,0,5,1,yes,0,0xA00,0,magic,0, AB_CLEARANCE,Clearance //CHECK Also shows this as a magic attack. Why?
+2053,0,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, AB_EXPIATIO,Expiatio //CHECK Does this also give the buff to party members?
+2054,0,6,4,6,0x1,0,10,1,yes,0,0,0,none,0, AB_DUPLELIGHT,Duple Light //CHECK Had issues adding a skill level check to make the % go higher with the skills level. Will do later.
+2055,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, AB_DUPLELIGHT_MELEE,Duple Light Melee
+2056,-1,6,1,0,0,0,10,1,no,0,0,0,magic,0, AB_DUPLELIGHT_MAGIC,Duple Light Magic
+2057,0,6,4,6,0x3,4:5:6:7:8,5,1,yes,0,0,0,magic,0, AB_SILENTIUM,Silentium //CHECk Marked magic attack as well. Hmmmm....
+
+2515,11,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, AB_SECRAMENT,Secrament
+
+//****
+// RA Ranger
+//****
+2233,9,8,1,-1,0x2,3:3:3:3:3:4:4:4:4:5,10,-3,yes,0,0,0,weapon,0, RA_ARROWSTORM,Arrow Storm
+2234,0,6,4,0,0,0,5,1,yes,0,0,0,none,0, RA_FEARBREEZE,Fear Breeze
+2235,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RA_RANGERMAIN,Ranger Main
+2236,9,8,1,-1,0,0,10,1,yes,0,0,0,weapon,0, RA_AIMEDBOLT,Aimed Bolt
+2237,9,6,2,0,0x3,3,1,1,no,0,0,0,none,0, RA_DETONATOR,Detonator
+2238,3,6,2,0,0x3,2,5,1,no,0,0x80,3,misc,0, RA_ELECTRICSHOCKER,Electric Shocker
+2239,3,6,2,0,0x42,3,5,1,no,0,0x80,3,misc,0, RA_CLUSTERBOMB,Cluster Bomb
+2240,0,6,4,0,0,0,1,1,no,0,0,0,none,0, RA_WUGMASTERY,Warg Mastery
+2241,0,6,4,0,0,0,3,1,no,0,0,0,none,0, RA_WUGRIDER,Warg Rider
+2242,0,6,4,-1,0x2,1,1,0,no,0,0,0,weapon,0, RA_WUGDASH,Warg Dash
+2243,9,6,1,0,0,0,5,1,no,0,0,0,weapon,0, RA_WUGSTRIKE,Warg Strike
+2244,9,6,1,0,0,0,5,1,no,0,0,0,weapon,0, RA_WUGBITE,Warg Bite
+2245,0,0,0,0,0,0,10,0,no,0,0,0,none,0, RA_TOOTHOFWUG,Tooth of Warg
+2246,0,6,4,0,0x2,3:4:5:6:7,5,1,no,0,0,0,weapon,0, RA_SENSITIVEKEEN,Sensitive Keen
+2247,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, RA_CAMOUFLAGE,Camouflage
+2248,0,0,0,0,0,0,5,0,no,0,0,0,none,0, RA_RESEARCHTRAP,Research Trap
+2249,3,6,2,3,0x43,2,1,1,no,0,0x80,1,misc,0, RA_MAGENTATRAP,Magenta Trap
+2250,3,6,2,1,0x43,2,1,1,no,0,0x80,1,misc,0, RA_COBALTTRAP,Cobalt Trap
+2251,3,6,2,2,0x43,2,1,1,no,0,0x80,1,misc,0, RA_MAIZETRAP,Maize Trap
+2252,3,6,2,4,0x43,2,1,1,no,0,0x80,1,misc,0, RA_VERDURETRAP,Verdure Trap
+2253,3,6,2,0,0x42,2,5,1,no,0,0x80,2,misc,0, RA_FIRINGTRAP,Firing Trap
+2254,3,6,2,0,0x42,2,5,1,no,0,0x80,2,misc,0, RA_ICEBOUNDTRAP,Icebound Trap
+
+//****
+// NC Mechanic
+2255,0,0,0,0,0,0,5,0,no,0,0,0,none,0, NC_MADOLICENCE,Mado License
+2256,11,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, NC_BOOSTKNUCKLE,Boost Knuckle
+2257,3,6,1,-1,0,0,3,1,no,0,0,0,weapon,0, NC_PILEBUNKER,Pile Bunker
+2258,13,6,1,-1,0,0,3,1,no,0,0,0,weapon,0, NC_VULCANARM,Vulcan Arm
+2259,5,6,1,3,0,2,3,1,no,0,0,5,weapon,0, NC_FLAMELAUNCHER,Flame Launcher
+2260,7,6,2,1,0x2,2:3:4,3,1,no,0,0,0,weapon,0, NC_COLDSLOWER,Cold Slower
+2261,7,6,2,-1,0x42,3:2:1,3,1,no,0,0,0,weapon,0, NC_ARMSCANNON,Arm Cannon
+2262,0,6,4,0,0x1,0,3,1,no,0,0,0,none,0, NC_ACCELERATION,Acceleration
+2263,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, NC_HOVERING,Hovering
+2264,0,6,4,0,0x1,0,1,1,no,0,0,0,none,7, NC_F_SIDESLIDE,Front-Side Slide
+2265,0,6,4,0,0x1,0,1,1,no,0,0,0,none,7, NC_B_SIDESLIDE,Back-Side Slide
+2266,0,0,0,0,0,0,4,0,no,0,0,0,none,0, NC_MAINFRAME,Mainframe Restructure // Check me. Part of the code notes translated to "The amount of fuel have".
+2267,0,6,4,-1,0x42,2:3:4,3,1,no,0,0,0,misc,5, NC_SELFDESTRUCTION,Self Destruction
+2268,0,6,4,0,0x1,0,4,1,yes,0,0,0,none,0, NC_SHAPESHIFT,Shape Shift
+2269,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, NC_EMERGENCYCOOL,Emergency Cool
+2270,0,6,4,0,0x3,7,1,1,yes,0,0,0,none,0, NC_INFRAREDSCAN,Infrared Scan
+2271,9,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, NC_ANALYZE,Analyze
+2272,0,6,4,0,0x3,1:2:3,3,1,yes,0,0,0,none,0, NC_MAGNETICFIELD,Magnetic Field
+2273,0,6,4,0,0x1,0,3,1,yes,0,0,0,none,0, NC_NEUTRALBARRIER,Neutral Barrier
+2274,0,6,4,0,0x1,0,3,1,yes,0,0,0,none,0, NC_STEALTHFIELD,Stealth Field
+2275,5,6,16,0,0x1,0,5,1,yes,0,0,0,magic,0, NC_REPAIR,Repair
+2276,0,0,0,0,0,0,10,0,no,0,0,0,none,0, NC_TRAININGAXE,Axe Training
+2277,0,0,0,0,0,0,5,0,no,0,0,0,none,0, NC_RESEARCHFE,Research Fire/Earth
+2278,4:5:6:7:8,6,1,-1,0,0,5,1,no,0,0,0,weapon,2:3:4:5:6, NC_AXEBOOMERANG,Axe Boomerang
+2279,1,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, NC_POWERSWING,Power Swing
+2280,0,8,4,-1,0x2,2:2:3:3:3,5,-6,no,0,0,0,weapon,0, NC_AXETORNADO,Axe Tornado // Check me. Takes 20 * Skill LV amount of HP each use.
+2281,2,6,2,0,0x1,0,5,1,yes,0,0,2,none,0, NC_SILVERSNIPER,FAW - Silver Sniper
+2282,2,6,2,0,0x1,0,5,1,yes,0,0,2,none,0, NC_MAGICDECOY,FAW - Magic Decoy //CHECK FIX ME!!!! Wind and Earth stones spawning opposite decoys.
+2283,2,6,1,0,0x1,0,1,1,no,0,0,0,none,0, NC_DISJOINT,FAW Removal
+
+//****
+// SC Shadow Chaser
+2284,1,6,1,-1,0x2,1,5,1,no,0,0,0,weapon,0, SC_FATALMENACE,Fatal Menace
+2285,0,6,4,0,0x1,0,10,1,no,0,0,0,none,0, SC_REPRODUCE,Reproduce
+2286,0,6,4,0,0x1,0,10,1,yes,0,0,0,none,0, SC_AUTOSHADOWSPELL,Auto Shadow Spell
+2287,5,6,1,0,0x1,0,5,1,no,0,0,0,none,0, SC_SHADOWFORM,Shadow Form
+2288,7:7:7:9:9:9:9:11:11:11,8,1,-1,0,0,10,-3,yes,0,0,0,weapon,3, SC_TRIANGLESHOT,Triangle Shot
+2289,0,6,4,0,0x3,2,5,1,no,0,0,0,none,0, SC_BODYPAINT,Body Painting
+2290,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, SC_INVISIBILITY,Invisibility
+2291,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SC_DEADLYINFECT,Deadly Infect
+2292,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_ENERVATION,Masquerade - Enervation
+2293,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_GROOMY,Masquerade - Gloomy
+2294,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_IGNORANCE,Masquerade - Ignorance
+2295,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_LAZINESS,Masquerade - Laziness
+2296,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_UNLUCKY,Masquerade - Unlucky
+2297,3,6,1,0,0x1,0,3,1,yes,0,0,0,none,0, SC_WEAKNESS,Masquerade - Weakness
+2298,3,6,1,0,0x1,0,5,1,yes,0,0,0,weapon,0, SC_STRIPACCESSARY,Strip Accessory //CHECK Is weapon attack type needed?
+2299,7,6,2,0,0x1,0,3,1,yes,0,0,3,none,0, SC_MANHOLE,Man Hole
+2300,7,6,2,0,0x1,0,3,1,yes,0,0,1,none,0, SC_DIMENSIONDOOR,Dimension Door
+2301,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0, SC_CHAOSPANIC,Chaos Panic
+2302,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0, SC_MAELSTROM,Maelstrom
+2303,7,6,2,0,0x1,0,3,1,yes,0,0,0,none,0, SC_BLOODYLUST,Bloody Lust
+2304,0,6,4,-1,0,0,3,1,no,0,0,0,weapon,0, SC_FEINTBOMB,Feint Bomb
+
+//****
+// LG Royal Guard
+2307,11,8,1,-1,0,2,5,1,no,0,0,10,weapon,0, LG_CANNONSPEAR,Cannon Spear
+2308,7,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, LG_BANISHINGPOINT,Banishing Point
+2309,0,6,4,0,0x3,2,3,1,no,0,0,0,none,0, LG_TRAMPLE,Trample
+2310,1,6,1,0,0,0,5,1,no,0,0,0,weapon,0, LG_SHIELDPRESS,Shield Press
+2311,0,6,4,0,0x3,3,5,1,no,0,0,0,none,0, LG_REFLECTDAMAGE,Reflect Damage
+2312,5,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, LG_PINPOINTATTACK,Pinpoint Attack
+2313,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_FORCEOFVANGUARD,Force of Vanguard
+2314,1,6,1,-1,0,0,1,1,no,0,0,0,weapon,0, LG_RAGEBURST,Rage Burst
+2315,0,6,4,0,0x2,3,3,1,yes,0,0,0,none,2, LG_SHIELDSPELL,Shield Spell
+2316,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_EXEEDBREAK,Exceed Break
+2317,1,6,2,-1,0x2,5,5,1,yes,0,0,0,none,3:4:5:6:7, LG_OVERBRAND,Over Brand //CHECK I know the splash is needed somehow for the strange AoE it gives.
+2318,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_PRESTIGE,Prestige
+2319,0,6,4,0,0x1,3,5,1,no,0,0,0,weapon,0, LG_BANDING,Banding //CHECK Splash isnt needed right? Banding has its own UNIT ID.
+2320,0,6,4,-1,0x2,3,5,1,yes,0,0,0,weapon,0, LG_MOONSLASHER,Moon Slasher
+2321,1,8,2,6,0x2,5,5,-7,yes,0,0,0,weapon,0, LG_RAYOFGENESIS,Ray of Genesis
+2322,0,6,16,0,0x3,1,5,1,yes,0,0,0,none,0, LG_PIETY,Piety
+2323,0,8,4,2,0x2,1:1:2:2:3,5,-5,yes,0,0,0,weapon,0, LG_EARTHDRIVE,Earth Drive
+2324,3,8,1,-1,0,0,5,3,yes,0,0,0,weapon,0, LG_HESPERUSLIT,Hesperus Lit
+2325,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, LG_INSPIRATION,Inspiration
+
+//****
+// SR Sura
+2326,-2,8,1,-1,0,0,10,2,no,0,0,0,weapon,0, SR_DRAGONCOMBO,Dragon Combo //CHECK Is this 2 regular hits or sub hits? Yes its 2 sub hits.
+2327,0,8,4,-1,0x2,2,5,-3,no,0,0,0,weapon,3, SR_SKYNETBLOW,Sky Net Blow //CHECK Video shows 3 hits. Its sub hits right? Data check shows no sub, one source shows 3 hits, another shows 5.
+2328,0,6,4,-1,0x2,1:2:3:4:5,5,1,no,0,0,0,weapon,0, SR_EARTHSHAKER,Earth Shaker //CHECK Must add a check in battle.c to triple damage if hitting a hidden target.
+2329,-2,8,4,-1,0,0,5,-2,no,0,0x200,0,weapon,0, SR_FALLENEMPIRE,Fallen Empire //CHECK Video shows 2 hits. Is it sub hits? Yes its divided between 2 hits.
+2330,-2,6,1,-1,0x42,1:1:1:1:1:2:2:2:2:2,10,1,yes,0,0,0,weapon,0, SR_TIGERCANNON,Tiger Cannon //CHECK Need to fix to be enemy targeted and also combo after Fallen Empire.
+2331,0,0,0,0,0,0,10,0,no,0,0,0,none,0, SR_HELLGATE,Hell Gate
+2332,5,6,4,-1,0x2,3,5,1,no,0,0,0,weapon,0, SR_RAMPAGEBLASTER,Rampage Blaster
+2333,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SR_CRESCENTELBOW,Crescent Elbow //CHECK Check the autospell ID.
+2334,0,6,4,0,0x3,1:1:2:2:3,5,1,no,0,0,0,none,0, SR_CURSEDCIRCLE,Cursed Circle //CHECK Code shows it takes up to 5% of your HP upon use?
+2335,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SR_LIGHTNINGWALK,Lightning Walk
+2336,7:8:9:10:11,6,1,-1,0,0,5,1,no,0,0,0,weapon,2:3:4:5:6, SR_KNUCKLEARROW,Knuckle Arrow
+2337,0,6,4,-1,0x2,2,1,1,yes,0,0,0,weapon,0, SR_WINDMILL,Windmill
+2338,0,6,4,0,0x1,0,10,1,no,0,0,0,none,0, SR_RAISINGDRAGON,Raising Dragon
+2339,0,0,0,0,0,0,5,1,no,0,0,0,none,0, SR_GENTLETOUCH,Gentle Touch
+2340,0,6,4,0,0x3,2,1,1,no,0,0,0,none,0, SR_ASSIMILATEPOWER,Assimilate Power
+2341,3,6,16,0,0x1,0,1,1,yes,0,0x200,0,none,0, SR_POWERVELOCITY,Power Velocity
+2342,1,6,1,-1,0x20,0,5,1,no,0,0,0,weapon,3, SR_CRESCENTELBOW_AUTOSPELL,Crescent Elbow Autospell //CHECK Does this ignore defense?
+2343,1:2:3:3:4:4:5:5:6:7,8,1,0,0,0,10,-7,yes,0,0,0,weapon,0, SR_GATEOFHELL,Gate of Hell //CHECK Need to fix to be enemy targeted and also combo after Fallen Empire
+2344,2,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, SR_GENTLETOUCH_QUIET,Gentle Touch - Quiet
+2345,2,6,16,0,0x1,0,5,1,no,0,0,0,magic,0, SR_GENTLETOUCH_CURE,Gentle Touch - Cure //CHECK Its a healing skill. Guessing it has to be magic type? Healing isnt working.
+2346,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, SR_GENTLETOUCH_ENERGYGAIN,Gentle Touch - Energy Gain
+2347,2,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, SR_GENTLETOUCH_CHANGE,Gentle Touch - Change
+2348,2,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, SR_GENTLETOUCH_REVITALIZE,Gentle Touch - Revitalize
+//More from Sura but not following ID order
+2517,0,6,4,-1,0x2,3:4:5:6:7,5,1,no,0,0,0,weapon,0, SR_HOWLINGOFLION,Howling of Lion
+2518,11,6,2,-1,0x2,2:2:3:3:4,5,1,no,0,0,0,weapon,0, SR_RIDEINLIGHTNING,Ride In Lightening
+
+//****
+// WA Wanderer
+2350,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, WA_SWING_DANCE,Swing Dance
+2351,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, WA_SYMPHONY_OF_LOVER,Symphony of Lovers
+2352,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, WA_MOONLIT_SERENADE,Moonlit Serenade
+
+//****
+// MI Minstrel
+2381,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, MI_RUSH_WINDMILL,Windmill Rush Attack
+2382,0,6,4,0,0x3,7:8:9:10:11,5,1,yes,0,0,0,none,0, MI_ECHOSONG,Echo Song
+2383,9,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, MI_HARMONIZE,Harmonize
+
+//****
+// WM Wanderer/Minstrel
+2412,0,0,0,0,0,0,10,0,no,0,0,0,none,0, WM_LESSON,Lesson
+2413,9,8,1,-1,0,0,5,-2:-2:-3:-3:-4,yes,0,0,0,magic,0, WM_METALICSOUND,Metallic Sound
+2414,9,6,2,-1,0x3,1,5,1,yes,0,0x80,3,none,0, WM_REVERBERATION,Reverberation
+2415,0,6,1,-1,0x6,1,5,1,no,0,0,0,weapon,0, WM_REVERBERATION_MELEE,Reverberation Melee
+2416,0,6,1,0,0x6,1,5,1,no,0,0,0,magic,0, WM_REVERBERATION_MAGIC,Reverberation Magic
+2417,11,6,2,0,0x3,5,1,1,no,0,0,0,none,0, WM_DOMINION_IMPULSE,Dominion Impulse
+2418,9,6,2,-1,0x1,0,5,1,yes,0,0,0,none,0, WM_SEVERE_RAINSTORM,Severe Rainstorm
+2419,9,6,2,0,0x3,1,5,1,yes,0,0x80,5,none,0, WM_POEMOFNETHERWORLD,Poem of The Netherworld //CHECK May need to recode too.
+2420,0,6,4,0,0x2,2:3:4:5:6,5,1,yes,0,0,0,none,0, WM_VOICEOFSIREN,Voice of Siren
+2421,7,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, WM_DEADHILLHERE,Valley of Death
+2422,7,6,4,0,0x3,5:6:7:8:9,5,1,yes,0,0,0,none,0, WM_LULLABY_DEEPSLEEP,Deep Sleep Lullaby
+2423,0,6,4,0,0x3,3:4:5:6:7,5,1,yes,0,0,0,none,0, WM_SIRCLEOFNATURE,Circle of Nature's Sound
+2424,9,6,4,0,0x1,0,5,1,yes,0,0,0,magic,0, WM_RANDOMIZESPELL,Improvised Song
+2425,9,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, WM_GLOOMYDAY,Gloomy Day
+2426,9,6,2,0,0x2,2:3:3:4:4,5,1,yes,0,0x4000,0,weapon,0, WM_GREAT_ECHO,Great Echo
+2427,0,6,4,0,0x3,5:6:7:8:9,5,1,yes,0,0x4000,0,none,0, WM_SONG_OF_MANA,Song of Mana
+2428,0,6,4,0,0x3,5:6:7:8:9,5,1,yes,0,0x4000,0,none,0, WM_DANCE_WITH_WUG,Dance With A Warg
+2429,9,6,1,0,0x2,2:2:3:3:4,5,1,yes,0,0x4000,0,weapon,0, WM_SOUND_OF_DESTRUCTION,Sound of Destruction //CHECK Source shows its magic attack. Need to confirm before changing.
+2430,0,6,4,0,0x3,3:4:5:6:7,5,1,yes,0,0x4000,0,none,0, WM_SATURDAY_NIGHT_FEVER,Saturday Night Fever
+2431,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,magic,0, WM_LERADS_DEW,Lerad's Dew
+2432,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,none,0, WM_MELODYOFSINK,Melody of Sink
+2433,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,none,0, WM_BEYOND_OF_WARCRY,Warcry of Beyond
+2434,0,6,4,0,0x3,5,5,1,yes,0,0x4000,0,none,0, WM_UNLIMITED_HUMMING_VOICE,Unlimited Humming Voice
+2516,11,6,1,-1,0x2,5,5,1,no,0,0,0,weapon,0, WM_SEVERE_RAINSTORM_MELEE,Severe Rainstorm Melee
+
+//****
+// SO Sorcerer
+2443,0,6,4,3,0,0,5,1,yes,0,0,8:10:12:14:16,magic,0, SO_FIREWALK,Fire Walk //CHECK Video and data shows each cell only hits once.
+2444,0,6,4,4,0,0,5,1,yes,0,0,8:10:12:14:16,magic,0, SO_ELECTRICWALK,Electric Walk
+2445,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, SO_SPELLFIST,Spell Fist
+2446,9,6,2,2,0,0,5,-3,yes,0,0,0,magic,0, SO_EARTHGRAVE,Earth Grave
+2447,9,6,2,1,0,0,5,-5,yes,0,0,0,magic,0, SO_DIAMONDDUST,Diamond Dust
+2448,9,6,1,5,0x2,1:1:1:1:2,5,1,yes,0,0,0,magic,0, SO_POISON_BUSTER,Poison Buster
+2449,9,6,2,0,0,0,5,1,yes,0,0,0,magic,0, SO_PSYCHIC_WAVE,Psychic Wave
+2450,9,6,2,5,0,0,5,1,yes,0,0,0,magic,0, SO_CLOUD_KILL,Cloud Kill
+2451,9,6,16,0,0x1,0,5,1,yes,0,0,0,none,0, SO_STRIKING,Striking //CHECK Data shows a % for increased successful refine rate. Is this true?
+2452,9,6,2,3,0x1,0,5,1,yes,0,0,0,magic,0, SO_WARMER,Warmer
+2453,9,6,2,0,0x1,0,5,1,yes,0,0,0,magic,0, SO_VACUUM_EXTREME,Vacuum Extreme
+2454,9,6,1,4,0x2,1:1:2:2:3,5,1,yes,0,0,0,magic,0, SO_VARETYR_SPEAR,Varetyr Spear
+2455,9,6,2,0,0x3,1:1:2:2:3,5,1,yes,0,0,0,magic,0, SO_ARRULLO,Arrullo
+2456,0,6,4,0,0x1,0,4,1,yes,0,0,0,none,0, SO_EL_CONTROL,Spirit Control
+2457,0,6,4,3,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_AGNI,Summon Fire Spirit Agni
+2458,0,6,4,1,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_AQUA,Summon Water Spirit Aqua
+2459,0,6,4,4,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_VENTUS,Summon Wind Spirit Ventus
+2460,0,6,4,2,0x1,0,3,1,yes,0,0,0,none,0, SO_SUMMON_TERA,Summon Earth Spirit Tera
+2461,5,6,1,0,0x1,0,1,1,no,0,0,0,none,0, SO_EL_ACTION,Elemental Action
+2462,0,6,4,0,0x1,0,2,1,yes,0,0,0,none,0, SO_EL_ANALYSIS,Four Spirit Analysis
+2463,0,0,0,0,0,0,5,0,no,0,0,0,none,0, SO_EL_SYMPATHY,Spirit Sympathy
+2464,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, SO_EL_CURE,Spirit Recovery
+2465,9,6,2,3,0x1,0,3,1,yes,0,0,1,magic,0, SO_FIRE_INSIGNIA,Fire Insignia //CHECK All 4 insignia skills can be targeted and animations work
+2466,9,6,2,1,0x1,0,3,1,yes,0,0,1,magic,0, SO_WATER_INSIGNIA,Water Insignia // but its effects havent been coded yet.
+2467,9,6,2,4,0x1,0,3,1,yes,0,0,1,magic,0, SO_WIND_INSIGNIA,Wind Insignia
+2468,9,6,2,2,0x1,0,3,1,yes,0,0,1,magic,0, SO_EARTH_INSIGNIA,Earth Insignia
+
+//****
+// GN Genetic
+2474,0,0,0,0,0,0,5,0,no,0,0,0,none,0, GN_TRAINING_SWORD,Sword Training
+2475,0,0,0,0,0,0,5,0,no,0,0,0,none,0, GN_REMODELING_CART,Cart Remodeling
+2476,0,6,4,-1,0x2,2,5,1,no,0,0,0,weapon,2, GN_CART_TORNADO,Cart Tornado
+2477,7:8:9:10:11,6,1,-1,0x2,1:1:2:2:3,5,1,yes,0,0,0,weapon,0, GN_CARTCANNON,Cart Cannon
+2478,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0, GN_CARTBOOST,Cart Boost
+2479,9,6,2,0,0,0,5,1,yes,0,0x80,5,misc,0, GN_THORNS_TRAP,Thorn Trap
+2480,11,6,1,0,0x1,0,5,1,yes,0,0,3,misc,0, GN_BLOOD_SUCKER,Blood Sucker //CHECK Data says its a magic attack. Hmmmm....
+2481,11,6,1,-1,0x2,1:2:3:4:5,5,1,yes,0,0,0,weapon,0, GN_SPORE_EXPLOSION,Spore Explosion //CHECK Data says its element is set to neutral. Need to confirm.
+2482,11,6,16,0,0,0,5,1,yes,0,0,1,weapon,2, GN_WALLOFTHORN,Wall of Thorns
+2483,11,6,2,0,0x3,4,10,1,yes,0,0x2000,0,weapon,0, GN_CRAZYWEED,Crazy Weed
+2484,0,6,2,2,0x2,3,10,1,no,0,0x2000,0,weapon,0, GN_CRAZYWEED_ATK,Crazy Weed Attack
+2485,9,6,2,3,0,0,5,1,yes,0,0,0,magic,0, GN_DEMONIC_FIRE,Demonic Fire
+2486,9,6,2,0,0,0,5,1,yes,0,0,0,none,0, GN_FIRE_EXPANSION,Fire Expansion //CHECK FIX ME!!!! Level 1 is reducing the damage. Should increase it by 50%
+2487,9,6,2,0,0,0,1,1,no,0,0,0,none,0, GN_FIRE_EXPANSION_SMOKE_POWDER,Fire Expansion Smoke Powder
+2488,9,6,2,0,0,0,1,1,no,0,0,0,none,0, GN_FIRE_EXPANSION_TEAR_GAS,Fire Expansion Tear Gas
+2489,11,6,1,0,0,0,10,1:2:3:4:5:6:7:8:9:10,no,0,0,0,weapon,0, GN_FIRE_EXPANSION_ACID,Fire Expansion Acid
+2490,9,6,2,0,0x3,1,5,1,yes,0,0x80,2:3:4:5:6,none,0, GN_HELLS_PLANT,Hell's Plant
+2491,0,6,1,0,0xC0,0,5,1,no,0,0,0,misc,0, GN_HELLS_PLANT_ATK,Hell's Plant Attack
+2492,0,6,4,0,0x3,6:7:8:9:10,5,1,yes,0,0,0,none,0, GN_MANDRAGORA,Howling of Mandragora
+2493,11,6,16,0,0x1,0,1,1,yes,0,0,0,none,0, GN_SLINGITEM,Sling Item
+2494,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, GN_CHANGEMATERIAL,Change Material
+2495,0,6,4,0,0x1,0,2,1,no,0,0,0,none,0, GN_MIX_COOKING,Mix Cooking
+2496,0,6,4,0,0x1,0,2,1,no,0,0,0,none,0, GN_MAKEBOMB,Create Bomb
+2497,0,6,4,0,0x1,0,10,1,no,0,0,0,none,0, GN_S_PHARMACY,Special Pharmacy
+2498,11,6,1,0,0,0,1,1,no,0,0,0,weapon,0, GN_SLINGITEM_RANGEMELEEATK,Sling Item Attack
+
+// Episode 13.3
+//2533,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, ALL_ODINS_RECALL,Odin's Recall
+2534,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, RETURN_TO_ELDICASTES,Return To Eldicastes
+2535,0,0,4,0,0x1,0,1,0,no,0,0x1,0,none,0, ALL_BUYING_STORE,Open Buying Store
+2536,0,0,4,0,0x1,0,1,0,no,0,0,0,none,0, ALL_GUARDIAN_RECALL,Guardian's Recall
+2537,9,6,16,0,0x1,0,2,1,yes,0,0,0,magic,0, ALL_ODINS_POWER,Odin's Power
+//2538,0,0,0,0,0,0,??,0,no,0,0,0,none,0, BEER_BOTTLE_CAP,Beer Bottle Cap
+//2539,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_ASSASSINCROSS,Assassin Cross of Sunset 2
+//2540,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_DISSONANCE,Dissonance 2
+//2541,0,0,0,0,0,0,??,0,no,0,0,0,none,0, NPC_UGLYDANCE,Ugly Dance 2
+//2542,0,0,0,0,0,0,??,0,no,0,0,0,none,0, ALL_TETANY,Tetany
+//2543,0,0,0,0,0,0,??,0,no,0,0,0,none,0, ALL_RAY_OF_PROTECTION,Ray of Protection
+//2544,0,0,0,0,0,0,??,0,no,0,0,0,none,0, MC_CARTDECORATE,Decorate Cart
+
+//****
+// Kagerou & Oboro
+3001,0,6,4,0,0,0,1,1,no,0,0,0,none,0, KO_YAMIKUMO,Yamikumo
+3002,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, KO_RIGHT,Right Hand Mastery
+3003,0,0,0,0,0,0,5,0,no,0,0,0,weapon,0, KO_LEFT,Left Hand Mastery
+3004,3:4:5:6:7,8,1,-1,0,0,5,-2,no,0,0,0,weapon,0, KO_JYUMONJIKIRI,Cross Strike
+3005,2,6,1,-1,0,0,5,1,no,0,0,0,weapon,0, KO_SETSUDAN,Soul Sever
+3006,7:8:9:10:11,6,2,0,0x2,2,5,0,no,0,0,0,weapon,0, KO_BAKURETSU,Bakuretsu Kunai
+3007,0,6,4,-1,0x42,4:4:4:4:5,5,0,no,0,0,0,misc,0, KO_HAPPOKUNAI,Happo Kunai
+3008,9,8,2,0,0x52,2,10,-10,no,0,0,0,misc,0, KO_MUCHANAGE,Mucha Nage
+3009,9:10:11:12:13,8,2,-1,0x2,3,5,2,no,0,0,0,weapon,0, KO_HUUMARANKA,Huuma Shuriken Ranka
+3010,3,6,4,0,0x43,0,5,1,no,0,0x80,0,misc,0, KO_MAKIBISHI,Makibishi
+3011,0,6,4,0,0x1,0,5,0,yes,0,0,0,none,0, KO_MEIKYOUSISUI,Meikyo Shisui
+3012,0,6,4,0,0x1,0,5,0,no,0,0,1,none,7, KO_ZANZOU,Zanzou
+3013,5,6,1,0,0x1,0,5,0,no,0,0,0,none,0, KO_KYOUGAKU,Kyougaku
+3014,5,6,1,0,0x1,0,5,0,no,0,0,0,none,0, KO_JYUSATSU,Jyusatsu
+3015,0,6,4,3,0x1,0,1,1,no,0,0,0,none,0, KO_KAHU_ENTEN,Kahu Enten
+3016,0,6,4,1,0x1,0,1,1,no,0,0,0,none,0, KO_HYOUHU_HUBUKI,Hyouhu Hubuki
+3017,0,6,4,4,0x1,0,1,1,no,0,0,0,none,0, KO_KAZEHU_SEIRAN,Kazehu Seiran
+3018,0,6,4,2,0x1,0,1,1,no,0,0,0,none,0, KO_DOHU_KOUKAI,Dohu Koukai
+3019,11,6,1,0,0,0,5,0,no,0,0,0,weapon,0, KO_KAIHOU,Technique Kaihou
+3020,7,6,2,0,0,0,1,3,yes,0,0,0,magic,0, KO_ZENKAI,Zenkai
+3021,5:6:7:8:9,6,16,0,0x1,0,5,1,no,0,0,0,none,0, KO_GENWAKU,Genwaku
+3022,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, KO_IZAYOI,Izayoi
+3023,0,6,4,0,0x3,2:3:4:5:6,5,0,no,0,0,0,none,0, KG_KAGEHUMI,Kagehumi
+3024,7,6,1,0,0x1,0,5,1,no,0,0,0,none,0, KG_KYOMU,Kyomu
+3025,7,6,16,0,0x1,0,5,1,no,0,0,0,none,0, KG_KAGEMUSYA,Kagemusha
+3026,7,6,16,0,0x1,0,5,1,no,0,0,0,none,0, OB_ZANGETSU,Zangetsu
+3027,7,6,16,0,0x1,0,5,1,no,0,0,0,none,0, OB_OBOROGENSOU,Oboro Gensou
+3028,1,6,4,0,0x2,3,1,1,no,0,0,0,weapon,0, OB_OBOROGENSOU_TRANSITION_ATK,
+3029,7,6,1,0,0x1,0,5,0,no,0,0,0,none,0, OB_AKAITSUKI,Akaitsuki
+
+8001,9,6,4,0,0x1,0,5,1,no,0,0,0,magic,0, HLIF_HEAL,Healing Touch
+8002,0,6,4,0,0x3,-1,5,1,no,0,0,0,none,0, HLIF_AVOID,Avoid
+8003,0,0,0,0,0,1,5,0,no,0,0,0,none,0, HLIF_BRAIN,Brain Surgery
+8004,0,6,4,0,0x1,0,3,0,no,0,0,0,none,0, HLIF_CHANGE,Change
+8005,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HAMI_CASTLE,Castling
+8006,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HAMI_DEFENCE,Defense
+8007,0,0,0,0,0x1,0,5,0,no,0,0,0,none,0, HAMI_SKIN,Adamantium Skin
+8008,0,6,4,0,0x1,0,3,0,no,0,0,0,none,0, HAMI_BLOODLUST,Bloodlust
+8009,1,8,1,0,0,0,5,-1:-2:-2:-2:-3,no,0,0,0,weapon,0, HFLI_MOON,Moonlight
+8010,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HFLI_FLEET,Fleeting Move
+8011,0,6,4,0,0x1,0,5,0,yes,0,0,0,misc,0, HFLI_SPEED,Speed
+8012,1,6,1,0,0,0,3,0,no,0,0,0,none,0, HFLI_SBR44,S.B.R.44
+8013,9,6,1,0,0,0,5,1:2:3:4:5,no,0,0,0,magic,0, HVAN_CAPRICE,Caprice
+8014,0,6,4,0,0x1,0,5,0,no,0,0,0,none,0, HVAN_CHAOTIC,Benediction of Chaos
+8015,0,0,0,0,0x1,0,5,0,no,0,0,0,none,0, HVAN_INSTRUCT,Instruct
+8016,4,6,4,-1,0xD2,4,3,1,no,0,0,0,misc,0, HVAN_EXPLOSION,Bio Explosion
+//
+8018,9,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_SUMMON_LEGION,Summon Legion
+8019,5,6,1,5,0,0,5,1,no,0,0,0,weapon,0, MH_NEEDLE_OF_PARALYZE,Needle of Paralyze
+8020,5,6,2,5,0,0,5,1,no,0,0,1,weapon,0, MH_POISON_MIST,Poison Mist
+8021,1,6,1,0,0x1,0,5,1,no,0,0,0,none,0, MH_PAIN_KILLER,Pain Killer
+8022,0,6,4,0,0,0x1,5,1,no,0,0,0,none,0, MH_LIGHT_OF_REGENE,Light of Regene
+8023,0,6,4,0,0,0x1,5,1,no,0,0,0,none,0, MH_OVERED_BOOST,Overed Boost
+8024,7,6,1,4:0:4:0:4,0,0,5,1,no,0,0,0,magic,0, MH_ERASER_CUTTER,Eraser Cutter
+8025,7,6,2,4:0:4:0:4,0,0,5,1,no,0,0,0,magic,0, MH_XENO_SLASHER,Xeno Slasher
+8026,5:5:7:7:9,6,16,0,0x1,0,5,1,no,0,0,0,magic,0, MH_SILENT_BREEZE,Silent Breeze
+8027,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, MH_STYLE_CHANGE,Style Change
+8028,1,8,1,0,0,0,5,1,no,0,0,0,weapon,0, MH_SONIC_CRAW,Sonic Claw
+8029,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_SILVERVEIN_RUSH,Silver Bain Rush
+8030,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_MIDNIGHT_FRENZY,Midnight Frenzy
+8031,5:6:7:8:9,6,1,0,0,0,5,1,no,0,0,0,weapon,3, MH_STAHL_HORN,Steel Horn
+8032,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_GOLDENE_FERSE,Golden Heel
+8033,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_STEINWAND,Stone Wall
+8034,9,6,1,6,0x2,1:1:1:1:2,5,1,no,0,0,0,magic,0, MH_HEILIGE_STANGE,Holy Pole
+8035,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_ANGRIFFS_MODUS,Attack Mode
+8036,3:4:5:6:7,6,1,0,0,0,5,1,no,0,0,0,weapon,0, MH_TINDER_BREAKER,Tinder Breaker
+8037,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_CBC,Continual Break Combo
+8038,1,6,4,0,0,0,5,1,no,0,0x200,0,weapon,0, MH_EQC,Eternal Quick Combo
+8039,0,6,4,3,0x2,1:1:1:2:2,5,1,no,0,0,0,weapon,0, MH_MAGMA_FLOW,Magma Flow
+8040,0,6,4,0,0x1,0,5,1,no,0,0,0,none,0, MH_GRANITIC_ARMOR,Granitic Armor
+8041,7,6,2,3,0x2,0,5,1,no,0,0,1,weapon,0, MH_LAVA_SLIDE,Lava Slide
+8042,0,6,4,3,0x1,0,5,1,no,0,0,0,none,0, MH_PYROCLASTIC,Pyroclastic
+8043,7,6,2,0,0x1,0,5,1,no,0,0,3,none,0, MH_VOLCANIC_ASH,Volcanic Ash
+
+// Mercenary Skill Place holders
+8201,-1,6,1,-1,0,0,10,1,no,0,0,0,weapon,0, MS_BASH,Bash
+8202,0,6,4,3,0x2,2,10,1,no,0,0,0,weapon,2, MS_MAGNUM,Magnum_Break
+8203,-2,6,1,-1,0x2,1,10,1,no,33,0,0,weapon,1, MS_BOWLINGBASH,Bowling_Bash
+8204,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, MS_PARRYING,Parry
+8205,0,6,4,0,0,0,10,1,no,0,0,0,weapon,0, MS_REFLECTSHIELD,Shield_Reflect
+8206,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, MS_BERSERK,Frenzy
+8207,-9,8,1,-1,0,0,10,2,no,0,0,0,weapon,0, MA_DOUBLE,Double_Strafe
+8208,-9,6,2,-1,0x2,2,10,1,no,0,0x2000,0,weapon,2, MA_SHOWER,Arrow_Shower
+8209,3,6,2,0,0x1,0,5,1,no,0,0x80,0,misc,6:7:8:9:10, MA_SKIDTRAP,Skid_Trap
+8210,3,6,2,2,0x40,0,5,1,no,0,0x80,0,misc,0, MA_LANDMINE,Land_Mine
+8211,3,6,2,0,0x3,2,5,1,no,0,0x80,0,misc,0, MA_SANDMAN,Sandman
+8212,3,6,2,1,0x42,1,5,1,no,0,0x80,0,weapon,0, MA_FREEZINGTRAP,Freezing_Trap
+8213,2,6,32,0,0x1,0,1,1,no,0,0,0,misc,0, MA_REMOVETRAP,Remove_Trap
+8214,-9,6,1,-1,0x2,0,1,1,no,0,0x1,0,weapon,6, MA_CHARGEARROW,Arrow_Repel
+8215,9,8,1,-1,0,2,5,1,yes,0,0,13,weapon,0, MA_SHARPSHOOTING,Focused_Arrow_Strike
+8216,-2,8,1,-1,0,0,10,3,no,0,0,0,weapon,0, ML_PIERCE,Pierce
+8217,-2,6,1,-1,0x1,0,10,1,no,33,0,0,weapon,3, ML_BRANDISH,Brandish_Spear
+8218,5,8,1,-1,0x20,0,5,5,no,0,0,0,weapon,0, ML_SPIRALPIERCE,Spiral_Pierce
+8219,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0, ML_DEFENDER,Defending_Aura
+8220,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, ML_AUTOGUARD,Guard
+8221,7:8:9:10:11,6,16,0,0x1,0,5,1,yes,0,0x600,0,none,0, ML_DEVOTION,Sacrifice
+8222,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0, MER_MAGNIFICAT,Magnificat
+8223,0,6,4,0,0x1,0,10,1,no,0,0,0,weapon,0, MER_QUICKEN,Two-Hand_Quicken
+8224,0,6,4,3,0x3,3,1,1,yes,0,0,0,magic,0, MER_SIGHT,Sight
+8225,1,8,1,-1,0,0,5,3,no,0,0,0,weapon,0, MER_CRASH,Crash
+8226,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_REGAIN,Regain
+8227,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_TENDER,Tender
+8228,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_BENEDICTION,Benediction
+8229,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_RECUPERATE,Recuperate
+8230,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_MENTALCURE,Mental_Cure
+8231,9,6,16,0,0x1,0,1,1,yes,0,0x1000,0,magic,0, MER_COMPRESS,Compress
+8232,9,6,1,0,0x1,0,10,1,no,0,0,0,none,0, MER_PROVOKE,Provoke
+8233,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0, MER_AUTOBERSERK,Berserk
+8234,9,6,1,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_DECAGI,Decrease_AGI
+8235,0,6,4,0,0x1,0,1,1,yes,0,0,0,none,0, MER_SCAPEGOAT,Scapegoat
+8236,5,6,1,0,0x1,0,10,0,yes,0,0,0,magic,0, MER_LEXDIVINA,Lex_Divina
+8237,9,6,1,0,0x1,0,1,1,yes,0,0,0,magic,0, MER_ESTIMATION,Sense
+8238,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_KYRIE,Kyrie Eleison
+8239,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_BLESSING,Blessing
+8240,9,6,16,0,0x1,0,10,1,yes,0,0,0,magic,0, MER_INCAGI,Increase Agility
+
+// Elemental Spirits Skills
+8401,0,6,4,3,0,0,1,1,no,0,0,0,weapon,2, EL_CIRCLE_OF_FIRE,Circle of Fire
+8402,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_FIRE_CLOAK,Fire Cloak
+8403,0,6,4,3,0,0,1,1,no,0,0,3,magic,2, EL_FIRE_MANTLE,Fire Mantle
+8404,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WATER_SCREEN,Water Screen
+8405,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WATER_DROP,Water Drop
+8406,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WATER_BARRIER,Water Barrier
+8407,0,6,4,0,0x1,0,1,1,no,0,0,0,none,5, EL_WIND_STEP,Wind Step
+8408,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WIND_CURTAIN,Wind Curtain
+8409,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_ZEPHYR,Zephyr
+8410,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_SOLID_SKIN,Solid Skin
+8411,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_STONE_SHIELD,Stone Shield
+8412,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_POWER_OF_GAIA,Power of Gaia
+8413,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_PYROTECHNIC,Pyrotechnic
+8414,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_HEATER,Heater
+8415,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_TROPIC,Tropic
+8416,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_AQUAPLAY,Aqua Play
+8417,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_COOLER,Cooler
+8418,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_CHILLY_AIR,Cool Air
+8419,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_GUST,Gust
+8420,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_BLAST,Blast
+8421,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_WILD_STORM,Wild Storm
+8422,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_PETROLOGY,Petrology
+8423,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_CURSED_SOIL,Cursed Soil
+8424,0,6,4,0,0x1,0,1,1,no,0,0,0,none,0, EL_UPHEAVAL,Upheaval
+8425,6,6,1,3,0,0,1,1,no,0,0,0,magic,0, EL_FIRE_ARROW,Fire Arrow
+8426,6,6,1,3,0,1,1,1,no,0,0,0,magic,0, EL_FIRE_BOMB,Fire Bomb
+8427,6,6,1,3,0,1,1,1,no,0,0,0,weapon,0, EL_FIRE_BOMB_ATK,Fire Bomb Attack
+8428,6,6,1,3,0,1,1,1,no,0,0,0,magic,0, EL_FIRE_WAVE,Fire Wave
+8429,6,6,1,3,0,1,1,1,no,0,0,0,weapon,0, EL_FIRE_WAVE_ATK,Fire Wave Attack
+8430,9,6,1,1,0,0,1,1,no,0,0,0,magic,0, EL_ICE_NEEDLE,Ice Needle
+8431,9,6,1,1,0,1,1,1,no,0,0,0,magic,0, EL_WATER_SCREW,Water Screw
+8432,9,6,1,1,0,1,1,1,no,0,0,0,weapon,0, EL_WATER_SCREW_ATK,Water Screw Attack
+8433,9,6,1,1,0,1,1,1,no,0,0,0,weapon,0, EL_TIDAL_WEAPON,Tidal Weapon
+8434,11,6,1,4,0,0,1,1,no,0,0,0,weapon,0, EL_WIND_SLASH,Wind Slasher
+8435,11,6,1,4,0,1,1,1,no,0,0,0,weapon,0, EL_HURRICANE,Hurricane Rage
+8436,7,6,1,4,0,0,1,1,no,0,0,0,magic,0, EL_HURRICANE_ATK,Hurricane Rage Attack
+8437,11,8,1,4,0,1,1,-3,no,0,0,0,weapon,0, EL_TYPOON_MIS,Typhoon Missile
+8438,11,8,1,4,0,1,1,-3,no,0,0,0,magic,0, EL_TYPOON_MIS_ATK,Typhoon Missile Attack
+8439,5,6,1,2,0,0,1,1,no,0,0,0,weapon,0, EL_STONE_HAMMER,Stone Hammer
+8440,3,6,1,2,0,1,1,1,no,0,0,0,weapon,0, EL_ROCK_CRUSHER,Rock Launcher
+8441,5,6,1,2,0,1,1,1,no,0,0,0,magic,0, EL_ROCK_CRUSHER_ATK,Rock Launcher Attack
+8442,9,6,1,2,0,1,1,-5,no,0,0,0,weapon,0, EL_STONE_RAIN,Stone Rain
+
+10000,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_APPROVAL,Official Guild Approval
+10001,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_KAFRACONTRACT,Kafra Contract
+10002,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_GUARDRESEARCH,Guardian Research
+10003,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_GUARDUP,Strengthen Guardians
+10004,0,0,0,0,0,0,10,0,no,0,0x10,0,none,0, GD_EXTENSION,Guild Extension
+10005,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_GLORYGUILD,Guild's Glory
+10006,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_LEADERSHIP,Great Leadership
+10007,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_GLORYWOUNDS,Glorious Wounds
+10008,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_SOULCOLD,Cold Heart
+10009,0,0,0,0,0,2,1,0,no,0,0x10,0,none,0, GD_HAWKEYES,Sharp Gaze
+10010,0,0,4,0,0x3,15,1,0,yes,0,0x10,0,none,0, GD_BATTLEORDER,Battle Orders
+10011,0,0,4,0,0x3,15,3,0,yes,0,0x10,0,none,0, GD_REGENERATION,Regeneration
+10012,0,0,4,0,0x3,15,1,0,yes,0,0x10,0,none,0, GD_RESTORE,Restoration
+10013,0,0,4,0,0x3,0,1,0,yes,0,0x10,0,none,0, GD_EMERGENCYCALL,Urgent Call
+10014,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_DEVELOPMENT,Permanent Development
+//10015,0,0,0,0,0,0,1,0,no,0,0x10,0,none,0, GD_ITEMEMERGENCYCALL,Unknown Skill
diff --git a/src/map/clif.c b/src/map/clif.c
index 06c74a5f8..c314a6f33 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -1,17128 +1,17128 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include "../common/cbasetypes.h"
-#include "../common/socket.h"
-#include "../common/timer.h"
-#include "../common/grfio.h"
-#include "../common/malloc.h"
-#include "../common/nullpo.h"
-#include "../common/random.h"
-#include "../common/showmsg.h"
-#include "../common/strlib.h"
-#include "../common/utils.h"
-#include "../common/ers.h"
-
-#include "map.h"
-#include "chrif.h"
-#include "pc.h"
-#include "status.h"
-#include "npc.h"
-#include "itemdb.h"
-#include "chat.h"
-#include "trade.h"
-#include "storage.h"
-#include "script.h"
-#include "skill.h"
-#include "atcommand.h"
-#include "intif.h"
-#include "battle.h"
-#include "battleground.h"
-#include "mob.h"
-#include "party.h"
-#include "unit.h"
-#include "guild.h"
-#include "vending.h"
-#include "pet.h"
-#include "homunculus.h"
-#include "instance.h"
-#include "mercenary.h"
-#include "elemental.h"
-#include "log.h"
-#include "clif.h"
-#include "mail.h"
-#include "quest.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <time.h>
-
-/* for clif_clearunit_delayed */
-static struct eri *delay_clearunit_ers;
-
-//#define DUMP_UNKNOWN_PACKET
-//#define DUMP_INVALID_PACKET
-
-struct Clif_Config {
- int packet_db_ver; //Preferred packet version.
- int connect_cmd[MAX_PACKET_VER + 1]; //Store the connect command for all versions. [Skotlex]
-} clif_config;
-
-struct s_packet_db packet_db[MAX_PACKET_VER + 1][MAX_PACKET_DB + 1];
-
-//Converts item type in case of pet eggs.
-static inline int itemtype(int type)
-{
- return ( type == IT_PETEGG ) ? IT_WEAPON : type;
-}
-
-
-static inline void WBUFPOS(uint8* p, unsigned short pos, short x, short y, unsigned char dir)
-{
- p += pos;
- p[0] = (uint8)(x>>2);
- p[1] = (uint8)((x<<6) | ((y>>4)&0x3f));
- p[2] = (uint8)((y<<4) | (dir&0xf));
-}
-
-
-// client-side: x0+=sx0*0.0625-0.5 and y0+=sy0*0.0625-0.5
-static inline void WBUFPOS2(uint8* p, unsigned short pos, short x0, short y0, short x1, short y1, unsigned char sx0, unsigned char sy0)
-{
- p += pos;
- p[0] = (uint8)(x0>>2);
- p[1] = (uint8)((x0<<6) | ((y0>>4)&0x3f));
- p[2] = (uint8)((y0<<4) | ((x1>>6)&0x0f));
- p[3] = (uint8)((x1<<2) | ((y1>>8)&0x03));
- p[4] = (uint8)y1;
- p[5] = (uint8)((sx0<<4) | (sy0&0x0f));
-}
-
-
-static inline void WFIFOPOS(int fd, unsigned short pos, short x, short y, unsigned char dir)
-{
- WBUFPOS(WFIFOP(fd,pos), 0, x, y, dir);
-}
-
-
-static inline void WFIFOPOS2(int fd, unsigned short pos, short x0, short y0, short x1, short y1, unsigned char sx0, unsigned char sy0)
-{
- WBUFPOS2(WFIFOP(fd,pos), 0, x0, y0, x1, y1, sx0, sy0);
-}
-
-
-static inline void RBUFPOS(const uint8* p, unsigned short pos, short* x, short* y, unsigned char* dir)
-{
- p += pos;
-
- if( x ) {
- x[0] = ( ( p[0] & 0xff ) << 2 ) | ( p[1] >> 6 );
- }
-
- if( y ) {
- y[0] = ( ( p[1] & 0x3f ) << 4 ) | ( p[2] >> 4 );
- }
-
- if( dir ) {
- dir[0] = ( p[2] & 0x0f );
- }
-}
-
-
-static inline void RBUFPOS2(const uint8* p, unsigned short pos, short* x0, short* y0, short* x1, short* y1, unsigned char* sx0, unsigned char* sy0)
-{
- p += pos;
-
- if( x0 ) {
- x0[0] = ( ( p[0] & 0xff ) << 2 ) | ( p[1] >> 6 );
- }
-
- if( y0 ) {
- y0[0] = ( ( p[1] & 0x3f ) << 4 ) | ( p[2] >> 4 );
- }
-
- if( x1 ) {
- x1[0] = ( ( p[2] & 0x0f ) << 6 ) | ( p[3] >> 2 );
- }
-
- if( y1 ) {
- y1[0] = ( ( p[3] & 0x03 ) << 8 ) | ( p[4] >> 0 );
- }
-
- if( sx0 ) {
- sx0[0] = ( p[5] & 0xf0 ) >> 4;
- }
-
- if( sy0 ) {
- sy0[0] = ( p[5] & 0x0f ) >> 0;
- }
-}
-
-
-static inline void RFIFOPOS(int fd, unsigned short pos, short* x, short* y, unsigned char* dir)
-{
- RBUFPOS(RFIFOP(fd,pos), 0, x, y, dir);
-}
-
-
-static inline void RFIFOPOS2(int fd, unsigned short pos, short* x0, short* y0, short* x1, short* y1, unsigned char* sx0, unsigned char* sy0)
-{
- RBUFPOS2(WFIFOP(fd,pos), 0, x0, y0, x1, y1, sx0, sy0);
-}
-
-
-//To idenfity disguised characters.
-static inline bool disguised(struct block_list* bl)
-{
- return (bool)( bl->type == BL_PC && ((TBL_PC*)bl)->disguise );
-}
-
-
-//Guarantees that the given string does not exceeds the allowed size, as well as making sure it's null terminated. [Skotlex]
-static inline unsigned int mes_len_check(char* mes, unsigned int len, unsigned int max)
-{
- if( len > max )
- len = max;
-
- mes[len-1] = '\0';
-
- return len;
-}
-
-
-static char map_ip_str[128];
-static uint32 map_ip;
-static uint32 bind_ip = INADDR_ANY;
-static uint16 map_port = 5121;
-int map_fd;
-
-static int clif_parse (int fd);
-
-/*==========================================
- * Ip setting of map-server
- *------------------------------------------*/
-int clif_setip(const char* ip)
-{
- char ip_str[16];
- map_ip = host2ip(ip);
- if (!map_ip) {
- ShowWarning("Failed to Resolve Map Server Address! (%s)\n", ip);
- return 0;
- }
-
- strncpy(map_ip_str, ip, sizeof(map_ip_str));
- ShowInfo("Map Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%s"CL_RESET"'.\n", ip, ip2str(map_ip, ip_str));
- return 1;
-}
-
-void clif_setbindip(const char* ip)
-{
- char ip_str[16];
- bind_ip = host2ip(ip);
- if (bind_ip) {
- ShowInfo("Map Server Bind IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%s"CL_RESET"'.\n", ip, ip2str(bind_ip, ip_str));
- } else {
- ShowWarning("Failed to Resolve Map Server Address! (%s)\n", ip);
- }
-}
-
-/*==========================================
- * Sets map port to 'port'
- * is run from map.c upon loading map server configuration
- *------------------------------------------*/
-void clif_setport(uint16 port)
-{
- map_port = port;
-}
-
-/*==========================================
- * Returns map server IP
- *------------------------------------------*/
-uint32 clif_getip(void)
-{
- return map_ip;
-}
-
-//Refreshes map_server ip, returns the new ip if the ip changed, otherwise it returns 0.
-uint32 clif_refresh_ip(void)
-{
- uint32 new_ip;
-
- new_ip = host2ip(map_ip_str);
- if (new_ip && new_ip != map_ip) {
- map_ip = new_ip;
- ShowInfo("Updating IP resolution of [%s].\n", map_ip_str);
- return map_ip;
- }
- return 0;
-}
-
-/*==========================================
- * Returns map port which is set by clif_setport()
- *------------------------------------------*/
-uint16 clif_getport(void)
-{
- return map_port;
-}
-
-#if PACKETVER >= 20071106
-static inline unsigned char clif_bl_type(struct block_list *bl) {
- switch (bl->type) {
- case BL_PC: return disguised(bl)?0x1:0x0; //PC_TYPE
- case BL_ITEM: return 0x2; //ITEM_TYPE
- case BL_SKILL: return 0x3; //SKILL_TYPE
- case BL_CHAT: return 0x4; //UNKNOWN_TYPE
- case BL_MOB: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x5; //NPC_MOB_TYPE
- case BL_NPC: return 0x6; //NPC_EVT_TYPE
- case BL_PET: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x7; //NPC_PET_TYPE
- case BL_HOM: return 0x8; //NPC_HOM_TYPE
- case BL_MER: return 0x9; //NPC_MERSOL_TYPE
- case BL_ELEM: return 0xa; //NPC_ELEMENTAL_TYPE
- default: return 0x1; //NPC_TYPE
- }
-}
-#endif
-
-/*==========================================
- * sub process of clif_send
- * Called from a map_foreachinarea (grabs all players in specific area and subjects them to this function)
- * In order to send area-wise packets, such as:
- * - AREA : everyone nearby your area
- * - AREA_WOSC (AREA WITHOUT SAME CHAT) : Not run for people in the same chat as yours
- * - AREA_WOC (AREA WITHOUT CHAT) : Not run for people inside a chat
- * - AREA_WOS (AREA WITHOUT SELF) : Not run for self
- * - AREA_CHAT_WOC : Everyone in the area of your chat without a chat
- *------------------------------------------*/
-static int clif_send_sub(struct block_list *bl, va_list ap)
-{
- struct block_list *src_bl;
- struct map_session_data *sd;
- unsigned char *buf;
- int len, type, fd;
-
- nullpo_ret(bl);
- nullpo_ret(sd = (struct map_session_data *)bl);
-
- fd = sd->fd;
- if (!fd) //Don't send to disconnected clients.
- return 0;
-
- buf = va_arg(ap,unsigned char*);
- len = va_arg(ap,int);
- nullpo_ret(src_bl = va_arg(ap,struct block_list*));
- type = va_arg(ap,int);
-
- switch(type)
- {
- case AREA_WOS:
- if (bl == src_bl)
- return 0;
- break;
- case AREA_WOC:
- if (sd->chatID || bl == src_bl)
- return 0;
- break;
- case AREA_WOSC:
- {
- if(src_bl->type == BL_PC){
- struct map_session_data *ssd = (struct map_session_data *)src_bl;
- if (ssd && sd->chatID && (sd->chatID == ssd->chatID))
- return 0;
- }
- else if(src_bl->type == BL_NPC) {
- struct npc_data *nd = (struct npc_data *)src_bl;
- if (nd && sd->chatID && (sd->chatID == nd->chat_id))
- return 0;
- }
- }
- break;
- }
-
- if (session[fd] == NULL)
- return 0;
-
- WFIFOHEAD(fd, len);
- if (WFIFOP(fd,0) == buf) {
- ShowError("WARNING: Invalid use of clif_send function\n");
- ShowError(" Packet x%4x use a WFIFO of a player instead of to use a buffer.\n", WBUFW(buf,0));
- ShowError(" Please correct your code.\n");
- // don't send to not move the pointer of the packet for next sessions in the loop
- //WFIFOSET(fd,0);//## TODO is this ok?
- //NO. It is not ok. There is the chance WFIFOSET actually sends the buffer data, and shifts elements around, which will corrupt the buffer.
- return 0;
- }
-
- if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
- memcpy(WFIFOP(fd,0), buf, len);
- WFIFOSET(fd,len);
- }
-
- return 0;
-}
-
-/*==========================================
- * Packet Delegation (called on all packets that require data to be sent to more than one client)
- * functions that are sent solely to one use whose ID it posses use WFIFOSET
- *------------------------------------------*/
-int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target type)
-{
- int i;
- struct map_session_data *sd, *tsd;
- struct party_data *p = NULL;
- struct guild *g = NULL;
- struct battleground_data *bg = NULL;
- int x0 = 0, x1 = 0, y0 = 0, y1 = 0, fd;
- struct s_mapiterator* iter;
-
- if( type != ALL_CLIENT && type != CHAT_MAINCHAT )
- nullpo_ret(bl);
-
- sd = BL_CAST(BL_PC, bl);
-
- switch(type) {
-
- case ALL_CLIENT: //All player clients.
- iter = mapit_getallusers();
- while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
- {
- if( packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
- { // packet must exist for the client version
- WFIFOHEAD(tsd->fd, len);
- memcpy(WFIFOP(tsd->fd,0), buf, len);
- WFIFOSET(tsd->fd,len);
- }
- }
- mapit_free(iter);
- break;
-
- case ALL_SAMEMAP: //All players on the same map
- iter = mapit_getallusers();
- while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
- {
- if( bl->m == tsd->bl.m && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
- { // packet must exist for the client version
- WFIFOHEAD(tsd->fd, len);
- memcpy(WFIFOP(tsd->fd,0), buf, len);
- WFIFOSET(tsd->fd,len);
- }
- }
- mapit_free(iter);
- break;
-
- case AREA:
- case AREA_WOSC:
- if (sd && bl->prev == NULL) //Otherwise source misses the packet.[Skotlex]
- clif_send (buf, len, bl, SELF);
- case AREA_WOC:
- case AREA_WOS:
- map_foreachinarea(clif_send_sub, bl->m, bl->x-AREA_SIZE, bl->y-AREA_SIZE, bl->x+AREA_SIZE, bl->y+AREA_SIZE,
- BL_PC, buf, len, bl, type);
- break;
- case AREA_CHAT_WOC:
- map_foreachinarea(clif_send_sub, bl->m, bl->x-(AREA_SIZE-5), bl->y-(AREA_SIZE-5),
- bl->x+(AREA_SIZE-5), bl->y+(AREA_SIZE-5), BL_PC, buf, len, bl, AREA_WOC);
- break;
-
- case CHAT:
- case CHAT_WOS:
- {
- struct chat_data *cd;
- if (sd) {
- cd = (struct chat_data*)map_id2bl(sd->chatID);
- } else if (bl->type == BL_CHAT) {
- cd = (struct chat_data*)bl;
- } else break;
- if (cd == NULL)
- break;
- for(i = 0; i < cd->users; i++) {
- if (type == CHAT_WOS && cd->usersd[i] == sd)
- continue;
- if (packet_db[cd->usersd[i]->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
- if ((fd=cd->usersd[i]->fd) >0 && session[fd]) // Added check to see if session exists [PoW]
- {
- WFIFOHEAD(fd,len);
- memcpy(WFIFOP(fd,0), buf, len);
- WFIFOSET(fd,len);
- }
- }
- }
- }
- break;
-
- case CHAT_MAINCHAT: //[LuzZza]
- iter = mapit_getallusers();
- while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
- {
- if( tsd->state.mainchat && tsd->chatID == 0 && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
- { // packet must exist for the client version
- WFIFOHEAD(tsd->fd, len);
- memcpy(WFIFOP(tsd->fd,0), buf, len);
- WFIFOSET(tsd->fd,len);
- }
- }
- mapit_free(iter);
- break;
-
- case PARTY_AREA:
- case PARTY_AREA_WOS:
- x0 = bl->x - AREA_SIZE;
- y0 = bl->y - AREA_SIZE;
- x1 = bl->x + AREA_SIZE;
- y1 = bl->y + AREA_SIZE;
- case PARTY:
- case PARTY_WOS:
- case PARTY_SAMEMAP:
- case PARTY_SAMEMAP_WOS:
- if (sd && sd->status.party_id)
- p = party_search(sd->status.party_id);
-
- if (p) {
- for(i=0;i<MAX_PARTY;i++){
- if( (sd = p->data[i].sd) == NULL )
- continue;
-
- if( !(fd=sd->fd) )
- continue;
-
- if( sd->bl.id == bl->id && (type == PARTY_WOS || type == PARTY_SAMEMAP_WOS || type == PARTY_AREA_WOS) )
- continue;
-
- if( type != PARTY && type != PARTY_WOS && bl->m != sd->bl.m )
- continue;
-
- if( (type == PARTY_AREA || type == PARTY_AREA_WOS) && (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1) )
- continue;
-
- if( packet_db[sd->packet_ver][RBUFW(buf,0)].len )
- { // packet must exist for the client version
- WFIFOHEAD(fd,len);
- memcpy(WFIFOP(fd,0), buf, len);
- WFIFOSET(fd,len);
- }
- }
- if (!enable_spy) //Skip unnecessary parsing. [Skotlex]
- break;
-
- iter = mapit_getallusers();
- while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
- {
- if( tsd->partyspy == p->party.party_id && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
- { // packet must exist for the client version
- WFIFOHEAD(tsd->fd, len);
- memcpy(WFIFOP(tsd->fd,0), buf, len);
- WFIFOSET(tsd->fd,len);
- }
- }
- mapit_free(iter);
- }
- break;
-
- case DUEL:
- case DUEL_WOS:
- if (!sd || !sd->duel_group) break; //Invalid usage.
-
- iter = mapit_getallusers();
- while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
- {
- if( type == DUEL_WOS && bl->id == tsd->bl.id )
- continue;
- if( sd->duel_group == tsd->duel_group && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
- { // packet must exist for the client version
- WFIFOHEAD(tsd->fd, len);
- memcpy(WFIFOP(tsd->fd,0), buf, len);
- WFIFOSET(tsd->fd,len);
- }
- }
- mapit_free(iter);
- break;
-
- case SELF:
- if (sd && (fd=sd->fd) && packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
- WFIFOHEAD(fd,len);
- memcpy(WFIFOP(fd,0), buf, len);
- WFIFOSET(fd,len);
- }
- break;
-
- // New definitions for guilds [Valaris] - Cleaned up and reorganized by [Skotlex]
- case GUILD_AREA:
- case GUILD_AREA_WOS:
- x0 = bl->x - AREA_SIZE;
- y0 = bl->y - AREA_SIZE;
- x1 = bl->x + AREA_SIZE;
- y1 = bl->y + AREA_SIZE;
- case GUILD_SAMEMAP:
- case GUILD_SAMEMAP_WOS:
- case GUILD:
- case GUILD_WOS:
- case GUILD_NOBG:
- if (sd && sd->status.guild_id)
- g = guild_search(sd->status.guild_id);
-
- if (g) {
- for(i = 0; i < g->max_member; i++) {
- if( (sd = g->member[i].sd) != NULL )
- {
- if( !(fd=sd->fd) )
- continue;
-
- if( type == GUILD_NOBG && sd->bg_id )
- continue;
-
- if( sd->bl.id == bl->id && (type == GUILD_WOS || type == GUILD_SAMEMAP_WOS || type == GUILD_AREA_WOS) )
- continue;
-
- if( type != GUILD && type != GUILD_NOBG && type != GUILD_WOS && sd->bl.m != bl->m )
- continue;
-
- if( (type == GUILD_AREA || type == GUILD_AREA_WOS) && (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1) )
- continue;
-
- if( packet_db[sd->packet_ver][RBUFW(buf,0)].len )
- { // packet must exist for the client version
- WFIFOHEAD(fd,len);
- memcpy(WFIFOP(fd,0), buf, len);
- WFIFOSET(fd,len);
- }
- }
- }
- if (!enable_spy) //Skip unnecessary parsing. [Skotlex]
- break;
-
- iter = mapit_getallusers();
- while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
- {
- if( tsd->guildspy == g->guild_id && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
- { // packet must exist for the client version
- WFIFOHEAD(tsd->fd, len);
- memcpy(WFIFOP(tsd->fd,0), buf, len);
- WFIFOSET(tsd->fd,len);
- }
- }
- mapit_free(iter);
- }
- break;
-
- case BG_AREA:
- case BG_AREA_WOS:
- x0 = bl->x - AREA_SIZE;
- y0 = bl->y - AREA_SIZE;
- x1 = bl->x + AREA_SIZE;
- y1 = bl->y + AREA_SIZE;
- case BG_SAMEMAP:
- case BG_SAMEMAP_WOS:
- case BG:
- case BG_WOS:
- if( sd && sd->bg_id && (bg = bg_team_search(sd->bg_id)) != NULL )
- {
- for( i = 0; i < MAX_BG_MEMBERS; i++ )
- {
- if( (sd = bg->members[i].sd) == NULL || !(fd = sd->fd) )
- continue;
- if( sd->bl.id == bl->id && (type == BG_WOS || type == BG_SAMEMAP_WOS || type == BG_AREA_WOS) )
- continue;
- if( type != BG && type != BG_WOS && sd->bl.m != bl->m )
- continue;
- if( (type == BG_AREA || type == BG_AREA_WOS) && (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1) )
- continue;
- if( packet_db[sd->packet_ver][RBUFW(buf,0)].len )
- { // packet must exist for the client version
- WFIFOHEAD(fd,len);
- memcpy(WFIFOP(fd,0), buf, len);
- WFIFOSET(fd,len);
- }
- }
- }
- break;
-
- default:
- ShowError("clif_send: Unrecognized type %d\n",type);
- return -1;
- }
-
- return 0;
-}
-
-
-/// Notifies the client, that it's connection attempt was accepted.
-/// 0073 <start time>.L <position>.3B <x size>.B <y size>.B (ZC_ACCEPT_ENTER)
-/// 02eb <start time>.L <position>.3B <x size>.B <y size>.B <font>.W (ZC_ACCEPT_ENTER2)
-void clif_authok(struct map_session_data *sd)
-{
-#if PACKETVER < 20080102
- const int cmd = 0x73;
-#else
- const int cmd = 0x2eb;
-#endif
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(cmd));
- WFIFOW(fd, 0) = cmd;
- WFIFOL(fd, 2) = gettick();
- WFIFOPOS(fd, 6, sd->bl.x, sd->bl.y, sd->ud.dir);
- WFIFOB(fd, 9) = 5; // ignored
- WFIFOB(fd,10) = 5; // ignored
-#if PACKETVER >= 20080102
- WFIFOW(fd,11) = sd->user_font; // FIXME: Font is currently not saved.
-#endif
- WFIFOSET(fd,packet_len(cmd));
-}
-
-
-/// Notifies the client, that it's connection attempt was refused (ZC_REFUSE_ENTER).
-/// 0074 <error code>.B
-/// error code:
-/// 0 = client type mismatch
-/// 1 = ID mismatch
-/// 2 = mobile - out of available time
-/// 3 = mobile - already logged in
-/// 4 = mobile - waiting state
-void clif_authrefuse(int fd, uint8 error_code)
-{
- WFIFOHEAD(fd,packet_len(0x74));
- WFIFOW(fd,0) = 0x74;
- WFIFOB(fd,2) = error_code;
- WFIFOSET(fd,packet_len(0x74));
-}
-
-
-/// Notifies the client of a ban or forced disconnect (SC_NOTIFY_BAN).
-/// 0081 <error code>.B
-/// error code:
-/// 0 = BAN_UNFAIR
-/// 1 = server closed -> MsgStringTable[4]
-/// 2 = ID already logged in -> MsgStringTable[5]
-/// 3 = timeout/too much lag -> MsgStringTable[241]
-/// 4 = server full -> MsgStringTable[264]
-/// 5 = underaged -> MsgStringTable[305]
-/// 8 = Server sill recognizes last connection -> MsgStringTable[441]
-/// 9 = too many connections from this ip -> MsgStringTable[529]
-/// 10 = out of available time paid for -> MsgStringTable[530]
-/// 11 = BAN_PAY_SUSPEND
-/// 12 = BAN_PAY_CHANGE
-/// 13 = BAN_PAY_WRONGIP
-/// 14 = BAN_PAY_PNGAMEROOM
-/// 15 = disconnected by a GM -> if( servicetype == taiwan ) MsgStringTable[579]
-/// 16 = BAN_JAPAN_REFUSE1
-/// 17 = BAN_JAPAN_REFUSE2
-/// 18 = BAN_INFORMATION_REMAINED_ANOTHER_ACCOUNT
-/// 100 = BAN_PC_IP_UNFAIR
-/// 101 = BAN_PC_IP_COUNT_ALL
-/// 102 = BAN_PC_IP_COUNT
-/// 103 = BAN_GRAVITY_MEM_AGREE
-/// 104 = BAN_GAME_MEM_AGREE
-/// 105 = BAN_HAN_VALID
-/// 106 = BAN_PC_IP_LIMIT_ACCESS
-/// 107 = BAN_OVER_CHARACTER_LIST
-/// 108 = BAN_IP_BLOCK
-/// 109 = BAN_INVALID_PWD_CNT
-/// 110 = BAN_NOT_ALLOWED_JOBCLASS
-/// ? = disconnected -> MsgStringTable[3]
-void clif_authfail_fd(int fd, int type)
-{
- if (!fd || !session[fd] || session[fd]->func_parse != clif_parse) //clif_authfail should only be invoked on players!
- return;
-
- WFIFOHEAD(fd, packet_len(0x81));
- WFIFOW(fd,0) = 0x81;
- WFIFOB(fd,2) = type;
- WFIFOSET(fd,packet_len(0x81));
- set_eof(fd);
-}
-
-
-/// Notifies the client, whether it can disconnect and change servers (ZC_RESTART_ACK).
-/// 00b3 <type>.B
-/// type:
-/// 1 = disconnect, char-select
-/// ? = nothing
-void clif_charselectok(int id, uint8 ok)
-{
- struct map_session_data* sd;
- int fd;
-
- if ((sd = map_id2sd(id)) == NULL || !sd->fd)
- return;
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0xb3));
- WFIFOW(fd,0) = 0xb3;
- WFIFOB(fd,2) = ok;
- WFIFOSET(fd,packet_len(0xb3));
-}
-
-/// Makes an item appear on the ground.
-/// 009e <id>.L <name id>.W <identified>.B <x>.W <y>.W <subX>.B <subY>.B <amount>.W (ZC_ITEM_FALL_ENTRY)
-/// 084b (ZC_ITEM_FALL_ENTRY4)
-void clif_dropflooritem(struct flooritem_data* fitem)
-{
- uint8 buf[17];
- int view;
-
- nullpo_retv(fitem);
-
- if (fitem->item_data.nameid <= 0)
- return;
-
- WBUFW(buf, 0) = 0x9e;
- WBUFL(buf, 2) = fitem->bl.id;
- WBUFW(buf, 6) = ((view = itemdb_viewid(fitem->item_data.nameid)) > 0) ? view : fitem->item_data.nameid;
- WBUFB(buf, 8) = fitem->item_data.identify;
- WBUFW(buf, 9) = fitem->bl.x;
- WBUFW(buf,11) = fitem->bl.y;
- WBUFB(buf,13) = fitem->subx;
- WBUFB(buf,14) = fitem->suby;
- WBUFW(buf,15) = fitem->item_data.amount;
-
- clif_send(buf, packet_len(0x9e), &fitem->bl, AREA);
-}
-
-
-
-/// Makes an item disappear from the ground.
-/// 00a1 <id>.L (ZC_ITEM_DISAPPEAR)
-void clif_clearflooritem(struct flooritem_data *fitem, int fd)
-{
- unsigned char buf[16];
-
- nullpo_retv(fitem);
-
- WBUFW(buf,0) = 0xa1;
- WBUFL(buf,2) = fitem->bl.id;
-
- if (fd == 0) {
- clif_send(buf, packet_len(0xa1), &fitem->bl, AREA);
- } else {
- WFIFOHEAD(fd,packet_len(0xa1));
- memcpy(WFIFOP(fd,0), buf, packet_len(0xa1));
- WFIFOSET(fd,packet_len(0xa1));
- }
-}
-
-
-/// Makes a unit (char, npc, mob, homun) disappear to one client (ZC_NOTIFY_VANISH).
-/// 0080 <id>.L <type>.B
-/// type:
-/// 0 = out of sight
-/// 1 = died
-/// 2 = logged out
-/// 3 = teleport
-/// 4 = trickdead
-void clif_clearunit_single(int id, clr_type type, int fd)
-{
- WFIFOHEAD(fd, packet_len(0x80));
- WFIFOW(fd,0) = 0x80;
- WFIFOL(fd,2) = id;
- WFIFOB(fd,6) = type;
- WFIFOSET(fd, packet_len(0x80));
-}
-
-/// Makes a unit (char, npc, mob, homun) disappear to all clients in area (ZC_NOTIFY_VANISH).
-/// 0080 <id>.L <type>.B
-/// type:
-/// 0 = out of sight
-/// 1 = died
-/// 2 = logged out
-/// 3 = teleport
-/// 4 = trickdead
-void clif_clearunit_area(struct block_list* bl, clr_type type)
-{
- unsigned char buf[8];
-
- nullpo_retv(bl);
-
- WBUFW(buf,0) = 0x80;
- WBUFL(buf,2) = bl->id;
- WBUFB(buf,6) = type;
-
- clif_send(buf, packet_len(0x80), bl, type == CLR_DEAD ? AREA : AREA_WOS);
-
- if(disguised(bl)) {
- WBUFL(buf,2) = -bl->id;
- clif_send(buf, packet_len(0x80), bl, SELF);
- }
-}
-
-
-/// Used to make monsters with player-sprites disappear after dying
-/// like normal monsters, because the client does not remove those
-/// automatically.
-static int clif_clearunit_delayed_sub(int tid, unsigned int tick, int id, intptr_t data)
-{
- struct block_list *bl = (struct block_list *)data;
- clif_clearunit_area(bl, (clr_type) id);
- ers_free(delay_clearunit_ers,bl);
- return 0;
-}
-void clif_clearunit_delayed(struct block_list* bl, clr_type type, unsigned int tick)
-{
- struct block_list *tbl = ers_alloc(delay_clearunit_ers, struct block_list);
- memcpy (tbl, bl, sizeof (struct block_list));
- add_timer(tick, clif_clearunit_delayed_sub, (int)type, (intptr_t)tbl);
-}
-
-void clif_get_weapon_view(struct map_session_data* sd, unsigned short *rhand, unsigned short *lhand)
-{
- if(sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER))
- {
- *rhand = *lhand = 0;
- return;
- }
-
-#if PACKETVER < 4
- *rhand = sd->status.weapon;
- *lhand = sd->status.shield;
-#else
- if (sd->equip_index[EQI_HAND_R] >= 0 &&
- sd->inventory_data[sd->equip_index[EQI_HAND_R]])
- {
- struct item_data* id = sd->inventory_data[sd->equip_index[EQI_HAND_R]];
- if (id->view_id > 0)
- *rhand = id->view_id;
- else
- *rhand = id->nameid;
- } else
- *rhand = 0;
-
- if (sd->equip_index[EQI_HAND_L] >= 0 &&
- sd->equip_index[EQI_HAND_L] != sd->equip_index[EQI_HAND_R] &&
- sd->inventory_data[sd->equip_index[EQI_HAND_L]])
- {
- struct item_data* id = sd->inventory_data[sd->equip_index[EQI_HAND_L]];
- if (id->view_id > 0)
- *lhand = id->view_id;
- else
- *lhand = id->nameid;
- } else
- *lhand = 0;
-#endif
-}
-
-//To make the assignation of the level based on limits clearer/easier. [Skotlex]
-static int clif_setlevel_sub(int lv)
-{
- if( lv < battle_config.max_lv )
- {
- ;
- }
- else if( lv < battle_config.aura_lv )
- {
- lv = battle_config.max_lv - 1;
- }
- else
- {
- lv = battle_config.max_lv;
- }
-
- return lv;
-}
-
-static int clif_setlevel(struct block_list* bl)
-{
- int lv = status_get_lv(bl);
- if( battle_config.client_limit_unit_lv&bl->type )
- return clif_setlevel_sub(lv);
- switch( bl->type )
- {
- case BL_NPC:
- case BL_PET:
- // npcs and pets do not have level
- return 0;
- }
- return lv;
-}
-
-/*==========================================
- * Prepares 'unit standing/spawning' packet
- *------------------------------------------*/
-static int clif_set_unit_idle(struct block_list* bl, unsigned char* buffer, bool spawn)
-{
- struct map_session_data* sd;
- struct status_change* sc = status_get_sc(bl);
- struct view_data* vd = status_get_viewdata(bl);
- unsigned char *buf = WBUFP(buffer,0);
-#if PACKETVER < 20091103
- bool type = !pcdb_checkid(vd->class_);
-#endif
- unsigned short offset = 0;
-#if PACKETVER >= 20091103
- const char *name;
-#endif
- sd = BL_CAST(BL_PC, bl);
-
-#if PACKETVER < 20091103
- if(type)
- WBUFW(buf,0) = spawn?0x7c:0x78;
- else
-#endif
-#if PACKETVER < 4
- WBUFW(buf,0) = spawn?0x79:0x78;
-#elif PACKETVER < 7
- WBUFW(buf,0) = spawn?0x1d9:0x1d8;
-#elif PACKETVER < 20080102
- WBUFW(buf,0) = spawn?0x22b:0x22a;
-#elif PACKETVER < 20091103
- WBUFW(buf,0) = spawn?0x2ed:0x2ee;
-#elif PACKETVER < 20101124
- WBUFW(buf,0) = spawn?0x7f8:0x7f9;
-#else
- WBUFW(buf,0) = spawn?0x858:0x857;
-#endif
-
-#if PACKETVER >= 20091103
- name = status_get_name(bl);
-#if PACKETVER < 20110111
- WBUFW(buf,2) = (spawn?62:63)+strlen(name);
-#else
- WBUFW(buf,2) = (spawn?64:65)+strlen(name);
-#endif
- WBUFB(buf,4) = clif_bl_type(bl);
- offset+=3;
- buf = WBUFP(buffer,offset);
-#elif PACKETVER >= 20071106
- if (type) { //Non-player packets
- WBUFB(buf,2) = clif_bl_type(bl);
- offset++;
- buf = WBUFP(buffer,offset);
- }
-#endif
- WBUFL(buf, 2) = bl->id;
- WBUFW(buf, 6) = status_get_speed(bl);
- WBUFW(buf, 8) = (sc)? sc->opt1 : 0;
- WBUFW(buf,10) = (sc)? sc->opt2 : 0;
-#if PACKETVER < 20091103
- if (type&&spawn) { //uses an older and different packet structure
- WBUFW(buf,12) = (sc)? sc->option : 0;
- WBUFW(buf,14) = vd->hair_style;
- WBUFW(buf,16) = vd->weapon;
- WBUFW(buf,18) = vd->head_bottom;
- WBUFW(buf,20) = vd->class_; //Pet armor (ignored by client)
- WBUFW(buf,22) = vd->shield;
- } else {
-#endif
-#if PACKETVER >= 20091103
- WBUFL(buf,12) = (sc)? sc->option : 0;
- offset+=2;
- buf = WBUFP(buffer,offset);
-#elif PACKETVER >= 7
- if (!type) {
- WBUFL(buf,12) = (sc)? sc->option : 0;
- offset+=2;
- buf = WBUFP(buffer,offset);
- } else
- WBUFW(buf,12) = (sc)? sc->option : 0;
-#else
- WBUFW(buf,12) = (sc)? sc->option : 0;
-#endif
- WBUFW(buf,14) = vd->class_;
- WBUFW(buf,16) = vd->hair_style;
- WBUFW(buf,18) = vd->weapon;
-#if PACKETVER < 4
- WBUFW(buf,20) = vd->head_bottom;
- WBUFW(buf,22) = vd->shield;
-#else
- WBUFW(buf,20) = vd->shield;
- WBUFW(buf,22) = vd->head_bottom;
-#endif
-#if PACKETVER < 20091103
- }
-#endif
- WBUFW(buf,24) = vd->head_top;
- WBUFW(buf,26) = vd->head_mid;
-
- if( bl->type == BL_NPC && vd->class_ == FLAG_CLASS )
- { //The hell, why flags work like this?
- WBUFW(buf,22) = status_get_emblem_id(bl);
- WBUFW(buf,24) = GetWord(status_get_guild_id(bl), 1);
- WBUFW(buf,26) = GetWord(status_get_guild_id(bl), 0);
- }
-
- WBUFW(buf,28) = vd->hair_color;
- WBUFW(buf,30) = vd->cloth_color;
- WBUFW(buf,32) = (sd)? sd->head_dir : 0;
-#if PACKETVER < 20091103
- if (type&&spawn) { //End of packet 0x7c
- WBUFB(buf,34) = (sd)?sd->status.karma:0; // karma
- WBUFB(buf,35) = vd->sex;
- WBUFPOS(buf,36,bl->x,bl->y,unit_getdir(bl));
- WBUFB(buf,39) = 0;
- WBUFB(buf,40) = 0;
- return packet_len(0x7c);
- }
-#endif
-#if PACKETVER >= 20110111
- WBUFW(buf,34) = vd->robe;
- offset+= 2;
- buf = WBUFP(buffer,offset);
-#endif
- WBUFL(buf,34) = status_get_guild_id(bl);
- WBUFW(buf,38) = status_get_emblem_id(bl);
- WBUFW(buf,40) = (sd)? sd->status.manner : 0;
-#if PACKETVER >= 20091103
- WBUFL(buf,42) = (sc)? sc->opt3 : 0;
- offset+=2;
- buf = WBUFP(buffer,offset);
-#elif PACKETVER >= 7
- if (!type) {
- WBUFL(buf,42) = (sc)? sc->opt3 : 0;
- offset+=2;
- buf = WBUFP(buffer,offset);
- } else
- WBUFW(buf,42) = (sc)? sc->opt3 : 0;
-#else
- WBUFW(buf,42) = (sc)? sc->opt3 : 0;
-#endif
- WBUFB(buf,44) = (sd)? sd->status.karma : 0;
- WBUFB(buf,45) = vd->sex;
- WBUFPOS(buf,46,bl->x,bl->y,unit_getdir(bl));
- WBUFB(buf,49) = (sd)? 5 : 0;
- WBUFB(buf,50) = (sd)? 5 : 0;
- if (!spawn) {
- WBUFB(buf,51) = vd->dead_sit;
- offset++;
- buf = WBUFP(buffer,offset);
- }
- WBUFW(buf,51) = clif_setlevel(bl);
-#if PACKETVER < 20091103
- if (type) //End for non-player packet
- return packet_len(WBUFW(buffer,0));
-#endif
-#if PACKETVER >= 20080102
- WBUFW(buf,53) = sd?sd->user_font:0;
-#endif
-#if PACKETVER >= 20091103
- memcpy((char*)WBUFP(buf,55), name, NAME_LENGTH);
- return WBUFW(buffer,2);
-#else
- return packet_len(WBUFW(buffer,0));
-#endif
-}
-
-/*==========================================
- * Prepares 'unit walking' packet
- *------------------------------------------*/
-static int clif_set_unit_walking(struct block_list* bl, struct unit_data* ud, unsigned char* buffer)
-{
- struct map_session_data* sd;
- struct status_change* sc = status_get_sc(bl);
- struct view_data* vd = status_get_viewdata(bl);
- unsigned char* buf = WBUFP(buffer,0);
-#if PACKETVER >= 7
- unsigned short offset = 0;
-#endif
-#if PACKETVER >= 20091103
- const char *name;
-#endif
-
- sd = BL_CAST(BL_PC, bl);
-
-#if PACKETVER < 4
- WBUFW(buf, 0) = 0x7b;
-#elif PACKETVER < 7
- WBUFW(buf, 0) = 0x1da;
-#elif PACKETVER < 20080102
- WBUFW(buf, 0) = 0x22c;
-#elif PACKETVER < 20091103
- WBUFW(buf, 0) = 0x2ec;
-#elif PACKETVER < 20101124
- WBUFW(buf, 0) = 0x7f7;
-#else
- WBUFW(buf, 0) = 0x856;
-#endif
-
-#if PACKETVER >= 20091103
- name = status_get_name(bl);
-#if PACKETVER < 20110111
- WBUFW(buf, 2) = 69+strlen(name);
-#else
- WBUFW(buf, 2) = 71+strlen(name);
-#endif
- offset+=2;
- buf = WBUFP(buffer,offset);
-#endif
-#if PACKETVER >= 20071106
- WBUFB(buf, 2) = clif_bl_type(bl);
- offset++;
- buf = WBUFP(buffer,offset);
-#endif
- WBUFL(buf, 2) = bl->id;
- WBUFW(buf, 6) = status_get_speed(bl);
- WBUFW(buf, 8) = (sc)? sc->opt1 : 0;
- WBUFW(buf,10) = (sc)? sc->opt2 : 0;
-#if PACKETVER < 7
- WBUFW(buf,12) = (sc)? sc->option : 0;
-#else
- WBUFL(buf,12) = (sc)? sc->option : 0;
- offset+=2; //Shift the rest of elements by 2 bytes.
- buf = WBUFP(buffer,offset);
-#endif
- WBUFW(buf,14) = vd->class_;
- WBUFW(buf,16) = vd->hair_style;
- WBUFW(buf,18) = vd->weapon;
-#if PACKETVER < 4
- WBUFW(buf,20) = vd->head_bottom;
- WBUFL(buf,22) = gettick();
- WBUFW(buf,26) = vd->shield;
-#else
- WBUFW(buf,20) = vd->shield;
- WBUFW(buf,22) = vd->head_bottom;
- WBUFL(buf,24) = gettick();
-#endif
- WBUFW(buf,28) = vd->head_top;
- WBUFW(buf,30) = vd->head_mid;
- WBUFW(buf,32) = vd->hair_color;
- WBUFW(buf,34) = vd->cloth_color;
- WBUFW(buf,36) = (sd)? sd->head_dir : 0;
-#if PACKETVER >= 20110111
- WBUFW(buf,38) = vd->robe;
- offset+= 2;
- buf = WBUFP(buffer,offset);
-#endif
- WBUFL(buf,38) = status_get_guild_id(bl);
- WBUFW(buf,42) = status_get_emblem_id(bl);
- WBUFW(buf,44) = (sd)? sd->status.manner : 0;
-#if PACKETVER < 7
- WBUFW(buf,46) = (sc)? sc->opt3 : 0;
-#else
- WBUFL(buf,46) = (sc)? sc->opt3 : 0;
- offset+=2; //Shift the rest of elements by 2 bytes.
- buf = WBUFP(buffer,offset);
-#endif
- WBUFB(buf,48) = (sd)? sd->status.karma : 0;
- WBUFB(buf,49) = vd->sex;
- WBUFPOS2(buf,50,bl->x,bl->y,ud->to_x,ud->to_y,8,8);
- WBUFB(buf,56) = (sd)? 5 : 0;
- WBUFB(buf,57) = (sd)? 5 : 0;
- WBUFW(buf,58) = clif_setlevel(bl);
-#if PACKETVER >= 20080102
- WBUFW(buf,60) = sd?sd->user_font:0;
-#endif
-#if PACKETVER >= 20091103
- memcpy((char*)WBUFP(buf,62), name, NAME_LENGTH);
- return WBUFW(buffer,2);
-#else
- return packet_len(WBUFW(buffer,0));
-#endif
-}
-
-//Modifies the buffer for disguise characters and sends it to self.
-//Used for spawn/walk packets, where the ID offset changes for packetver >=9
-static void clif_setdisguise(struct block_list *bl, unsigned char *buf,int len)
-{
-#if PACKETVER >= 20091103
- WBUFB(buf,4)= pcdb_checkid(status_get_viewdata(bl)->class_) ? 0x0 : 0x5; //PC_TYPE : NPC_MOB_TYPE
- WBUFL(buf,5)=-bl->id;
-#elif PACKETVER >= 20071106
- WBUFB(buf,2)= pcdb_checkid(status_get_viewdata(bl)->class_) ? 0x0 : 0x5; //PC_TYPE : NPC_MOB_TYPE
- WBUFL(buf,3)=-bl->id;
-#else
- WBUFL(buf,2)=-bl->id;
-#endif
- clif_send(buf, len, bl, SELF);
-}
-
-
-/// Changes sprite of an NPC object (ZC_NPCSPRITE_CHANGE).
-/// 01b0 <id>.L <type>.B <value>.L
-/// type:
-/// unused
-void clif_class_change(struct block_list *bl,int class_,int type)
-{
- unsigned char buf[16];
-
- nullpo_retv(bl);
-
- if(!pcdb_checkid(class_))
- {// player classes yield missing sprites
- WBUFW(buf,0)=0x1b0;
- WBUFL(buf,2)=bl->id;
- WBUFB(buf,6)=type;
- WBUFL(buf,7)=class_;
- clif_send(buf,packet_len(0x1b0),bl,AREA);
- }
-}
-
-
-/// Notifies the client of an object's spirits.
-/// 01d0 <id>.L <amount>.W (ZC_SPIRITS)
-/// 01e1 <id>.L <amount>.W (ZC_SPIRITS2)
-static void clif_spiritball_single(int fd, struct map_session_data *sd)
-{
- WFIFOHEAD(fd, packet_len(0x1e1));
- WFIFOW(fd,0)=0x1e1;
- WFIFOL(fd,2)=sd->bl.id;
- WFIFOW(fd,6)=sd->spiritball;
- WFIFOSET(fd, packet_len(0x1e1));
-}
-
-/*==========================================
- * Kagerou/Oboro amulet spirit
- *------------------------------------------*/
-static void clif_talisman_single(int fd, struct map_session_data *sd, short type)
-{
- WFIFOHEAD(fd, packet_len(0x08cf));
- WFIFOW(fd,0)=0x08cf;
- WFIFOL(fd,2)=sd->bl.id;
- WFIFOW(fd,6)=type;
- WFIFOW(fd,8)=sd->talisman[type];
- WFIFOSET(fd, packet_len(0x08cf));
-}
-
-/*==========================================
- * Run when player changes map / refreshes
- * Tells its client to display all weather settings being used by this map
- *------------------------------------------*/
-static void clif_weather_check(struct map_session_data *sd)
-{
- int16 m = sd->bl.m;
- int fd = sd->fd;
-
- if (map[m].flag.snow
- || map[m].flag.clouds
- || map[m].flag.fog
- || map[m].flag.fireworks
- || map[m].flag.sakura
- || map[m].flag.leaves
- /**
- * No longer available, keeping here just in case it's back someday. [Ind]
- **/
- //|| map[m].flag.rain
- || map[m].flag.clouds2)
- {
- if (map[m].flag.snow)
- clif_specialeffect_single(&sd->bl, 162, fd);
- if (map[m].flag.clouds)
- clif_specialeffect_single(&sd->bl, 233, fd);
- if (map[m].flag.clouds2)
- clif_specialeffect_single(&sd->bl, 516, fd);
- if (map[m].flag.fog)
- clif_specialeffect_single(&sd->bl, 515, fd);
- if (map[m].flag.fireworks) {
- clif_specialeffect_single(&sd->bl, 297, fd);
- clif_specialeffect_single(&sd->bl, 299, fd);
- clif_specialeffect_single(&sd->bl, 301, fd);
- }
- if (map[m].flag.sakura)
- clif_specialeffect_single(&sd->bl, 163, fd);
- if (map[m].flag.leaves)
- clif_specialeffect_single(&sd->bl, 333, fd);
- /**
- * No longer available, keeping here just in case it's back someday. [Ind]
- **/
- //if (map[m].flag.rain)
- // clif_specialeffect_single(&sd->bl, 161, fd);
- }
-}
-/**
- * Run when the weather on a map changes, throws all players in map id 'm' to clif_weather_check function
- **/
-void clif_weather(int16 m)
-{
- struct s_mapiterator* iter;
- struct map_session_data *sd=NULL;
-
- iter = mapit_getallusers();
- for( sd = (struct map_session_data*)mapit_first(iter); mapit_exists(iter); sd = (struct map_session_data*)mapit_next(iter) )
- {
- if( sd->bl.m == m )
- clif_weather_check(sd);
- }
- mapit_free(iter);
-}
-/**
- * Main function to spawn a unit on the client (player/mob/pet/etc)
- **/
-int clif_spawn(struct block_list *bl)
-{
- unsigned char buf[128];
- struct view_data *vd;
- int len;
-
- vd = status_get_viewdata(bl);
- if( !vd || vd->class_ == INVISIBLE_CLASS )
- return 0;
-
- /**
- * Hide NPC from maya purple card.
- **/
- if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE))
- return 0;
-
- len = clif_set_unit_idle(bl, buf,true);
- clif_send(buf, len, bl, AREA_WOS);
- if (disguised(bl))
- clif_setdisguise(bl, buf, len);
-
- if (vd->cloth_color)
- clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS);
-
- switch (bl->type)
- {
- case BL_PC:
- {
- TBL_PC *sd = ((TBL_PC*)bl);
- int i;
- if (sd->spiritball > 0)
- clif_spiritball(&sd->bl);
- if(sd->state.size==SZ_BIG) // tiny/big players [Valaris]
- clif_specialeffect(bl,423,AREA);
- else if(sd->state.size==SZ_MEDIUM)
- clif_specialeffect(bl,421,AREA);
- if( sd->bg_id && map[sd->bl.m].flag.battleground )
- clif_sendbgemblem_area(sd);
- if( sd->sc.option&OPTION_MOUNTING ) {
- //New Mounts are not complaint to the original method, so we gotta tell this guy that he is mounting.
- clif_status_load_notick(&sd->bl,SI_ALL_RIDING,2,1,0,0);
- }
- for(i = 1; i < 5; i++){
- if( sd->talisman[i] > 0 )
- clif_talisman(sd, i);
- }
- #ifdef NEW_CARTS
- if( sd->sc.data[SC_PUSH_CART] )
- clif_status_load_notick(&sd->bl, SI_ON_PUSH_CART, 2, sd->sc.data[SC_PUSH_CART]->val1, 0, 0);
- #endif
- #if PACKETVER <= 20120207
- if (sd->status.robe)
- clif_refreshlook(bl,bl->id,LOOK_ROBE,sd->status.robe,AREA);
- #endif
- }
- break;
- case BL_MOB:
- {
- TBL_MOB *md = ((TBL_MOB*)bl);
- if(md->special_state.size==SZ_BIG) // tiny/big mobs [Valaris]
- clif_specialeffect(&md->bl,423,AREA);
- else if(md->special_state.size==SZ_MEDIUM)
- clif_specialeffect(&md->bl,421,AREA);
- }
- break;
- case BL_NPC:
- {
- TBL_NPC *nd = ((TBL_NPC*)bl);
- if( nd->size == SZ_BIG )
- clif_specialeffect(&nd->bl,423,AREA);
- else if( nd->size == SZ_MEDIUM )
- clif_specialeffect(&nd->bl,421,AREA);
- }
- break;
- case BL_PET:
- if (vd->head_bottom)
- clif_pet_equip_area((TBL_PET*)bl); // needed to display pet equip properly
- break;
- }
- return 0;
-}
-
-/// Sends information about owned homunculus to the client (ZC_PROPERTY_HOMUN). [orn]
-/// 022e <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <equip id>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.W <max hp>.W <sp>.W <max sp>.W <exp>.L <max exp>.L <skill points>.W <atk range>.W
-void clif_hominfo(struct map_session_data *sd, struct homun_data *hd, int flag)
-{
- struct status_data *status;
- unsigned char buf[128];
- int m_class;
-
- nullpo_retv(hd);
-
- status = &hd->battle_status;
- m_class = hom_class2mapid(hd->homunculus.class_);
-
- memset(buf,0,packet_len(0x22e));
- WBUFW(buf,0)=0x22e;
- memcpy(WBUFP(buf,2),hd->homunculus.name,NAME_LENGTH);
- // Bit field, bit 0 : rename_flag (1 = already renamed), bit 1 : homunc vaporized (1 = true), bit 2 : homunc dead (1 = true)
- WBUFB(buf,26)=(battle_config.hom_rename?0:hd->homunculus.rename_flag) | (hd->homunculus.vaporize << 1) | (hd->homunculus.hp?0:4);
- WBUFW(buf,27)=hd->homunculus.level;
- WBUFW(buf,29)=hd->homunculus.hunger;
- WBUFW(buf,31)=(unsigned short) (hd->homunculus.intimacy / 100) ;
- WBUFW(buf,33)=0; // equip id
- WBUFW(buf,35)=cap_value(status->rhw.atk2+status->batk, 0, INT16_MAX);
- WBUFW(buf,37)=cap_value(status->matk_max, 0, INT16_MAX);
- WBUFW(buf,39)=status->hit;
- if (battle_config.hom_setting&0x10)
- WBUFW(buf,41)=status->luk/3 + 1; //crit is a +1 decimal value! Just display purpose.[Vicious]
- else
- WBUFW(buf,41)=status->cri/10;
- WBUFW(buf,43)=status->def + status->vit ;
- WBUFW(buf,45)=status->mdef;
- WBUFW(buf,47)=status->flee;
- WBUFW(buf,49)=(flag)?0:status->amotion;
- if (status->max_hp > INT16_MAX) {
- WBUFW(buf,51) = status->hp/(status->max_hp/100);
- WBUFW(buf,53) = 100;
- } else {
- WBUFW(buf,51)=status->hp;
- WBUFW(buf,53)=status->max_hp;
- }
- if (status->max_sp > INT16_MAX) {
- WBUFW(buf,55) = status->sp/(status->max_sp/100);
- WBUFW(buf,57) = 100;
- } else {
- WBUFW(buf,55)=status->sp;
- WBUFW(buf,57)=status->max_sp;
- }
- WBUFL(buf,59)=hd->homunculus.exp;
- if( ((m_class&HOM_REG) && hd->homunculus.level >= battle_config.hom_max_level) || ((m_class&HOM_S) && hd->homunculus.level >= battle_config.hom_S_max_level) )
- WBUFL(buf,63)=0;
- else
- WBUFL(buf,63)=hd->exp_next;
- WBUFW(buf,67)=hd->homunculus.skillpts;
- WBUFW(buf,69)=status_get_range(&hd->bl);
- clif_send(buf,packet_len(0x22e),&sd->bl,SELF);
-}
-
-
-/// Notification about a change in homunuculus' state (ZC_CHANGESTATE_MER).
-/// 0230 <type>.B <state>.B <id>.L <data>.L
-/// type:
-/// unused
-/// state:
-/// 0 = pre-init
-/// 1 = intimacy
-/// 2 = hunger
-/// 3 = accessory?
-/// ? = ignored
-void clif_send_homdata(struct map_session_data *sd, int state, int param)
-{ //[orn]
- int fd = sd->fd;
-
- if ( (state == SP_INTIMATE) && (param >= 910) && (sd->hd->homunculus.class_ == sd->hd->homunculusDB->evo_class) )
- merc_hom_calc_skilltree(sd->hd, 0);
-
- WFIFOHEAD(fd, packet_len(0x230));
- WFIFOW(fd,0)=0x230;
- WFIFOB(fd,2)=0;
- WFIFOB(fd,3)=state;
- WFIFOL(fd,4)=sd->hd->bl.id;
- WFIFOL(fd,8)=param;
- WFIFOSET(fd,packet_len(0x230));
-}
-
-
-int clif_homskillinfoblock(struct map_session_data *sd)
-{ //[orn]
- struct homun_data *hd;
- int fd = sd->fd;
- int i,j,len=4,id;
- WFIFOHEAD(fd, 4+37*MAX_HOMUNSKILL);
-
- hd = sd->hd;
- if ( !hd )
- return 0 ;
-
- WFIFOW(fd,0)=0x235;
- for ( i = 0; i < MAX_HOMUNSKILL; i++){
- if( (id = hd->homunculus.hskill[i].id) != 0 ){
- j = id - HM_SKILLBASE;
- WFIFOW(fd,len ) = id;
- WFIFOW(fd,len+2) = skill_get_inf(id);
- WFIFOW(fd,len+4) = 0;
- WFIFOW(fd,len+6) = hd->homunculus.hskill[j].lv;
- WFIFOW(fd,len+8) = skill_get_sp(id,hd->homunculus.hskill[j].lv);
- WFIFOW(fd,len+10)= skill_get_range2(&sd->hd->bl, id,hd->homunculus.hskill[j].lv);
- safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
- WFIFOB(fd,len+36) = (hd->homunculus.hskill[j].lv < merc_skill_tree_get_max(id, hd->homunculus.class_))?1:0;
- len+=37;
- }
- }
- WFIFOW(fd,2)=len;
- WFIFOSET(fd,len);
-
- return 0;
-}
-
-void clif_homskillup(struct map_session_data *sd, uint16 skill_id)
-{ //[orn]
- struct homun_data *hd;
- int fd, idx;
- nullpo_retv(sd);
- idx = skill_id - HM_SKILLBASE;
-
- fd=sd->fd;
- hd=sd->hd;
-
- WFIFOHEAD(fd, packet_len(0x239));
- WFIFOW(fd,0) = 0x239;
- WFIFOW(fd,2) = skill_id;
- WFIFOW(fd,4) = hd->homunculus.hskill[idx].lv;
- WFIFOW(fd,6) = skill_get_sp(skill_id,hd->homunculus.hskill[idx].lv);
- WFIFOW(fd,8) = skill_get_range2(&hd->bl, skill_id,hd->homunculus.hskill[idx].lv);
- WFIFOB(fd,10) = (hd->homunculus.hskill[idx].lv < skill_get_max(hd->homunculus.hskill[idx].id)) ? 1 : 0;
- WFIFOSET(fd,packet_len(0x239));
-}
-
-int clif_hom_food(struct map_session_data *sd,int foodid,int fail) //[orn]
-{
- int fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x22f));
- WFIFOW(fd,0)=0x22f;
- WFIFOB(fd,2)=fail;
- WFIFOW(fd,3)=foodid;
- WFIFOSET(fd,packet_len(0x22f));
-
- return 0;
-}
-
-
-/// Notifies the client, that it is walking (ZC_NOTIFY_PLAYERMOVE).
-/// 0087 <walk start time>.L <walk data>.6B
-void clif_walkok(struct map_session_data *sd)
-{
- int fd=sd->fd;
-
- WFIFOHEAD(fd, packet_len(0x87));
- WFIFOW(fd,0)=0x87;
- WFIFOL(fd,2)=gettick();
- WFIFOPOS2(fd,6,sd->bl.x,sd->bl.y,sd->ud.to_x,sd->ud.to_y,8,8);
- WFIFOSET(fd,packet_len(0x87));
-}
-
-
-static void clif_move2(struct block_list *bl, struct view_data *vd, struct unit_data *ud)
-{
- uint8 buf[128];
- int len;
-
- len = clif_set_unit_walking(bl,ud,buf);
- clif_send(buf,len,bl,AREA_WOS);
- if (disguised(bl))
- clif_setdisguise(bl, buf, len);
-
- if(vd->cloth_color)
- clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS);
-
- switch(bl->type)
- {
- case BL_PC:
- {
- TBL_PC *sd = ((TBL_PC*)bl);
-// clif_movepc(sd);
- if(sd->state.size==SZ_BIG) // tiny/big players [Valaris]
- clif_specialeffect(&sd->bl,423,AREA);
- else if(sd->state.size==SZ_MEDIUM)
- clif_specialeffect(&sd->bl,421,AREA);
- }
- break;
- case BL_MOB:
- {
- TBL_MOB *md = ((TBL_MOB*)bl);
- if(md->special_state.size==SZ_BIG) // tiny/big mobs [Valaris]
- clif_specialeffect(&md->bl,423,AREA);
- else if(md->special_state.size==SZ_MEDIUM)
- clif_specialeffect(&md->bl,421,AREA);
- }
- break;
- case BL_PET:
- if( vd->head_bottom )
- {// needed to display pet equip properly
- clif_pet_equip_area((TBL_PET*)bl);
- }
- break;
- }
-}
-
-
-/// Notifies clients in an area, that an other visible object is walking (ZC_NOTIFY_PLAYERMOVE).
-/// 0086 <id>.L <walk data>.6B <walk start time>.L
-/// Note: unit must not be self
-void clif_move(struct unit_data *ud)
-{
- unsigned char buf[16];
- struct view_data* vd;
- struct block_list* bl = ud->bl;
-
- vd = status_get_viewdata(bl);
- if (!vd || vd->class_ == INVISIBLE_CLASS)
- return; //This performance check is needed to keep GM-hidden objects from being notified to bots.
-
- /**
- * Hide NPC from maya purple card.
- **/
- if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE))
- return;
-
- if (ud->state.speed_changed) {
- // Since we don't know how to update the speed of other objects,
- // use the old walk packet to update the data.
- ud->state.speed_changed = 0;
- clif_move2(bl, vd, ud);
- return;
- }
-
- WBUFW(buf,0)=0x86;
- WBUFL(buf,2)=bl->id;
- WBUFPOS2(buf,6,bl->x,bl->y,ud->to_x,ud->to_y,8,8);
- WBUFL(buf,12)=gettick();
- clif_send(buf, packet_len(0x86), bl, AREA_WOS);
- if (disguised(bl))
- {
- WBUFL(buf,2)=-bl->id;
- clif_send(buf, packet_len(0x86), bl, SELF);
- }
-}
-
-
-/*==========================================
- * Delays the map_quit of a player after they are disconnected. [Skotlex]
- *------------------------------------------*/
-static int clif_delayquit(int tid, unsigned int tick, int id, intptr_t data)
-{
- struct map_session_data *sd = NULL;
-
- //Remove player from map server
- if ((sd = map_id2sd(id)) != NULL && sd->fd == 0) //Should be a disconnected player.
- map_quit(sd);
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-void clif_quitsave(int fd,struct map_session_data *sd)
-{
- if (!battle_config.prevent_logout ||
- DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout)
- map_quit(sd);
- else if (sd->fd)
- { //Disassociate session from player (session is deleted after this function was called)
- //And set a timer to make him quit later.
- session[sd->fd]->session_data = NULL;
- sd->fd = 0;
- add_timer(gettick() + 10000, clif_delayquit, sd->bl.id, 0);
- }
-}
-
-/// Notifies the client of a position change to coordinates on given map (ZC_NPCACK_MAPMOVE).
-/// 0091 <map name>.16B <x>.W <y>.W
-void clif_changemap(struct map_session_data *sd, short map, int x, int y)
-{
- int fd;
- nullpo_retv(sd);
- fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x91));
- WFIFOW(fd,0) = 0x91;
- mapindex_getmapname_ext(mapindex_id2name(map), (char*)WFIFOP(fd,2));
- WFIFOW(fd,18) = x;
- WFIFOW(fd,20) = y;
- WFIFOSET(fd,packet_len(0x91));
-}
-
-
-/// Notifies the client of a position change to coordinates on given map, which is on another map-server (ZC_NPCACK_SERVERMOVE).
-/// 0092 <map name>.16B <x>.W <y>.W <ip>.L <port>.W
-void clif_changemapserver(struct map_session_data* sd, unsigned short map_index, int x, int y, uint32 ip, uint16 port)
-{
- int fd;
- nullpo_retv(sd);
- fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x92));
- WFIFOW(fd,0) = 0x92;
- mapindex_getmapname_ext(mapindex_id2name(map_index), (char*)WFIFOP(fd,2));
- WFIFOW(fd,18) = x;
- WFIFOW(fd,20) = y;
- WFIFOL(fd,22) = htonl(ip);
- WFIFOW(fd,26) = ntows(htons(port)); // [!] LE byte order here [!]
- WFIFOSET(fd,packet_len(0x92));
-}
-
-
-void clif_blown(struct block_list *bl)
-{
-//Aegis packets says fixpos, but it's unsure whether slide works better or not.
-// clif_fixpos(bl);
- clif_slide(bl, bl->x, bl->y);
-}
-
-
-/// Visually moves(slides) a character to x,y. If the target cell
-/// isn't walkable, the char doesn't move at all. If the char is
-/// sitting it will stand up (ZC_STOPMOVE).
-/// 0088 <id>.L <x>.W <y>.W
-void clif_fixpos(struct block_list *bl)
-{
- unsigned char buf[10];
- nullpo_retv(bl);
-
- WBUFW(buf,0) = 0x88;
- WBUFL(buf,2) = bl->id;
- WBUFW(buf,6) = bl->x;
- WBUFW(buf,8) = bl->y;
- clif_send(buf, packet_len(0x88), bl, AREA);
-
- if( disguised(bl) )
- {
- WBUFL(buf,2) = -bl->id;
- clif_send(buf, packet_len(0x88), bl, SELF);
- }
-}
-
-
-/// Displays the buy/sell dialog of an NPC shop (ZC_SELECT_DEALTYPE).
-/// 00c4 <shop id>.L
-void clif_npcbuysell(struct map_session_data* sd, int id)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd, packet_len(0xc4));
- WFIFOW(fd,0)=0xc4;
- WFIFOL(fd,2)=id;
- WFIFOSET(fd,packet_len(0xc4));
-}
-
-
-/// Presents list of items, that can be bought in an NPC shop (ZC_PC_PURCHASE_ITEMLIST).
-/// 00c6 <packet len>.W { <price>.L <discount price>.L <item type>.B <name id>.W }*
-void clif_buylist(struct map_session_data *sd, struct npc_data *nd)
-{
- int fd,i,c;
-
- nullpo_retv(sd);
- nullpo_retv(nd);
-
- fd = sd->fd;
- WFIFOHEAD(fd, 4 + nd->u.shop.count * 11);
- WFIFOW(fd,0) = 0xc6;
-
- c = 0;
- for( i = 0; i < nd->u.shop.count; i++ )
- {
- struct item_data* id = itemdb_exists(nd->u.shop.shop_item[i].nameid);
- int val = nd->u.shop.shop_item[i].value;
- if( id == NULL )
- continue;
- WFIFOL(fd, 4+c*11) = val;
- WFIFOL(fd, 8+c*11) = pc_modifybuyvalue(sd,val);
- WFIFOB(fd,12+c*11) = itemtype(id->type);
- WFIFOW(fd,13+c*11) = ( id->view_id > 0 ) ? id->view_id : id->nameid;
- c++;
- }
-
- WFIFOW(fd,2) = 4 + c*11;
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Presents list of items, that can be sold to an NPC shop (ZC_PC_SELL_ITEMLIST).
-/// 00c7 <packet len>.W { <index>.W <price>.L <overcharge price>.L }*
-void clif_selllist(struct map_session_data *sd)
-{
- int fd,i,c=0,val;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd, MAX_INVENTORY * 10 + 4);
- WFIFOW(fd,0)=0xc7;
- for( i = 0; i < MAX_INVENTORY; i++ )
- {
- if( sd->status.inventory[i].nameid > 0 && sd->inventory_data[i] )
- {
- if( !itemdb_cansell(&sd->status.inventory[i], pc_get_group_level(sd)) )
- continue;
-
- if( sd->status.inventory[i].expire_time )
- continue; // Cannot Sell Rental Items
-
- val=sd->inventory_data[i]->value_sell;
- if( val < 0 )
- continue;
- WFIFOW(fd,4+c*10)=i+2;
- WFIFOL(fd,6+c*10)=val;
- WFIFOL(fd,10+c*10)=pc_modifysellvalue(sd,val);
- c++;
- }
- }
- WFIFOW(fd,2)=c*10+4;
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Displays an NPC dialog message (ZC_SAY_DIALOG).
-/// 00b4 <packet len>.W <npc id>.L <message>.?B
-/// Client behavior (dialog window):
-/// - disable mouse targeting
-/// - open the dialog window
-/// - set npcid of dialog window (0 by default)
-/// - if set to clear on next mes, clear contents
-/// - append this text
-void clif_scriptmes(struct map_session_data *sd, int npcid, const char *mes)
-{
- int fd = sd->fd;
- int slen = strlen(mes) + 9;
-
- WFIFOHEAD(fd, slen);
- WFIFOW(fd,0)=0xb4;
- WFIFOW(fd,2)=slen;
- WFIFOL(fd,4)=npcid;
- memcpy((char*)WFIFOP(fd,8), mes, slen-8);
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Adds a 'next' button to an NPC dialog (ZC_WAIT_DIALOG).
-/// 00b5 <npc id>.L
-/// Client behavior (dialog window):
-/// - disable mouse targeting
-/// - open the dialog window
-/// - add 'next' button
-/// When 'next' is pressed:
-/// - 00B9 <npcid of dialog window>.L
-/// - set to clear on next mes
-/// - remove 'next' button
-void clif_scriptnext(struct map_session_data *sd,int npcid)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd, packet_len(0xb5));
- WFIFOW(fd,0)=0xb5;
- WFIFOL(fd,2)=npcid;
- WFIFOSET(fd,packet_len(0xb5));
-}
-
-
-/// Adds a 'close' button to an NPC dialog (ZC_CLOSE_DIALOG).
-/// 00b6 <npc id>.L
-/// Client behavior:
-/// - if dialog window is open:
-/// - remove 'next' button
-/// - add 'close' button
-/// - else:
-/// - enable mouse targeting
-/// - close the dialog window
-/// - close the menu window
-/// When 'close' is pressed:
-/// - enable mouse targeting
-/// - close the dialog window
-/// - close the menu window
-/// - 0146 <npcid of dialog window>.L
-void clif_scriptclose(struct map_session_data *sd, int npcid)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd, packet_len(0xb6));
- WFIFOW(fd,0)=0xb6;
- WFIFOL(fd,2)=npcid;
- WFIFOSET(fd,packet_len(0xb6));
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-void clif_sendfakenpc(struct map_session_data *sd, int npcid)
-{
- unsigned char *buf;
- int fd = sd->fd;
- sd->state.using_fake_npc = 1;
-
- WFIFOHEAD(fd, packet_len(0x78));
- buf = WFIFOP(fd,0);
- memset(WBUFP(buf,0), 0, packet_len(0x78));
- WBUFW(buf,0)=0x78;
-#if PACKETVER >= 20071106
- WBUFB(buf,2) = 0; // object type
- buf = WFIFOP(fd,1);
-#endif
- WBUFL(buf,2)=npcid;
- WBUFW(buf,14)=111;
- WBUFPOS(buf,46,sd->bl.x,sd->bl.y,sd->ud.dir);
- WBUFB(buf,49)=5;
- WBUFB(buf,50)=5;
- WFIFOSET(fd, packet_len(0x78));
-}
-
-
-/// Displays an NPC dialog menu (ZC_MENU_LIST).
-/// 00b7 <packet len>.W <npc id>.L <menu items>.?B
-/// Client behavior:
-/// - disable mouse targeting
-/// - close the menu window
-/// - open the menu window
-/// - add options to the menu (separated in the text by ":")
-/// - set npcid of menu window
-/// - if dialog window is open:
-/// - remove 'next' button
-/// When 'ok' is pressed:
-/// - 00B8 <npcid of menu window>.L <selected option>.B
-/// - close the menu window
-/// When 'cancel' is pressed:
-/// - 00B8 <npcid of menu window>.L <-1>.B
-/// - enable mouse targeting
-/// - close a bunch of windows...
-/// WARNING: the 'cancel' button closes other windows besides the dialog window and the menu window.
-/// Which suggests their have intertwined behavior. (probably the mouse targeting)
-/// TODO investigate behavior of other windows [FlavioJS]
-void clif_scriptmenu(struct map_session_data* sd, int npcid, const char* mes)
-{
- int fd = sd->fd;
- int slen = strlen(mes) + 9;
- struct block_list *bl = NULL;
-
- if (!sd->state.using_fake_npc && (npcid == fake_nd->bl.id || ((bl = map_id2bl(npcid)) && (bl->m!=sd->bl.m ||
- bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 ||
- bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1))))
- clif_sendfakenpc(sd, npcid);
-
- WFIFOHEAD(fd, slen);
- WFIFOW(fd,0)=0xb7;
- WFIFOW(fd,2)=slen;
- WFIFOL(fd,4)=npcid;
- memcpy((char*)WFIFOP(fd,8), mes, slen-8);
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Displays an NPC dialog input box for numbers (ZC_OPEN_EDITDLG).
-/// 0142 <npc id>.L
-/// Client behavior (inputnum window):
-/// - if npcid exists in the client:
-/// - open the inputnum window
-/// - set npcid of inputnum window
-/// When 'ok' is pressed:
-/// - if inputnum window has text:
-/// - if npcid exists in the client:
-/// - 0143 <npcid of inputnum window>.L <atoi(text)>.L
-/// - close inputnum window
-void clif_scriptinput(struct map_session_data *sd, int npcid)
-{
- int fd;
- struct block_list *bl = NULL;
-
- nullpo_retv(sd);
-
- if (!sd->state.using_fake_npc && (npcid == fake_nd->bl.id || ((bl = map_id2bl(npcid)) && (bl->m!=sd->bl.m ||
- bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 ||
- bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1))))
- clif_sendfakenpc(sd, npcid);
-
- fd=sd->fd;
- WFIFOHEAD(fd, packet_len(0x142));
- WFIFOW(fd,0)=0x142;
- WFIFOL(fd,2)=npcid;
- WFIFOSET(fd,packet_len(0x142));
-}
-
-
-/// Displays an NPC dialog input box for numbers (ZC_OPEN_EDITDLGSTR).
-/// 01d4 <npc id>.L
-/// Client behavior (inputstr window):
-/// - if npcid is 0 or npcid exists in the client:
-/// - open the inputstr window
-/// - set npcid of inputstr window
-/// When 'ok' is pressed:
-/// - if inputstr window has text and isn't an insult(manner.txt):
-/// - if npcid is 0 or npcid exists in the client:
-/// - 01d5 <packetlen>.W <npcid of inputstr window>.L <text>.?B
-/// - close inputstr window
-void clif_scriptinputstr(struct map_session_data *sd, int npcid)
-{
- int fd;
- struct block_list *bl = NULL;
-
- nullpo_retv(sd);
-
- if (!sd->state.using_fake_npc && (npcid == fake_nd->bl.id || ((bl = map_id2bl(npcid)) && (bl->m!=sd->bl.m ||
- bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 ||
- bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1))))
- clif_sendfakenpc(sd, npcid);
-
- fd=sd->fd;
- WFIFOHEAD(fd, packet_len(0x1d4));
- WFIFOW(fd,0)=0x1d4;
- WFIFOL(fd,2)=npcid;
- WFIFOSET(fd,packet_len(0x1d4));
-}
-
-
-/// Marks a position on client's minimap (ZC_COMPASS).
-/// 0144 <npc id>.L <type>.L <x>.L <y>.L <id>.B <color>.L
-/// npc id:
-/// is ignored in the client
-/// type:
-/// 0 = display mark for 15 seconds
-/// 1 = display mark until dead or teleported
-/// 2 = remove mark
-/// color:
-/// 0x00RRGGBB
-void clif_viewpoint(struct map_session_data *sd, int npc_id, int type, int x, int y, int id, int color)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd, packet_len(0x144));
- WFIFOW(fd,0)=0x144;
- WFIFOL(fd,2)=npc_id;
- WFIFOL(fd,6)=type;
- WFIFOL(fd,10)=x;
- WFIFOL(fd,14)=y;
- WFIFOB(fd,18)=id;
- WFIFOL(fd,19)=color;
- WFIFOSET(fd,packet_len(0x144));
-}
-
-
-/// Displays an illustration image.
-/// 0145 <image name>.16B <type>.B (ZC_SHOW_IMAGE)
-/// 01b3 <image name>.64B <type>.B (ZC_SHOW_IMAGE2)
-/// type:
-/// 0 = bottom left corner
-/// 1 = bottom middle
-/// 2 = bottom right corner
-/// 3 = middle of screen, inside a movable window
-/// 4 = middle of screen, movable with a close button, chrome-less
-void clif_cutin(struct map_session_data* sd, const char* image, int type)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd, packet_len(0x1b3));
- WFIFOW(fd,0)=0x1b3;
- strncpy((char*)WFIFOP(fd,2),image,64);
- WFIFOB(fd,66)=type;
- WFIFOSET(fd,packet_len(0x1b3));
-}
-
-
-/*==========================================
- * Fills in card data from the given item and into the buffer. [Skotlex]
- *------------------------------------------*/
-static void clif_addcards(unsigned char* buf, struct item* item)
-{
- int i=0,j;
- if( item == NULL ) { //Blank data
- WBUFW(buf,0) = 0;
- WBUFW(buf,2) = 0;
- WBUFW(buf,4) = 0;
- WBUFW(buf,6) = 0;
- return;
- }
- if( item->card[0] == CARD0_PET ) { //pet eggs
- WBUFW(buf,0) = 0;
- WBUFW(buf,2) = 0;
- WBUFW(buf,4) = 0;
- WBUFW(buf,6) = item->card[3]; //Pet renamed flag.
- return;
- }
- if( item->card[0] == CARD0_FORGE || item->card[0] == CARD0_CREATE ) { //Forged/created items
- WBUFW(buf,0) = item->card[0];
- WBUFW(buf,2) = item->card[1];
- WBUFW(buf,4) = item->card[2];
- WBUFW(buf,6) = item->card[3];
- return;
- }
- //Client only receives four cards.. so randomly send them a set of cards. [Skotlex]
- if( MAX_SLOTS > 4 && (j = itemdb_slot(item->nameid)) > 4 )
- i = rnd()%(j-3); //eg: 6 slots, possible i values: 0->3, 1->4, 2->5 => i = rnd()%3;
-
- //Normal items.
- if( item->card[i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 )
- WBUFW(buf,0) = j;
- else
- WBUFW(buf,0) = item->card[i];
-
- if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 )
- WBUFW(buf,2) = j;
- else
- WBUFW(buf,2) = item->card[i];
-
- if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 )
- WBUFW(buf,4) = j;
- else
- WBUFW(buf,4) = item->card[i];
-
- if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 )
- WBUFW(buf,6) = j;
- else
- WBUFW(buf,6) = item->card[i];
-}
-
-
-/// Notifies the client, about a received inventory item or the result of a pick-up request.
-/// 00a0 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B (ZC_ITEM_PICKUP_ACK)
-/// 029a <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B <expire time>.L (ZC_ITEM_PICKUP_ACK2)
-/// 02d4 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B <expire time>.L <bindOnEquipType>.W (ZC_ITEM_PICKUP_ACK3)
-void clif_additem(struct map_session_data *sd, int n, int amount, int fail)
-{
- int fd;
-#if PACKETVER < 20061218
- const int cmd = 0xa0;
-#elif PACKETVER < 20071002
- const int cmd = 0x29a;
-#else
- const int cmd = 0x2d4;
-#endif
- nullpo_retv(sd);
-
- fd = sd->fd;
- if( !session_isActive(fd) ) //Sasuke-
- return;
-
- WFIFOHEAD(fd,packet_len(cmd));
- if( fail )
- {
- WFIFOW(fd,0)=cmd;
- WFIFOW(fd,2)=n+2;
- WFIFOW(fd,4)=amount;
- WFIFOW(fd,6)=0;
- WFIFOB(fd,8)=0;
- WFIFOB(fd,9)=0;
- WFIFOB(fd,10)=0;
- WFIFOW(fd,11)=0;
- WFIFOW(fd,13)=0;
- WFIFOW(fd,15)=0;
- WFIFOW(fd,17)=0;
- WFIFOW(fd,19)=0;
- WFIFOB(fd,21)=0;
- WFIFOB(fd,22)=fail;
-#if PACKETVER >= 20061218
- WFIFOL(fd,23)=0;
-#endif
-#if PACKETVER >= 20071002
- WFIFOW(fd,27)=0; // unknown
-#endif
- }
- else
- {
- if( n < 0 || n >= MAX_INVENTORY || sd->status.inventory[n].nameid <=0 || sd->inventory_data[n] == NULL )
- return;
-
- WFIFOW(fd,0)=cmd;
- WFIFOW(fd,2)=n+2;
- WFIFOW(fd,4)=amount;
- if (sd->inventory_data[n]->view_id > 0)
- WFIFOW(fd,6)=sd->inventory_data[n]->view_id;
- else
- WFIFOW(fd,6)=sd->status.inventory[n].nameid;
- WFIFOB(fd,8)=sd->status.inventory[n].identify;
- WFIFOB(fd,9)=sd->status.inventory[n].attribute;
- WFIFOB(fd,10)=sd->status.inventory[n].refine;
- clif_addcards(WFIFOP(fd,11), &sd->status.inventory[n]);
- WFIFOW(fd,19)=pc_equippoint(sd,n);
- WFIFOB(fd,21)=itemtype(sd->inventory_data[n]->type);
- WFIFOB(fd,22)=fail;
-#if PACKETVER >= 20061218
- WFIFOL(fd,23)=sd->status.inventory[n].expire_time;
-#endif
-#if PACKETVER >= 20071002
- WFIFOW(fd,27)=0; // unknown
-#endif
- }
-
- WFIFOSET(fd,packet_len(cmd));
-}
-
-
-/// Notifies the client, that an inventory item was deleted or dropped (ZC_ITEM_THROW_ACK).
-/// 00af <index>.W <amount>.W
-void clif_dropitem(struct map_session_data *sd,int n,int amount)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd, packet_len(0xaf));
- WFIFOW(fd,0)=0xaf;
- WFIFOW(fd,2)=n+2;
- WFIFOW(fd,4)=amount;
- WFIFOSET(fd,packet_len(0xaf));
-}
-
-
-/// Notifies the client, that an inventory item was deleted (ZC_DELETE_ITEM_FROM_BODY).
-/// 07fa <delete type>.W <index>.W <amount>.W
-/// delete type:
-/// 0 = Normal
-/// 1 = Item used for a skill
-/// 2 = Refine failed
-/// 3 = Material changed
-/// 4 = Moved to storage
-/// 5 = Moved to cart
-/// 6 = Item sold
-/// 7 = Consumed by Four Spirit Analysis (SO_EL_ANALYSIS) skill
-void clif_delitem(struct map_session_data *sd,int n,int amount, short reason)
-{
-#if PACKETVER < 20091117
- clif_dropitem(sd,n,amount);
-#else
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
-
- WFIFOHEAD(fd, packet_len(0x7fa));
- WFIFOW(fd,0)=0x7fa;
- WFIFOW(fd,2)=reason;
- WFIFOW(fd,4)=n+2;
- WFIFOW(fd,6)=amount;
- WFIFOSET(fd,packet_len(0x7fa));
-#endif
-}
-
-
-// Simplifies inventory/cart/storage packets by handling the packet section relevant to items. [Skotlex]
-// Equip is >= 0 for equippable items (holds the equip-point, is 0 for pet
-// armor/egg) -1 for stackable items, -2 for stackable items where arrows must send in the equip-point.
-void clif_item_sub(unsigned char *buf, int n, struct item *i, struct item_data *id, int equip)
-{
- if (id->view_id > 0)
- WBUFW(buf,n)=id->view_id;
- else
- WBUFW(buf,n)=i->nameid;
- WBUFB(buf,n+2)=itemtype(id->type);
- WBUFB(buf,n+3)=i->identify;
- if (equip >= 0) { //Equippable item
- WBUFW(buf,n+4)=equip;
- WBUFW(buf,n+6)=i->equip;
- WBUFB(buf,n+8)=i->attribute;
- WBUFB(buf,n+9)=i->refine;
- } else { //Stackable item.
- WBUFW(buf,n+4)=i->amount;
- if (equip == -2 && id->equip == EQP_AMMO)
- WBUFW(buf,n+6)=EQP_AMMO;
- else
- WBUFW(buf,n+6)=0;
- }
-
-}
-void clif_favorite_item(struct map_session_data* sd, unsigned short index);
-//Unified inventory function which sends all of the inventory (requires two packets, one for equipable items and one for stackable ones. [Skotlex]
-void clif_inventorylist(struct map_session_data *sd)
-{
- int i,n,ne,arrow=-1;
- unsigned char *buf;
- unsigned char *bufe;
-
-#if PACKETVER < 5
- const int s = 10; //Entry size.
-#elif PACKETVER < 20080102
- const int s = 18;
-#else
- const int s = 22;
-#endif
-#if PACKETVER < 20071002
- const int se = 20;
-#elif PACKETVER < 20100629
- const int se = 26;
-#else
- const int se = 28;
-#endif
-
- buf = (unsigned char*)aMalloc(MAX_INVENTORY * s + 4);
- bufe = (unsigned char*)aMalloc(MAX_INVENTORY * se + 4);
-
- for( i = 0, n = 0, ne = 0; i < MAX_INVENTORY; i++ )
- {
- if( sd->status.inventory[i].nameid <=0 || sd->inventory_data[i] == NULL )
- continue;
-
- if( !itemdb_isstackable2(sd->inventory_data[i]) )
- { //Non-stackable (Equippable)
- WBUFW(bufe,ne*se+4)=i+2;
- clif_item_sub(bufe, ne*se+6, &sd->status.inventory[i], sd->inventory_data[i], pc_equippoint(sd,i));
- clif_addcards(WBUFP(bufe, ne*se+16), &sd->status.inventory[i]);
-#if PACKETVER >= 20071002
- WBUFL(bufe,ne*se+24)=sd->status.inventory[i].expire_time;
- WBUFW(bufe,ne*se+28)=0; //Unknown
-#endif
-#if PACKETVER >= 20100629
- if (sd->inventory_data[i]->equip&EQP_VISIBLE)
- WBUFW(bufe,ne*se+30)= sd->inventory_data[i]->look;
- else
- WBUFW(bufe,ne*se+30)=0;
-#endif
- ne++;
- }
- else
- { //Stackable.
- WBUFW(buf,n*s+4)=i+2;
- clif_item_sub(buf, n*s+6, &sd->status.inventory[i], sd->inventory_data[i], -2);
- if( sd->inventory_data[i]->equip == EQP_AMMO && sd->status.inventory[i].equip )
- arrow=i;
-#if PACKETVER >= 5
- clif_addcards(WBUFP(buf, n*s+14), &sd->status.inventory[i]);
-#endif
-#if PACKETVER >= 20080102
- WBUFL(buf,n*s+22)=sd->status.inventory[i].expire_time;
-#endif
- n++;
- }
- }
- if( n )
- {
-#if PACKETVER < 5
- WBUFW(buf,0)=0xa3;
-#elif PACKETVER < 20080102
- WBUFW(buf,0)=0x1ee;
-#else
- WBUFW(buf,0)=0x2e8;
-#endif
- WBUFW(buf,2)=4+n*s;
- clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
- }
- if( arrow >= 0 )
- clif_arrowequip(sd,arrow);
-
- if( ne )
- {
-#if PACKETVER < 20071002
- WBUFW(bufe,0)=0xa4;
-#else
- WBUFW(bufe,0)=0x2d0;
-#endif
- WBUFW(bufe,2)=4+ne*se;
- clif_send(bufe, WBUFW(bufe,2), &sd->bl, SELF);
- }
-#if PACKETVER >= 20111122
- for( i = 0; i < MAX_INVENTORY; i++ ) {
- if( sd->status.inventory[i].nameid <= 0 || sd->inventory_data[i] == NULL )
- continue;
-
- if ( sd->status.inventory[i].favorite )
- clif_favorite_item(sd, i);
- }
-#endif
-
- if( buf ) aFree(buf);
- if( bufe ) aFree(bufe);
-}
-
-//Required when items break/get-repaired. Only sends equippable item list.
-void clif_equiplist(struct map_session_data *sd)
-{
- int i,n,fd = sd->fd;
- unsigned char *buf;
-#if PACKETVER < 20071002
- const int cmd = 20;
-#elif PACKETVER < 20100629
- const int cmd = 26;
-#else
- const int cmd = 28;
-#endif
-
- WFIFOHEAD(fd, MAX_INVENTORY * cmd + 4);
- buf = WFIFOP(fd,0);
-
- for(i=0,n=0;i<MAX_INVENTORY;i++){
- if (sd->status.inventory[i].nameid <=0 || sd->inventory_data[i] == NULL)
- continue;
-
- if(itemdb_isstackable2(sd->inventory_data[i]))
- continue;
- //Equippable
- WBUFW(buf,n*cmd+4)=i+2;
- clif_item_sub(buf, n*cmd+6, &sd->status.inventory[i], sd->inventory_data[i], pc_equippoint(sd,i));
- clif_addcards(WBUFP(buf, n*cmd+16), &sd->status.inventory[i]);
-#if PACKETVER >= 20071002
- WBUFL(buf,n*cmd+24)=sd->status.inventory[i].expire_time;
- WBUFW(buf,n*cmd+28)=0; //Unknown
-#endif
-#if PACKETVER >= 20100629
- if (sd->inventory_data[i]->equip&EQP_VISIBLE)
- WBUFW(buf,n*cmd+30)= sd->inventory_data[i]->look;
- else
- WBUFW(buf,n*cmd+30)=0;
-#endif
- n++;
- }
- if (n) {
-#if PACKETVER < 20071002
- WBUFW(buf,0)=0xa4;
-#else
- WBUFW(buf,0)=0x2d0;
-#endif
- WBUFW(buf,2)=4+n*cmd;
- WFIFOSET(fd,WFIFOW(fd,2));
- }
-}
-
-void clif_storagelist(struct map_session_data* sd, struct item* items, int items_length)
-{
- struct item_data *id;
- int i,n,ne;
- unsigned char *buf;
- unsigned char *bufe;
-#if PACKETVER < 5
- const int s = 10; //Entry size.
-#elif PACKETVER < 20080102
- const int s = 18;
-#else
- const int s = 22;
-#endif
-#if PACKETVER < 20071002
- const int cmd = 20;
-#elif PACKETVER < 20100629
- const int cmd = 26;
-#else
- const int cmd = 28;
-#endif
-
- buf = (unsigned char*)aMalloc(items_length * s + 4);
- bufe = (unsigned char*)aMalloc(items_length * cmd + 4);
-
- for( i = 0, n = 0, ne = 0; i < items_length; i++ )
- {
- if( items[i].nameid <= 0 )
- continue;
- id = itemdb_search(items[i].nameid);
- if( !itemdb_isstackable2(id) )
- { //Equippable
- WBUFW(bufe,ne*cmd+4)=i+1;
- clif_item_sub(bufe, ne*cmd+6, &items[i], id, id->equip);
- clif_addcards(WBUFP(bufe, ne*cmd+16), &items[i]);
-#if PACKETVER >= 20071002
- WBUFL(bufe,ne*cmd+24)=items[i].expire_time;
- WBUFW(bufe,ne*cmd+28)=0; //Unknown
-#endif
- ne++;
- }
- else
- { //Stackable
- WBUFW(buf,n*s+4)=i+1;
- clif_item_sub(buf, n*s+6, &items[i], id,-1);
-#if PACKETVER >= 5
- clif_addcards(WBUFP(buf,n*s+14), &items[i]);
-#endif
-#if PACKETVER >= 20080102
- WBUFL(buf,n*s+22)=items[i].expire_time;
-#endif
- n++;
- }
- }
- if( n )
- {
-#if PACKETVER < 5
- WBUFW(buf,0)=0xa5;
-#elif PACKETVER < 20080102
- WBUFW(buf,0)=0x1f0;
-#else
- WBUFW(buf,0)=0x2ea;
-#endif
- WBUFW(buf,2)=4+n*s;
- clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
- }
- if( ne )
- {
-#if PACKETVER < 20071002
- WBUFW(bufe,0)=0xa6;
-#else
- WBUFW(bufe,0)=0x2d1;
-#endif
- WBUFW(bufe,2)=4+ne*cmd;
- clif_send(bufe, WBUFW(bufe,2), &sd->bl, SELF);
- }
-
- if( buf ) aFree(buf);
- if( bufe ) aFree(bufe);
-}
-
-void clif_cartlist(struct map_session_data *sd)
-{
- struct item_data *id;
- int i,n,ne;
- unsigned char *buf;
- unsigned char *bufe;
-#if PACKETVER < 5
- const int s = 10; //Entry size.
-#elif PACKETVER < 20080102
- const int s = 18;
-#else
- const int s = 22;
-#endif
-#if PACKETVER < 20071002
- const int cmd = 20;
-#elif PACKETVER < 20100629
- const int cmd = 26;
-#else
- const int cmd = 28;
-#endif
-
- buf = (unsigned char*)aMalloc(MAX_CART * s + 4);
- bufe = (unsigned char*)aMalloc(MAX_CART * cmd + 4);
-
- for( i = 0, n = 0, ne = 0; i < MAX_CART; i++ )
- {
- if( sd->status.cart[i].nameid <= 0 )
- continue;
- id = itemdb_search(sd->status.cart[i].nameid);
- if( !itemdb_isstackable2(id) )
- { //Equippable
- WBUFW(bufe,ne*cmd+4)=i+2;
- clif_item_sub(bufe, ne*cmd+6, &sd->status.cart[i], id, id->equip);
- clif_addcards(WBUFP(bufe, ne*cmd+16), &sd->status.cart[i]);
-#if PACKETVER >= 20071002
- WBUFL(bufe,ne*cmd+24)=sd->status.cart[i].expire_time;
- WBUFW(bufe,ne*cmd+28)=0; //Unknown
-#endif
- ne++;
- }
- else
- { //Stackable
- WBUFW(buf,n*s+4)=i+2;
- clif_item_sub(buf, n*s+6, &sd->status.cart[i], id,-1);
-#if PACKETVER >= 5
- clif_addcards(WBUFP(buf,n*s+14), &sd->status.cart[i]);
-#endif
-#if PACKETVER >= 20080102
- WBUFL(buf,n*s+22)=sd->status.cart[i].expire_time;
-#endif
- n++;
- }
- }
- if( n )
- {
-#if PACKETVER < 5
- WBUFW(buf,0)=0x123;
-#elif PACKETVER < 20080102
- WBUFW(buf,0)=0x1ef;
-#else
- WBUFW(buf,0)=0x2e9;
-#endif
- WBUFW(buf,2)=4+n*s;
- clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
- }
- if( ne )
- {
-#if PACKETVER < 20071002
- WBUFW(bufe,0)=0x122;
-#else
- WBUFW(bufe,0)=0x2d2;
-#endif
- WBUFW(bufe,2)=4+ne*cmd;
- clif_send(bufe, WBUFW(bufe,2), &sd->bl, SELF);
- }
-
- if( buf ) aFree(buf);
- if( bufe ) aFree(bufe);
-}
-
-
-/// Removes cart (ZC_CARTOFF).
-/// 012b
-/// Client behaviour:
-/// Closes the cart storage and removes all it's items from memory.
-/// The Num & Weight values of the cart are left untouched and the cart is NOT removed.
-void clif_clearcart(int fd)
-{
- WFIFOHEAD(fd, packet_len(0x12b));
- WFIFOW(fd,0) = 0x12b;
- WFIFOSET(fd, packet_len(0x12b));
-
-}
-
-
-/// Guild XY locators (ZC_NOTIFY_POSITION_TO_GUILDM) [Valaris]
-/// 01eb <account id>.L <x>.W <y>.W
-void clif_guild_xy(struct map_session_data *sd)
-{
- unsigned char buf[10];
-
- nullpo_retv(sd);
-
- WBUFW(buf,0)=0x1eb;
- WBUFL(buf,2)=sd->status.account_id;
- WBUFW(buf,6)=sd->bl.x;
- WBUFW(buf,8)=sd->bl.y;
- clif_send(buf,packet_len(0x1eb),&sd->bl,GUILD_SAMEMAP_WOS);
-}
-
-/*==========================================
- * Sends x/y dot to a single fd. [Skotlex]
- *------------------------------------------*/
-void clif_guild_xy_single(int fd, struct map_session_data *sd)
-{
- if( sd->bg_id )
- return;
-
- WFIFOHEAD(fd,packet_len(0x1eb));
- WFIFOW(fd,0)=0x1eb;
- WFIFOL(fd,2)=sd->status.account_id;
- WFIFOW(fd,6)=sd->bl.x;
- WFIFOW(fd,8)=sd->bl.y;
- WFIFOSET(fd,packet_len(0x1eb));
-}
-
-// Guild XY locators [Valaris]
-void clif_guild_xy_remove(struct map_session_data *sd)
-{
- unsigned char buf[10];
-
- nullpo_retv(sd);
-
- WBUFW(buf,0)=0x1eb;
- WBUFL(buf,2)=sd->status.account_id;
- WBUFW(buf,6)=-1;
- WBUFW(buf,8)=-1;
- clif_send(buf,packet_len(0x1eb),&sd->bl,GUILD_SAMEMAP_WOS);
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-static int clif_hpmeter_sub(struct block_list *bl, va_list ap)
-{
- struct map_session_data *sd, *tsd;
-#if PACKETVER < 20100126
- const int cmd = 0x106;
-#else
- const int cmd = 0x80e;
-#endif
-
- sd = va_arg(ap, struct map_session_data *);
- tsd = (TBL_PC *)bl;
-
- nullpo_ret(sd);
- nullpo_ret(tsd);
-
- if( !tsd->fd || tsd == sd )
- return 0;
-
- if( !pc_has_permission(tsd, PC_PERM_VIEW_HPMETER) )
- return 0;
- WFIFOHEAD(tsd->fd,packet_len(cmd));
- WFIFOW(tsd->fd,0) = cmd;
- WFIFOL(tsd->fd,2) = sd->status.account_id;
-#if PACKETVER < 20100126
- if( sd->battle_status.max_hp > INT16_MAX )
- { //To correctly display the %hp bar. [Skotlex]
- WFIFOW(tsd->fd,6) = sd->battle_status.hp/(sd->battle_status.max_hp/100);
- WFIFOW(tsd->fd,8) = 100;
- } else {
- WFIFOW(tsd->fd,6) = sd->battle_status.hp;
- WFIFOW(tsd->fd,8) = sd->battle_status.max_hp;
- }
-#else
- WFIFOL(tsd->fd,6) = sd->battle_status.hp;
- WFIFOL(tsd->fd,10) = sd->battle_status.max_hp;
-#endif
- WFIFOSET(tsd->fd,packet_len(cmd));
- return 0;
-}
-
-/*==========================================
- * Server tells all players that are allowed to view HP bars
- * and are nearby 'sd' that 'sd' hp bar was updated.
- *------------------------------------------*/
-static int clif_hpmeter(struct map_session_data *sd)
-{
- nullpo_ret(sd);
- map_foreachinarea(clif_hpmeter_sub, sd->bl.m, sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE, sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_PC, sd);
- return 0;
-}
-
-/// Notifies client of a character parameter change.
-/// 00b0 <var id>.W <value>.L (ZC_PAR_CHANGE)
-/// 00b1 <var id>.W <value>.L (ZC_LONGPAR_CHANGE)
-/// 00be <status id>.W <value>.B (ZC_STATUS_CHANGE)
-/// 0121 <current count>.W <max count>.W <current weight>.L <max weight>.L (ZC_NOTIFY_CARTITEM_COUNTINFO)
-/// 013a <atk range>.W (ZC_ATTACK_RANGE)
-/// 0141 <status id>.L <base status>.L <plus status>.L (ZC_COUPLESTATUS)
-/// TODO: Extract individual packets.
-/// FIXME: Packet lengths from packet_len(cmd)
-void clif_updatestatus(struct map_session_data *sd,int type)
-{
- int fd,len=8;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
-
- if ( !session_isActive(fd) ) // Invalid pointer fix, by sasuke [Kevin]
- return;
-
- WFIFOHEAD(fd, 14);
- WFIFOW(fd,0)=0xb0;
- WFIFOW(fd,2)=type;
- switch(type){
- // 00b0
- case SP_WEIGHT:
- pc_updateweightstatus(sd);
- WFIFOHEAD(fd,14);
- WFIFOW(fd,0)=0xb0; //Need to re-set as pc_updateweightstatus can alter the buffer. [Skotlex]
- WFIFOW(fd,2)=type;
- WFIFOL(fd,4)=sd->weight;
- break;
- case SP_MAXWEIGHT:
- WFIFOL(fd,4)=sd->max_weight;
- break;
- case SP_SPEED:
- WFIFOL(fd,4)=sd->battle_status.speed;
- break;
- case SP_BASELEVEL:
- WFIFOL(fd,4)=sd->status.base_level;
- break;
- case SP_JOBLEVEL:
- WFIFOL(fd,4)=sd->status.job_level;
- break;
- case SP_KARMA: // Adding this back, I wonder if the client intercepts this - [Lance]
- WFIFOL(fd,4)=sd->status.karma;
- break;
- case SP_MANNER:
- WFIFOL(fd,4)=sd->status.manner;
- break;
- case SP_STATUSPOINT:
- WFIFOL(fd,4)=sd->status.status_point;
- break;
- case SP_SKILLPOINT:
- WFIFOL(fd,4)=sd->status.skill_point;
- break;
- case SP_HIT:
- WFIFOL(fd,4)=sd->battle_status.hit;
- break;
- case SP_FLEE1:
- WFIFOL(fd,4)=sd->battle_status.flee;
- break;
- case SP_FLEE2:
- WFIFOL(fd,4)=sd->battle_status.flee2/10;
- break;
- case SP_MAXHP:
- WFIFOL(fd,4)=sd->battle_status.max_hp;
- break;
- case SP_MAXSP:
- WFIFOL(fd,4)=sd->battle_status.max_sp;
- break;
- case SP_HP:
- WFIFOL(fd,4)=sd->battle_status.hp;
- // TODO: Won't these overwrite the current packet?
- clif_hpmeter(sd);
- if( !battle_config.party_hp_mode && sd->status.party_id )
- clif_party_hp(sd);
- if( sd->bg_id )
- clif_bg_hp(sd);
- break;
- case SP_SP:
- WFIFOL(fd,4)=sd->battle_status.sp;
- break;
- case SP_ASPD:
- WFIFOL(fd,4)=sd->battle_status.amotion;
- break;
- case SP_ATK1:
- WFIFOL(fd,4)=pc_leftside_atk(sd);
- break;
- case SP_DEF1:
- WFIFOL(fd,4)=pc_leftside_def(sd);
- break;
- case SP_MDEF1:
- WFIFOL(fd,4)=pc_leftside_mdef(sd);
- break;
- case SP_ATK2:
- WFIFOL(fd,4)=pc_rightside_atk(sd);
- break;
- case SP_DEF2:
- WFIFOL(fd,4)=pc_rightside_def(sd);
- break;
- case SP_MDEF2: {
- //negative check (in case you have something like Berserk active)
- int mdef2 = pc_rightside_mdef(sd);
-
- WFIFOL(fd,4)=
-#ifndef RENEWAL
- ( mdef2 < 0 ) ? 0 :
-#endif
- mdef2;
-
- }
- break;
- case SP_CRITICAL:
- WFIFOL(fd,4)=sd->battle_status.cri/10;
- break;
- case SP_MATK1:
- WFIFOL(fd,4)=pc_rightside_matk(sd);
- break;
- case SP_MATK2:
- WFIFOL(fd,4)=pc_leftside_matk(sd);
- break;
-
-
- case SP_ZENY:
- WFIFOW(fd,0)=0xb1;
- WFIFOL(fd,4)=sd->status.zeny;
- break;
- case SP_BASEEXP:
- WFIFOW(fd,0)=0xb1;
- WFIFOL(fd,4)=sd->status.base_exp;
- break;
- case SP_JOBEXP:
- WFIFOW(fd,0)=0xb1;
- WFIFOL(fd,4)=sd->status.job_exp;
- break;
- case SP_NEXTBASEEXP:
- WFIFOW(fd,0)=0xb1;
- WFIFOL(fd,4)=pc_nextbaseexp(sd);
- break;
- case SP_NEXTJOBEXP:
- WFIFOW(fd,0)=0xb1;
- WFIFOL(fd,4)=pc_nextjobexp(sd);
- break;
-
- /**
- * SP_U<STAT> are used to update the amount of points necessary to increase that stat
- **/
- case SP_USTR:
- case SP_UAGI:
- case SP_UVIT:
- case SP_UINT:
- case SP_UDEX:
- case SP_ULUK:
- WFIFOW(fd,0)=0xbe;
- WFIFOB(fd,4)=pc_need_status_point(sd,type-SP_USTR+SP_STR,1);
- len=5;
- break;
-
- /**
- * Tells the client how far it is allowed to attack (weapon range)
- **/
- case SP_ATTACKRANGE:
- WFIFOW(fd,0)=0x13a;
- WFIFOW(fd,2)=sd->battle_status.rhw.range;
- len=4;
- break;
-
- case SP_STR:
- WFIFOW(fd,0)=0x141;
- WFIFOL(fd,2)=type;
- WFIFOL(fd,6)=sd->status.str;
- WFIFOL(fd,10)=sd->battle_status.str - sd->status.str;
- len=14;
- break;
- case SP_AGI:
- WFIFOW(fd,0)=0x141;
- WFIFOL(fd,2)=type;
- WFIFOL(fd,6)=sd->status.agi;
- WFIFOL(fd,10)=sd->battle_status.agi - sd->status.agi;
- len=14;
- break;
- case SP_VIT:
- WFIFOW(fd,0)=0x141;
- WFIFOL(fd,2)=type;
- WFIFOL(fd,6)=sd->status.vit;
- WFIFOL(fd,10)=sd->battle_status.vit - sd->status.vit;
- len=14;
- break;
- case SP_INT:
- WFIFOW(fd,0)=0x141;
- WFIFOL(fd,2)=type;
- WFIFOL(fd,6)=sd->status.int_;
- WFIFOL(fd,10)=sd->battle_status.int_ - sd->status.int_;
- len=14;
- break;
- case SP_DEX:
- WFIFOW(fd,0)=0x141;
- WFIFOL(fd,2)=type;
- WFIFOL(fd,6)=sd->status.dex;
- WFIFOL(fd,10)=sd->battle_status.dex - sd->status.dex;
- len=14;
- break;
- case SP_LUK:
- WFIFOW(fd,0)=0x141;
- WFIFOL(fd,2)=type;
- WFIFOL(fd,6)=sd->status.luk;
- WFIFOL(fd,10)=sd->battle_status.luk - sd->status.luk;
- len=14;
- break;
-
- case SP_CARTINFO:
- WFIFOW(fd,0)=0x121;
- WFIFOW(fd,2)=sd->cart_num;
- WFIFOW(fd,4)=MAX_CART;
- WFIFOL(fd,6)=sd->cart_weight;
- WFIFOL(fd,10)=sd->cart_weight_max;
- len=14;
- break;
-
- default:
- ShowError("clif_updatestatus : unrecognized type %d\n",type);
- return;
- }
- WFIFOSET(fd,len);
-}
-
-
-/// Notifies client of a parameter change of an another player (ZC_PAR_CHANGE_USER).
-/// 01ab <account id>.L <var id>.W <value>.L
-void clif_changestatus(struct map_session_data* sd,int type,int val)
-{
- unsigned char buf[12];
-
- nullpo_retv(sd);
-
- WBUFW(buf,0)=0x1ab;
- WBUFL(buf,2)=sd->bl.id;
- WBUFW(buf,6)=type;
-
- switch(type)
- {
- case SP_MANNER:
- WBUFL(buf,8)=val;
- break;
- default:
- ShowError("clif_changestatus : unrecognized type %d.\n",type);
- return;
- }
-
- clif_send(buf,packet_len(0x1ab),&sd->bl,AREA_WOS);
-}
-
-
-/// Updates sprite/style properties of an object.
-/// 00c3 <id>.L <type>.B <value>.B (ZC_SPRITE_CHANGE)
-/// 01d7 <id>.L <type>.B <value>.L (ZC_SPRITE_CHANGE2)
-void clif_changelook(struct block_list *bl,int type,int val)
-{
- unsigned char buf[16];
- struct map_session_data* sd = NULL;
- struct status_change* sc;
- struct view_data* vd;
- enum send_target target = AREA;
- nullpo_retv(bl);
-
- sd = BL_CAST(BL_PC, bl);
- sc = status_get_sc(bl);
- vd = status_get_viewdata(bl);
- //nullpo_ret(vd);
- if( vd ) //temp hack to let Warp Portal change appearance
- switch(type)
- {
- case LOOK_WEAPON:
- if (sd)
- {
- clif_get_weapon_view(sd, &vd->weapon, &vd->shield);
- val = vd->weapon;
- }
- else vd->weapon = val;
- break;
- case LOOK_SHIELD:
- if (sd)
- {
- clif_get_weapon_view(sd, &vd->weapon, &vd->shield);
- val = vd->shield;
- }
- else vd->shield = val;
- break;
- case LOOK_BASE:
- vd->class_ = val;
- if (vd->class_ == JOB_WEDDING || vd->class_ == JOB_XMAS || vd->class_ == JOB_SUMMER)
- vd->weapon = vd->shield = 0;
- if (vd->cloth_color && (
- (vd->class_ == JOB_WEDDING && battle_config.wedding_ignorepalette) ||
- (vd->class_ == JOB_XMAS && battle_config.xmas_ignorepalette) ||
- (vd->class_ == JOB_SUMMER && battle_config.summer_ignorepalette)
- ))
- clif_changelook(bl,LOOK_CLOTHES_COLOR,0);
- break;
- case LOOK_HAIR:
- vd->hair_style = val;
- break;
- case LOOK_HEAD_BOTTOM:
- vd->head_bottom = val;
- break;
- case LOOK_HEAD_TOP:
- vd->head_top = val;
- break;
- case LOOK_HEAD_MID:
- vd->head_mid = val;
- break;
- case LOOK_HAIR_COLOR:
- vd->hair_color = val;
- break;
- case LOOK_CLOTHES_COLOR:
- if (val && (
- (vd->class_ == JOB_WEDDING && battle_config.wedding_ignorepalette) ||
- (vd->class_ == JOB_XMAS && battle_config.xmas_ignorepalette) ||
- (vd->class_ == JOB_SUMMER && battle_config.summer_ignorepalette)
- ))
- val = 0;
- vd->cloth_color = val;
- break;
- case LOOK_SHOES:
-#if PACKETVER > 3
- if (sd) {
- int n;
- if((n = sd->equip_index[2]) >= 0 && sd->inventory_data[n]) {
- if(sd->inventory_data[n]->view_id > 0)
- val = sd->inventory_data[n]->view_id;
- else
- val = sd->status.inventory[n].nameid;
- } else
- val = 0;
- }
-#endif
- //Shoes? No packet uses this....
- break;
- case LOOK_BODY:
- case LOOK_FLOOR:
- // unknown purpose
- break;
- case LOOK_ROBE:
-#if PACKETVER < 20110111
- return;
-#else
- vd->robe = val;
-#endif
- break;
- }
-
- // prevent leaking the presence of GM-hidden objects
- if( sc && sc->option&OPTION_INVISIBLE )
- target = SELF;
-
-#if PACKETVER < 4
- WBUFW(buf,0)=0xc3;
- WBUFL(buf,2)=bl->id;
- WBUFB(buf,6)=type;
- WBUFB(buf,7)=val;
- clif_send(buf,packet_len(0xc3),bl,target);
-#else
- WBUFW(buf,0)=0x1d7;
- WBUFL(buf,2)=bl->id;
- if(type == LOOK_WEAPON || type == LOOK_SHIELD) {
- WBUFB(buf,6)=LOOK_WEAPON;
- WBUFW(buf,7)=vd->weapon;
- WBUFW(buf,9)=vd->shield;
- } else {
- WBUFB(buf,6)=type;
- WBUFL(buf,7)=val;
- }
- clif_send(buf,packet_len(0x1d7),bl,target);
-#endif
-}
-
-//Sends a change-base-look packet required for traps as they are triggered.
-void clif_changetraplook(struct block_list *bl,int val)
-{
- unsigned char buf[32];
-#if PACKETVER < 4
- WBUFW(buf,0)=0xc3;
- WBUFL(buf,2)=bl->id;
- WBUFB(buf,6)=LOOK_BASE;
- WBUFB(buf,7)=val;
- clif_send(buf,packet_len(0xc3),bl,AREA);
-#else
- WBUFW(buf,0)=0x1d7;
- WBUFL(buf,2)=bl->id;
- WBUFB(buf,6)=LOOK_BASE;
- WBUFW(buf,7)=val;
- WBUFW(buf,9)=0;
- clif_send(buf,packet_len(0x1d7),bl,AREA);
-#endif
-}
-
-//For the stupid cloth-dye bug. Resends the given view data to the area specified by bl.
-void clif_refreshlook(struct block_list *bl,int id,int type,int val,enum send_target target)
-{
- unsigned char buf[32];
-#if PACKETVER < 4
- WBUFW(buf,0)=0xc3;
- WBUFL(buf,2)=id;
- WBUFB(buf,6)=type;
- WBUFB(buf,7)=val;
- clif_send(buf,packet_len(0xc3),bl,target);
-#else
- WBUFW(buf,0)=0x1d7;
- WBUFL(buf,2)=id;
- WBUFB(buf,6)=type;
- WBUFW(buf,7)=val;
- WBUFW(buf,9)=0;
- clif_send(buf,packet_len(0x1d7),bl,target);
-#endif
-}
-
-
-/// Character status (ZC_STATUS).
-/// 00bd <stpoint>.W <str>.B <need str>.B <agi>.B <need agi>.B <vit>.B <need vit>.B
-/// <int>.B <need int>.B <dex>.B <need dex>.B <luk>.B <need luk>.B <atk>.W <atk2>.W
-/// <matk min>.W <matk max>.W <def>.W <def2>.W <mdef>.W <mdef2>.W <hit>.W
-/// <flee>.W <flee2>.W <crit>.W <aspd>.W <aspd2>.W
-void clif_initialstatus(struct map_session_data *sd)
-{
- int fd, mdef2;
- unsigned char *buf;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0xbd));
- buf=WFIFOP(fd,0);
-
- WBUFW(buf,0)=0xbd;
- WBUFW(buf,2)=min(sd->status.status_point, INT16_MAX);
- WBUFB(buf,4)=min(sd->status.str, UINT8_MAX);
- WBUFB(buf,5)=pc_need_status_point(sd,SP_STR,1);
- WBUFB(buf,6)=min(sd->status.agi, UINT8_MAX);
- WBUFB(buf,7)=pc_need_status_point(sd,SP_AGI,1);
- WBUFB(buf,8)=min(sd->status.vit, UINT8_MAX);
- WBUFB(buf,9)=pc_need_status_point(sd,SP_VIT,1);
- WBUFB(buf,10)=min(sd->status.int_, UINT8_MAX);
- WBUFB(buf,11)=pc_need_status_point(sd,SP_INT,1);
- WBUFB(buf,12)=min(sd->status.dex, UINT8_MAX);
- WBUFB(buf,13)=pc_need_status_point(sd,SP_DEX,1);
- WBUFB(buf,14)=min(sd->status.luk, UINT8_MAX);
- WBUFB(buf,15)=pc_need_status_point(sd,SP_LUK,1);
-
- WBUFW(buf,16) = pc_leftside_atk(sd);
- WBUFW(buf,18) = pc_rightside_atk(sd);
- WBUFW(buf,20) = pc_rightside_matk(sd);
- WBUFW(buf,22) = pc_leftside_matk(sd);
- WBUFW(buf,24) = pc_leftside_def(sd);
- WBUFW(buf,26) = pc_rightside_def(sd);
- WBUFW(buf,28) = pc_leftside_mdef(sd);
- mdef2 = pc_rightside_mdef(sd);
- WBUFW(buf,30) =
-#ifndef RENEWAL
- ( mdef2 < 0 ) ? 0 : //Negative check for Frenzy'ed characters.
-#endif
- mdef2;
- WBUFW(buf,32) = sd->battle_status.hit;
- WBUFW(buf,34) = sd->battle_status.flee;
- WBUFW(buf,36) = sd->battle_status.flee2/10;
- WBUFW(buf,38) = sd->battle_status.cri/10;
- WBUFW(buf,40) = sd->battle_status.amotion; // aspd
- WBUFW(buf,42) = 0; // always 0 (plusASPD)
-
- WFIFOSET(fd,packet_len(0xbd));
-
- clif_updatestatus(sd,SP_STR);
- clif_updatestatus(sd,SP_AGI);
- clif_updatestatus(sd,SP_VIT);
- clif_updatestatus(sd,SP_INT);
- clif_updatestatus(sd,SP_DEX);
- clif_updatestatus(sd,SP_LUK);
-
- clif_updatestatus(sd,SP_ATTACKRANGE);
- clif_updatestatus(sd,SP_ASPD);
-}
-
-
-/// Marks an ammunition item in inventory as equipped (ZC_EQUIP_ARROW).
-/// 013c <index>.W
-void clif_arrowequip(struct map_session_data *sd,int val)
-{
- int fd;
-
- nullpo_retv(sd);
-
- pc_stop_attack(sd); // [Valaris]
-
- fd=sd->fd;
- WFIFOHEAD(fd, packet_len(0x013c));
- WFIFOW(fd,0)=0x013c;
- WFIFOW(fd,2)=val+2; //Item ID of the arrow
- WFIFOSET(fd,packet_len(0x013c));
-}
-
-
-/// Ammunition action message (ZC_ACTION_FAILURE).
-/// 013b <type>.W
-/// type:
-/// 0 = MsgStringTable[242]="Please equip the proper ammunition first."
-/// 1 = MsgStringTable[243]="You can't Attack or use Skills because your Weight Limit has been exceeded."
-/// 2 = MsgStringTable[244]="You can't use Skills because Weight Limit has been exceeded."
-/// 3 = assassin, baby_assassin, assassin_cross => MsgStringTable[1040]="You have equipped throwing daggers."
-/// gunslinger => MsgStringTable[1175]="Bullets have been equipped."
-/// NOT ninja => MsgStringTable[245]="Ammunition has been equipped."
-void clif_arrow_fail(struct map_session_data *sd,int type)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd, packet_len(0x013b));
- WFIFOW(fd,0)=0x013b;
- WFIFOW(fd,2)=type;
- WFIFOSET(fd,packet_len(0x013b));
-}
-
-
-/// Presents a list of items, that can be processed by Arrow Crafting (ZC_MAKINGARROW_LIST).
-/// 01ad <packet len>.W { <name id>.W }*
-void clif_arrow_create_list(struct map_session_data *sd)
-{
- int i, c, j;
- int fd;
-
- nullpo_retv(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd, MAX_SKILL_ARROW_DB*2+4);
- WFIFOW(fd,0) = 0x1ad;
-
- for (i = 0, c = 0; i < MAX_SKILL_ARROW_DB; i++) {
- if (skill_arrow_db[i].nameid > 0 &&
- (j = pc_search_inventory(sd, skill_arrow_db[i].nameid)) >= 0 &&
- !sd->status.inventory[j].equip && sd->status.inventory[j].identify)
- {
- if ((j = itemdb_viewid(skill_arrow_db[i].nameid)) > 0)
- WFIFOW(fd,c*2+4) = j;
- else
- WFIFOW(fd,c*2+4) = skill_arrow_db[i].nameid;
- c++;
- }
- }
- WFIFOW(fd,2) = c*2+4;
- WFIFOSET(fd, WFIFOW(fd,2));
- if (c > 0) {
- sd->menuskill_id = AC_MAKINGARROW;
- sd->menuskill_val = c;
- }
-}
-
-
-/// Notifies the client, about the result of an status change request (ZC_STATUS_CHANGE_ACK).
-/// 00bc <status id>.W <result>.B <value>.B
-/// status id:
-/// SP_STR ~ SP_LUK
-/// result:
-/// 0 = failure
-/// 1 = success
-void clif_statusupack(struct map_session_data *sd,int type,int ok,int val)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0xbc));
- WFIFOW(fd,0)=0xbc;
- WFIFOW(fd,2)=type;
- WFIFOB(fd,4)=ok;
- WFIFOB(fd,5)=cap_value(val,0,UINT8_MAX);
- WFIFOSET(fd,packet_len(0xbc));
-}
-
-
-/// Notifies the client about the result of a request to equip an item (ZC_REQ_WEAR_EQUIP_ACK).
-/// 00aa <index>.W <equip location>.W <result>.B
-/// 00aa <index>.W <equip location>.W <view id>.W <result>.B (PACKETVER >= 20100629)
-/// result:
-/// 0 = failure
-/// 1 = success
-/// 2 = failure due to low level
-void clif_equipitemack(struct map_session_data *sd,int n,int pos,int ok)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0xaa));
- WFIFOW(fd,0)=0xaa;
- WFIFOW(fd,2)=n+2;
- WFIFOW(fd,4)=pos;
-#if PACKETVER < 20100629
- WFIFOB(fd,6)=ok;
-#else
- if (ok && sd->inventory_data[n]->equip&EQP_VISIBLE)
- WFIFOW(fd,6)=sd->inventory_data[n]->look;
- else
- WFIFOW(fd,6)=0;
- WFIFOB(fd,8)=ok;
-#endif
- WFIFOSET(fd,packet_len(0xaa));
-}
-
-
-/// Notifies the client about the result of a request to take off an item (ZC_REQ_TAKEOFF_EQUIP_ACK).
-/// 00ac <index>.W <equip location>.W <result>.B
-/// result:
-/// 0 = failure
-/// 1 = success
-void clif_unequipitemack(struct map_session_data *sd,int n,int pos,int ok)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0xac));
- WFIFOW(fd,0)=0xac;
- WFIFOW(fd,2)=n+2;
- WFIFOW(fd,4)=pos;
- WFIFOB(fd,6)=ok;
- WFIFOSET(fd,packet_len(0xac));
-}
-
-
-/// Notifies clients in the area about an special/visual effect (ZC_NOTIFY_EFFECT).
-/// 019b <id>.L <effect id>.L
-/// effect id:
-/// 0 = base level up
-/// 1 = job level up
-/// 2 = refine failure
-/// 3 = refine success
-/// 4 = game over
-/// 5 = pharmacy success
-/// 6 = pharmacy failure
-/// 7 = base level up (super novice)
-/// 8 = job level up (super novice)
-/// 9 = base level up (taekwon)
-void clif_misceffect(struct block_list* bl,int type)
-{
- unsigned char buf[32];
-
- nullpo_retv(bl);
-
- WBUFW(buf,0) = 0x19b;
- WBUFL(buf,2) = bl->id;
- WBUFL(buf,6) = type;
-
- clif_send(buf,packet_len(0x19b),bl,AREA);
-}
-
-
-/// Notifies clients in the area of a state change.
-/// 0119 <id>.L <body state>.W <health state>.W <effect state>.W <pk mode>.B (ZC_STATE_CHANGE)
-/// 0229 <id>.L <body state>.W <health state>.W <effect state>.L <pk mode>.B (ZC_STATE_CHANGE3)
-void clif_changeoption(struct block_list* bl)
-{
- unsigned char buf[32];
- struct status_change *sc;
- struct map_session_data* sd;
-
- nullpo_retv(bl);
- sc = status_get_sc(bl);
- if (!sc) return; //How can an option change if there's no sc?
- sd = BL_CAST(BL_PC, bl);
-
-#if PACKETVER >= 7
- WBUFW(buf,0) = 0x229;
- WBUFL(buf,2) = bl->id;
- WBUFW(buf,6) = sc->opt1;
- WBUFW(buf,8) = sc->opt2;
- WBUFL(buf,10) = sc->option;
- WBUFB(buf,14) = (sd)? sd->status.karma : 0;
- if(disguised(bl)) {
- clif_send(buf,packet_len(0x229),bl,AREA_WOS);
- WBUFL(buf,2) = -bl->id;
- clif_send(buf,packet_len(0x229),bl,SELF);
- WBUFL(buf,2) = bl->id;
- WBUFL(buf,10) = OPTION_INVISIBLE;
- clif_send(buf,packet_len(0x229),bl,SELF);
- } else
- clif_send(buf,packet_len(0x229),bl,AREA);
-#else
- WBUFW(buf,0) = 0x119;
- WBUFL(buf,2) = bl->id;
- WBUFW(buf,6) = sc->opt1;
- WBUFW(buf,8) = sc->opt2;
- WBUFW(buf,10) = sc->option;
- WBUFB(buf,12) = (sd)? sd->status.karma : 0;
- if(disguised(bl)) {
- clif_send(buf,packet_len(0x119),bl,AREA_WOS);
- WBUFL(buf,2) = -bl->id;
- clif_send(buf,packet_len(0x119),bl,SELF);
- WBUFL(buf,2) = bl->id;
- WBUFW(buf,10) = OPTION_INVISIBLE;
- clif_send(buf,packet_len(0x119),bl,SELF);
- } else
- clif_send(buf,packet_len(0x119),bl,AREA);
-#endif
-}
-
-
-/// Displays status change effects on NPCs/monsters (ZC_NPC_SHOWEFST_UPDATE).
-/// 028a <id>.L <effect state>.L <level>.L <showEFST>.L
-void clif_changeoption2(struct block_list* bl)
-{
- unsigned char buf[20];
- struct status_change *sc;
-
- sc = status_get_sc(bl);
- if (!sc) return; //How can an option change if there's no sc?
-
- WBUFW(buf,0) = 0x28a;
- WBUFL(buf,2) = bl->id;
- WBUFL(buf,6) = sc->option;
- WBUFL(buf,10) = clif_setlevel(bl);
- WBUFL(buf,14) = sc->opt3;
- if(disguised(bl)) {
- clif_send(buf,packet_len(0x28a),bl,AREA_WOS);
- WBUFL(buf,2) = -bl->id;
- clif_send(buf,packet_len(0x28a),bl,SELF);
- WBUFL(buf,2) = bl->id;
- WBUFL(buf,6) = OPTION_INVISIBLE;
- clif_send(buf,packet_len(0x28a),bl,SELF);
- } else
- clif_send(buf,packet_len(0x28a),bl,AREA);
-}
-
-
-/// Notifies the client about the result of an item use request.
-/// 00a8 <index>.W <amount>.W <result>.B (ZC_USE_ITEM_ACK)
-/// 01c8 <index>.W <name id>.W <id>.L <amount>.W <result>.B (ZC_USE_ITEM_ACK2)
-void clif_useitemack(struct map_session_data *sd,int index,int amount,bool ok)
-{
- nullpo_retv(sd);
-
- if(!ok) {
- int fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0xa8));
- WFIFOW(fd,0)=0xa8;
- WFIFOW(fd,2)=index+2;
- WFIFOW(fd,4)=amount;
- WFIFOB(fd,6)=ok;
- WFIFOSET(fd,packet_len(0xa8));
- }
- else {
-#if PACKETVER < 3
- int fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0xa8));
- WFIFOW(fd,0)=0xa8;
- WFIFOW(fd,2)=index+2;
- WFIFOW(fd,4)=amount;
- WFIFOB(fd,6)=ok;
- WFIFOSET(fd,packet_len(0xa8));
-#else
- unsigned char buf[32];
-
- WBUFW(buf,0)=0x1c8;
- WBUFW(buf,2)=index+2;
- if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0)
- WBUFW(buf,4)=sd->inventory_data[index]->view_id;
- else
- WBUFW(buf,4)=sd->status.inventory[index].nameid;
- WBUFL(buf,6)=sd->bl.id;
- WBUFW(buf,10)=amount;
- WBUFB(buf,12)=ok;
- clif_send(buf,packet_len(0x1c8),&sd->bl,AREA);
-#endif
- }
-}
-
-
-/// Inform client whether chatroom creation was successful or not (ZC_ACK_CREATE_CHATROOM).
-/// 00d6 <flag>.B
-/// flag:
-/// 0 = Room has been successfully created (opens chat room)
-/// 1 = Room limit exceeded
-/// 2 = Same room already exists
-void clif_createchat(struct map_session_data* sd, int flag)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0xd6));
- WFIFOW(fd,0) = 0xd6;
- WFIFOB(fd,2) = flag;
- WFIFOSET(fd,packet_len(0xd6));
-}
-
-
-/// Display a chat above the owner (ZC_ROOM_NEWENTRY).
-/// 00d7 <packet len>.W <owner id>.L <char id>.L <limit>.W <users>.W <type>.B <title>.?B
-/// type:
-/// 0 = private (password protected)
-/// 1 = public
-/// 2 = arena (npc waiting room)
-/// 3 = PK zone (non-clickable)
-void clif_dispchat(struct chat_data* cd, int fd)
-{
- unsigned char buf[128];
- uint8 type;
-
- if( cd == NULL || cd->owner == NULL )
- return;
-
- type = (cd->owner->type == BL_PC ) ? (cd->pub) ? 1 : 0
- : (cd->owner->type == BL_NPC) ? (cd->limit) ? 2 : 3
- : 1;
-
- WBUFW(buf, 0) = 0xd7;
- WBUFW(buf, 2) = 17 + strlen(cd->title);
- WBUFL(buf, 4) = cd->owner->id;
- WBUFL(buf, 8) = cd->bl.id;
- WBUFW(buf,12) = cd->limit;
- WBUFW(buf,14) = (cd->owner->type == BL_NPC) ? cd->users+1 : cd->users;
- WBUFB(buf,16) = type;
- memcpy((char*)WBUFP(buf,17), cd->title, strlen(cd->title)); // not zero-terminated
-
- if( fd ) {
- WFIFOHEAD(fd,WBUFW(buf,2));
- memcpy(WFIFOP(fd,0),buf,WBUFW(buf,2));
- WFIFOSET(fd,WBUFW(buf,2));
- } else {
- clif_send(buf,WBUFW(buf,2),cd->owner,AREA_WOSC);
- }
-}
-
-
-/// Chatroom properties adjustment (ZC_CHANGE_CHATROOM).
-/// 00df <packet len>.W <owner id>.L <chat id>.L <limit>.W <users>.W <type>.B <title>.?B
-/// type:
-/// 0 = private (password protected)
-/// 1 = public
-/// 2 = arena (npc waiting room)
-/// 3 = PK zone (non-clickable)
-void clif_changechatstatus(struct chat_data* cd)
-{
- unsigned char buf[128];
- uint8 type;
-
- if( cd == NULL || cd->usersd[0] == NULL )
- return;
-
- type = (cd->owner->type == BL_PC ) ? (cd->pub) ? 1 : 0
- : (cd->owner->type == BL_NPC) ? (cd->limit) ? 2 : 3
- : 1;
-
- WBUFW(buf, 0) = 0xdf;
- WBUFW(buf, 2) = 17 + strlen(cd->title);
- WBUFL(buf, 4) = cd->owner->id;
- WBUFL(buf, 8) = cd->bl.id;
- WBUFW(buf,12) = cd->limit;
- WBUFW(buf,14) = (cd->owner->type == BL_NPC) ? cd->users+1 : cd->users;
- WBUFB(buf,16) = type;
- memcpy((char*)WBUFP(buf,17), cd->title, strlen(cd->title)); // not zero-terminated
-
- clif_send(buf,WBUFW(buf,2),cd->owner,CHAT);
-}
-
-
-/// Removes the chatroom (ZC_DESTROY_ROOM).
-/// 00d8 <chat id>.L
-void clif_clearchat(struct chat_data *cd,int fd)
-{
- unsigned char buf[32];
-
- nullpo_retv(cd);
-
- WBUFW(buf,0) = 0xd8;
- WBUFL(buf,2) = cd->bl.id;
- if( fd ) {
- WFIFOHEAD(fd,packet_len(0xd8));
- memcpy(WFIFOP(fd,0),buf,packet_len(0xd8));
- WFIFOSET(fd,packet_len(0xd8));
- } else {
- clif_send(buf,packet_len(0xd8),cd->owner,AREA_WOSC);
- }
-}
-
-
-/// Displays messages regarding join chat failures (ZC_REFUSE_ENTER_ROOM).
-/// 00da <result>.B
-/// result:
-/// 0 = room full
-/// 1 = wrong password
-/// 2 = kicked
-/// 3 = success (no message)
-/// 4 = no enough zeny
-/// 5 = too low level
-/// 6 = too high level
-/// 7 = unsuitable job class
-void clif_joinchatfail(struct map_session_data *sd,int flag)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0xda));
- WFIFOW(fd,0) = 0xda;
- WFIFOB(fd,2) = flag;
- WFIFOSET(fd,packet_len(0xda));
-}
-
-
-/// Notifies the client about entering a chatroom (ZC_ENTER_ROOM).
-/// 00db <packet len>.W <chat id>.L { <role>.L <name>.24B }*
-/// role:
-/// 0 = owner (menu)
-/// 1 = normal
-void clif_joinchatok(struct map_session_data *sd,struct chat_data* cd)
-{
- int fd;
- int i,t;
-
- nullpo_retv(sd);
- nullpo_retv(cd);
-
- fd = sd->fd;
- if (!session_isActive(fd))
- return;
- t = (int)(cd->owner->type == BL_NPC);
- WFIFOHEAD(fd, 8 + (28*(cd->users+t)));
- WFIFOW(fd, 0) = 0xdb;
- WFIFOW(fd, 2) = 8 + (28*(cd->users+t));
- WFIFOL(fd, 4) = cd->bl.id;
-
- if(cd->owner->type == BL_NPC){
- WFIFOL(fd, 30) = 1;
- WFIFOL(fd, 8) = 0;
- memcpy(WFIFOP(fd, 12), ((struct npc_data *)cd->owner)->name, NAME_LENGTH);
- for (i = 0; i < cd->users; i++) {
- WFIFOL(fd, 8+(i+1)*28) = 1;
- memcpy(WFIFOP(fd, 8+(i+t)*28+4), cd->usersd[i]->status.name, NAME_LENGTH);
- }
- } else
- for (i = 0; i < cd->users; i++) {
- WFIFOL(fd, 8+i*28) = (i != 0 || cd->owner->type == BL_NPC);
- memcpy(WFIFOP(fd, 8+(i+t)*28+4), cd->usersd[i]->status.name, NAME_LENGTH);
- }
- WFIFOSET(fd, WFIFOW(fd, 2));
-}
-
-
-/// Notifies clients in a chat about a new member (ZC_MEMBER_NEWENTRY).
-/// 00dc <users>.W <name>.24B
-void clif_addchat(struct chat_data* cd,struct map_session_data *sd)
-{
- unsigned char buf[32];
-
- nullpo_retv(sd);
- nullpo_retv(cd);
-
- WBUFW(buf, 0) = 0xdc;
- WBUFW(buf, 2) = cd->users;
- memcpy(WBUFP(buf, 4),sd->status.name,NAME_LENGTH);
- clif_send(buf,packet_len(0xdc),&sd->bl,CHAT_WOS);
-}
-
-
-/// Announce the new owner (ZC_ROLE_CHANGE).
-/// 00e1 <role>.L <nick>.24B
-/// role:
-/// 0 = owner (menu)
-/// 1 = normal
-void clif_changechatowner(struct chat_data* cd, struct map_session_data* sd)
-{
- unsigned char buf[64];
-
- nullpo_retv(sd);
- nullpo_retv(cd);
-
- WBUFW(buf, 0) = 0xe1;
- WBUFL(buf, 2) = 1;
- memcpy(WBUFP(buf,6),cd->usersd[0]->status.name,NAME_LENGTH);
-
- WBUFW(buf,30) = 0xe1;
- WBUFL(buf,32) = 0;
- memcpy(WBUFP(buf,36),sd->status.name,NAME_LENGTH);
-
- clif_send(buf,packet_len(0xe1)*2,&sd->bl,CHAT);
-}
-
-
-/// Notify about user leaving the chatroom (ZC_MEMBER_EXIT).
-/// 00dd <users>.W <nick>.24B <flag>.B
-/// flag:
-/// 0 = left
-/// 1 = kicked
-void clif_leavechat(struct chat_data* cd, struct map_session_data* sd, bool flag)
-{
- unsigned char buf[32];
-
- nullpo_retv(sd);
- nullpo_retv(cd);
-
- WBUFW(buf, 0) = 0xdd;
- WBUFW(buf, 2) = cd->users-1;
- memcpy(WBUFP(buf,4),sd->status.name,NAME_LENGTH);
- WBUFB(buf,28) = flag;
-
- clif_send(buf,packet_len(0xdd),&sd->bl,CHAT);
-}
-
-
-/// Opens a trade request window from char 'name'.
-/// 00e5 <nick>.24B (ZC_REQ_EXCHANGE_ITEM)
-/// 01f4 <nick>.24B <charid>.L <baselvl>.W (ZC_REQ_EXCHANGE_ITEM2)
-void clif_traderequest(struct map_session_data* sd, const char* name)
-{
- int fd = sd->fd;
-
-#if PACKETVER < 6
- WFIFOHEAD(fd,packet_len(0xe5));
- WFIFOW(fd,0) = 0xe5;
- safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH);
- WFIFOSET(fd,packet_len(0xe5));
-#else
- struct map_session_data* tsd = map_id2sd(sd->trade_partner);
- if( !tsd ) return;
-
- WFIFOHEAD(fd,packet_len(0x1f4));
- WFIFOW(fd,0) = 0x1f4;
- safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH);
- WFIFOL(fd,26) = tsd->status.char_id;
- WFIFOW(fd,30) = tsd->status.base_level;
- WFIFOSET(fd,packet_len(0x1f4));
-#endif
-}
-
-
-/// Reply to a trade-request.
-/// 00e7 <result>.B (ZC_ACK_EXCHANGE_ITEM)
-/// 01f5 <result>.B <charid>.L <baselvl>.W (ZC_ACK_EXCHANGE_ITEM2)
-/// result:
-/// 0 = Char is too far
-/// 1 = Character does not exist
-/// 2 = Trade failed
-/// 3 = Accept
-/// 4 = Cancel
-/// 5 = Busy
-void clif_tradestart(struct map_session_data* sd, uint8 type)
-{
- int fd = sd->fd;
- struct map_session_data* tsd = map_id2sd(sd->trade_partner);
- if( PACKETVER < 6 || !tsd ) {
- WFIFOHEAD(fd,packet_len(0xe7));
- WFIFOW(fd,0) = 0xe7;
- WFIFOB(fd,2) = type;
- WFIFOSET(fd,packet_len(0xe7));
- } else {
- WFIFOHEAD(fd,packet_len(0x1f5));
- WFIFOW(fd,0) = 0x1f5;
- WFIFOB(fd,2) = type;
- WFIFOL(fd,3) = tsd->status.char_id;
- WFIFOW(fd,7) = tsd->status.base_level;
- WFIFOSET(fd,packet_len(0x1f5));
- }
-}
-
-
-/// Notifies the client about an item from other player in current trade.
-/// 00e9 <amount>.L <nameid>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_EXCHANGE_ITEM)
-/// 080f <nameid>.W <item type>.B <amount>.L <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_EXCHANGE_ITEM2)
-void clif_tradeadditem(struct map_session_data* sd, struct map_session_data* tsd, int index, int amount)
-{
- int fd;
- unsigned char *buf;
-#if PACKETVER < 20100223
- const int cmd = 0xe9;
-#else
- const int cmd = 0x80f;
-#endif
- nullpo_retv(sd);
- nullpo_retv(tsd);
-
- fd = tsd->fd;
- buf = WFIFOP(fd,0);
- WFIFOHEAD(fd,packet_len(cmd));
- WBUFW(buf,0) = cmd;
- if( index == 0 )
- {
-#if PACKETVER < 20100223
- WBUFL(buf,2) = amount; //amount
- WBUFW(buf,6) = 0; // type id
-#else
- WBUFW(buf,2) = 0; // type id
- WBUFB(buf,4) = 0; // item type
- WBUFL(buf,5) = amount; // amount
- buf = WBUFP(buf,1); //Advance 1B
-#endif
- WBUFB(buf,8) = 0; //identify flag
- WBUFB(buf,9) = 0; // attribute
- WBUFB(buf,10)= 0; //refine
- WBUFW(buf,11)= 0; //card (4w)
- WBUFW(buf,13)= 0; //card (4w)
- WBUFW(buf,15)= 0; //card (4w)
- WBUFW(buf,17)= 0; //card (4w)
- }
- else
- {
- index -= 2; //index fix
-#if PACKETVER < 20100223
- WBUFL(buf,2) = amount; //amount
- if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0)
- WBUFW(buf,6) = sd->inventory_data[index]->view_id;
- else
- WBUFW(buf,6) = sd->status.inventory[index].nameid; // type id
-#else
- if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0)
- WBUFW(buf,2) = sd->inventory_data[index]->view_id;
- else
- WBUFW(buf,2) = sd->status.inventory[index].nameid; // type id
- WBUFB(buf,4) = sd->inventory_data[index]->type; // item type
- WBUFL(buf,5) = amount; // amount
- buf = WBUFP(buf,1); //Advance 1B
-#endif
- WBUFB(buf,8) = sd->status.inventory[index].identify; //identify flag
- WBUFB(buf,9) = sd->status.inventory[index].attribute; // attribute
- WBUFB(buf,10)= sd->status.inventory[index].refine; //refine
- clif_addcards(WBUFP(buf, 11), &sd->status.inventory[index]);
- }
- WFIFOSET(fd,packet_len(cmd));
-}
-
-
-/// Notifies the client about the result of request to add an item to the current trade (ZC_ACK_ADD_EXCHANGE_ITEM).
-/// 00ea <index>.W <result>.B
-/// result:
-/// 0 = success
-/// 1 = overweight
-/// 2 = trade canceled
-void clif_tradeitemok(struct map_session_data* sd, int index, int fail)
-{
- int fd;
- nullpo_retv(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0xea));
- WFIFOW(fd,0) = 0xea;
- WFIFOW(fd,2) = index;
- WFIFOB(fd,4) = fail;
- WFIFOSET(fd,packet_len(0xea));
-}
-
-
-/// Notifies the client about finishing one side of the current trade (ZC_CONCLUDE_EXCHANGE_ITEM).
-/// 00ec <who>.B
-/// who:
-/// 0 = self
-/// 1 = other player
-void clif_tradedeal_lock(struct map_session_data* sd, int fail)
-{
- int fd;
- nullpo_retv(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0xec));
- WFIFOW(fd,0) = 0xec;
- WFIFOB(fd,2) = fail;
- WFIFOSET(fd,packet_len(0xec));
-}
-
-
-/// Notifies the client about the trade being canceled (ZC_CANCEL_EXCHANGE_ITEM).
-/// 00ee
-void clif_tradecancelled(struct map_session_data* sd)
-{
- int fd;
- nullpo_retv(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0xee));
- WFIFOW(fd,0) = 0xee;
- WFIFOSET(fd,packet_len(0xee));
-}
-
-
-/// Result of a trade (ZC_EXEC_EXCHANGE_ITEM).
-/// 00f0 <result>.B
-/// result:
-/// 0 = success
-/// 1 = failure
-void clif_tradecompleted(struct map_session_data* sd, int fail)
-{
- int fd;
- nullpo_retv(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0xf0));
- WFIFOW(fd,0) = 0xf0;
- WFIFOB(fd,2) = fail;
- WFIFOSET(fd,packet_len(0xf0));
-}
-
-
-/// Resets the trade window on the send side (ZC_EXCHANGEITEM_UNDO).
-/// 00f1
-/// NOTE: Unknown purpose. Items are not removed until the window is
-/// refreshed (ex. by putting another item in there).
-void clif_tradeundo(struct map_session_data* sd)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0xf1));
- WFIFOW(fd,0) = 0xf1;
- WFIFOSET(fd,packet_len(0xf1));
-}
-
-
-/// Updates storage total amount (ZC_NOTIFY_STOREITEM_COUNTINFO).
-/// 00f2 <current count>.W <max count>.W
-void clif_updatestorageamount(struct map_session_data* sd, int amount, int max_amount)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0xf2));
- WFIFOW(fd,0) = 0xf2;
- WFIFOW(fd,2) = amount;
- WFIFOW(fd,4) = max_amount;
- WFIFOSET(fd,packet_len(0xf2));
-}
-
-
-/// Notifies the client of an item being added to the storage.
-/// 00f4 <index>.W <amount>.L <nameid>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_STORE)
-/// 01c4 <index>.W <amount>.L <nameid>.W <type>.B <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_STORE2)
-void clif_storageitemadded(struct map_session_data* sd, struct item* i, int index, int amount)
-{
- int view,fd;
-
- nullpo_retv(sd);
- nullpo_retv(i);
- fd=sd->fd;
- view = itemdb_viewid(i->nameid);
-
-#if PACKETVER < 5
- WFIFOHEAD(fd,packet_len(0xf4));
- WFIFOW(fd, 0) = 0xf4; // Storage item added
- WFIFOW(fd, 2) = index+1; // index
- WFIFOL(fd, 4) = amount; // amount
- WFIFOW(fd, 8) = ( view > 0 ) ? view : i->nameid; // id
- WFIFOB(fd,10) = i->identify; //identify flag
- WFIFOB(fd,11) = i->attribute; // attribute
- WFIFOB(fd,12) = i->refine; //refine
- clif_addcards(WFIFOP(fd,13), i);
- WFIFOSET(fd,packet_len(0xf4));
-#else
- WFIFOHEAD(fd,packet_len(0x1c4));
- WFIFOW(fd, 0) = 0x1c4; // Storage item added
- WFIFOW(fd, 2) = index+1; // index
- WFIFOL(fd, 4) = amount; // amount
- WFIFOW(fd, 8) = ( view > 0 ) ? view : i->nameid; // id
- WFIFOB(fd,10) = itemdb_type(i->nameid); //type
- WFIFOB(fd,11) = i->identify; //identify flag
- WFIFOB(fd,12) = i->attribute; // attribute
- WFIFOB(fd,13) = i->refine; //refine
- clif_addcards(WFIFOP(fd,14), i);
- WFIFOSET(fd,packet_len(0x1c4));
-#endif
-}
-
-
-/// Notifies the client of an item being deleted from the storage (ZC_DELETE_ITEM_FROM_STORE).
-/// 00f6 <index>.W <amount>.L
-void clif_storageitemremoved(struct map_session_data* sd, int index, int amount)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0xf6));
- WFIFOW(fd,0)=0xf6; // Storage item removed
- WFIFOW(fd,2)=index+1;
- WFIFOL(fd,4)=amount;
- WFIFOSET(fd,packet_len(0xf6));
-}
-
-
-/// Closes storage (ZC_CLOSE_STORE).
-/// 00f8
-void clif_storageclose(struct map_session_data* sd)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0xf8));
- WFIFOW(fd,0) = 0xf8; // Storage Closed
- WFIFOSET(fd,packet_len(0xf8));
-}
-
-/*==========================================
- * Server tells 'sd' player client the abouts of 'dstsd' player
- *------------------------------------------*/
-static void clif_getareachar_pc(struct map_session_data* sd,struct map_session_data* dstsd)
-{
- struct block_list *d_bl;
- int i;
-
- if( dstsd->chatID ) {
- struct chat_data *cd = NULL;
- if( (cd = (struct chat_data*)map_id2bl(dstsd->chatID)) && cd->usersd[0]==dstsd)
- clif_dispchat(cd,sd->fd);
- } else if( dstsd->state.vending )
- clif_showvendingboard(&dstsd->bl,dstsd->message,sd->fd);
- else if( dstsd->state.buyingstore )
- clif_buyingstore_entry_single(sd, dstsd);
-
- if(dstsd->spiritball > 0)
- clif_spiritball_single(sd->fd, dstsd);
- for(i = 1; i < 5; i++){
- if( dstsd->talisman[i] > 0 )
- clif_talisman_single(sd->fd, dstsd, i);
- }
- if( dstsd->sc.option&OPTION_MOUNTING ) {
- //New Mounts are not complaint to the original method, so we gotta tell this guy that I'm mounting.
- clif_status_load_single(sd->fd,dstsd->bl.id,SI_ALL_RIDING,2,1,0,0);
- }
-#ifdef NEW_CARTS
- if( dstsd->sc.data[SC_PUSH_CART] )
- clif_status_load_single(sd->fd, dstsd->bl.id, SI_ON_PUSH_CART, 2, dstsd->sc.data[SC_PUSH_CART]->val1, 0, 0);
-#endif
- if( (sd->status.party_id && dstsd->status.party_id == sd->status.party_id) || //Party-mate, or hpdisp setting.
- (sd->bg_id && sd->bg_id == dstsd->bg_id) || //BattleGround
- pc_has_permission(sd, PC_PERM_VIEW_HPMETER)
- )
- clif_hpmeter_single(sd->fd, dstsd->bl.id, dstsd->battle_status.hp, dstsd->battle_status.max_hp);
-
- // display link (sd - dstsd) to sd
- ARR_FIND( 0, 5, i, sd->devotion[i] == dstsd->bl.id );
- if( i < 5 ) clif_devotion(&sd->bl, sd);
- // display links (dstsd - devotees) to sd
- ARR_FIND( 0, 5, i, dstsd->devotion[i] > 0 );
- if( i < 5 ) clif_devotion(&dstsd->bl, sd);
- // display link (dstsd - crusader) to sd
- if( dstsd->sc.data[SC_DEVOTION] && (d_bl = map_id2bl(dstsd->sc.data[SC_DEVOTION]->val1)) != NULL )
- clif_devotion(d_bl, sd);
-}
-
-void clif_getareachar_unit(struct map_session_data* sd,struct block_list *bl)
-{
- uint8 buf[128];
- struct unit_data *ud;
- struct view_data *vd;
- int len;
-
- vd = status_get_viewdata(bl);
- if (!vd || vd->class_ == INVISIBLE_CLASS)
- return;
-
- /**
- * Hide NPC from maya purple card.
- **/
- if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE))
- return;
-
- ud = unit_bl2ud(bl);
- len = ( ud && ud->walktimer != INVALID_TIMER ) ? clif_set_unit_walking(bl,ud,buf) : clif_set_unit_idle(bl,buf,false);
- clif_send(buf,len,&sd->bl,SELF);
-
- if (vd->cloth_color)
- clif_refreshlook(&sd->bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,SELF);
-
- switch (bl->type)
- {
- case BL_PC:
- {
- TBL_PC* tsd = (TBL_PC*)bl;
- clif_getareachar_pc(sd, tsd);
- if(tsd->state.size==SZ_BIG) // tiny/big players [Valaris]
- clif_specialeffect_single(bl,423,sd->fd);
- else if(tsd->state.size==SZ_MEDIUM)
- clif_specialeffect_single(bl,421,sd->fd);
- if( tsd->bg_id && map[tsd->bl.m].flag.battleground )
- clif_sendbgemblem_single(sd->fd,tsd);
- if( tsd->sc.data[SC_CAMOUFLAGE] )
- clif_status_load(bl,SI_CAMOUFLAGE,1);
- }
- break;
- case BL_MER: // Devotion Effects
- if( ((TBL_MER*)bl)->devotion_flag )
- clif_devotion(bl, sd);
- break;
- case BL_NPC:
- {
- TBL_NPC* nd = (TBL_NPC*)bl;
- if( nd->chat_id )
- clif_dispchat((struct chat_data*)map_id2bl(nd->chat_id),sd->fd);
- if( nd->size == SZ_BIG )
- clif_specialeffect_single(bl,423,sd->fd);
- else if( nd->size == SZ_MEDIUM )
- clif_specialeffect_single(bl,421,sd->fd);
- }
- break;
- case BL_MOB:
- {
- TBL_MOB* md = (TBL_MOB*)bl;
- if(md->special_state.size==SZ_BIG) // tiny/big mobs [Valaris]
- clif_specialeffect_single(bl,423,sd->fd);
- else if(md->special_state.size==SZ_MEDIUM)
- clif_specialeffect_single(bl,421,sd->fd);
-#if PACKETVER >= 20120404
- if( !(md->status.mode&MD_BOSS) ){
- int i;
- for(i = 0; i < DAMAGELOG_SIZE; i++)// must show hp bar to all char who already hit the mob.
- if( md->dmglog[i].id == sd->status.char_id )
- clif_monster_hp_bar(md, sd->fd);
- }
-#endif
- }
- break;
- case BL_PET:
- if (vd->head_bottom)
- clif_pet_equip(sd, (TBL_PET*)bl); // needed to display pet equip properly
- break;
- }
-}
-
-//Modifies the type of damage according to status changes [Skotlex]
-//Aegis data specifies that: 4 endure against single hit sources, 9 against multi-hit.
-static inline int clif_calc_delay(int type, int div, int damage, int delay)
-{
- return ( delay == 0 && damage > 0 ) ? ( div > 1 ? 9 : 4 ) : type;
-}
-
-/*==========================================
- * Estimates walk delay based on the damage criteria. [Skotlex]
- *------------------------------------------*/
-static int clif_calc_walkdelay(struct block_list *bl,int delay, int type, int damage, int div_)
-{
- if (type == 4 || type == 9 || damage <=0)
- return 0;
-
- if (bl->type == BL_PC) {
- if (battle_config.pc_walk_delay_rate != 100)
- delay = delay*battle_config.pc_walk_delay_rate/100;
- } else
- if (battle_config.walk_delay_rate != 100)
- delay = delay*battle_config.walk_delay_rate/100;
-
- if (div_ > 1) //Multi-hit skills mean higher delays.
- delay += battle_config.multihit_delay*(div_-1);
-
- return delay>0?delay:1; //Return 1 to specify there should be no noticeable delay, but you should stop walking.
-}
-
-
-/// Sends a 'damage' packet (src performs action on dst)
-/// 008a <src ID>.L <dst ID>.L <server tick>.L <src speed>.L <dst speed>.L <damage>.W <div>.W <type>.B <damage2>.W (ZC_NOTIFY_ACT)
-/// 02e1 <src ID>.L <dst ID>.L <server tick>.L <src speed>.L <dst speed>.L <damage>.L <div>.W <type>.B <damage2>.L (ZC_NOTIFY_ACT2)
-/// type:
-/// 0 = damage [ damage: total damage, div: amount of hits, damage2: assassin dual-wield damage ]
-/// 1 = pick up item
-/// 2 = sit down
-/// 3 = stand up
-/// 4 = damage (endure)
-/// 5 = (splash?)
-/// 6 = (skill?)
-/// 7 = (repeat damage?)
-/// 8 = multi-hit damage
-/// 9 = multi-hit damage (endure)
-/// 10 = critical hit
-/// 11 = lucky dodge
-/// 12 = (touch skill?)
-int clif_damage(struct block_list* src, struct block_list* dst, unsigned int tick, int sdelay, int ddelay, int damage, int div, int type, int damage2)
-{
- unsigned char buf[33];
- struct status_change *sc;
-#if PACKETVER < 20071113
- const int cmd = 0x8a;
-#else
- const int cmd = 0x2e1;
-#endif
-
- nullpo_ret(src);
- nullpo_ret(dst);
-
- type = clif_calc_delay(type,div,damage+damage2,ddelay);
- sc = status_get_sc(dst);
- if(sc && sc->count) {
- if(sc->data[SC_HALLUCINATION]) {
- if(damage) damage = damage*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100;
- if(damage2) damage2 = damage2*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100;
- }
- }
-
- WBUFW(buf,0)=cmd;
- WBUFL(buf,2)=src->id;
- WBUFL(buf,6)=dst->id;
- WBUFL(buf,10)=tick;
- WBUFL(buf,14)=sdelay;
- WBUFL(buf,18)=ddelay;
-#if PACKETVER < 20071113
- if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
- WBUFW(buf,22)=damage?div:0;
- WBUFW(buf,27)=damage2?div:0;
- } else {
- WBUFW(buf,22)=min(damage, INT16_MAX);
- WBUFW(buf,27)=damage2;
- }
- WBUFW(buf,24)=div;
- WBUFB(buf,26)=type;
-#else
- if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
- WBUFL(buf,22)=damage?div:0;
- WBUFL(buf,29)=damage2?div:0;
- } else {
- WBUFL(buf,22)=damage;
- WBUFL(buf,29)=damage2;
- }
- WBUFW(buf,26)=div;
- WBUFB(buf,28)=type;
-#endif
- if(disguised(dst)) {
- clif_send(buf,packet_len(cmd),dst,AREA_WOS);
- WBUFL(buf,6) = -dst->id;
- clif_send(buf,packet_len(cmd),dst,SELF);
- } else
- clif_send(buf,packet_len(cmd),dst,AREA);
-
- if(disguised(src)) {
- WBUFL(buf,2) = -src->id;
- if (disguised(dst))
- WBUFL(buf,6) = dst->id;
-#if PACKETVER < 20071113
- if(damage > 0) WBUFW(buf,22) = -1;
- if(damage2 > 0) WBUFW(buf,27) = -1;
-#else
- if(damage > 0) WBUFL(buf,22) = -1;
- if(damage2 > 0) WBUFL(buf,29) = -1;
-#endif
- clif_send(buf,packet_len(cmd),src,SELF);
- }
-
- if(src == dst) {
- unit_setdir(src,unit_getdir(src));
- }
- //Return adjusted can't walk delay for further processing.
- return clif_calc_walkdelay(dst,ddelay,type,damage+damage2,div);
-}
-
-/*==========================================
- * src picks up dst
- *------------------------------------------*/
-void clif_takeitem(struct block_list* src, struct block_list* dst)
-{
- //clif_damage(src,dst,0,0,0,0,0,1,0);
- unsigned char buf[32];
-
- nullpo_retv(src);
- nullpo_retv(dst);
-
- WBUFW(buf, 0) = 0x8a;
- WBUFL(buf, 2) = src->id;
- WBUFL(buf, 6) = dst->id;
- WBUFB(buf,26) = 1;
- clif_send(buf, packet_len(0x8a), src, AREA);
-
-}
-
-/*==========================================
- * inform clients in area that `bl` is sitting
- *------------------------------------------*/
-void clif_sitting(struct block_list* bl)
-{
- unsigned char buf[32];
- nullpo_retv(bl);
-
- WBUFW(buf, 0) = 0x8a;
- WBUFL(buf, 2) = bl->id;
- WBUFB(buf,26) = 2;
- clif_send(buf, packet_len(0x8a), bl, AREA);
-
- if(disguised(bl)) {
- WBUFL(buf, 2) = - bl->id;
- clif_send(buf, packet_len(0x8a), bl, SELF);
- }
-}
-
-/*==========================================
- * inform clients in area that `bl` is standing
- *------------------------------------------*/
-void clif_standing(struct block_list* bl)
-{
- unsigned char buf[32];
- nullpo_retv(bl);
-
- WBUFW(buf, 0) = 0x8a;
- WBUFL(buf, 2) = bl->id;
- WBUFB(buf,26) = 3;
- clif_send(buf, packet_len(0x8a), bl, AREA);
-
- if(disguised(bl)) {
- WBUFL(buf, 2) = - bl->id;
- clif_send(buf, packet_len(0x8a), bl, SELF);
- }
-}
-
-
-/// Inform client(s) about a map-cell change (ZC_UPDATE_MAPINFO).
-/// 0192 <x>.W <y>.W <type>.W <map name>.16B
-void clif_changemapcell(int fd, int16 m, int x, int y, int type, enum send_target target)
-{
- unsigned char buf[32];
-
- WBUFW(buf,0) = 0x192;
- WBUFW(buf,2) = x;
- WBUFW(buf,4) = y;
- WBUFW(buf,6) = type;
- mapindex_getmapname_ext(map[m].name,(char*)WBUFP(buf,8));
-
- if( fd )
- {
- WFIFOHEAD(fd,packet_len(0x192));
- memcpy(WFIFOP(fd,0), buf, packet_len(0x192));
- WFIFOSET(fd,packet_len(0x192));
- }
- else
- {
- struct block_list dummy_bl;
- dummy_bl.type = BL_NUL;
- dummy_bl.x = x;
- dummy_bl.y = y;
- dummy_bl.m = m;
- clif_send(buf,packet_len(0x192),&dummy_bl,target);
- }
-}
-
-
-/// Notifies the client about an item on floor (ZC_ITEM_ENTRY).
-/// 009d <id>.L <name id>.W <identified>.B <x>.W <y>.W <amount>.W <subX>.B <subY>.B
-void clif_getareachar_item(struct map_session_data* sd,struct flooritem_data* fitem)
-{
- int view,fd;
- fd=sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x9d));
- WFIFOW(fd,0)=0x9d;
- WFIFOL(fd,2)=fitem->bl.id;
- if((view = itemdb_viewid(fitem->item_data.nameid)) > 0)
- WFIFOW(fd,6)=view;
- else
- WFIFOW(fd,6)=fitem->item_data.nameid;
- WFIFOB(fd,8)=fitem->item_data.identify;
- WFIFOW(fd,9)=fitem->bl.x;
- WFIFOW(fd,11)=fitem->bl.y;
- WFIFOW(fd,13)=fitem->item_data.amount;
- WFIFOB(fd,15)=fitem->subx;
- WFIFOB(fd,16)=fitem->suby;
- WFIFOSET(fd,packet_len(0x9d));
-}
-
-
-/// Notifies the client of a skill unit.
-/// 011f <id>.L <creator id>.L <x>.W <y>.W <unit id>.B <visible>.B (ZC_SKILL_ENTRY)
-/// 01c9 <id>.L <creator id>.L <x>.W <y>.W <unit id>.B <visible>.B <has msg>.B <msg>.80B (ZC_SKILL_ENTRY2)
-static void clif_getareachar_skillunit(struct map_session_data *sd, struct skill_unit *unit)
-{
- int fd = sd->fd;
-
- if( unit->group->state.guildaura )
- return;
-
-#if PACKETVER >= 3
- if(unit->group->unit_id==UNT_GRAFFITI) { // Graffiti [Valaris]
- WFIFOHEAD(fd,packet_len(0x1c9));
- WFIFOW(fd, 0)=0x1c9;
- WFIFOL(fd, 2)=unit->bl.id;
- WFIFOL(fd, 6)=unit->group->src_id;
- WFIFOW(fd,10)=unit->bl.x;
- WFIFOW(fd,12)=unit->bl.y;
- WFIFOB(fd,14)=unit->group->unit_id;
- WFIFOB(fd,15)=1;
- WFIFOB(fd,16)=1;
- safestrncpy((char*)WFIFOP(fd,17),unit->group->valstr,MESSAGE_SIZE);
- WFIFOSET(fd,packet_len(0x1c9));
- return;
- }
-#endif
- WFIFOHEAD(fd,packet_len(0x11f));
- WFIFOW(fd, 0)=0x11f;
- WFIFOL(fd, 2)=unit->bl.id;
- WFIFOL(fd, 6)=unit->group->src_id;
- WFIFOW(fd,10)=unit->bl.x;
- WFIFOW(fd,12)=unit->bl.y;
- if (battle_config.traps_setting&1 && skill_get_inf2(unit->group->skill_id)&INF2_TRAP)
- WFIFOB(fd,14)=UNT_DUMMYSKILL; //Use invisible unit id for traps.
- else if (skill_get_unit_flag(unit->group->skill_id) & UF_RANGEDSINGLEUNIT && !(unit->val2 & UF_RANGEDSINGLEUNIT))
- WFIFOB(fd,14)=UNT_DUMMYSKILL; //Use invisible unit id for traps.
- else
- WFIFOB(fd,14)=unit->group->unit_id;
- WFIFOB(fd,15)=1; // ignored by client (always gets set to 1)
- WFIFOSET(fd,packet_len(0x11f));
-
- if(unit->group->skill_id == WZ_ICEWALL)
- clif_changemapcell(fd,unit->bl.m,unit->bl.x,unit->bl.y,5,SELF);
-}
-
-
-/*==========================================
- * Server tells client to remove unit of id 'unit->bl.id'
- *------------------------------------------*/
-static void clif_clearchar_skillunit(struct skill_unit *unit, int fd)
-{
- nullpo_retv(unit);
-
- WFIFOHEAD(fd,packet_len(0x120));
- WFIFOW(fd, 0)=0x120;
- WFIFOL(fd, 2)=unit->bl.id;
- WFIFOSET(fd,packet_len(0x120));
-
- if(unit->group && unit->group->skill_id == WZ_ICEWALL)
- clif_changemapcell(fd,unit->bl.m,unit->bl.x,unit->bl.y,unit->val2,SELF);
-}
-
-
-/// Removes a skill unit (ZC_SKILL_DISAPPEAR).
-/// 0120 <id>.L
-void clif_skill_delunit(struct skill_unit *unit)
-{
- unsigned char buf[16];
-
- nullpo_retv(unit);
-
- WBUFW(buf, 0)=0x120;
- WBUFL(buf, 2)=unit->bl.id;
- clif_send(buf,packet_len(0x120),&unit->bl,AREA);
-}
-
-
-/// Sent when an object gets ankle-snared (ZC_SKILL_UPDATE).
-/// 01ac <id>.L
-/// Only affects units with class [139,153] client-side.
-void clif_skillunit_update(struct block_list* bl)
-{
- unsigned char buf[6];
- nullpo_retv(bl);
-
- WBUFW(buf,0) = 0x1ac;
- WBUFL(buf,2) = bl->id;
-
- clif_send(buf,packet_len(0x1ac),bl,AREA);
-}
-
-
-/*==========================================
- *
- *------------------------------------------*/
-static int clif_getareachar(struct block_list* bl,va_list ap)
-{
- struct map_session_data *sd;
-
- nullpo_ret(bl);
-
- sd=va_arg(ap,struct map_session_data*);
-
- if (sd == NULL || !sd->fd)
- return 0;
-
- switch(bl->type){
- case BL_ITEM:
- clif_getareachar_item(sd,(struct flooritem_data*) bl);
- break;
- case BL_SKILL:
- clif_getareachar_skillunit(sd,(TBL_SKILL*)bl);
- break;
- default:
- if(&sd->bl == bl)
- break;
- clif_getareachar_unit(sd,bl);
- break;
- }
- return 0;
-}
-
-/*==========================================
- * tbl has gone out of view-size of bl
- *------------------------------------------*/
-int clif_outsight(struct block_list *bl,va_list ap)
-{
- struct block_list *tbl;
- struct view_data *vd;
- TBL_PC *sd, *tsd;
- tbl=va_arg(ap,struct block_list*);
- if(bl == tbl) return 0;
- sd = BL_CAST(BL_PC, bl);
- tsd = BL_CAST(BL_PC, tbl);
-
- if (tsd && tsd->fd)
- { //tsd has lost sight of the bl object.
- switch(bl->type){
- case BL_PC:
- if (sd->vd.class_ != INVISIBLE_CLASS)
- clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
- if(sd->chatID){
- struct chat_data *cd;
- cd=(struct chat_data*)map_id2bl(sd->chatID);
- if(cd->usersd[0]==sd)
- clif_dispchat(cd,tsd->fd);
- }
- if( sd->state.vending )
- clif_closevendingboard(bl,tsd->fd);
- if( sd->state.buyingstore )
- clif_buyingstore_disappear_entry_single(tsd, sd);
- break;
- case BL_ITEM:
- clif_clearflooritem((struct flooritem_data*)bl,tsd->fd);
- break;
- case BL_SKILL:
- clif_clearchar_skillunit((struct skill_unit *)bl,tsd->fd);
- break;
- case BL_NPC:
- if( !(((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE) )
- clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
- break;
- default:
- if ((vd=status_get_viewdata(bl)) && vd->class_ != INVISIBLE_CLASS)
- clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
- break;
- }
- }
- if (sd && sd->fd)
- { //sd is watching tbl go out of view.
- if (((vd=status_get_viewdata(tbl)) && vd->class_ != INVISIBLE_CLASS) &&
- !(tbl->type == BL_NPC && (((TBL_NPC*)tbl)->sc.option&OPTION_INVISIBLE)))
- clif_clearunit_single(tbl->id,CLR_OUTSIGHT,sd->fd);
- }
- return 0;
-}
-
-/*==========================================
- * tbl has come into view of bl
- *------------------------------------------*/
-int clif_insight(struct block_list *bl,va_list ap)
-{
- struct block_list *tbl;
- TBL_PC *sd, *tsd;
- tbl=va_arg(ap,struct block_list*);
-
- if (bl == tbl) return 0;
-
- sd = BL_CAST(BL_PC, bl);
- tsd = BL_CAST(BL_PC, tbl);
-
- if (tsd && tsd->fd)
- { //Tell tsd that bl entered into his view
- switch(bl->type){
- case BL_ITEM:
- clif_getareachar_item(tsd,(struct flooritem_data*)bl);
- break;
- case BL_SKILL:
- clif_getareachar_skillunit(tsd,(TBL_SKILL*)bl);
- break;
- default:
- clif_getareachar_unit(tsd,bl);
- break;
- }
- }
- if (sd && sd->fd)
- { //Tell sd that tbl walked into his view
- clif_getareachar_unit(sd,tbl);
- }
- return 0;
-}
-
-
-/// Updates whole skill tree (ZC_SKILLINFO_LIST).
-/// 010f <packet len>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <skill name>.24B <upgradable>.B }*
-void clif_skillinfoblock(struct map_session_data *sd)
-{
- int fd;
- int i,len,id;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- if (!fd) return;
-
- WFIFOHEAD(fd, MAX_SKILL * 37 + 4);
- WFIFOW(fd,0) = 0x10f;
- for ( i = 0, len = 4; i < MAX_SKILL; i++)
- {
- if( (id = sd->status.skill[i].id) != 0 )
- {
- // workaround for bugreport:5348
- if (len + 37 > 8192)
- break;
-
- WFIFOW(fd,len) = id;
- WFIFOL(fd,len+2) = skill_get_inf(id);
- WFIFOW(fd,len+6) = sd->status.skill[i].lv;
- WFIFOW(fd,len+8) = skill_get_sp(id,sd->status.skill[i].lv);
- WFIFOW(fd,len+10)= skill_get_range2(&sd->bl, id,sd->status.skill[i].lv);
- safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
- if(sd->status.skill[i].flag == SKILL_FLAG_PERMANENT)
- WFIFOB(fd,len+36) = (sd->status.skill[i].lv < skill_tree_get_max(id, sd->status.class_))? 1:0;
- else
- WFIFOB(fd,len+36) = 0;
- len += 37;
- }
- }
- WFIFOW(fd,2)=len;
- WFIFOSET(fd,len);
-
- // workaround for bugreport:5348; send the remaining skills one by one to bypass packet size limit
- for ( ; i < MAX_SKILL; i++)
- {
- if( (id = sd->status.skill[i].id) != 0 )
- {
- clif_addskill(sd, id);
- clif_skillinfo(sd, id, 0);
- }
- }
-}
-/**
- * Server tells client 'sd' to add skill of id 'id' to it's skill tree (e.g. with Ice Falcion item)
- **/
-
-/// Adds new skill to the skill tree (ZC_ADD_SKILL).
-/// 0111 <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <skill name>.24B <upgradable>.B
-void clif_addskill(struct map_session_data *sd, int id)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd = sd->fd;
- if (!fd) return;
-
- if( sd->status.skill[id].id <= 0 )
- return;
-
- WFIFOHEAD(fd, packet_len(0x111));
- WFIFOW(fd,0) = 0x111;
- WFIFOW(fd,2) = id;
- WFIFOL(fd,4) = skill_get_inf(id);
- WFIFOW(fd,8) = sd->status.skill[id].lv;
- WFIFOW(fd,10) = skill_get_sp(id,sd->status.skill[id].lv);
- WFIFOW(fd,12)= skill_get_range2(&sd->bl, id,sd->status.skill[id].lv);
- safestrncpy((char*)WFIFOP(fd,14), skill_get_name(id), NAME_LENGTH);
- if( sd->status.skill[id].flag == SKILL_FLAG_PERMANENT )
- WFIFOB(fd,38) = (sd->status.skill[id].lv < skill_tree_get_max(id, sd->status.class_))? 1:0;
- else
- WFIFOB(fd,38) = 0;
- WFIFOSET(fd,packet_len(0x111));
-}
-
-
-/// Deletes a skill from the skill tree (ZC_SKILLINFO_DELETE).
-/// 0441 <skill id>.W
-void clif_deleteskill(struct map_session_data *sd, int id)
-{
-#if PACKETVER >= 20081217
- int fd;
-
- nullpo_retv(sd);
- fd = sd->fd;
- if( !fd ) return;
-
- WFIFOHEAD(fd,packet_len(0x441));
- WFIFOW(fd,0) = 0x441;
- WFIFOW(fd,2) = id;
- WFIFOSET(fd,packet_len(0x441));
-#endif
- clif_skillinfoblock(sd);
-}
-
-
-/// Updates a skill in the skill tree (ZC_SKILLINFO_UPDATE).
-/// 010e <skill id>.W <level>.W <sp cost>.W <attack range>.W <upgradable>.B
-void clif_skillup(struct map_session_data *sd,uint16 skill_id)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x10e));
- WFIFOW(fd,0) = 0x10e;
- WFIFOW(fd,2) = skill_id;
- WFIFOW(fd,4) = sd->status.skill[skill_id].lv;
- WFIFOW(fd,6) = skill_get_sp(skill_id,sd->status.skill[skill_id].lv);
- WFIFOW(fd,8) = skill_get_range2(&sd->bl,skill_id,sd->status.skill[skill_id].lv);
- WFIFOB(fd,10) = (sd->status.skill[skill_id].lv < skill_tree_get_max(sd->status.skill[skill_id].id, sd->status.class_)) ? 1 : 0;
- WFIFOSET(fd,packet_len(0x10e));
-}
-
-
-/// Updates a skill in the skill tree (ZC_SKILLINFO_UPDATE2).
-/// 07e1 <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <upgradable>.B
-void clif_skillinfo(struct map_session_data *sd,int skill, int inf)
-{
- const int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x7e1));
- WFIFOW(fd,0) = 0x7e1;
- WFIFOW(fd,2) = skill;
- WFIFOL(fd,4) = inf?inf:skill_get_inf(skill);
- WFIFOW(fd,8) = sd->status.skill[skill].lv;
- WFIFOW(fd,10) = skill_get_sp(skill,sd->status.skill[skill].lv);
- WFIFOW(fd,12) = skill_get_range2(&sd->bl,skill,sd->status.skill[skill].lv);
- if( sd->status.skill[skill].flag == SKILL_FLAG_PERMANENT )
- WFIFOB(fd,14) = (sd->status.skill[skill].lv < skill_tree_get_max(skill, sd->status.class_))? 1:0;
- else
- WFIFOB(fd,14) = 0;
- WFIFOSET(fd,packet_len(0x7e1));
-}
-
-
-/// Notifies clients in area, that an object is about to use a skill.
-/// 013e <src id>.L <dst id>.L <x>.W <y>.W <skill id>.W <property>.L <delaytime>.L (ZC_USESKILL_ACK)
-/// 07fb <src id>.L <dst id>.L <x>.W <y>.W <skill id>.W <property>.L <delaytime>.L <is disposable>.B (ZC_USESKILL_ACK2)
-/// property:
-/// 0 = Yellow cast aura
-/// 1 = Water elemental cast aura
-/// 2 = Earth elemental cast aura
-/// 3 = Fire elemental cast aura
-/// 4 = Wind elemental cast aura
-/// 5 = Poison elemental cast aura
-/// 6 = Holy elemental cast aura
-/// ? = like 0
-/// is disposable:
-/// 0 = yellow chat text "[src name] will use skill [skill name]."
-/// 1 = no text
-void clif_skillcasting(struct block_list* bl, int src_id, int dst_id, int dst_x, int dst_y, uint16 skill_id, int property, int casttime)
-{
-#if PACKETVER < 20091124
- const int cmd = 0x13e;
-#else
- const int cmd = 0x7fb;
-#endif
- unsigned char buf[32];
-
- WBUFW(buf,0) = cmd;
- WBUFL(buf,2) = src_id;
- WBUFL(buf,6) = dst_id;
- WBUFW(buf,10) = dst_x;
- WBUFW(buf,12) = dst_y;
- WBUFW(buf,14) = skill_id;
- WBUFL(buf,16) = property<0?0:property; //Avoid sending negatives as element [Skotlex]
- WBUFL(buf,20) = casttime;
-#if PACKETVER >= 20091124
- WBUFB(buf,24) = 0; // isDisposable
-#endif
-
- if (disguised(bl)) {
- clif_send(buf,packet_len(cmd), bl, AREA_WOS);
- WBUFL(buf,2) = -src_id;
- clif_send(buf,packet_len(cmd), bl, SELF);
- } else
- clif_send(buf,packet_len(cmd), bl, AREA);
-}
-
-
-/// Notifies clients in area, that an object canceled casting (ZC_DISPEL).
-/// 01b9 <id>.L
-void clif_skillcastcancel(struct block_list* bl)
-{
- unsigned char buf[16];
-
- nullpo_retv(bl);
-
- WBUFW(buf,0) = 0x1b9;
- WBUFL(buf,2) = bl->id;
- clif_send(buf,packet_len(0x1b9), bl, AREA);
-}
-
-
-/// Notifies the client about the result of a skill use request (ZC_ACK_TOUSESKILL).
-/// 0110 <skill id>.W <num>.L <result>.B <cause>.B
-/// num (only used when skill id = NV_BASIC and cause = 0):
-/// 0 = "skill failed" MsgStringTable[159]
-/// 1 = "no emotions" MsgStringTable[160]
-/// 2 = "no sit" MsgStringTable[161]
-/// 3 = "no chat" MsgStringTable[162]
-/// 4 = "no party" MsgStringTable[163]
-/// 5 = "no shout" MsgStringTable[164]
-/// 6 = "no PKing" MsgStringTable[165]
-/// 7 = "no alligning" MsgStringTable[383]
-/// ? = ignored
-/// cause:
-/// 0 = "not enough skill level" MsgStringTable[214] (AL_WARP)
-/// "steal failed" MsgStringTable[205] (TF_STEAL)
-/// "envenom failed" MsgStringTable[207] (TF_POISON)
-/// "skill failed" MsgStringTable[204] (otherwise)
-/// ... = @see enum useskill_fail_cause
-/// ? = ignored
-///
-/// if(result!=0) doesn't display any of the previous messages
-/// Note: when this packet is received an unknown flag is always set to 0,
-/// suggesting this is an ACK packet for the UseSkill packets and should be sent on success too [FlavioJS]
-void clif_skill_fail(struct map_session_data *sd,uint16 skill_id,enum useskill_fail_cause cause,int btype)
-{
- int fd;
-
- if (!sd) { //Since this is the most common nullpo....
- ShowDebug("clif_skill_fail: Error, received NULL sd for skill %d\n", skill_id);
- return;
- }
-
- fd=sd->fd;
- if (!fd) return;
-
- if(battle_config.display_skill_fail&1)
- return; //Disable all skill failed messages
-
- if(cause==USESKILL_FAIL_SKILLINTERVAL && !sd->state.showdelay)
- return; //Disable delay failed messages
-
- if(skill_id == RG_SNATCHER && battle_config.display_skill_fail&4)
- return;
-
- if(skill_id == TF_POISON && battle_config.display_skill_fail&8)
- return;
-
- WFIFOHEAD(fd,packet_len(0x110));
- WFIFOW(fd,0) = 0x110;
- WFIFOW(fd,2) = skill_id;
- WFIFOL(fd,4) = btype;
- WFIFOB(fd,8) = 0;// success
- WFIFOB(fd,9) = cause;
- WFIFOSET(fd,packet_len(0x110));
-}
-
-
-/// Skill cooldown display icon (ZC_SKILL_POSTDELAY).
-/// 043d <skill ID>.W <tick>.L
-void clif_skill_cooldown(struct map_session_data *sd, uint16 skill_id, unsigned int tick)
-{
-#if PACKETVER>=20081112
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x43d));
- WFIFOW(fd,0) = 0x43d;
- WFIFOW(fd,2) = skill_id;
- WFIFOL(fd,4) = tick;
- WFIFOSET(fd,packet_len(0x43d));
-#endif
-}
-
-
-/// Skill attack effect and damage.
-/// 0114 <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <damage>.W <level>.W <div>.W <type>.B (ZC_NOTIFY_SKILL)
-/// 01de <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <damage>.L <level>.W <div>.W <type>.B (ZC_NOTIFY_SKILL2)
-int clif_skill_damage(struct block_list *src,struct block_list *dst,unsigned int tick,int sdelay,int ddelay,int damage,int div,uint16 skill_id,uint16 skill_lv,int type)
-{
- unsigned char buf[64];
- struct status_change *sc;
-
- nullpo_ret(src);
- nullpo_ret(dst);
-
- type = clif_calc_delay(type,div,damage,ddelay);
- sc = status_get_sc(dst);
- if(sc && sc->count) {
- if(sc->data[SC_HALLUCINATION] && damage)
- damage = damage*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100;
- }
-
-#if PACKETVER < 3
- WBUFW(buf,0)=0x114;
- WBUFW(buf,2)=skill_id;
- WBUFL(buf,4)=src->id;
- WBUFL(buf,8)=dst->id;
- WBUFL(buf,12)=tick;
- WBUFL(buf,16)=sdelay;
- WBUFL(buf,20)=ddelay;
- if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
- WBUFW(buf,24)=damage?div:0;
- } else {
- WBUFW(buf,24)=damage;
- }
- WBUFW(buf,26)=skill_lv;
- WBUFW(buf,28)=div;
- WBUFB(buf,30)=type;
- if (disguised(dst)) {
- clif_send(buf,packet_len(0x114),dst,AREA_WOS);
- WBUFL(buf,8)=-dst->id;
- clif_send(buf,packet_len(0x114),dst,SELF);
- } else
- clif_send(buf,packet_len(0x114),dst,AREA);
-
- if(disguised(src)) {
- WBUFL(buf,4)=-src->id;
- if (disguised(dst))
- WBUFL(buf,8)=dst->id;
- if(damage > 0)
- WBUFW(buf,24)=-1;
- clif_send(buf,packet_len(0x114),src,SELF);
- }
-#else
- WBUFW(buf,0)=0x1de;
- WBUFW(buf,2)=skill_id;
- WBUFL(buf,4)=src->id;
- WBUFL(buf,8)=dst->id;
- WBUFL(buf,12)=tick;
- WBUFL(buf,16)=sdelay;
- WBUFL(buf,20)=ddelay;
- if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
- WBUFL(buf,24)=damage?div:0;
- } else {
- WBUFL(buf,24)=damage;
- }
- WBUFW(buf,28)=skill_lv;
- WBUFW(buf,30)=div;
- WBUFB(buf,32)=type;
- if (disguised(dst)) {
- clif_send(buf,packet_len(0x1de),dst,AREA_WOS);
- WBUFL(buf,8)=-dst->id;
- clif_send(buf,packet_len(0x1de),dst,SELF);
- } else
- clif_send(buf,packet_len(0x1de),dst,AREA);
-
- if(disguised(src)) {
- WBUFL(buf,4)=-src->id;
- if (disguised(dst))
- WBUFL(buf,8)=dst->id;
- if(damage > 0)
- WBUFL(buf,24)=-1;
- clif_send(buf,packet_len(0x1de),src,SELF);
- }
-#endif
-
- //Because the damage delay must be synced with the client, here is where the can-walk tick must be updated. [Skotlex]
- return clif_calc_walkdelay(dst,ddelay,type,damage,div);
-}
-
-
-/// Ground skill attack effect and damage (ZC_NOTIFY_SKILL_POSITION).
-/// 0115 <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <x>.W <y>.W <damage>.W <level>.W <div>.W <type>.B
-/*
-int clif_skill_damage2(struct block_list *src,struct block_list *dst,unsigned int tick,int sdelay,int ddelay,int damage,int div,uint16 skill_id,uint16 skill_lv,int type)
-{
- unsigned char buf[64];
- struct status_change *sc;
-
- nullpo_ret(src);
- nullpo_ret(dst);
-
- type = (type>0)?type:skill_get_hit(skill_id);
- type = clif_calc_delay(type,div,damage,ddelay);
- sc = status_get_sc(dst);
-
- if(sc && sc->count) {
- if(sc->data[SC_HALLUCINATION] && damage)
- damage = damage*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100;
- }
-
- WBUFW(buf,0)=0x115;
- WBUFW(buf,2)=skill_id;
- WBUFL(buf,4)=src->id;
- WBUFL(buf,8)=dst->id;
- WBUFL(buf,12)=tick;
- WBUFL(buf,16)=sdelay;
- WBUFL(buf,20)=ddelay;
- WBUFW(buf,24)=dst->x;
- WBUFW(buf,26)=dst->y;
- if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
- WBUFW(buf,28)=damage?div:0;
- } else {
- WBUFW(buf,28)=damage;
- }
- WBUFW(buf,30)=skill_lv;
- WBUFW(buf,32)=div;
- WBUFB(buf,34)=type;
- clif_send(buf,packet_len(0x115),src,AREA);
- if(disguised(src)) {
- WBUFL(buf,4)=-src->id;
- if(damage > 0)
- WBUFW(buf,28)=-1;
- clif_send(buf,packet_len(0x115),src,SELF);
- }
- if (disguised(dst)) {
- WBUFL(buf,8)=-dst->id;
- if (disguised(src))
- WBUFL(buf,4)=src->id;
- else if(damage > 0)
- WBUFW(buf,28)=-1;
- clif_send(buf,packet_len(0x115),dst,SELF);
- }
-
- //Because the damage delay must be synced with the client, here is where the can-walk tick must be updated. [Skotlex]
- return clif_calc_walkdelay(dst,ddelay,type,damage,div);
-}
-*/
-
-
-/// Non-damaging skill effect (ZC_USE_SKILL).
-/// 011a <skill id>.W <skill lv>.W <dst id>.L <src id>.L <result>.B
-int clif_skill_nodamage(struct block_list *src,struct block_list *dst,uint16 skill_id,int heal,int fail)
-{
- unsigned char buf[32];
-
- nullpo_ret(dst);
-
- WBUFW(buf,0)=0x11a;
- WBUFW(buf,2)=skill_id;
- WBUFW(buf,4)=min(heal, INT16_MAX);
- WBUFL(buf,6)=dst->id;
- WBUFL(buf,10)=src?src->id:0;
- WBUFB(buf,14)=fail;
-
- if (disguised(dst)) {
- clif_send(buf,packet_len(0x11a),dst,AREA_WOS);
- WBUFL(buf,6)=-dst->id;
- clif_send(buf,packet_len(0x11a),dst,SELF);
- } else
- clif_send(buf,packet_len(0x11a),dst,AREA);
-
- if(src && disguised(src)) {
- WBUFL(buf,10)=-src->id;
- if (disguised(dst))
- WBUFL(buf,6)=dst->id;
- clif_send(buf,packet_len(0x11a),src,SELF);
- }
-
- return fail;
-}
-
-
-/// Non-damaging ground skill effect (ZC_NOTIFY_GROUNDSKILL).
-/// 0117 <skill id>.W <src id>.L <level>.W <x>.W <y>.W <tick>.L
-void clif_skill_poseffect(struct block_list *src,uint16 skill_id,int val,int x,int y,int tick)
-{
- unsigned char buf[32];
-
- nullpo_retv(src);
-
- WBUFW(buf,0)=0x117;
- WBUFW(buf,2)=skill_id;
- WBUFL(buf,4)=src->id;
- WBUFW(buf,8)=val;
- WBUFW(buf,10)=x;
- WBUFW(buf,12)=y;
- WBUFL(buf,14)=tick;
- if(disguised(src)) {
- clif_send(buf,packet_len(0x117),src,AREA_WOS);
- WBUFL(buf,4)=-src->id;
- clif_send(buf,packet_len(0x117),src,SELF);
- } else
- clif_send(buf,packet_len(0x117),src,AREA);
-}
-
-
-/*==========================================
- * Tells all client's nearby 'unit' sight range that it spawned
- *------------------------------------------*/
-//FIXME: this is just an AREA version of clif_getareachar_skillunit()
-void clif_skill_setunit(struct skill_unit *unit)
-{
- unsigned char buf[128];
-
- nullpo_retv(unit);
-
- if( unit->group->state.guildaura )
- return;
-
-#if PACKETVER >= 3
- if(unit->group->unit_id==UNT_GRAFFITI) { // Graffiti [Valaris]
- WBUFW(buf, 0)=0x1c9;
- WBUFL(buf, 2)=unit->bl.id;
- WBUFL(buf, 6)=unit->group->src_id;
- WBUFW(buf,10)=unit->bl.x;
- WBUFW(buf,12)=unit->bl.y;
- WBUFB(buf,14)=unit->group->unit_id;
- WBUFB(buf,15)=1;
- WBUFB(buf,16)=1;
- safestrncpy((char*)WBUFP(buf,17),unit->group->valstr,MESSAGE_SIZE);
- clif_send(buf,packet_len(0x1c9),&unit->bl,AREA);
- return;
- }
-#endif
- WBUFW(buf, 0)=0x11f;
- WBUFL(buf, 2)=unit->bl.id;
- WBUFL(buf, 6)=unit->group->src_id;
- WBUFW(buf,10)=unit->bl.x;
- WBUFW(buf,12)=unit->bl.y;
- if (unit->group->state.song_dance&0x1 && unit->val2&UF_ENSEMBLE)
- WBUFB(buf,14)=unit->val2&UF_SONG?UNT_DISSONANCE:UNT_UGLYDANCE;
- else if (skill_get_unit_flag(unit->group->skill_id) & UF_RANGEDSINGLEUNIT && !(unit->val2 & UF_RANGEDSINGLEUNIT))
- WBUFB(buf, 14) = UNT_DUMMYSKILL; // Only display the unit at center.
- else
- WBUFB(buf,14)=unit->group->unit_id;
- WBUFB(buf,15)=1; // ignored by client (always gets set to 1)
- clif_send(buf,packet_len(0x11f),&unit->bl,AREA);
-}
-
-
-/// Presents a list of available warp destinations (ZC_WARPLIST).
-/// 011c <skill id>.W { <map name>.16B }*4
-void clif_skill_warppoint(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv, unsigned short map1, unsigned short map2, unsigned short map3, unsigned short map4)
-{
- int fd;
- nullpo_retv(sd);
- fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x11c));
- WFIFOW(fd,0) = 0x11c;
- WFIFOW(fd,2) = skill_id;
- memset(WFIFOP(fd,4), 0x00, 4*MAP_NAME_LENGTH_EXT);
- if (map1 == (unsigned short)-1) strcpy((char*)WFIFOP(fd,4), "Random");
- else // normal map name
- if (map1 > 0) mapindex_getmapname_ext(mapindex_id2name(map1), (char*)WFIFOP(fd,4));
- if (map2 > 0) mapindex_getmapname_ext(mapindex_id2name(map2), (char*)WFIFOP(fd,20));
- if (map3 > 0) mapindex_getmapname_ext(mapindex_id2name(map3), (char*)WFIFOP(fd,36));
- if (map4 > 0) mapindex_getmapname_ext(mapindex_id2name(map4), (char*)WFIFOP(fd,52));
- WFIFOSET(fd,packet_len(0x11c));
-
- sd->menuskill_id = skill_id;
- if (skill_id == AL_WARP)
- sd->menuskill_val = (sd->ud.skillx<<16)|sd->ud.skilly; //Store warp position here.
- else
- sd->menuskill_val = skill_lv;
-}
-
-
-/// Memo message (ZC_ACK_REMEMBER_WARPPOINT).
-/// 011e <type>.B
-/// type:
-/// 0 = "Saved location as a Memo Point for Warp skill." in color 0xFFFF00 (cyan)
-/// 1 = "Skill Level is not high enough." in color 0x0000FF (red)
-/// 2 = "You haven't learned Warp." in color 0x0000FF (red)
-///
-/// @param sd Who receives the message
-/// @param type What message
-void clif_skill_memomessage(struct map_session_data* sd, int type)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x11e));
- WFIFOW(fd,0)=0x11e;
- WFIFOB(fd,2)=type;
- WFIFOSET(fd,packet_len(0x11e));
-}
-
-
-/// Teleport message (ZC_NOTIFY_MAPINFO).
-/// 0189 <type>.W
-/// type:
-/// 0 = "Unable to Teleport in this area" in color 0xFFFF00 (cyan)
-/// 1 = "Saved point cannot be memorized." in color 0x0000FF (red)
-///
-/// @param sd Who receives the message
-/// @param type What message
-void clif_skill_teleportmessage(struct map_session_data *sd, int type)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x189));
- WFIFOW(fd,0)=0x189;
- WFIFOW(fd,2)=type;
- WFIFOSET(fd,packet_len(0x189));
-}
-
-
-/// Displays Sense (WZ_ESTIMATION) information window (ZC_MONSTER_INFO).
-/// 018c <class>.W <level>.W <size>.W <hp>.L <def>.W <race>.W <mdef>.W <element>.W
-/// <water%>.B <earth%>.B <fire%>.B <wind%>.B <poison%>.B <holy%>.B <shadow%>.B <ghost%>.B <undead%>.B
-void clif_skill_estimation(struct map_session_data *sd,struct block_list *dst)
-{
- struct status_data *status;
- unsigned char buf[64];
- int i;//, fix;
-
- nullpo_retv(sd);
- nullpo_retv(dst);
-
- if( dst->type != BL_MOB )
- return;
-
- status = status_get_status_data(dst);
-
- WBUFW(buf, 0)=0x18c;
- WBUFW(buf, 2)=status_get_class(dst);
- WBUFW(buf, 4)=status_get_lv(dst);
- WBUFW(buf, 6)=status->size;
- WBUFL(buf, 8)=status->hp;
- WBUFW(buf,12)= (battle_config.estimation_type&1?status->def:0)
- +(battle_config.estimation_type&2?status->def2:0);
- WBUFW(buf,14)=status->race;
- WBUFW(buf,16)= (battle_config.estimation_type&1?status->mdef:0)
- +(battle_config.estimation_type&2?status->mdef2:0);
- WBUFW(buf,18)= status->def_ele;
- for(i=0;i<9;i++)
- WBUFB(buf,20+i)= (unsigned char)battle_attr_ratio(i+1,status->def_ele, status->ele_lv);
-// The following caps negative attributes to 0 since the client displays them as 255-fix. [Skotlex]
-// WBUFB(buf,20+i)= (unsigned char)((fix=battle_attr_ratio(i+1,status->def_ele, status->ele_lv))<0?0:fix);
-
- clif_send(buf,packet_len(0x18c),&sd->bl,sd->status.party_id>0?PARTY_SAMEMAP:SELF);
-}
-
-
-/// Presents a textual list of producable items (ZC_MAKABLEITEMLIST).
-/// 018d <packet len>.W { <name id>.W { <material id>.W }*3 }*
-/// material id:
-/// unused by the client
-void clif_skill_produce_mix_list(struct map_session_data *sd, int skill_id , int trigger)
-{
- int i,c,view,fd;
- nullpo_retv(sd);
-
- if(sd->menuskill_id == skill_id)
- return; //Avoid resending the menu twice or more times...
- if( skill_id == GC_CREATENEWPOISON )
- skill_id = GC_RESEARCHNEWPOISON;
-
- fd=sd->fd;
- WFIFOHEAD(fd, MAX_SKILL_PRODUCE_DB * 8 + 8);
- WFIFOW(fd, 0)=0x18d;
-
- for(i=0,c=0;i<MAX_SKILL_PRODUCE_DB;i++){
- if( skill_can_produce_mix(sd,skill_produce_db[i].nameid, trigger, 1) &&
- ( skill_id > 0 && skill_produce_db[i].req_skill == skill_id || skill_id < 0 )
- ){
- if((view = itemdb_viewid(skill_produce_db[i].nameid)) > 0)
- WFIFOW(fd,c*8+ 4)= view;
- else
- WFIFOW(fd,c*8+ 4)= skill_produce_db[i].nameid;
- WFIFOW(fd,c*8+ 6)= 0;
- WFIFOW(fd,c*8+ 8)= 0;
- WFIFOW(fd,c*8+10)= 0;
- c++;
- }
- }
- WFIFOW(fd, 2)=c*8+8;
- WFIFOSET(fd,WFIFOW(fd,2));
- if(c > 0) {
- sd->menuskill_id = skill_id;
- sd->menuskill_val = trigger;
- return;
- }
-}
-
-
-/// Present a list of producable items (ZC_MAKINGITEM_LIST).
-/// 025a <packet len>.W <mk type>.W { <name id>.W }*
-/// mk type:
-/// 1 = cooking
-/// 2 = arrow
-/// 3 = elemental
-/// 4 = GN_MIX_COOKING
-/// 5 = GN_MAKEBOMB
-/// 6 = GN_S_PHARMACY
-void clif_cooking_list(struct map_session_data *sd, int trigger, uint16 skill_id, int qty, int list_type)
-{
- int fd;
- int i, c;
- int view;
-
- nullpo_retv(sd);
- fd = sd->fd;
-
- WFIFOHEAD(fd, 6 + 2 * MAX_SKILL_PRODUCE_DB);
- WFIFOW(fd,0) = 0x25a;
- WFIFOW(fd,4) = list_type; // list type
-
- c = 0;
- for( i = 0; i < MAX_SKILL_PRODUCE_DB; i++ ) {
- if( !skill_can_produce_mix(sd,skill_produce_db[i].nameid,trigger, qty) )
- continue;
-
- if( (view = itemdb_viewid(skill_produce_db[i].nameid)) > 0 )
- WFIFOW(fd, 6 + 2 * c) = view;
- else
- WFIFOW(fd, 6 + 2 * c) = skill_produce_db[i].nameid;
-
- c++;
- }
-
- if( skill_id == AM_PHARMACY ) { // Only send it while Cooking else check for c.
- WFIFOW(fd,2) = 6 + 2 * c;
- WFIFOSET(fd,WFIFOW(fd,2));
- }
-
- if( c > 0 ) {
- sd->menuskill_id = skill_id;
- sd->menuskill_val = trigger;
- if( skill_id != AM_PHARMACY ) {
- sd->menuskill_val2 = qty; // amount.
- WFIFOW(fd,2) = 6 + 2 * c;
- WFIFOSET(fd,WFIFOW(fd,2));
- }
- } else {
- clif_menuskill_clear(sd);
- if( skill_id != AM_PHARMACY ) { // AM_PHARMACY is used to Cooking.
- // It fails.
-#if PACKETVER >= 20090922
- clif_msg_skill(sd,skill_id,0x625);
-#else
- WFIFOW(fd,2) = 6 + 2 * c;
- WFIFOSET(fd,WFIFOW(fd,2));
-#endif
- }
- }
-}
-
-
-/// Notifies clients of a status change.
-/// 0196 <index>.W <id>.L <state>.B (ZC_MSG_STATE_CHANGE) [used for ending status changes and starting them on non-pc units (when needed)]
-/// 043f <index>.W <id>.L <state>.B <remain msec>.L { <val>.L }*3 (ZC_MSG_STATE_CHANGE2) [used exclusively for starting statuses on pcs]
-void clif_status_change(struct block_list *bl,int type,int flag,int tick,int val1, int val2, int val3)
-{
- unsigned char buf[32];
- struct map_session_data *sd;
-
- if (type == SI_BLANK) //It shows nothing on the client...
- return;
-
- nullpo_retv(bl);
-
- sd = BL_CAST(BL_PC, bl);
-
- if (!(status_type2relevant_bl_types(type)&bl->type)) // only send status changes that actually matter to the client
- return;
-
-#if PACKETVER >= 20090121
- if(flag && battle_config.display_status_timers && sd)
- WBUFW(buf,0)=0x43f;
- else
-#endif
- WBUFW(buf,0)=0x196;
- WBUFW(buf,2)=type;
- WBUFL(buf,4)=bl->id;
- WBUFB(buf,8)=flag;
-#if PACKETVER >= 20090121
- if(flag && battle_config.display_status_timers && sd)
- {
- if (tick <= 0)
- tick = 9999; // this is indeed what official servers do
-
- WBUFL(buf,9) = tick;
- WBUFL(buf,13) = val1;
- WBUFL(buf,17) = val2;
- WBUFL(buf,21) = val3;
- }
-#endif
- clif_send(buf,packet_len(WBUFW(buf,0)),bl, (sd && sd->status.option&OPTION_INVISIBLE) ? SELF : AREA);
-}
-
-/// Send message (modified by [Yor]) (ZC_NOTIFY_PLAYERCHAT).
-/// 008e <packet len>.W <message>.?B
-void clif_displaymessage(const int fd, const char* mes)
-{
- nullpo_retv(mes);
-
- //Scrapped, as these are shared by disconnected players =X [Skotlex]
- if (fd == 0)
- ;
- else {
- char *message, *line;
-
- message = aStrdup(mes);
- line = strtok(message, "\n");
- while(line != NULL) {
- // Limit message to 255+1 characters (otherwise it causes a buffer overflow in the client)
- int len = strnlen(line, 255);
-
- if (len > 0) { // don't send a void message (it's not displaying on the client chat). @help can send void line.
- WFIFOHEAD(fd, 5 + len);
- WFIFOW(fd,0) = 0x8e;
- WFIFOW(fd,2) = 5 + len; // 4 + len + NULL teminate
- safestrncpy((char *)WFIFOP(fd,4), line, len + 1);
- WFIFOSET(fd, 5 + len);
- }
- line = strtok(NULL, "\n");
- }
- aFree(message);
- }
-}
-
-/// Send broadcast message in yellow or blue without font formatting (ZC_BROADCAST).
-/// 009a <packet len>.W <message>.?B
-void clif_broadcast(struct block_list* bl, const char* mes, int len, int type, enum send_target target)
-{
- int lp = type ? 4 : 0;
- unsigned char *buf = (unsigned char*)aMalloc((4 + lp + len)*sizeof(unsigned char));
-
- WBUFW(buf,0) = 0x9a;
- WBUFW(buf,2) = 4 + lp + len;
- if (type == 0x10) // bc_blue
- WBUFL(buf,4) = 0x65756c62; //If there's "blue" at the beginning of the message, game client will display it in blue instead of yellow.
- else if (type == 0x20) // bc_woe
- WBUFL(buf,4) = 0x73737373; //If there's "ssss", game client will recognize message as 'WoE broadcast'.
- memcpy(WBUFP(buf, 4 + lp), mes, len);
- clif_send(buf, WBUFW(buf,2), bl, target);
-
- if (buf)
- aFree(buf);
-}
-
-/*==========================================
- * Displays a message on a 'bl' to all it's nearby clients
- * Used by npc_globalmessage
- *------------------------------------------*/
-void clif_GlobalMessage(struct block_list* bl, const char* message) {
- char buf[100];
- int len;
- nullpo_retv(bl);
-
- if(!message)
- return;
-
- len = strlen(message)+1;
-
- if( len > sizeof(buf)-8 ) {
- ShowWarning("clif_GlobalMessage: Truncating too long message '%s' (len=%d).\n", message, len);
- len = sizeof(buf)-8;
- }
-
- WBUFW(buf,0)=0x8d;
- WBUFW(buf,2)=len+8;
- WBUFL(buf,4)=bl->id;
- safestrncpy((char *) WBUFP(buf,8),message,len);
- clif_send((unsigned char *) buf,WBUFW(buf,2),bl,ALL_CLIENT);
-
-}
-
-/*==========================================
- * Send main chat message [LuzZza]
- *------------------------------------------*/
-void clif_MainChatMessage(const char* message) {
- uint8 buf[200];
- int len;
-
- if(!message)
- return;
-
- len = strlen(message)+1;
- if (len+8 > sizeof(buf)) {
- ShowDebug("clif_MainChatMessage: Received message too long (len %d): %s\n", len, message);
- len = sizeof(buf)-8;
- }
- WBUFW(buf,0)=0x8d;
- WBUFW(buf,2)=len+8;
- WBUFL(buf,4)=0;
- safestrncpy((char *) WBUFP(buf,8),message,len);
- clif_send(buf,WBUFW(buf,2),NULL,CHAT_MAINCHAT);
-}
-
-/// Send broadcast message with font formatting (ZC_BROADCAST2).
-/// 01c3 <packet len>.W <fontColor>.L <fontType>.W <fontSize>.W <fontAlign>.W <fontY>.W <message>.?B
-void clif_broadcast2(struct block_list* bl, const char* mes, int len, unsigned long fontColor, short fontType, short fontSize, short fontAlign, short fontY, enum send_target target)
-{
- unsigned char *buf = (unsigned char*)aMalloc((16 + len)*sizeof(unsigned char));
-
- WBUFW(buf,0) = 0x1c3;
- WBUFW(buf,2) = len + 16;
- WBUFL(buf,4) = fontColor;
- WBUFW(buf,8) = fontType;
- WBUFW(buf,10) = fontSize;
- WBUFW(buf,12) = fontAlign;
- WBUFW(buf,14) = fontY;
- memcpy(WBUFP(buf,16), mes, len);
- clif_send(buf, WBUFW(buf,2), bl, target);
-
- if (buf)
- aFree(buf);
-}
-
-
-/// Displays heal effect (ZC_RECOVERY).
-/// 013d <var id>.W <amount>.W
-/// var id:
-/// 5 = HP (SP_HP)
-/// 7 = SP (SP_SP)
-/// ? = ignored
-void clif_heal(int fd,int type,int val)
-{
- WFIFOHEAD(fd,packet_len(0x13d));
- WFIFOW(fd,0)=0x13d;
- WFIFOW(fd,2)=type;
- WFIFOW(fd,4)=cap_value(val,0,INT16_MAX);
- WFIFOSET(fd,packet_len(0x13d));
-}
-
-
-/// Displays resurrection effect (ZC_RESURRECTION).
-/// 0148 <id>.L <type>.W
-/// type:
-/// ignored
-void clif_resurrection(struct block_list *bl,int type)
-{
- unsigned char buf[16];
-
- nullpo_retv(bl);
-
- WBUFW(buf,0)=0x148;
- WBUFL(buf,2)=bl->id;
- WBUFW(buf,6)=0;
-
- clif_send(buf,packet_len(0x148),bl,type==1 ? AREA : AREA_WOS);
- if (disguised(bl))
- clif_spawn(bl);
-}
-
-
-/// Sets the map property (ZC_NOTIFY_MAPPROPERTY).
-/// 0199 <type>.W
-void clif_map_property(struct map_session_data* sd, enum map_property property)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x199));
- WFIFOW(fd,0)=0x199;
- WFIFOW(fd,2)=property;
- WFIFOSET(fd,packet_len(0x199));
-}
-
-
-/// Set the map type (ZC_NOTIFY_MAPPROPERTY2).
-/// 01d6 <type>.W
-void clif_map_type(struct map_session_data* sd, enum map_type type)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x1D6));
- WFIFOW(fd,0)=0x1D6;
- WFIFOW(fd,2)=type;
- WFIFOSET(fd,packet_len(0x1D6));
-}
-
-
-/// Updates PvP ranking (ZC_NOTIFY_RANKING).
-/// 019a <id>.L <ranking>.L <total>.L
-void clif_pvpset(struct map_session_data *sd,int pvprank,int pvpnum,int type)
-{
- if(type == 2) {
- int fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0x19a));
- WFIFOW(fd,0) = 0x19a;
- WFIFOL(fd,2) = sd->bl.id;
- WFIFOL(fd,6) = pvprank;
- WFIFOL(fd,10) = pvpnum;
- WFIFOSET(fd,packet_len(0x19a));
- } else {
- unsigned char buf[32];
- WBUFW(buf,0) = 0x19a;
- WBUFL(buf,2) = sd->bl.id;
- if(sd->sc.option&(OPTION_HIDE|OPTION_CLOAK))
- WBUFL(buf,6) = UINT32_MAX; //On client displays as --
- else
- WBUFL(buf,6) = pvprank;
- WBUFL(buf,10) = pvpnum;
- if(sd->sc.option&OPTION_INVISIBLE || sd->disguise) //Causes crashes when a 'mob' with pvp info dies.
- clif_send(buf,packet_len(0x19a),&sd->bl,SELF);
- else if(!type)
- clif_send(buf,packet_len(0x19a),&sd->bl,AREA);
- else
- clif_send(buf,packet_len(0x19a),&sd->bl,ALL_SAMEMAP);
- }
-}
-
-
-/*==========================================
- *
- *------------------------------------------*/
-void clif_map_property_mapall(int map, enum map_property property)
-{
- struct block_list bl;
- unsigned char buf[16];
-
- bl.id = 0;
- bl.type = BL_NUL;
- bl.m = map;
- WBUFW(buf,0)=0x199;
- WBUFW(buf,2)=property;
- clif_send(buf,packet_len(0x199),&bl,ALL_SAMEMAP);
-}
-
-
-/// Notifies the client about the result of a refine attempt (ZC_ACK_ITEMREFINING).
-/// 0188 <result>.W <index>.W <refine>.W
-/// result:
-/// 0 = success
-/// 1 = failure
-/// 2 = downgrade
-void clif_refine(int fd, int fail, int index, int val)
-{
- WFIFOHEAD(fd,packet_len(0x188));
- WFIFOW(fd,0)=0x188;
- WFIFOW(fd,2)=fail;
- WFIFOW(fd,4)=index+2;
- WFIFOW(fd,6)=val;
- WFIFOSET(fd,packet_len(0x188));
-}
-
-
-/// Notifies the client about the result of a weapon refine attempt (ZC_ACK_WEAPONREFINE).
-/// 0223 <result>.L <nameid>.W
-/// result:
-/// 0 = "weapon upgraded: %s" MsgStringTable[911] in rgb(0,255,255)
-/// 1 = "weapon upgraded: %s" MsgStringTable[912] in rgb(0,205,205)
-/// 2 = "cannot upgrade %s until you level up the upgrade weapon skill" MsgStringTable[913] in rgb(255,200,200)
-/// 3 = "you lack the item %s to upgrade the weapon" MsgStringTable[914] in rgb(255,200,200)
-void clif_upgrademessage(int fd, int result, int item_id)
-{
- WFIFOHEAD(fd,packet_len(0x223));
- WFIFOW(fd,0)=0x223;
- WFIFOL(fd,2)=result;
- WFIFOW(fd,6)=item_id;
- WFIFOSET(fd,packet_len(0x223));
-}
-
-
-/// Whisper is transmitted to the destination player (ZC_WHISPER).
-/// 0097 <packet len>.W <nick>.24B <message>.?B
-/// 0097 <packet len>.W <nick>.24B <isAdmin>.L <message>.?B (PACKETVER >= 20091104)
-void clif_wis_message(int fd, const char* nick, const char* mes, int mes_len)
-{
-#if PACKETVER < 20091104
- WFIFOHEAD(fd, mes_len + NAME_LENGTH + 4);
- WFIFOW(fd,0) = 0x97;
- WFIFOW(fd,2) = mes_len + NAME_LENGTH + 4;
- safestrncpy((char*)WFIFOP(fd,4), nick, NAME_LENGTH);
- safestrncpy((char*)WFIFOP(fd,28), mes, mes_len);
- WFIFOSET(fd,WFIFOW(fd,2));
-#else
- WFIFOHEAD(fd, mes_len + NAME_LENGTH + 8);
- WFIFOW(fd,0) = 0x97;
- WFIFOW(fd,2) = mes_len + NAME_LENGTH + 8;
- safestrncpy((char*)WFIFOP(fd,4), nick, NAME_LENGTH);
- WFIFOL(fd,28) = 0; // isAdmin; if nonzero, also displays text above char
- // TODO: WFIFOL(fd,28) = pc_get_group_level(ssd);
- safestrncpy((char*)WFIFOP(fd,32), mes, mes_len);
- WFIFOSET(fd,WFIFOW(fd,2));
-#endif
-}
-
-
-/// Inform the player about the result of his whisper action (ZC_ACK_WHISPER).
-/// 0098 <result>.B
-/// result:
-/// 0 = success to send wisper
-/// 1 = target character is not loged in
-/// 2 = ignored by target
-/// 3 = everyone ignored by target
-void clif_wis_end(int fd, int flag)
-{
- WFIFOHEAD(fd,packet_len(0x98));
- WFIFOW(fd,0) = 0x98;
- WFIFOW(fd,2) = flag;
- WFIFOSET(fd,packet_len(0x98));
-}
-
-
-/// Returns character name requested by char_id (ZC_ACK_REQNAME_BYGID).
-/// 0194 <char id>.L <name>.24B
-void clif_solved_charname(int fd, int charid, const char* name)
-{
- WFIFOHEAD(fd,packet_len(0x194));
- WFIFOW(fd,0)=0x194;
- WFIFOL(fd,2)=charid;
- safestrncpy((char*)WFIFOP(fd,6), name, NAME_LENGTH);
- WFIFOSET(fd,packet_len(0x194));
-}
-
-
-/// Presents a list of items that can be carded/composed (ZC_ITEMCOMPOSITION_LIST).
-/// 017b <packet len>.W { <name id>.W }*
-void clif_use_card(struct map_session_data *sd,int idx)
-{
- int i,c,ep;
- int fd=sd->fd;
-
- nullpo_retv(sd);
- if (idx < 0 || idx >= MAX_INVENTORY) //Crash-fix from bad packets.
- return;
-
- if (!sd->inventory_data[idx] || sd->inventory_data[idx]->type != IT_CARD)
- return; //Avoid parsing invalid item indexes (no card/no item)
-
- ep=sd->inventory_data[idx]->equip;
- WFIFOHEAD(fd,MAX_INVENTORY * 2 + 4);
- WFIFOW(fd,0)=0x17b;
-
- for(i=c=0;i<MAX_INVENTORY;i++){
- int j;
-
- if(sd->inventory_data[i] == NULL)
- continue;
- if(sd->inventory_data[i]->type!=IT_WEAPON && sd->inventory_data[i]->type!=IT_ARMOR)
- continue;
- if(itemdb_isspecial(sd->status.inventory[i].card[0])) //Can't slot it
- continue;
-
- if(sd->status.inventory[i].identify==0 ) //Not identified
- continue;
-
- if((sd->inventory_data[i]->equip&ep)==0) //Not equippable on this part.
- continue;
-
- if(sd->inventory_data[i]->type==IT_WEAPON && ep==EQP_SHIELD) //Shield card won't go on left weapon.
- continue;
-
- ARR_FIND( 0, sd->inventory_data[i]->slot, j, sd->status.inventory[i].card[j] == 0 );
- if( j == sd->inventory_data[i]->slot ) // No room
- continue;
-
- WFIFOW(fd,4+c*2)=i+2;
- c++;
- }
- WFIFOW(fd,2)=4+c*2;
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Notifies the client about the result of item carding/composition (ZC_ACK_ITEMCOMPOSITION).
-/// 017d <equip index>.W <card index>.W <result>.B
-/// result:
-/// 0 = success
-/// 1 = failure
-void clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x17d));
- WFIFOW(fd,0)=0x17d;
- WFIFOW(fd,2)=idx_equip+2;
- WFIFOW(fd,4)=idx_card+2;
- WFIFOB(fd,6)=flag;
- WFIFOSET(fd,packet_len(0x17d));
-}
-
-
-/// Presents a list of items that can be identified (ZC_ITEMIDENTIFY_LIST).
-/// 0177 <packet len>.W { <name id>.W }*
-void clif_item_identify_list(struct map_session_data *sd)
-{
- int i,c;
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
-
- WFIFOHEAD(fd,MAX_INVENTORY * 2 + 4);
- WFIFOW(fd,0)=0x177;
- for(i=c=0;i<MAX_INVENTORY;i++){
- if(sd->status.inventory[i].nameid > 0 && !sd->status.inventory[i].identify){
- WFIFOW(fd,c*2+4)=i+2;
- c++;
- }
- }
- if(c > 0) {
- WFIFOW(fd,2)=c*2+4;
- WFIFOSET(fd,WFIFOW(fd,2));
- sd->menuskill_id = MC_IDENTIFY;
- sd->menuskill_val = c;
- }
-}
-
-
-/// Notifies the client about the result of a item identify request (ZC_ACK_ITEMIDENTIFY).
-/// 0179 <index>.W <result>.B
-void clif_item_identified(struct map_session_data *sd,int idx,int flag)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x179));
- WFIFOW(fd, 0)=0x179;
- WFIFOW(fd, 2)=idx+2;
- WFIFOB(fd, 4)=flag;
- WFIFOSET(fd,packet_len(0x179));
-}
-
-
-/// Presents a list of items that can be repaired (ZC_REPAIRITEMLIST).
-/// 01fc <packet len>.W { <index>.W <name id>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }*
-void clif_item_repair_list(struct map_session_data *sd,struct map_session_data *dstsd, int lv)
-{
- int i,c;
- int fd;
- int nameid;
-
- nullpo_retv(sd);
- nullpo_retv(dstsd);
-
- fd=sd->fd;
-
- WFIFOHEAD(fd, MAX_INVENTORY * 13 + 4);
- WFIFOW(fd,0)=0x1fc;
- for(i=c=0;i<MAX_INVENTORY;i++){
- if((nameid=dstsd->status.inventory[i].nameid) > 0 && dstsd->status.inventory[i].attribute!=0){// && skill_can_repair(sd,nameid)){
- WFIFOW(fd,c*13+4) = i;
- WFIFOW(fd,c*13+6) = nameid;
- WFIFOB(fd,c*13+8) = dstsd->status.inventory[i].refine;
- clif_addcards(WFIFOP(fd,c*13+9), &dstsd->status.inventory[i]);
- c++;
- }
- }
- if(c > 0) {
- WFIFOW(fd,2)=c*13+4;
- WFIFOSET(fd,WFIFOW(fd,2));
- sd->menuskill_id = BS_REPAIRWEAPON;
- sd->menuskill_val = dstsd->bl.id;
- sd->menuskill_val2 = lv;
- }else
- clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0);
-}
-
-
-/// Notifies the client about the result of a item repair request (ZC_ACK_ITEMREPAIR).
-/// 01fe <index>.W <result>.B
-/// index:
-/// ignored (inventory index)
-/// result:
-/// 0 = Item repair success.
-/// 1 = Item repair failure.
-void clif_item_repaireffect(struct map_session_data *sd,int idx,int flag)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x1fe));
- WFIFOW(fd, 0)=0x1fe;
- WFIFOW(fd, 2)=idx+2;
- WFIFOB(fd, 4)=flag;
- WFIFOSET(fd,packet_len(0x1fe));
-
-}
-
-
-/// Displays a message, that an equipment got damaged (ZC_EQUIPITEM_DAMAGED).
-/// 02bb <equip location>.W <account id>.L
-void clif_item_damaged(struct map_session_data* sd, unsigned short position)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x2bb));
- WFIFOW(fd,0) = 0x2bb;
- WFIFOW(fd,2) = position;
- WFIFOL(fd,4) = sd->bl.id; // TODO: the packet seems to be sent to other people as well, probably party and/or guild.
- WFIFOSET(fd,packet_len(0x2bb));
-}
-
-
-/// Presents a list of weapon items that can be refined [Taken from jAthena] (ZC_NOTIFY_WEAPONITEMLIST).
-/// 0221 <packet len>.W { <index>.W <name id>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }*
-void clif_item_refine_list(struct map_session_data *sd)
-{
- int i,c;
- int fd;
- uint16 skill_lv;
- int wlv;
- int refine_item[5];
-
- nullpo_retv(sd);
-
- skill_lv = pc_checkskill(sd,WS_WEAPONREFINE);
-
- fd=sd->fd;
-
- refine_item[0] = -1;
- refine_item[1] = pc_search_inventory(sd,1010);
- refine_item[2] = pc_search_inventory(sd,1011);
- refine_item[3] = refine_item[4] = pc_search_inventory(sd,984);
-
- WFIFOHEAD(fd, MAX_INVENTORY * 13 + 4);
- WFIFOW(fd,0)=0x221;
- for(i=c=0;i<MAX_INVENTORY;i++){
- if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].refine < skill_lv &&
- sd->status.inventory[i].identify && (wlv=itemdb_wlv(sd->status.inventory[i].nameid)) >=1 &&
- refine_item[wlv]!=-1 && !(sd->status.inventory[i].equip&EQP_ARMS)){
- WFIFOW(fd,c*13+ 4)=i+2;
- WFIFOW(fd,c*13+ 6)=sd->status.inventory[i].nameid;
- WFIFOB(fd,c*13+ 8)=sd->status.inventory[i].refine;
- clif_addcards(WFIFOP(fd,c*13+9), &sd->status.inventory[i]);
- c++;
- }
- }
- WFIFOW(fd,2)=c*13+4;
- WFIFOSET(fd,WFIFOW(fd,2));
- if (c > 0) {
- sd->menuskill_id = WS_WEAPONREFINE;
- sd->menuskill_val = skill_lv;
- }
-}
-
-
-/// Notification of an auto-casted skill (ZC_AUTORUN_SKILL).
-/// 0147 <skill id>.W <type>.L <level>.W <sp cost>.W <atk range>.W <skill name>.24B <upgradable>.B
-void clif_item_skill(struct map_session_data *sd,uint16 skill_id,uint16 skill_lv)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x147));
- WFIFOW(fd, 0)=0x147;
- WFIFOW(fd, 2)=skill_id;
- WFIFOW(fd, 4)=skill_get_inf(skill_id);
- WFIFOW(fd, 6)=0;
- WFIFOW(fd, 8)=skill_lv;
- WFIFOW(fd,10)=skill_get_sp(skill_id,skill_lv);
- WFIFOW(fd,12)=skill_get_range2(&sd->bl, skill_id,skill_lv);
- safestrncpy((char*)WFIFOP(fd,14),skill_get_name(skill_id),NAME_LENGTH);
- WFIFOB(fd,38)=0;
- WFIFOSET(fd,packet_len(0x147));
-}
-
-
-/// Adds an item to character's cart.
-/// 0124 <index>.W <amount>.L <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_CART)
-/// 01c5 <index>.W <amount>.L <name id>.W <type>.B <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_CART2)
-void clif_cart_additem(struct map_session_data *sd,int n,int amount,int fail)
-{
- int view,fd;
- unsigned char *buf;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- if(n<0 || n>=MAX_CART || sd->status.cart[n].nameid<=0)
- return;
-
-#if PACKETVER < 5
- WFIFOHEAD(fd,packet_len(0x124));
- buf=WFIFOP(fd,0);
- WBUFW(buf,0)=0x124;
- WBUFW(buf,2)=n+2;
- WBUFL(buf,4)=amount;
- if((view = itemdb_viewid(sd->status.cart[n].nameid)) > 0)
- WBUFW(buf,8)=view;
- else
- WBUFW(buf,8)=sd->status.cart[n].nameid;
- WBUFB(buf,10)=sd->status.cart[n].identify;
- WBUFB(buf,11)=sd->status.cart[n].attribute;
- WBUFB(buf,12)=sd->status.cart[n].refine;
- clif_addcards(WBUFP(buf,13), &sd->status.cart[n]);
- WFIFOSET(fd,packet_len(0x124));
-#else
- WFIFOHEAD(fd,packet_len(0x1c5));
- buf=WFIFOP(fd,0);
- WBUFW(buf,0)=0x1c5;
- WBUFW(buf,2)=n+2;
- WBUFL(buf,4)=amount;
- if((view = itemdb_viewid(sd->status.cart[n].nameid)) > 0)
- WBUFW(buf,8)=view;
- else
- WBUFW(buf,8)=sd->status.cart[n].nameid;
- WBUFB(buf,10)=itemdb_type(sd->status.cart[n].nameid);
- WBUFB(buf,11)=sd->status.cart[n].identify;
- WBUFB(buf,12)=sd->status.cart[n].attribute;
- WBUFB(buf,13)=sd->status.cart[n].refine;
- clif_addcards(WBUFP(buf,14), &sd->status.cart[n]);
- WFIFOSET(fd,packet_len(0x1c5));
-#endif
-}
-
-
-/// Deletes an item from character's cart (ZC_DELETE_ITEM_FROM_CART).
-/// 0125 <index>.W <amount>.L
-void clif_cart_delitem(struct map_session_data *sd,int n,int amount)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x125));
- WFIFOW(fd,0)=0x125;
- WFIFOW(fd,2)=n+2;
- WFIFOL(fd,4)=amount;
- WFIFOSET(fd,packet_len(0x125));
-}
-
-
-/// Opens the shop creation menu (ZC_OPENSTORE).
-/// 012d <num>.W
-/// num:
-/// number of allowed item slots
-void clif_openvendingreq(struct map_session_data* sd, int num)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0x12d));
- WFIFOW(fd,0) = 0x12d;
- WFIFOW(fd,2) = num;
- WFIFOSET(fd,packet_len(0x12d));
-}
-
-
-/// Displays a vending board to target/area (ZC_STORE_ENTRY).
-/// 0131 <owner id>.L <message>.80B
-void clif_showvendingboard(struct block_list* bl, const char* message, int fd)
-{
- unsigned char buf[128];
-
- nullpo_retv(bl);
-
- WBUFW(buf,0) = 0x131;
- WBUFL(buf,2) = bl->id;
- safestrncpy((char*)WBUFP(buf,6), message, 80);
-
- if( fd ) {
- WFIFOHEAD(fd,packet_len(0x131));
- memcpy(WFIFOP(fd,0),buf,packet_len(0x131));
- WFIFOSET(fd,packet_len(0x131));
- } else {
- clif_send(buf,packet_len(0x131),bl,AREA_WOS);
- }
-}
-
-
-/// Removes a vending board from screen (ZC_DISAPPEAR_ENTRY).
-/// 0132 <owner id>.L
-void clif_closevendingboard(struct block_list* bl, int fd)
-{
- unsigned char buf[16];
-
- nullpo_retv(bl);
-
- WBUFW(buf,0) = 0x132;
- WBUFL(buf,2) = bl->id;
- if( fd ) {
- WFIFOHEAD(fd,packet_len(0x132));
- memcpy(WFIFOP(fd,0),buf,packet_len(0x132));
- WFIFOSET(fd,packet_len(0x132));
- } else {
- clif_send(buf,packet_len(0x132),bl,AREA_WOS);
- }
-}
-
-
-/// Sends a list of items in a shop.
-/// R 0133 <packet len>.W <owner id>.L { <price>.L <amount>.W <index>.W <type>.B <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }* (ZC_PC_PURCHASE_ITEMLIST_FROMMC)
-/// R 0800 <packet len>.W <owner id>.L <unique id>.L { <price>.L <amount>.W <index>.W <type>.B <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }* (ZC_PC_PURCHASE_ITEMLIST_FROMMC2)
-void clif_vendinglist(struct map_session_data* sd, int id, struct s_vending* vending)
-{
- int i,fd;
- int count;
- struct map_session_data* vsd;
-#if PACKETVER < 20100105
- const int cmd = 0x133;
- const int offset = 8;
-#else
- const int cmd = 0x800;
- const int offset = 12;
-#endif
-
- nullpo_retv(sd);
- nullpo_retv(vending);
- nullpo_retv(vsd=map_id2sd(id));
-
- fd = sd->fd;
- count = vsd->vend_num;
-
- WFIFOHEAD(fd, offset+count*22);
- WFIFOW(fd,0) = cmd;
- WFIFOW(fd,2) = offset+count*22;
- WFIFOL(fd,4) = id;
-#if PACKETVER >= 20100105
- WFIFOL(fd,8) = vsd->vender_id;
-#endif
-
- for( i = 0; i < count; i++ )
- {
- int index = vending[i].index;
- struct item_data* data = itemdb_search(vsd->status.cart[index].nameid);
- WFIFOL(fd,offset+ 0+i*22) = vending[i].value;
- WFIFOW(fd,offset+ 4+i*22) = vending[i].amount;
- WFIFOW(fd,offset+ 6+i*22) = vending[i].index + 2;
- WFIFOB(fd,offset+ 8+i*22) = itemtype(data->type);
- WFIFOW(fd,offset+ 9+i*22) = ( data->view_id > 0 ) ? data->view_id : vsd->status.cart[index].nameid;
- WFIFOB(fd,offset+11+i*22) = vsd->status.cart[index].identify;
- WFIFOB(fd,offset+12+i*22) = vsd->status.cart[index].attribute;
- WFIFOB(fd,offset+13+i*22) = vsd->status.cart[index].refine;
- clif_addcards(WFIFOP(fd,offset+14+i*22), &vsd->status.cart[index]);
- }
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Shop purchase failure (ZC_PC_PURCHASE_RESULT_FROMMC).
-/// 0135 <index>.W <amount>.W <result>.B
-/// result:
-/// 0 = success
-/// 1 = not enough zeny
-/// 2 = overweight
-/// 4 = out of stock
-/// 5 = "cannot use an npc shop while in a trade"
-/// 6 = Because the store information was incorrect the item was not purchased.
-/// 7 = No sales information.
-void clif_buyvending(struct map_session_data* sd, int index, int amount, int fail)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0x135));
- WFIFOW(fd,0) = 0x135;
- WFIFOW(fd,2) = index+2;
- WFIFOW(fd,4) = amount;
- WFIFOB(fd,6) = fail;
- WFIFOSET(fd,packet_len(0x135));
-}
-
-
-/// Shop creation success (ZC_PC_PURCHASE_MYITEMLIST).
-/// 0136 <packet len>.W <owner id>.L { <price>.L <index>.W <amount>.W <type>.B <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }*
-void clif_openvending(struct map_session_data* sd, int id, struct s_vending* vending)
-{
- int i,fd;
- int count;
-
- nullpo_retv(sd);
-
- fd = sd->fd;
- count = sd->vend_num;
-
- WFIFOHEAD(fd, 8+count*22);
- WFIFOW(fd,0) = 0x136;
- WFIFOW(fd,2) = 8+count*22;
- WFIFOL(fd,4) = id;
- for( i = 0; i < count; i++ )
- {
- int index = vending[i].index;
- struct item_data* data = itemdb_search(sd->status.cart[index].nameid);
- WFIFOL(fd, 8+i*22) = vending[i].value;
- WFIFOW(fd,12+i*22) = vending[i].index + 2;
- WFIFOW(fd,14+i*22) = vending[i].amount;
- WFIFOB(fd,16+i*22) = itemtype(data->type);
- WFIFOW(fd,17+i*22) = ( data->view_id > 0 ) ? data->view_id : sd->status.cart[index].nameid;
- WFIFOB(fd,19+i*22) = sd->status.cart[index].identify;
- WFIFOB(fd,20+i*22) = sd->status.cart[index].attribute;
- WFIFOB(fd,21+i*22) = sd->status.cart[index].refine;
- clif_addcards(WFIFOP(fd,22+i*22), &sd->status.cart[index]);
- }
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Inform merchant that someone has bought an item (ZC_DELETEITEM_FROM_MCSTORE).
-/// 0137 <index>.W <amount>.W
-void clif_vendingreport(struct map_session_data* sd, int index, int amount)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0x137));
- WFIFOW(fd,0) = 0x137;
- WFIFOW(fd,2) = index+2;
- WFIFOW(fd,4) = amount;
- WFIFOSET(fd,packet_len(0x137));
-}
-
-
-/// Result of organizing a party (ZC_ACK_MAKE_GROUP).
-/// 00fa <result>.B
-/// result:
-/// 0 = opens party window and shows MsgStringTable[77]="party successfully organized"
-/// 1 = MsgStringTable[78]="party name already exists"
-/// 2 = MsgStringTable[79]="already in a party"
-/// 3 = cannot organize parties on this map
-/// ? = nothing
-void clif_party_created(struct map_session_data *sd,int result)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0xfa));
- WFIFOW(fd,0)=0xfa;
- WFIFOB(fd,2)=result;
- WFIFOSET(fd,packet_len(0xfa));
-}
-
-
-/// Adds new member to a party.
-/// 0104 <account id>.L <role>.L <x>.W <y>.W <state>.B <party name>.24B <char name>.24B <map name>.16B (ZC_ADD_MEMBER_TO_GROUP)
-/// 01e9 <account id>.L <role>.L <x>.W <y>.W <state>.B <party name>.24B <char name>.24B <map name>.16B <item pickup rule>.B <item share rule>.B (ZC_ADD_MEMBER_TO_GROUP2)
-/// role:
-/// 0 = leader
-/// 1 = normal
-/// state:
-/// 0 = connected
-/// 1 = disconnected
-void clif_party_member_info(struct party_data *p, struct map_session_data *sd)
-{
- unsigned char buf[81];
- int i;
-
- if (!sd) { //Pick any party member (this call is used when changing item share rules)
- ARR_FIND( 0, MAX_PARTY, i, p->data[i].sd != 0 );
- } else {
- ARR_FIND( 0, MAX_PARTY, i, p->data[i].sd == sd );
- }
- if (i >= MAX_PARTY) return; //Should never happen...
- sd = p->data[i].sd;
-
- WBUFW(buf, 0) = 0x1e9;
- WBUFL(buf, 2) = sd->status.account_id;
- WBUFL(buf, 6) = (p->party.member[i].leader)?0:1;
- WBUFW(buf,10) = sd->bl.x;
- WBUFW(buf,12) = sd->bl.y;
- WBUFB(buf,14) = (p->party.member[i].online)?0:1;
- memcpy(WBUFP(buf,15), p->party.name, NAME_LENGTH);
- memcpy(WBUFP(buf,39), sd->status.name, NAME_LENGTH);
- mapindex_getmapname_ext(map[sd->bl.m].name, (char*)WBUFP(buf,63));
- WBUFB(buf,79) = (p->party.item&1)?1:0;
- WBUFB(buf,80) = (p->party.item&2)?1:0;
- clif_send(buf,packet_len(0x1e9),&sd->bl,PARTY);
-}
-
-
-/// Sends party information (ZC_GROUP_LIST).
-/// 00fb <packet len>.W <party name>.24B { <account id>.L <nick>.24B <map name>.16B <role>.B <state>.B }*
-/// role:
-/// 0 = leader
-/// 1 = normal
-/// state:
-/// 0 = connected
-/// 1 = disconnected
-void clif_party_info(struct party_data* p, struct map_session_data *sd)
-{
- unsigned char buf[2+2+NAME_LENGTH+(4+NAME_LENGTH+MAP_NAME_LENGTH_EXT+1+1)*MAX_PARTY];
- struct map_session_data* party_sd = NULL;
- int i, c;
-
- nullpo_retv(p);
-
- WBUFW(buf,0) = 0xfb;
- memcpy(WBUFP(buf,4), p->party.name, NAME_LENGTH);
- for(i = 0, c = 0; i < MAX_PARTY; i++)
- {
- struct party_member* m = &p->party.member[i];
- if(!m->account_id) continue;
-
- if(party_sd == NULL) party_sd = p->data[i].sd;
-
- WBUFL(buf,28+c*46) = m->account_id;
- memcpy(WBUFP(buf,28+c*46+4), m->name, NAME_LENGTH);
- mapindex_getmapname_ext(mapindex_id2name(m->map), (char*)WBUFP(buf,28+c*46+28));
- WBUFB(buf,28+c*46+44) = (m->leader) ? 0 : 1;
- WBUFB(buf,28+c*46+45) = (m->online) ? 0 : 1;
- c++;
- }
- WBUFW(buf,2) = 28+c*46;
-
- if(sd) { // send only to self
- clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
- } else if (party_sd) { // send to whole party
- clif_send(buf, WBUFW(buf,2), &party_sd->bl, PARTY);
- }
-}
-
-
-/// The player's 'party invite' state, sent during login (ZC_PARTY_CONFIG).
-/// 02c9 <flag>.B
-/// flag:
-/// 0 = allow party invites
-/// 1 = auto-deny party invites
-void clif_partyinvitationstate(struct map_session_data* sd)
-{
- int fd;
- nullpo_retv(sd);
- fd = sd->fd;
-
- WFIFOHEAD(fd, packet_len(0x2c9));
- WFIFOW(fd, 0) = 0x2c9;
- WFIFOB(fd, 2) = 0; // not implemented
- WFIFOSET(fd, packet_len(0x2c9));
-}
-
-
-/// Party invitation request.
-/// 00fe <party id>.L <party name>.24B (ZC_REQ_JOIN_GROUP)
-/// 02c6 <party id>.L <party name>.24B (ZC_PARTY_JOIN_REQ)
-void clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd)
-{
-#if PACKETVER < 20070821
- const int cmd = 0xfe;
-#else
- const int cmd = 0x2c6;
-#endif
- int fd;
- struct party_data *p;
-
- nullpo_retv(sd);
- nullpo_retv(tsd);
-
- fd=tsd->fd;
-
- if( (p=party_search(sd->status.party_id))==NULL )
- return;
-
- WFIFOHEAD(fd,packet_len(cmd));
- WFIFOW(fd,0)=cmd;
- WFIFOL(fd,2)=sd->status.party_id;
- memcpy(WFIFOP(fd,6),p->party.name,NAME_LENGTH);
- WFIFOSET(fd,packet_len(cmd));
-}
-
-
-/// Party invite result.
-/// 00fd <nick>.24S <result>.B (ZC_ACK_REQ_JOIN_GROUP)
-/// 02c5 <nick>.24S <result>.L (ZC_PARTY_JOIN_REQ_ACK)
-/// result=0 : char is already in a party -> MsgStringTable[80]
-/// result=1 : party invite was rejected -> MsgStringTable[81]
-/// result=2 : party invite was accepted -> MsgStringTable[82]
-/// result=3 : party is full -> MsgStringTable[83]
-/// result=4 : char of the same account already joined the party -> MsgStringTable[608]
-/// result=5 : char blocked party invite -> MsgStringTable[1324] (since 20070904)
-/// result=7 : char is not online or doesn't exist -> MsgStringTable[71] (since 20070904)
-/// result=8 : (%s) TODO instance related? -> MsgStringTable[1388] (since 20080527)
-/// return=9 : TODO map prohibits party joining? -> MsgStringTable[1871] (since 20110205)
-void clif_party_inviteack(struct map_session_data* sd, const char* nick, int result)
-{
- int fd;
- nullpo_retv(sd);
- fd=sd->fd;
-
-#if PACKETVER < 20070904
- if( result == 7 ) {
- clif_displaymessage(fd, msg_txt(3));
- return;
- }
-#endif
-
-#if PACKETVER < 20070821
- WFIFOHEAD(fd,packet_len(0xfd));
- WFIFOW(fd,0) = 0xfd;
- safestrncpy((char*)WFIFOP(fd,2),nick,NAME_LENGTH);
- WFIFOB(fd,26) = result;
- WFIFOSET(fd,packet_len(0xfd));
-#else
- WFIFOHEAD(fd,packet_len(0x2c5));
- WFIFOW(fd,0) = 0x2c5;
- safestrncpy((char*)WFIFOP(fd,2),nick,NAME_LENGTH);
- WFIFOL(fd,26) = result;
- WFIFOSET(fd,packet_len(0x2c5));
-#endif
-}
-
-
-/// Updates party settings.
-/// 0101 <exp option>.L (ZC_GROUPINFO_CHANGE)
-/// 07d8 <exp option>.L <item pick rule>.B <item share rule>.B (ZC_REQ_GROUPINFO_CHANGE_V2)
-/// exp option:
-/// 0 = exp sharing disabled
-/// 1 = exp sharing enabled
-/// 2 = cannot change exp sharing
-///
-/// flag:
-/// 0 = send to party
-/// 1 = send to sd
-void clif_party_option(struct party_data *p,struct map_session_data *sd,int flag)
-{
- unsigned char buf[16];
-#if PACKETVER < 20090603
- const int cmd = 0x101;
-#else
- const int cmd = 0x7d8;
-#endif
-
- nullpo_retv(p);
-
- if(!sd && flag==0){
- int i;
- for(i=0;i<MAX_PARTY && !p->data[i].sd;i++);
- if (i < MAX_PARTY)
- sd = p->data[i].sd;
- }
- if(!sd) return;
- WBUFW(buf,0)=cmd;
- WBUFL(buf,2)=((flag&0x01)?2:p->party.exp);
-#if PACKETVER >= 20090603
- WBUFB(buf,6)=(p->party.item&1)?1:0;
- WBUFB(buf,7)=(p->party.item&2)?1:0;
-#endif
- if(flag==0)
- clif_send(buf,packet_len(cmd),&sd->bl,PARTY);
- else
- clif_send(buf,packet_len(cmd),&sd->bl,SELF);
-}
-
-
-/// 0105 <account id>.L <char name>.24B <result>.B (ZC_DELETE_MEMBER_FROM_GROUP).
-/// result:
-/// 0 = leave
-/// 1 = expel
-/// 2 = cannot leave party on this map
-/// 3 = cannot expel from party on this map
-void clif_party_withdraw(struct party_data* p, struct map_session_data* sd, int account_id, const char* name, int flag)
-{
- unsigned char buf[64];
- int i;
-
- nullpo_retv(p);
-
- if(!sd && (flag&0xf0)==0)
- {
- for(i=0;i<MAX_PARTY && !p->data[i].sd;i++);
- if (i < MAX_PARTY)
- sd = p->data[i].sd;
- }
-
- if(!sd) return;
-
- WBUFW(buf,0)=0x105;
- WBUFL(buf,2)=account_id;
- memcpy(WBUFP(buf,6),name,NAME_LENGTH);
- WBUFB(buf,30)=flag&0x0f;
- if((flag&0xf0)==0)
- clif_send(buf,packet_len(0x105),&sd->bl,PARTY);
- else
- clif_send(buf,packet_len(0x105),&sd->bl,SELF);
-}
-
-
-/// Party chat message (ZC_NOTIFY_CHAT_PARTY).
-/// 0109 <packet len>.W <account id>.L <message>.?B
-void clif_party_message(struct party_data* p, int account_id, const char* mes, int len)
-{
- struct map_session_data *sd;
- int i;
-
- nullpo_retv(p);
-
- for(i=0; i < MAX_PARTY && !p->data[i].sd;i++);
- if(i < MAX_PARTY){
- unsigned char buf[1024];
-
- if( len > sizeof(buf)-8 )
- {
- ShowWarning("clif_party_message: Truncated message '%s' (len=%d, max=%d, party_id=%d).\n", mes, len, sizeof(buf)-8, p->party.party_id);
- len = sizeof(buf)-8;
- }
-
- sd = p->data[i].sd;
- WBUFW(buf,0)=0x109;
- WBUFW(buf,2)=len+8;
- WBUFL(buf,4)=account_id;
- safestrncpy((char *)WBUFP(buf,8), mes, len);
- clif_send(buf,len+8,&sd->bl,PARTY);
- }
-}
-
-
-/// Updates the position of a party member on the minimap (ZC_NOTIFY_POSITION_TO_GROUPM).
-/// 0107 <account id>.L <x>.W <y>.W
-void clif_party_xy(struct map_session_data *sd)
-{
- unsigned char buf[16];
-
- nullpo_retv(sd);
-
- WBUFW(buf,0)=0x107;
- WBUFL(buf,2)=sd->status.account_id;
- WBUFW(buf,6)=sd->bl.x;
- WBUFW(buf,8)=sd->bl.y;
- clif_send(buf,packet_len(0x107),&sd->bl,PARTY_SAMEMAP_WOS);
-}
-
-
-/*==========================================
- * Sends x/y dot to a single fd. [Skotlex]
- *------------------------------------------*/
-void clif_party_xy_single(int fd, struct map_session_data *sd)
-{
- WFIFOHEAD(fd,packet_len(0x107));
- WFIFOW(fd,0)=0x107;
- WFIFOL(fd,2)=sd->status.account_id;
- WFIFOW(fd,6)=sd->bl.x;
- WFIFOW(fd,8)=sd->bl.y;
- WFIFOSET(fd,packet_len(0x107));
-}
-
-
-/// Updates HP bar of a party member.
-/// 0106 <account id>.L <hp>.W <max hp>.W (ZC_NOTIFY_HP_TO_GROUPM)
-/// 080e <account id>.L <hp>.L <max hp>.L (ZC_NOTIFY_HP_TO_GROUPM_R2)
-void clif_party_hp(struct map_session_data *sd)
-{
- unsigned char buf[16];
-#if PACKETVER < 20100126
- const int cmd = 0x106;
-#else
- const int cmd = 0x80e;
-#endif
-
- nullpo_retv(sd);
-
- WBUFW(buf,0)=cmd;
- WBUFL(buf,2)=sd->status.account_id;
-#if PACKETVER < 20100126
- if (sd->battle_status.max_hp > INT16_MAX) { //To correctly display the %hp bar. [Skotlex]
- WBUFW(buf,6) = sd->battle_status.hp/(sd->battle_status.max_hp/100);
- WBUFW(buf,8) = 100;
- } else {
- WBUFW(buf,6) = sd->battle_status.hp;
- WBUFW(buf,8) = sd->battle_status.max_hp;
- }
-#else
- WBUFL(buf,6) = sd->battle_status.hp;
- WBUFL(buf,10) = sd->battle_status.max_hp;
-#endif
- clif_send(buf,packet_len(cmd),&sd->bl,PARTY_AREA_WOS);
-}
-
-
-/*==========================================
- * Sends HP bar to a single fd. [Skotlex]
- *------------------------------------------*/
-void clif_hpmeter_single(int fd, int id, unsigned int hp, unsigned int maxhp)
-{
-#if PACKETVER < 20100126
- const int cmd = 0x106;
-#else
- const int cmd = 0x80e;
-#endif
- WFIFOHEAD(fd,packet_len(cmd));
- WFIFOW(fd,0) = cmd;
- WFIFOL(fd,2) = id;
-#if PACKETVER < 20100126
- if( maxhp > INT16_MAX )
- {// To correctly display the %hp bar. [Skotlex]
- WFIFOW(fd,6) = hp/(maxhp/100);
- WFIFOW(fd,8) = 100;
- } else {
- WFIFOW(fd,6) = hp;
- WFIFOW(fd,8) = maxhp;
- }
-#else
- WFIFOL(fd,6) = hp;
- WFIFOL(fd,10) = maxhp;
-#endif
- WFIFOSET(fd, packet_len(cmd));
-}
-
-/// Notifies the client, that it's attack target is too far (ZC_ATTACK_FAILURE_FOR_DISTANCE).
-/// 0139 <target id>.L <target x>.W <target y>.W <x>.W <y>.W <atk range>.W
-void clif_movetoattack(struct map_session_data *sd,struct block_list *bl)
-{
- int fd;
-
- nullpo_retv(sd);
- nullpo_retv(bl);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x139));
- WFIFOW(fd, 0)=0x139;
- WFIFOL(fd, 2)=bl->id;
- WFIFOW(fd, 6)=bl->x;
- WFIFOW(fd, 8)=bl->y;
- WFIFOW(fd,10)=sd->bl.x;
- WFIFOW(fd,12)=sd->bl.y;
- WFIFOW(fd,14)=sd->battle_status.rhw.range;
- WFIFOSET(fd,packet_len(0x139));
-}
-
-
-/// Notifies the client about the result of an item produce request (ZC_ACK_REQMAKINGITEM).
-/// 018f <result>.W <name id>.W
-/// result:
-/// 0 = success
-/// 1 = failure
-/// 2 = success (alchemist)
-/// 3 = failure (alchemist)
-void clif_produceeffect(struct map_session_data* sd,int flag,int nameid)
-{
- int view,fd;
-
- nullpo_retv(sd);
-
- fd = sd->fd;
- clif_solved_charname(fd, sd->status.char_id, sd->status.name);
- WFIFOHEAD(fd,packet_len(0x18f));
- WFIFOW(fd, 0)=0x18f;
- WFIFOW(fd, 2)=flag;
- if((view = itemdb_viewid(nameid)) > 0)
- WFIFOW(fd, 4)=view;
- else
- WFIFOW(fd, 4)=nameid;
- WFIFOSET(fd,packet_len(0x18f));
-}
-
-
-/// Initiates the pet taming process (ZC_START_CAPTURE).
-/// 019e
-void clif_catch_process(struct map_session_data *sd)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x19e));
- WFIFOW(fd,0)=0x19e;
- WFIFOSET(fd,packet_len(0x19e));
-}
-
-
-/// Displays the result of a pet taming attempt (ZC_TRYCAPTURE_MONSTER).
-/// 01a0 <result>.B
-/// 0 = failure
-/// 1 = success
-void clif_pet_roulette(struct map_session_data *sd,int data)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x1a0));
- WFIFOW(fd,0)=0x1a0;
- WFIFOB(fd,2)=data;
- WFIFOSET(fd,packet_len(0x1a0));
-}
-
-
-/// Presents a list of pet eggs that can be hatched (ZC_PETEGG_LIST).
-/// 01a6 <packet len>.W { <index>.W }*
-void clif_sendegg(struct map_session_data *sd)
-{
- int i,n=0,fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- if (battle_config.pet_no_gvg && map_flag_gvg(sd->bl.m))
- { //Disable pet hatching in GvG grounds during Guild Wars [Skotlex]
- clif_displaymessage(fd, msg_txt(666));
- return;
- }
- WFIFOHEAD(fd, MAX_INVENTORY * 2 + 4);
- WFIFOW(fd,0)=0x1a6;
- for(i=0,n=0;i<MAX_INVENTORY;i++){
- if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
- sd->inventory_data[i]->type!=IT_PETEGG ||
- sd->status.inventory[i].amount<=0)
- continue;
- WFIFOW(fd,n*2+4)=i+2;
- n++;
- }
- WFIFOW(fd,2)=4+n*2;
- WFIFOSET(fd,WFIFOW(fd,2));
-
- sd->menuskill_id = SA_TAMINGMONSTER;
- sd->menuskill_val = -1;
-}
-
-
-/// Sends a specific pet data update (ZC_CHANGESTATE_PET).
-/// 01a4 <type>.B <id>.L <data>.L
-/// type:
-/// 0 = pre-init (data = 0)
-/// 1 = intimacy (data = 0~4)
-/// 2 = hunger (data = 0~4)
-/// 3 = accessory
-/// 4 = performance (data = 1~3: normal, 4: special)
-/// 5 = hairstyle
-///
-/// If sd is null, the update is sent to nearby objects, otherwise it is sent only to that player.
-void clif_send_petdata(struct map_session_data* sd, struct pet_data* pd, int type, int param)
-{
- uint8 buf[16];
- nullpo_retv(pd);
-
- WBUFW(buf,0) = 0x1a4;
- WBUFB(buf,2) = type;
- WBUFL(buf,3) = pd->bl.id;
- WBUFL(buf,7) = param;
- if (sd)
- clif_send(buf, packet_len(0x1a4), &sd->bl, SELF);
- else
- clif_send(buf, packet_len(0x1a4), &pd->bl, AREA);
-}
-
-
-/// Pet's base data (ZC_PROPERTY_PET).
-/// 01a2 <name>.24B <renamed>.B <level>.W <hunger>.W <intimacy>.W <accessory id>.W <class>.W
-void clif_send_petstatus(struct map_session_data *sd)
-{
- int fd;
- struct s_pet *pet;
-
- nullpo_retv(sd);
- nullpo_retv(sd->pd);
-
- fd=sd->fd;
- pet = &sd->pd->pet;
- WFIFOHEAD(fd,packet_len(0x1a2));
- WFIFOW(fd,0)=0x1a2;
- memcpy(WFIFOP(fd,2),pet->name,NAME_LENGTH);
- WFIFOB(fd,26)=battle_config.pet_rename?0:pet->rename_flag;
- WFIFOW(fd,27)=pet->level;
- WFIFOW(fd,29)=pet->hungry;
- WFIFOW(fd,31)=pet->intimate;
- WFIFOW(fd,33)=pet->equip;
-#if PACKETVER >= 20081126
- WFIFOW(fd,35)=pet->class_;
-#endif
- WFIFOSET(fd,packet_len(0x1a2));
-}
-
-
-/// Notification about a pet's emotion/talk (ZC_PET_ACT).
-/// 01aa <id>.L <data>.L
-/// data:
-/// @see CZ_PET_ACT.
-void clif_pet_emotion(struct pet_data *pd,int param)
-{
- unsigned char buf[16];
-
- nullpo_retv(pd);
-
- memset(buf,0,packet_len(0x1aa));
-
- WBUFW(buf,0)=0x1aa;
- WBUFL(buf,2)=pd->bl.id;
- if(param >= 100 && pd->petDB->talk_convert_class) {
- if(pd->petDB->talk_convert_class < 0)
- return;
- else if(pd->petDB->talk_convert_class > 0) {
- // replace mob_id component of talk/act data
- param -= (pd->pet.class_ - 100)*100;
- param += (pd->petDB->talk_convert_class - 100)*100;
- }
- }
- WBUFL(buf,6)=param;
-
- clif_send(buf,packet_len(0x1aa),&pd->bl,AREA);
-}
-
-
-/// Result of request to feed a pet (ZC_FEED_PET).
-/// 01a3 <result>.B <name id>.W
-/// result:
-/// 0 = failure
-/// 1 = success
-void clif_pet_food(struct map_session_data *sd,int foodid,int fail)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x1a3));
- WFIFOW(fd,0)=0x1a3;
- WFIFOB(fd,2)=fail;
- WFIFOW(fd,3)=foodid;
- WFIFOSET(fd,packet_len(0x1a3));
-}
-
-
-/// Presents a list of skills that can be auto-spelled (ZC_AUTOSPELLLIST).
-/// 01cd { <skill id>.L }*7
-void clif_autospell(struct map_session_data *sd,uint16 skill_lv)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x1cd));
- WFIFOW(fd, 0)=0x1cd;
-
- if(skill_lv>0 && pc_checkskill(sd,MG_NAPALMBEAT)>0)
- WFIFOL(fd,2)= MG_NAPALMBEAT;
- else
- WFIFOL(fd,2)= 0x00000000;
- if(skill_lv>1 && pc_checkskill(sd,MG_COLDBOLT)>0)
- WFIFOL(fd,6)= MG_COLDBOLT;
- else
- WFIFOL(fd,6)= 0x00000000;
- if(skill_lv>1 && pc_checkskill(sd,MG_FIREBOLT)>0)
- WFIFOL(fd,10)= MG_FIREBOLT;
- else
- WFIFOL(fd,10)= 0x00000000;
- if(skill_lv>1 && pc_checkskill(sd,MG_LIGHTNINGBOLT)>0)
- WFIFOL(fd,14)= MG_LIGHTNINGBOLT;
- else
- WFIFOL(fd,14)= 0x00000000;
- if(skill_lv>4 && pc_checkskill(sd,MG_SOULSTRIKE)>0)
- WFIFOL(fd,18)= MG_SOULSTRIKE;
- else
- WFIFOL(fd,18)= 0x00000000;
- if(skill_lv>7 && pc_checkskill(sd,MG_FIREBALL)>0)
- WFIFOL(fd,22)= MG_FIREBALL;
- else
- WFIFOL(fd,22)= 0x00000000;
- if(skill_lv>9 && pc_checkskill(sd,MG_FROSTDIVER)>0)
- WFIFOL(fd,26)= MG_FROSTDIVER;
- else
- WFIFOL(fd,26)= 0x00000000;
-
- WFIFOSET(fd,packet_len(0x1cd));
- sd->menuskill_id = SA_AUTOSPELL;
- sd->menuskill_val = skill_lv;
-}
-
-
-/// Devotion's visual effect (ZC_DEVOTIONLIST).
-/// 01cf <devoter id>.L { <devotee id>.L }*5 <max distance>.W
-void clif_devotion(struct block_list *src, struct map_session_data *tsd)
-{
- unsigned char buf[56];
- int i;
-
- nullpo_retv(src);
- memset(buf,0,packet_len(0x1cf));
-
- WBUFW(buf,0) = 0x1cf;
- WBUFL(buf,2) = src->id;
- if( src->type == BL_MER )
- {
- struct mercenary_data *md = BL_CAST(BL_MER,src);
- if( md && md->master && md->devotion_flag )
- WBUFL(buf,6) = md->master->bl.id;
-
- WBUFW(buf,26) = skill_get_range2(src, ML_DEVOTION, mercenary_checkskill(md, ML_DEVOTION));
- }
- else
- {
- struct map_session_data *sd = BL_CAST(BL_PC,src);
- if( sd == NULL )
- return;
-
- for( i = 0; i < 5; i++ )
- WBUFL(buf,6+4*i) = sd->devotion[i];
- WBUFW(buf,26) = skill_get_range2(src, CR_DEVOTION, pc_checkskill(sd, CR_DEVOTION));
- }
-
- if( tsd )
- clif_send(buf, packet_len(0x1cf), &tsd->bl, SELF);
- else
- clif_send(buf, packet_len(0x1cf), src, AREA);
-}
-
-/*==========================================
- * Server tells clients nearby 'sd' (and himself) to display 'sd->spiritball' number of spiritballs on 'sd'
- * Notifies clients in an area of an object's spirits.
- * 01d0 <id>.L <amount>.W (ZC_SPIRITS)
- * 01e1 <id>.L <amount>.W (ZC_SPIRITS2)
- *------------------------------------------*/
-void clif_spiritball(struct block_list *bl) {
- unsigned char buf[16];
- TBL_PC *sd = BL_CAST(BL_PC,bl);
- TBL_HOM *hd = BL_CAST(BL_HOM,bl);
-
- nullpo_retv(bl);
-
- WBUFW(buf, 0) = 0x1d0;
- WBUFL(buf, 2) = bl->id;
- WBUFW(buf, 6) = 0; //init to 0
- switch(bl->type){
- case BL_PC: WBUFW(buf, 6) = sd->spiritball; break;
- case BL_HOM: WBUFW(buf, 6) = hd->homunculus.spiritball; break;
- }
- clif_send(buf, packet_len(0x1d0), bl, AREA);
-}
-
-
-/// Notifies clients in area of a character's combo delay (ZC_COMBODELAY).
-/// 01d2 <account id>.L <delay>.L
-void clif_combo_delay(struct block_list *bl,int wait)
-{
- unsigned char buf[32];
-
- nullpo_retv(bl);
-
- WBUFW(buf,0)=0x1d2;
- WBUFL(buf,2)=bl->id;
- WBUFL(buf,6)=wait;
- clif_send(buf,packet_len(0x1d2),bl,AREA);
-}
-
-
-/// Notifies clients in area that a character has blade-stopped another (ZC_BLADESTOP).
-/// 01d1 <src id>.L <dst id>.L <flag>.L
-/// flag:
-/// 0 = inactive
-/// 1 = active
-void clif_bladestop(struct block_list *src, int dst_id, int active)
-{
- unsigned char buf[32];
-
- nullpo_retv(src);
-
- WBUFW(buf,0)=0x1d1;
- WBUFL(buf,2)=src->id;
- WBUFL(buf,6)=dst_id;
- WBUFL(buf,10)=active;
-
- clif_send(buf,packet_len(0x1d1),src,AREA);
-}
-
-
-/// MVP effect (ZC_MVP).
-/// 010c <account id>.L
-void clif_mvp_effect(struct map_session_data *sd)
-{
- unsigned char buf[16];
-
- nullpo_retv(sd);
-
- WBUFW(buf,0)=0x10c;
- WBUFL(buf,2)=sd->bl.id;
- clif_send(buf,packet_len(0x10c),&sd->bl,AREA);
-}
-
-
-/// MVP item reward message (ZC_MVP_GETTING_ITEM).
-/// 010a <name id>.W
-void clif_mvp_item(struct map_session_data *sd,int nameid)
-{
- int view,fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x10a));
- WFIFOW(fd,0)=0x10a;
- if((view = itemdb_viewid(nameid)) > 0)
- WFIFOW(fd,2)=view;
- else
- WFIFOW(fd,2)=nameid;
- WFIFOSET(fd,packet_len(0x10a));
-}
-
-
-/// MVP EXP reward message (ZC_MVP_GETTING_SPECIAL_EXP).
-/// 010b <exp>.L
-void clif_mvp_exp(struct map_session_data *sd, unsigned int exp)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x10b));
- WFIFOW(fd,0)=0x10b;
- WFIFOL(fd,2)=cap_value(exp,0,INT32_MAX);
- WFIFOSET(fd,packet_len(0x10b));
-}
-
-
-/// Dropped MVP item reward message (ZC_THROW_MVPITEM).
-/// 010d
-///
-/// "You are the MVP, but cannot obtain the reward because
-/// you are overweight."
-void clif_mvp_noitem(struct map_session_data* sd)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x10d));
- WFIFOW(fd,0) = 0x10d;
- WFIFOSET(fd,packet_len(0x10d));
-}
-
-
-/// Guild creation result (ZC_RESULT_MAKE_GUILD).
-/// 0167 <result>.B
-/// result:
-/// 0 = "Guild has been created."
-/// 1 = "You are already in a Guild."
-/// 2 = "That Guild Name already exists."
-/// 3 = "You need the neccessary item to create a Guild."
-void clif_guild_created(struct map_session_data *sd,int flag)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x167));
- WFIFOW(fd,0)=0x167;
- WFIFOB(fd,2)=flag;
- WFIFOSET(fd,packet_len(0x167));
-}
-
-
-/// Notifies the client that it is belonging to a guild (ZC_UPDATE_GDID).
-/// 016c <guild id>.L <emblem id>.L <mode>.L <ismaster>.B <inter sid>.L <guild name>.24B
-/// mode:
-/// &0x01 = allow invite
-/// &0x10 = allow expel
-void clif_guild_belonginfo(struct map_session_data *sd, struct guild *g)
-{
- int ps,fd;
- nullpo_retv(sd);
- nullpo_retv(g);
-
- fd=sd->fd;
- ps=guild_getposition(g,sd);
- WFIFOHEAD(fd,packet_len(0x16c));
- WFIFOW(fd,0)=0x16c;
- WFIFOL(fd,2)=g->guild_id;
- WFIFOL(fd,6)=g->emblem_id;
- WFIFOL(fd,10)=g->position[ps].mode;
- WFIFOB(fd,14)=(bool)(sd->state.gmaster_flag==g);
- WFIFOL(fd,15)=0; // InterSID (unknown purpose)
- memcpy(WFIFOP(fd,19),g->name,NAME_LENGTH);
- WFIFOSET(fd,packet_len(0x16c));
-}
-
-
-/// Guild member login notice.
-/// 016d <account id>.L <char id>.L <status>.L (ZC_UPDATE_CHARSTAT)
-/// 01f2 <account id>.L <char id>.L <status>.L <gender>.W <hair style>.W <hair color>.W (ZC_UPDATE_CHARSTAT2)
-/// status:
-/// 0 = offline
-/// 1 = online
-void clif_guild_memberlogin_notice(struct guild *g,int idx,int flag)
-{
- unsigned char buf[64];
- struct map_session_data* sd;
-
- nullpo_retv(g);
-
- WBUFW(buf, 0)=0x1f2;
- WBUFL(buf, 2)=g->member[idx].account_id;
- WBUFL(buf, 6)=g->member[idx].char_id;
- WBUFL(buf,10)=flag;
-
- if( ( sd = g->member[idx].sd ) != NULL )
- {
- WBUFW(buf,14) = sd->status.sex;
- WBUFW(buf,16) = sd->status.hair;
- WBUFW(buf,18) = sd->status.hair_color;
- clif_send(buf,packet_len(0x1f2),&sd->bl,GUILD_WOS);
- }
- else if( ( sd = guild_getavailablesd(g) ) != NULL )
- {
- WBUFW(buf,14) = 0;
- WBUFW(buf,16) = 0;
- WBUFW(buf,18) = 0;
- clif_send(buf,packet_len(0x1f2),&sd->bl,GUILD);
- }
-}
-
-// Function `clif_guild_memberlogin_notice` sends info about
-// logins and logouts of a guild member to the rest members.
-// But at the 1st time (after a player login or map changing)
-// the client won't show the message.
-// So I suggest use this function for sending "first-time-info"
-// to some player on entering the game or changing location.
-// At next time the client would always show the message.
-// The function sends all the statuses in the single packet
-// to economize traffic. [LuzZza]
-void clif_guild_send_onlineinfo(struct map_session_data *sd)
-{
- struct guild *g;
- unsigned char buf[14*128];
- int i, count=0, p_len;
-
- nullpo_retv(sd);
-
- p_len = packet_len(0x16d);
-
- if(!(g = guild_search(sd->status.guild_id)))
- return;
-
- for(i=0; i<g->max_member; i++) {
-
- if(g->member[i].account_id > 0 &&
- g->member[i].account_id != sd->status.account_id) {
-
- WBUFW(buf,count*p_len) = 0x16d;
- WBUFL(buf,count*p_len+2) = g->member[i].account_id;
- WBUFL(buf,count*p_len+6) = g->member[i].char_id;
- WBUFL(buf,count*p_len+10) = g->member[i].online;
- count++;
- }
- }
-
- clif_send(buf, p_len*count, &sd->bl, SELF);
-}
-
-
-/// Bitmask of enabled guild window tabs (ZC_ACK_GUILD_MENUINTERFACE).
-/// 014e <menu flag>.L
-/// menu flag:
-/// 0x00 = Basic Info (always on)
-/// &0x01 = Member manager
-/// &0x02 = Positions
-/// &0x04 = Skills
-/// &0x10 = Expulsion list
-/// &0x40 = Unknown (GMENUFLAG_ALLGUILDLIST)
-/// &0x80 = Notice
-void clif_guild_masterormember(struct map_session_data *sd)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x14e));
- WFIFOW(fd,0) = 0x14e;
- WFIFOL(fd,2) = (sd->state.gmaster_flag) ? 0xd7 : 0x57;
- WFIFOSET(fd,packet_len(0x14e));
-}
-
-
-/// Guild basic information (Territories [Valaris])
-/// 0150 <guild id>.L <level>.L <member num>.L <member max>.L <exp>.L <max exp>.L <points>.L <honor>.L <virtue>.L <emblem id>.L <name>.24B <master name>.24B <manage land>.16B (ZC_GUILD_INFO)
-/// 01b6 <guild id>.L <level>.L <member num>.L <member max>.L <exp>.L <max exp>.L <points>.L <honor>.L <virtue>.L <emblem id>.L <name>.24B <master name>.24B <manage land>.16B <zeny>.L (ZC_GUILD_INFO2)
-void clif_guild_basicinfo(struct map_session_data *sd) {
- int fd;
- struct guild *g;
-
- nullpo_retv(sd);
- fd = sd->fd;
-
- if( (g = guild_search(sd->status.guild_id)) == NULL )
- return;
-
- WFIFOHEAD(fd,packet_len(0x1b6));
- WFIFOW(fd, 0)=0x1b6;//0x150;
- WFIFOL(fd, 2)=g->guild_id;
- WFIFOL(fd, 6)=g->guild_lv;
- WFIFOL(fd,10)=g->connect_member;
- WFIFOL(fd,14)=g->max_member;
- WFIFOL(fd,18)=g->average_lv;
- WFIFOL(fd,22)=(uint32)cap_value(g->exp,0,INT32_MAX);
- WFIFOL(fd,26)=g->next_exp;
- WFIFOL(fd,30)=0; // Tax Points
- WFIFOL(fd,34)=0; // Honor: (left) Vulgar [-100,100] Famed (right)
- WFIFOL(fd,38)=0; // Virtue: (down) Wicked [-100,100] Righteous (up)
- WFIFOL(fd,42)=g->emblem_id;
- memcpy(WFIFOP(fd,46),g->name, NAME_LENGTH);
- memcpy(WFIFOP(fd,70),g->master, NAME_LENGTH);
-
- safestrncpy((char*)WFIFOP(fd,94),msg_txt(300+guild_checkcastles(g)),16); // "'N' castles"
- WFIFOL(fd,110) = 0; // zeny
-
- WFIFOSET(fd,packet_len(0x1b6));
-}
-
-
-/// Guild alliance and opposition list (ZC_MYGUILD_BASIC_INFO).
-/// 014c <packet len>.W { <relation>.L <guild id>.L <guild name>.24B }*
-void clif_guild_allianceinfo(struct map_session_data *sd)
-{
- int fd,i,c;
- struct guild *g;
-
- nullpo_retv(sd);
- if( (g = guild_search(sd->status.guild_id)) == NULL )
- return;
-
- fd = sd->fd;
- WFIFOHEAD(fd, MAX_GUILDALLIANCE * 32 + 4);
- WFIFOW(fd, 0)=0x14c;
- for(i=c=0;i<MAX_GUILDALLIANCE;i++){
- struct guild_alliance *a=&g->alliance[i];
- if(a->guild_id>0){
- WFIFOL(fd,c*32+4)=a->opposition;
- WFIFOL(fd,c*32+8)=a->guild_id;
- memcpy(WFIFOP(fd,c*32+12),a->name,NAME_LENGTH);
- c++;
- }
- }
- WFIFOW(fd, 2)=c*32+4;
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Guild member manager information (ZC_MEMBERMGR_INFO).
-/// 0154 <packet len>.W { <account>.L <char id>.L <hair style>.W <hair color>.W <gender>.W <class>.W <level>.W <contrib exp>.L <state>.L <position>.L <memo>.50B <name>.24B }*
-/// state:
-/// 0 = offline
-/// 1 = online
-/// memo:
-/// probably member's self-introduction (unused, no client UI/packets for editing it)
-void clif_guild_memberlist(struct map_session_data *sd)
-{
- int fd;
- int i,c;
- struct guild *g;
- nullpo_retv(sd);
-
- if( (fd = sd->fd) == 0 )
- return;
- if( (g = guild_search(sd->status.guild_id)) == NULL )
- return;
-
- WFIFOHEAD(fd, g->max_member * 104 + 4);
- WFIFOW(fd, 0)=0x154;
- for(i=0,c=0;i<g->max_member;i++){
- struct guild_member *m=&g->member[i];
- if(m->account_id==0)
- continue;
- WFIFOL(fd,c*104+ 4)=m->account_id;
- WFIFOL(fd,c*104+ 8)=m->char_id;
- WFIFOW(fd,c*104+12)=m->hair;
- WFIFOW(fd,c*104+14)=m->hair_color;
- WFIFOW(fd,c*104+16)=m->gender;
- WFIFOW(fd,c*104+18)=m->class_;
- WFIFOW(fd,c*104+20)=m->lv;
- WFIFOL(fd,c*104+22)=(int)cap_value(m->exp,0,INT32_MAX);
- WFIFOL(fd,c*104+26)=m->online;
- WFIFOL(fd,c*104+30)=m->position;
- memset(WFIFOP(fd,c*104+34),0,50); //[Ind] - This is displayed in the 'note' column but being you can't edit it it's sent empty.
- memcpy(WFIFOP(fd,c*104+84),m->name,NAME_LENGTH);
- c++;
- }
- WFIFOW(fd, 2)=c*104+4;
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Guild position name information (ZC_POSITION_ID_NAME_INFO).
-/// 0166 <packet len>.W { <position id>.L <position name>.24B }*
-void clif_guild_positionnamelist(struct map_session_data *sd)
-{
- int i,fd;
- struct guild *g;
-
- nullpo_retv(sd);
- if( (g = guild_search(sd->status.guild_id)) == NULL )
- return;
-
- fd = sd->fd;
- WFIFOHEAD(fd, MAX_GUILDPOSITION * 28 + 4);
- WFIFOW(fd, 0)=0x166;
- for(i=0;i<MAX_GUILDPOSITION;i++){
- WFIFOL(fd,i*28+4)=i;
- memcpy(WFIFOP(fd,i*28+8),g->position[i].name,NAME_LENGTH);
- }
- WFIFOW(fd,2)=i*28+4;
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Guild position information (ZC_POSITION_INFO).
-/// 0160 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L }*
-/// mode:
-/// &0x01 = allow invite
-/// &0x10 = allow expel
-/// ranking:
-/// TODO
-void clif_guild_positioninfolist(struct map_session_data *sd)
-{
- int i,fd;
- struct guild *g;
-
- nullpo_retv(sd);
- if( (g = guild_search(sd->status.guild_id)) == NULL )
- return;
-
- fd = sd->fd;
- WFIFOHEAD(fd, MAX_GUILDPOSITION * 16 + 4);
- WFIFOW(fd, 0)=0x160;
- for(i=0;i<MAX_GUILDPOSITION;i++){
- struct guild_position *p=&g->position[i];
- WFIFOL(fd,i*16+ 4)=i;
- WFIFOL(fd,i*16+ 8)=p->mode;
- WFIFOL(fd,i*16+12)=i;
- WFIFOL(fd,i*16+16)=p->exp_mode;
- }
- WFIFOW(fd, 2)=i*16+4;
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Notifies clients in a guild about updated position information (ZC_ACK_CHANGE_GUILD_POSITIONINFO).
-/// 0174 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L <position name>.24B }*
-/// mode:
-/// &0x01 = allow invite
-/// &0x10 = allow expel
-/// ranking:
-/// TODO
-void clif_guild_positionchanged(struct guild *g,int idx)
-{
- // FIXME: This packet is intended to update the clients after a
- // commit of position info changes, not sending one packet per
- // position.
- struct map_session_data *sd;
- unsigned char buf[128];
-
- nullpo_retv(g);
-
- WBUFW(buf, 0)=0x174;
- WBUFW(buf, 2)=44; // packet len
- // GUILD_REG_POSITION_INFO{
- WBUFL(buf, 4)=idx;
- WBUFL(buf, 8)=g->position[idx].mode;
- WBUFL(buf,12)=idx;
- WBUFL(buf,16)=g->position[idx].exp_mode;
- memcpy(WBUFP(buf,20),g->position[idx].name,NAME_LENGTH);
- // }*
- if( (sd=guild_getavailablesd(g))!=NULL )
- clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD);
-}
-
-
-/// Notifies clients in a guild about updated member position assignments (ZC_ACK_REQ_CHANGE_MEMBERS).
-/// 0156 <packet len>.W { <account id>.L <char id>.L <position id>.L }*
-void clif_guild_memberpositionchanged(struct guild *g,int idx)
-{
- // FIXME: This packet is intended to update the clients after a
- // commit of member position assignment changes, not sending one
- // packet per position.
- struct map_session_data *sd;
- unsigned char buf[64];
-
- nullpo_retv(g);
-
- WBUFW(buf, 0)=0x156;
- WBUFW(buf, 2)=16; // packet len
- // MEMBER_POSITION_INFO{
- WBUFL(buf, 4)=g->member[idx].account_id;
- WBUFL(buf, 8)=g->member[idx].char_id;
- WBUFL(buf,12)=g->member[idx].position;
- // }*
- if( (sd=guild_getavailablesd(g))!=NULL )
- clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD);
-}
-
-
-/// Sends emblems bitmap data to the client that requested it (ZC_GUILD_EMBLEM_IMG).
-/// 0152 <packet len>.W <guild id>.L <emblem id>.L <emblem data>.?B
-void clif_guild_emblem(struct map_session_data *sd,struct guild *g)
-{
- int fd;
- nullpo_retv(sd);
- nullpo_retv(g);
-
- fd = sd->fd;
- if( g->emblem_len <= 0 )
- return;
-
- WFIFOHEAD(fd,g->emblem_len+12);
- WFIFOW(fd,0)=0x152;
- WFIFOW(fd,2)=g->emblem_len+12;
- WFIFOL(fd,4)=g->guild_id;
- WFIFOL(fd,8)=g->emblem_id;
- memcpy(WFIFOP(fd,12),g->emblem_data,g->emblem_len);
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Sends update of the guild id/emblem id to everyone in the area (ZC_CHANGE_GUILD).
-/// 01b4 <id>.L <guild id>.L <emblem id>.W
-void clif_guild_emblem_area(struct block_list* bl)
-{
- uint8 buf[12];
-
- nullpo_retv(bl);
-
- // TODO this packet doesn't force the update of ui components that have the emblem visible
- // (emblem in the flag npcs and emblem over the head in agit maps) [FlavioJS]
- WBUFW(buf,0) = 0x1b4;
- WBUFL(buf,2) = bl->id;
- WBUFL(buf,6) = status_get_guild_id(bl);
- WBUFW(buf,10) = status_get_emblem_id(bl);
- clif_send(buf, 12, bl, AREA_WOS);
-}
-
-
-/// Sends guild skills (ZC_GUILD_SKILLINFO).
-/// 0162 <packet len>.W <skill points>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <atk range>.W <skill name>.24B <upgradable>.B }*
-void clif_guild_skillinfo(struct map_session_data* sd)
-{
- int fd;
- struct guild* g;
- int i,c;
-
- nullpo_retv(sd);
- if( (g = guild_search(sd->status.guild_id)) == NULL )
- return;
-
- fd = sd->fd;
- WFIFOHEAD(fd, 6 + MAX_GUILDSKILL*37);
- WFIFOW(fd,0) = 0x0162;
- WFIFOW(fd,4) = g->skill_point;
- for(i = 0, c = 0; i < MAX_GUILDSKILL; i++)
- {
- if(g->skill[i].id > 0 && guild_check_skill_require(g, g->skill[i].id))
- {
- int id = g->skill[i].id;
- int p = 6 + c*37;
- WFIFOW(fd,p+0) = id;
- WFIFOL(fd,p+2) = skill_get_inf(id);
- WFIFOW(fd,p+6) = g->skill[i].lv;
- WFIFOW(fd,p+8) = skill_get_sp(id, g->skill[i].lv);
- WFIFOW(fd,p+10) = skill_get_range(id, g->skill[i].lv);
- safestrncpy((char*)WFIFOP(fd,p+12), skill_get_name(id), NAME_LENGTH);
- WFIFOB(fd,p+36)= (g->skill[i].lv < guild_skill_get_max(id) && sd == g->member[0].sd) ? 1 : 0;
- c++;
- }
- }
- WFIFOW(fd,2) = 6 + c*37;
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Sends guild notice to client (ZC_GUILD_NOTICE).
-/// 016f <subject>.60B <notice>.120B
-void clif_guild_notice(struct map_session_data* sd, struct guild* g)
-{
- int fd;
-
- nullpo_retv(sd);
- nullpo_retv(g);
-
- fd = sd->fd;
-
- if ( !session_isActive(fd) )
- return;
-
- if(g->mes1[0] == '\0' && g->mes2[0] == '\0')
- return;
-
- WFIFOHEAD(fd,packet_len(0x16f));
- WFIFOW(fd,0) = 0x16f;
- memcpy(WFIFOP(fd,2), g->mes1, MAX_GUILDMES1);
- memcpy(WFIFOP(fd,62), g->mes2, MAX_GUILDMES2);
- WFIFOSET(fd,packet_len(0x16f));
-}
-
-
-/// Guild invite (ZC_REQ_JOIN_GUILD).
-/// 016a <guild id>.L <guild name>.24B
-void clif_guild_invite(struct map_session_data *sd,struct guild *g)
-{
- int fd;
-
- nullpo_retv(sd);
- nullpo_retv(g);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x16a));
- WFIFOW(fd,0)=0x16a;
- WFIFOL(fd,2)=g->guild_id;
- memcpy(WFIFOP(fd,6),g->name,NAME_LENGTH);
- WFIFOSET(fd,packet_len(0x16a));
-}
-
-
-/// Reply to invite request (ZC_ACK_REQ_JOIN_GUILD).
-/// 0169 <answer>.B
-/// answer:
-/// 0 = Already in guild.
-/// 1 = Offer rejected.
-/// 2 = Offer accepted.
-/// 3 = Guild full.
-void clif_guild_inviteack(struct map_session_data *sd,int flag)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x169));
- WFIFOW(fd,0)=0x169;
- WFIFOB(fd,2)=flag;
- WFIFOSET(fd,packet_len(0x169));
-}
-
-
-/// Notifies clients of a guild of a leaving member (ZC_ACK_LEAVE_GUILD).
-/// 015a <char name>.24B <reason>.40B
-void clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes)
-{
- unsigned char buf[128];
-
- nullpo_retv(sd);
-
- WBUFW(buf, 0)=0x15a;
- memcpy(WBUFP(buf, 2),name,NAME_LENGTH);
- memcpy(WBUFP(buf,26),mes,40);
- clif_send(buf,packet_len(0x15a),&sd->bl,GUILD_NOBG);
-}
-
-
-/// Notifies clients of a guild of an expelled member.
-/// 015c <char name>.24B <reason>.40B <account name>.24B (ZC_ACK_BAN_GUILD)
-/// 0839 <char name>.24B <reason>.40B (ZC_ACK_BAN_GUILD_SSO)
-void clif_guild_expulsion(struct map_session_data* sd, const char* name, const char* mes, int account_id)
-{
- unsigned char buf[128];
-#if PACKETVER < 20100803
- const unsigned short cmd = 0x15c;
-#else
- const unsigned short cmd = 0x839;
-#endif
-
- nullpo_retv(sd);
-
- WBUFW(buf,0) = cmd;
- safestrncpy((char*)WBUFP(buf,2), name, NAME_LENGTH);
- safestrncpy((char*)WBUFP(buf,26), mes, 40);
-#if PACKETVER < 20100803
- memset(WBUFP(buf,66), 0, NAME_LENGTH); // account name (not used for security reasons)
-#endif
- clif_send(buf, packet_len(cmd), &sd->bl, GUILD_NOBG);
-}
-
-
-/// Guild expulsion list (ZC_BAN_LIST).
-/// 0163 <packet len>.W { <char name>.24B <account name>.24B <reason>.40B }*
-/// 0163 <packet len>.W { <char name>.24B <reason>.40B }* (PACKETVER >= 20100803)
-void clif_guild_expulsionlist(struct map_session_data* sd)
-{
-#if PACKETVER < 20100803
- const int offset = NAME_LENGTH*2+40;
-#else
- const int offset = NAME_LENGTH+40;
-#endif
- int fd, i, c = 0;
- struct guild* g;
-
- nullpo_retv(sd);
-
- if( (g = guild_search(sd->status.guild_id)) == NULL )
- return;
-
- fd = sd->fd;
-
- WFIFOHEAD(fd,4 + MAX_GUILDEXPULSION * offset);
- WFIFOW(fd,0) = 0x163;
-
- for( i = 0; i < MAX_GUILDEXPULSION; i++ )
- {
- struct guild_expulsion* e = &g->expulsion[i];
-
- if( e->account_id > 0 )
- {
- memcpy(WFIFOP(fd,4 + c*offset), e->name, NAME_LENGTH);
-#if PACKETVER < 20100803
- memset(WFIFOP(fd,4 + c*offset+24), 0, NAME_LENGTH); // account name (not used for security reasons)
- memcpy(WFIFOP(fd,4 + c*offset+48), e->mes, 40);
-#else
- memcpy(WFIFOP(fd,4 + c*offset+24), e->mes, 40);
-#endif
- c++;
- }
- }
- WFIFOW(fd,2) = 4 + c*offset;
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Guild chat message (ZC_GUILD_CHAT).
-/// 017f <packet len>.W <message>.?B
-void clif_guild_message(struct guild *g,int account_id,const char *mes,int len)
-{// TODO: account_id is not used, candidate for deletion? [Ai4rei]
- struct map_session_data *sd;
- uint8 buf[256];
-
- if( len == 0 )
- {
- return;
- }
- else if( len > sizeof(buf)-5 )
- {
- ShowWarning("clif_guild_message: Truncated message '%s' (len=%d, max=%d, guild_id=%d).\n", mes, len, sizeof(buf)-5, g->guild_id);
- len = sizeof(buf)-5;
- }
-
- WBUFW(buf, 0) = 0x17f;
- WBUFW(buf, 2) = len + 5;
- safestrncpy((char*)WBUFP(buf,4), mes, len+1);
-
- if ((sd = guild_getavailablesd(g)) != NULL)
- clif_send(buf, WBUFW(buf,2), &sd->bl, GUILD_NOBG);
-}
-
-
-/*==========================================
- * Server tells client 'sd' that his guild skill 'skill_id' gone to level 'lv'
- *------------------------------------------*/
-int clif_guild_skillup(struct map_session_data *sd,uint16 skill_id,int lv)
-{// TODO: Merge with clif_skillup (same packet).
- int fd;
-
- nullpo_ret(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,11);
- WFIFOW(fd,0) = 0x10e;
- WFIFOW(fd,2) = skill_id;
- WFIFOW(fd,4) = lv;
- WFIFOW(fd,6) = skill_get_sp(skill_id,lv);
- WFIFOW(fd,8) = skill_get_range(skill_id,lv);
- WFIFOB(fd,10) = 1;
- WFIFOSET(fd,11);
- return 0;
-}
-
-
-/// Request for guild alliance (ZC_REQ_ALLY_GUILD).
-/// 0171 <inviter account id>.L <guild name>.24B
-void clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x171));
- WFIFOW(fd,0)=0x171;
- WFIFOL(fd,2)=account_id;
- memcpy(WFIFOP(fd,6),name,NAME_LENGTH);
- WFIFOSET(fd,packet_len(0x171));
-}
-
-
-/// Notifies the client about the result of a alliance request (ZC_ACK_REQ_ALLY_GUILD).
-/// 0173 <answer>.B
-/// answer:
-/// 0 = Already allied.
-/// 1 = You rejected the offer.
-/// 2 = You accepted the offer.
-/// 3 = They have too any alliances.
-/// 4 = You have too many alliances.
-/// 5 = Alliances are disabled.
-void clif_guild_allianceack(struct map_session_data *sd,int flag)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x173));
- WFIFOW(fd,0)=0x173;
- WFIFOL(fd,2)=flag;
- WFIFOSET(fd,packet_len(0x173));
-}
-
-
-/// Notifies the client that a alliance or opposition has been removed (ZC_DELETE_RELATED_GUILD).
-/// 0184 <other guild id>.L <relation>.L
-/// relation:
-/// 0 = Ally
-/// 1 = Enemy
-void clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd = sd->fd;
- if (fd <= 0)
- return;
- WFIFOHEAD(fd,packet_len(0x184));
- WFIFOW(fd,0)=0x184;
- WFIFOL(fd,2)=guild_id;
- WFIFOL(fd,6)=flag;
- WFIFOSET(fd,packet_len(0x184));
-}
-
-
-/// Notifies the client about the result of a opposition request (ZC_ACK_REQ_HOSTILE_GUILD).
-/// 0181 <result>.B
-/// result:
-/// 0 = Antagonist has been set.
-/// 1 = Guild has too many Antagonists.
-/// 2 = Already set as an Antagonist.
-/// 3 = Antagonists are disabled.
-void clif_guild_oppositionack(struct map_session_data *sd,int flag)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x181));
- WFIFOW(fd,0)=0x181;
- WFIFOB(fd,2)=flag;
- WFIFOSET(fd,packet_len(0x181));
-}
-
-
-/// Adds alliance or opposition (ZC_ADD_RELATED_GUILD).
-/// 0185 <relation>.L <guild id>.L <guild name>.24B
-/*
-void clif_guild_allianceadded(struct guild *g,int idx)
-{
- unsigned char buf[64];
- WBUFW(buf,0)=0x185;
- WBUFL(buf,2)=g->alliance[idx].opposition;
- WBUFL(buf,6)=g->alliance[idx].guild_id;
- memcpy(WBUFP(buf,10),g->alliance[idx].name,NAME_LENGTH);
- clif_send(buf,packet_len(0x185),guild_getavailablesd(g),GUILD);
-}
-*/
-
-
-/// Notifies the client about the result of a guild break (ZC_ACK_DISORGANIZE_GUILD_RESULT).
-/// 015e <reason>.L
-/// 0 = success
-/// 1 = invalid key (guild name, @see clif_parse_GuildBreak)
-/// 2 = there are still members in the guild
-void clif_guild_broken(struct map_session_data *sd,int flag)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x15e));
- WFIFOW(fd,0)=0x15e;
- WFIFOL(fd,2)=flag;
- WFIFOSET(fd,packet_len(0x15e));
-}
-
-
-/// Displays emotion on an object (ZC_EMOTION).
-/// 00c0 <id>.L <type>.B
-/// type:
-/// enum emotion_type
-void clif_emotion(struct block_list *bl,int type)
-{
- unsigned char buf[8];
-
- nullpo_retv(bl);
-
- WBUFW(buf,0)=0xc0;
- WBUFL(buf,2)=bl->id;
- WBUFB(buf,6)=type;
- clif_send(buf,packet_len(0xc0),bl,AREA);
-}
-
-
-/// Displays the contents of a talkiebox trap (ZC_TALKBOX_CHATCONTENTS).
-/// 0191 <id>.L <contents>.80B
-void clif_talkiebox(struct block_list* bl, const char* talkie)
-{
- unsigned char buf[MESSAGE_SIZE+6];
- nullpo_retv(bl);
-
- WBUFW(buf,0) = 0x191;
- WBUFL(buf,2) = bl->id;
- safestrncpy((char*)WBUFP(buf,6),talkie,MESSAGE_SIZE);
- clif_send(buf,packet_len(0x191),bl,AREA);
-}
-
-
-/// Displays wedding effect centered on an object (ZC_CONGRATULATION).
-/// 01ea <id>.L
-void clif_wedding_effect(struct block_list *bl)
-{
- unsigned char buf[6];
-
- nullpo_retv(bl);
-
- WBUFW(buf,0) = 0x1ea;
- WBUFL(buf,2) = bl->id;
- clif_send(buf, packet_len(0x1ea), bl, AREA);
-}
-
-
-/// Notifies the client of the name of the partner character (ZC_COUPLENAME).
-/// 01e6 <partner name>.24B
-void clif_callpartner(struct map_session_data *sd)
-{
- unsigned char buf[26];
- const char *p;
-
- nullpo_retv(sd);
-
- WBUFW(buf,0) = 0x1e6;
-
- if( sd->status.partner_id )
- {
- if( ( p = map_charid2nick(sd->status.partner_id) ) != NULL )
- {
- memcpy(WBUFP(buf,2), p, NAME_LENGTH);
- }
- else
- {
- WBUFB(buf,2) = 0;
- }
- }
- else
- {// Send zero-length name if no partner, to initialize the client buffer.
- WBUFB(buf,2) = 0;
- }
-
- clif_send(buf, packet_len(0x1e6), &sd->bl, AREA);
-}
-
-
-/// Initiates the partner "taming" process [DracoRPG] (ZC_START_COUPLE).
-/// 01e4
-/// This packet while still implemented by the client is no longer being officially used.
-/*
-void clif_marriage_process(struct map_session_data *sd)
-{
- int fd;
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x1e4));
- WFIFOW(fd,0)=0x1e4;
- WFIFOSET(fd,packet_len(0x1e4));
-}
-*/
-
-
-/// Notice of divorce (ZC_DIVORCE).
-/// 0205 <partner name>.24B
-void clif_divorced(struct map_session_data* sd, const char* name)
-{
- int fd;
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x205));
- WFIFOW(fd,0)=0x205;
- memcpy(WFIFOP(fd,2), name, NAME_LENGTH);
- WFIFOSET(fd, packet_len(0x205));
-}
-
-
-/// Marriage proposal (ZC_REQ_COUPLE).
-/// 01e2 <account id>.L <char id>.L <char name>.24B
-/// This packet while still implemented by the client is no longer being officially used.
-/*
-void clif_marriage_proposal(int fd, struct map_session_data *sd, struct map_session_data* ssd)
-{
- nullpo_retv(sd);
-
- WFIFOHEAD(fd,packet_len(0x1e2));
- WFIFOW(fd,0) = 0x1e2;
- WFIFOL(fd,2) = ssd->status.account_id;
- WFIFOL(fd,6) = ssd->status.char_id;
- safestrncpy((char*)WFIFOP(fd,10), ssd->status.name, NAME_LENGTH);
- WFIFOSET(fd, packet_len(0x1e2));
-}
-*/
-
-
-/*==========================================
- *
- *------------------------------------------*/
-void clif_disp_onlyself(struct map_session_data *sd, const char *mes, int len)
-{
- clif_disp_message(&sd->bl, mes, len, SELF);
-}
-
-/*==========================================
- * Displays a message using the guild-chat colors to the specified targets. [Skotlex]
- *------------------------------------------*/
-void clif_disp_message(struct block_list* src, const char* mes, int len, enum send_target target)
-{
- unsigned char buf[256];
-
- if( len == 0 )
- {
- return;
- }
- else if( len > sizeof(buf)-5 )
- {
- ShowWarning("clif_disp_message: Truncated message '%s' (len=%d, max=%d, aid=%d).\n", mes, len, sizeof(buf)-5, src->id);
- len = sizeof(buf)-5;
- }
-
- WBUFW(buf, 0) = 0x17f;
- WBUFW(buf, 2) = len + 5;
- safestrncpy((char*)WBUFP(buf,4), mes, len+1);
- clif_send(buf, WBUFW(buf,2), src, target);
-}
-
-
-/// Notifies the client about the result of a request to disconnect another player (ZC_ACK_DISCONNECT_CHARACTER).
-/// 00cd <result>.L (unknown packet version or invalid information at packet_len_table)
-/// 00cd <result>.B
-/// result:
-/// 0 = failure
-/// 1 = success
-void clif_GM_kickack(struct map_session_data *sd, int id)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0xcd));
- WFIFOW(fd,0) = 0xcd;
- WFIFOB(fd,2) = id; // FIXME: this is not account id
- WFIFOSET(fd, packet_len(0xcd));
-}
-
-
-void clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd)
-{
- int fd = tsd->fd;
-
- if( fd > 0 )
- clif_authfail_fd(fd, 15);
- else
- map_quit(tsd);
-
- if( sd )
- clif_GM_kickack(sd,tsd->status.account_id);
-}
-
-
-/// Displays various manner-related status messages (ZC_ACK_GIVE_MANNER_POINT).
-/// 014a <result>.L
-/// result:
-/// 0 = "A manner point has been successfully aligned."
-/// 1 = MP_FAILURE_EXHAUST
-/// 2 = MP_FAILURE_ALREADY_GIVING
-/// 3 = "Chat Block has been applied by GM due to your ill-mannerous action."
-/// 4 = "Automated Chat Block has been applied due to Anti-Spam System."
-/// 5 = "You got a good point from %s."
-void clif_manner_message(struct map_session_data* sd, uint32 type)
-{
- int fd;
- nullpo_retv(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0x14a));
- WFIFOW(fd,0) = 0x14a;
- WFIFOL(fd,2) = type;
- WFIFOSET(fd, packet_len(0x14a));
-}
-
-
-/// Followup to 0x14a type 3/5, informs who did the manner adjustment action (ZC_NOTIFY_MANNER_POINT_GIVEN).
-/// 014b <type>.B <GM name>.24B
-/// type:
-/// 0 = positive (unmute)
-/// 1 = negative (mute)
-void clif_GM_silence(struct map_session_data* sd, struct map_session_data* tsd, uint8 type)
-{
- int fd;
- nullpo_retv(sd);
- nullpo_retv(tsd);
-
- fd = tsd->fd;
- WFIFOHEAD(fd,packet_len(0x14b));
- WFIFOW(fd,0) = 0x14b;
- WFIFOB(fd,2) = type;
- safestrncpy((char*)WFIFOP(fd,3), sd->status.name, NAME_LENGTH);
- WFIFOSET(fd, packet_len(0x14b));
-}
-
-
-/// Notifies the client about the result of a request to allow/deny whispers from a player (ZC_SETTING_WHISPER_PC).
-/// 00d1 <type>.B <result>.B
-/// type:
-/// 0 = /ex (deny)
-/// 1 = /in (allow)
-/// result:
-/// 0 = success
-/// 1 = failure
-/// 2 = too many blocks
-void clif_wisexin(struct map_session_data *sd,int type,int flag)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0xd1));
- WFIFOW(fd,0)=0xd1;
- WFIFOB(fd,2)=type;
- WFIFOB(fd,3)=flag;
- WFIFOSET(fd,packet_len(0xd1));
-}
-
-/// Notifies the client about the result of a request to allow/deny whispers from anyone (ZC_SETTING_WHISPER_STATE).
-/// 00d2 <type>.B <result>.B
-/// type:
-/// 0 = /exall (deny)
-/// 1 = /inall (allow)
-/// result:
-/// 0 = success
-/// 1 = failure
-void clif_wisall(struct map_session_data *sd,int type,int flag)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0xd2));
- WFIFOW(fd,0)=0xd2;
- WFIFOB(fd,2)=type;
- WFIFOB(fd,3)=flag;
- WFIFOSET(fd,packet_len(0xd2));
-}
-
-
-/// Play a BGM! [Rikter/Yommy] (ZC_PLAY_NPC_BGM).
-/// 07fe <bgm>.24B
-void clif_playBGM(struct map_session_data* sd, const char* name)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0x7fe));
- WFIFOW(fd,0) = 0x7fe;
- safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH);
- WFIFOSET(fd,packet_len(0x7fe));
-}
-
-
-/// Plays/stops a wave sound (ZC_SOUND).
-/// 01d3 <file name>.24B <act>.B <term>.L <npc id>.L
-/// file name:
-/// relative to data\wav
-/// act:
-/// 0 = play (once)
-/// 1 = play (repeat, does not work)
-/// 2 = stops all sound instances of file name (does not work)
-/// term:
-/// unknown purpose, only relevant to act = 1
-/// npc id:
-/// The accustic direction of the sound is determined by the
-/// relative position of the NPC to the player (3D sound).
-void clif_soundeffect(struct map_session_data* sd, struct block_list* bl, const char* name, int type)
-{
- int fd;
-
- nullpo_retv(sd);
- nullpo_retv(bl);
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0x1d3));
- WFIFOW(fd,0) = 0x1d3;
- safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH);
- WFIFOB(fd,26) = type;
- WFIFOL(fd,27) = 0;
- WFIFOL(fd,31) = bl->id;
- WFIFOSET(fd,packet_len(0x1d3));
-}
-
-void clif_soundeffectall(struct block_list* bl, const char* name, int type, enum send_target coverage)
-{
- unsigned char buf[40];
-
- nullpo_retv(bl);
-
- WBUFW(buf,0) = 0x1d3;
- safestrncpy((char*)WBUFP(buf,2), name, NAME_LENGTH);
- WBUFB(buf,26) = type;
- WBUFL(buf,27) = 0;
- WBUFL(buf,31) = bl->id;
- clif_send(buf, packet_len(0x1d3), bl, coverage);
-}
-
-
-/// Displays special effects (npcs, weather, etc) [Valaris] (ZC_NOTIFY_EFFECT2).
-/// 01f3 <id>.L <effect id>.L
-/// effect id:
-/// @see doc/effect_list.txt
-void clif_specialeffect(struct block_list* bl, int type, enum send_target target)
-{
- unsigned char buf[24];
-
- nullpo_retv(bl);
-
- memset(buf, 0, packet_len(0x1f3));
-
- WBUFW(buf,0) = 0x1f3;
- WBUFL(buf,2) = bl->id;
- WBUFL(buf,6) = type;
-
- clif_send(buf, packet_len(0x1f3), bl, target);
-
- if (disguised(bl)) {
- WBUFL(buf,2) = -bl->id;
- clif_send(buf, packet_len(0x1f3), bl, SELF);
- }
-}
-
-void clif_specialeffect_single(struct block_list* bl, int type, int fd)
-{
- WFIFOHEAD(fd,10);
- WFIFOW(fd,0) = 0x1f3;
- WFIFOL(fd,2) = bl->id;
- WFIFOL(fd,6) = type;
- WFIFOSET(fd,10);
-}
-
-
-/// Notifies clients of an special/visual effect that accepts an value (ZC_NOTIFY_EFFECT3).
-/// 0284 <id>.L <effect id>.L <num data>.L
-/// effect id:
-/// @see doc/effect_list.txt
-/// num data:
-/// effect-dependent value
-void clif_specialeffect_value(struct block_list* bl, int effect_id, int num, send_target target)
-{
- uint8 buf[14];
-
- WBUFW(buf,0) = 0x284;
- WBUFL(buf,2) = bl->id;
- WBUFL(buf,6) = effect_id;
- WBUFL(buf,10) = num;
-
- clif_send(buf, packet_len(0x284), bl, target);
-
- if( disguised(bl) )
- {
- WBUFL(buf,2) = -bl->id;
- clif_send(buf, packet_len(0x284), bl, SELF);
- }
-}
-// Modification of clif_messagecolor to send colored messages to players to chat log only (doesn't display overhead)
-/// 02c1 <packet len>.W <id>.L <color>.L <message>.?B
-int clif_colormes(struct map_session_data * sd, enum clif_colors color, const char* msg) {
- unsigned short msg_len = strlen(msg) + 1;
-
- WFIFOHEAD(sd->fd,msg_len + 12);
- WFIFOW(sd->fd,0) = 0x2C1;
- WFIFOW(sd->fd,2) = msg_len + 12;
- WFIFOL(sd->fd,4) = 0;
- WFIFOL(sd->fd,8) = color_table[color];
- safestrncpy((char*)WFIFOP(sd->fd,12), msg, msg_len);
- clif_send(WFIFOP(sd->fd,0), WFIFOW(sd->fd,2), &sd->bl, SELF);
-
- return 0;
-}
-
-/// Monster/NPC color chat [SnakeDrak] (ZC_NPC_CHAT).
-/// 02c1 <packet len>.W <id>.L <color>.L <message>.?B
-void clif_messagecolor(struct block_list* bl, unsigned long color, const char* msg) {
- unsigned short msg_len = strlen(msg) + 1;
- uint8 buf[256];
- color = (color & 0x0000FF) << 16 | (color & 0x00FF00) | (color & 0xFF0000) >> 16; // RGB to BGR
-
- nullpo_retv(bl);
-
- if( msg_len > sizeof(buf)-12 )
- {
- ShowWarning("clif_messagecolor: Truncating too long message '%s' (len=%u).\n", msg, msg_len);
- msg_len = sizeof(buf)-12;
- }
-
- WBUFW(buf,0) = 0x2C1;
- WBUFW(buf,2) = msg_len + 12;
- WBUFL(buf,4) = bl->id;
- WBUFL(buf,8) = color;
- memcpy(WBUFP(buf,12), msg, msg_len);
-
- clif_send(buf, WBUFW(buf,2), bl, AREA_CHAT_WOC);
-}
-
-/// Public chat message [Valaris] (ZC_NOTIFY_CHAT).
-/// 008d <packet len>.W <id>.L <message>.?B
-void clif_message(struct block_list* bl, const char* msg) {
- unsigned short msg_len = strlen(msg) + 1;
- uint8 buf[256];
- nullpo_retv(bl);
-
- if( msg_len > sizeof(buf)-8 ) {
- ShowWarning("clif_message: Truncating too long message '%s' (len=%u).\n", msg, msg_len);
- msg_len = sizeof(buf)-8;
- }
-
- WBUFW(buf,0) = 0x8d;
- WBUFW(buf,2) = msg_len + 8;
- WBUFL(buf,4) = bl->id;
- safestrncpy((char*)WBUFP(buf,8), msg, msg_len);
-
- clif_send(buf, WBUFW(buf,2), bl, AREA_CHAT_WOC);
-}
-
-// refresh the client's screen, getting rid of any effects
-void clif_refresh(struct map_session_data *sd)
-{
- int i;
- nullpo_retv(sd);
-
- clif_changemap(sd,sd->mapindex,sd->bl.x,sd->bl.y);
- clif_inventorylist(sd);
- if(pc_iscarton(sd)) {
- clif_cartlist(sd);
- clif_updatestatus(sd,SP_CARTINFO);
- }
- clif_updatestatus(sd,SP_WEIGHT);
- clif_updatestatus(sd,SP_MAXWEIGHT);
- clif_updatestatus(sd,SP_STR);
- clif_updatestatus(sd,SP_AGI);
- clif_updatestatus(sd,SP_VIT);
- clif_updatestatus(sd,SP_INT);
- clif_updatestatus(sd,SP_DEX);
- clif_updatestatus(sd,SP_LUK);
- if (sd->spiritball)
- clif_spiritball_single(sd->fd, sd);
- for(i = 1; i < 5; i++){
- if( sd->talisman[i] > 0 )
- clif_talisman_single(sd->fd, sd, i);
- }
- if (sd->vd.cloth_color)
- clif_refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF);
- if(merc_is_hom_active(sd->hd))
- clif_send_homdata(sd,SP_ACK,0);
- if( sd->md ) {
- clif_mercenary_info(sd);
- clif_mercenary_skillblock(sd);
- }
- if( sd->ed )
- clif_elemental_info(sd);
- map_foreachinrange(clif_getareachar,&sd->bl,AREA_SIZE,BL_ALL,sd);
- clif_weather_check(sd);
- if( sd->chatID )
- chat_leavechat(sd,0);
- if( sd->state.vending )
- clif_openvending(sd, sd->bl.id, sd->vending);
- if( pc_issit(sd) )
- clif_sitting(&sd->bl); // FIXME: just send to self, not area
- if( pc_isdead(sd) ) // When you refresh, resend the death packet.
- clif_clearunit_single(sd->bl.id,CLR_DEAD,sd->fd);
- else
- clif_changed_dir(&sd->bl, SELF);
-
- // unlike vending, resuming buyingstore crashes the client.
- buyingstore_close(sd);
-
- mail_clear(sd);
-}
-
-
-/// Updates the object's (bl) name on client.
-/// 0095 <id>.L <char name>.24B (ZC_ACK_REQNAME)
-/// 0195 <id>.L <char name>.24B <party name>.24B <guild name>.24B <position name>.24B (ZC_ACK_REQNAMEALL)
-void clif_charnameack (int fd, struct block_list *bl)
-{
- unsigned char buf[103];
- int cmd = 0x95, i, ps = -1;
-
- nullpo_retv(bl);
-
- WBUFW(buf,0) = cmd;
- WBUFL(buf,2) = bl->id;
-
- switch( bl->type )
- {
- case BL_PC:
- {
- struct map_session_data *ssd = (struct map_session_data *)bl;
- struct party_data *p = NULL;
- struct guild *g = NULL;
-
- //Requesting your own "shadow" name. [Skotlex]
- if (ssd->fd == fd && ssd->disguise)
- WBUFL(buf,2) = -bl->id;
-
- if( ssd->fakename[0] )
- {
- WBUFW(buf, 0) = cmd = 0x195;
- memcpy(WBUFP(buf,6), ssd->fakename, NAME_LENGTH);
- WBUFB(buf,30) = WBUFB(buf,54) = WBUFB(buf,78) = 0;
- break;
- }
- memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH);
-
- if( ssd->status.party_id )
- {
- p = party_search(ssd->status.party_id);
- }
- if( ssd->status.guild_id )
- {
- if( ( g = guild_search(ssd->status.guild_id) ) != NULL )
- {
- ARR_FIND(0, g->max_member, i, g->member[i].account_id == ssd->status.account_id && g->member[i].char_id == ssd->status.char_id);
- if( i < g->max_member ) ps = g->member[i].position;
- }
- }
-
- if( !battle_config.display_party_name && g == NULL )
- {// do not display party unless the player is also in a guild
- p = NULL;
- }
-
- if (p == NULL && g == NULL)
- break;
-
- WBUFW(buf, 0) = cmd = 0x195;
- if (p)
- memcpy(WBUFP(buf,30), p->party.name, NAME_LENGTH);
- else
- WBUFB(buf,30) = 0;
-
- if (g && ps >= 0 && ps < MAX_GUILDPOSITION)
- {
- memcpy(WBUFP(buf,54), g->name,NAME_LENGTH);
- memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH);
- } else { //Assume no guild.
- WBUFB(buf,54) = 0;
- WBUFB(buf,78) = 0;
- }
- }
- break;
- //[blackhole89]
- case BL_HOM:
- memcpy(WBUFP(buf,6), ((TBL_HOM*)bl)->homunculus.name, NAME_LENGTH);
- break;
- case BL_MER:
- memcpy(WBUFP(buf,6), ((TBL_MER*)bl)->db->name, NAME_LENGTH);
- break;
- case BL_PET:
- memcpy(WBUFP(buf,6), ((TBL_PET*)bl)->pet.name, NAME_LENGTH);
- break;
- case BL_NPC:
- memcpy(WBUFP(buf,6), ((TBL_NPC*)bl)->name, NAME_LENGTH);
- break;
- case BL_MOB:
- {
- struct mob_data *md = (struct mob_data *)bl;
- nullpo_retv(md);
-
- memcpy(WBUFP(buf,6), md->name, NAME_LENGTH);
- if( md->guardian_data && md->guardian_data->guild_id )
- {
- WBUFW(buf, 0) = cmd = 0x195;
- WBUFB(buf,30) = 0;
- memcpy(WBUFP(buf,54), md->guardian_data->guild_name, NAME_LENGTH);
- memcpy(WBUFP(buf,78), md->guardian_data->castle->castle_name, NAME_LENGTH);
- }
- else if( battle_config.show_mob_info )
- {
- char mobhp[50], *str_p = mobhp;
- WBUFW(buf, 0) = cmd = 0x195;
- if( battle_config.show_mob_info&4 )
- str_p += sprintf(str_p, "Lv. %d | ", md->level);
- if( battle_config.show_mob_info&1 )
- str_p += sprintf(str_p, "HP: %u/%u | ", md->status.hp, md->status.max_hp);
- if( battle_config.show_mob_info&2 )
- str_p += sprintf(str_p, "HP: %d%% | ", get_percentage(md->status.hp, md->status.max_hp));
- //Even thought mobhp ain't a name, we send it as one so the client
- //can parse it. [Skotlex]
- if( str_p != mobhp )
- {
- *(str_p-3) = '\0'; //Remove trailing space + pipe.
- memcpy(WBUFP(buf,30), mobhp, NAME_LENGTH);
- WBUFB(buf,54) = 0;
- WBUFB(buf,78) = 0;
- }
- }
- }
- break;
- case BL_CHAT: //FIXME: Clients DO request this... what should be done about it? The chat's title may not fit... [Skotlex]
-// memcpy(WBUFP(buf,6), (struct chat*)->title, NAME_LENGTH);
-// break;
- return;
- case BL_ELEM:
- memcpy(WBUFP(buf,6), ((TBL_ELEM*)bl)->db->name, NAME_LENGTH);
- break;
- default:
- ShowError("clif_charnameack: bad type %d(%d)\n", bl->type, bl->id);
- return;
- }
-
- // if no receipient specified just update nearby clients
- if (fd == 0)
- clif_send(buf, packet_len(cmd), bl, AREA);
- else {
- WFIFOHEAD(fd, packet_len(cmd));
- memcpy(WFIFOP(fd, 0), buf, packet_len(cmd));
- WFIFOSET(fd, packet_len(cmd));
- }
-}
-
-
-//Used to update when a char leaves a party/guild. [Skotlex]
-//Needed because when you send a 0x95 packet, the client will not remove the cached party/guild info that is not sent.
-void clif_charnameupdate (struct map_session_data *ssd)
-{
- unsigned char buf[103];
- int cmd = 0x195, ps = -1, i;
- struct party_data *p = NULL;
- struct guild *g = NULL;
-
- nullpo_retv(ssd);
-
- if( ssd->fakename[0] )
- return; //No need to update as the party/guild was not displayed anyway.
-
- WBUFW(buf,0) = cmd;
- WBUFL(buf,2) = ssd->bl.id;
-
- memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH);
-
- if (!battle_config.display_party_name) {
- if (ssd->status.party_id > 0 && ssd->status.guild_id > 0 && (g = guild_search(ssd->status.guild_id)) != NULL)
- p = party_search(ssd->status.party_id);
- }else{
- if (ssd->status.party_id > 0)
- p = party_search(ssd->status.party_id);
- }
-
- if( ssd->status.guild_id > 0 && (g = guild_search(ssd->status.guild_id)) != NULL )
- {
- ARR_FIND(0, g->max_member, i, g->member[i].account_id == ssd->status.account_id && g->member[i].char_id == ssd->status.char_id);
- if( i < g->max_member ) ps = g->member[i].position;
- }
-
- if( p )
- memcpy(WBUFP(buf,30), p->party.name, NAME_LENGTH);
- else
- WBUFB(buf,30) = 0;
-
- if( g && ps >= 0 && ps < MAX_GUILDPOSITION )
- {
- memcpy(WBUFP(buf,54), g->name,NAME_LENGTH);
- memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH);
- }
- else
- {
- WBUFB(buf,54) = 0;
- WBUFB(buf,78) = 0;
- }
-
- // Update nearby clients
- clif_send(buf, packet_len(cmd), &ssd->bl, AREA);
-}
-
-
-/// Taekwon Jump (TK_HIGHJUMP) effect (ZC_HIGHJUMP).
-/// 01ff <id>.L <x>.W <y>.W
-///
-/// Visually moves(instant) a character to x,y. The char moves even
-/// when the target cell isn't walkable. If the char is sitting it
-/// stays that way.
-void clif_slide(struct block_list *bl, int x, int y)
-{
- unsigned char buf[10];
- nullpo_retv(bl);
-
- WBUFW(buf, 0) = 0x01ff;
- WBUFL(buf, 2) = bl->id;
- WBUFW(buf, 6) = x;
- WBUFW(buf, 8) = y;
- clif_send(buf, packet_len(0x1ff), bl, AREA);
-
- if( disguised(bl) )
- {
- WBUFL(buf,2) = -bl->id;
- clif_send(buf, packet_len(0x1ff), bl, SELF);
- }
-}
-
-
-/*------------------------------------------
- * @me command by lordalfa, rewritten implementation by Skotlex
- *------------------------------------------*/
-void clif_disp_overhead(struct map_session_data *sd, const char* mes)
-{
- unsigned char buf[256]; //This should be more than sufficient, the theorical max is CHAT_SIZE + 8 (pads and extra inserted crap)
- int len_mes = strlen(mes)+1; //Account for \0
-
- if (len_mes > sizeof(buf)-8) {
- ShowError("clif_disp_overhead: Message too long (length %d)\n", len_mes);
- len_mes = sizeof(buf)-8; //Trunk it to avoid problems.
- }
- // send message to others
- WBUFW(buf,0) = 0x8d;
- WBUFW(buf,2) = len_mes + 8; // len of message + 8 (command+len+id)
- WBUFL(buf,4) = sd->bl.id;
- safestrncpy((char*)WBUFP(buf,8), mes, len_mes);
- clif_send(buf, WBUFW(buf,2), &sd->bl, AREA_CHAT_WOC);
-
- // send back message to the speaker
- WBUFW(buf,0) = 0x8e;
- WBUFW(buf, 2) = len_mes + 4;
- safestrncpy((char*)WBUFP(buf,4), mes, len_mes);
- clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
-}
-
-/*==========================
- * Minimap fix [Kevin]
- * Remove dot from minimap
- *--------------------------*/
-void clif_party_xy_remove(struct map_session_data *sd)
-{
- unsigned char buf[16];
- nullpo_retv(sd);
- WBUFW(buf,0)=0x107;
- WBUFL(buf,2)=sd->status.account_id;
- WBUFW(buf,6)=-1;
- WBUFW(buf,8)=-1;
- clif_send(buf,packet_len(0x107),&sd->bl,PARTY_SAMEMAP_WOS);
-}
-
-
-/// Displays a skill message (thanks to Rayce) (ZC_SKILLMSG).
-/// 0215 <msg id>.L
-/// msg id:
-/// 0x15 = End all negative status (PA_GOSPEL)
-/// 0x16 = Immunity to all status (PA_GOSPEL)
-/// 0x17 = MaxHP +100% (PA_GOSPEL)
-/// 0x18 = MaxSP +100% (PA_GOSPEL)
-/// 0x19 = All stats +20 (PA_GOSPEL)
-/// 0x1c = Enchant weapon with Holy element (PA_GOSPEL)
-/// 0x1d = Enchant armor with Holy element (PA_GOSPEL)
-/// 0x1e = DEF +25% (PA_GOSPEL)
-/// 0x1f = ATK +100% (PA_GOSPEL)
-/// 0x20 = HIT/Flee +50 (PA_GOSPEL)
-/// 0x28 = Full strip failed because of coating (ST_FULLSTRIP)
-/// ? = nothing
-void clif_gospel_info(struct map_session_data *sd, int type)
-{
- int fd=sd->fd;
- WFIFOHEAD(fd,packet_len(0x215));
- WFIFOW(fd,0)=0x215;
- WFIFOL(fd,2)=type;
- WFIFOSET(fd, packet_len(0x215));
-
-}
-
-
-/// Multi-purpose mission information packet (ZC_STARSKILL).
-/// 020e <mapname>.24B <monster_id>.L <star>.B <result>.B
-/// result:
-/// 0 = Star Gladiator %s has designed <mapname>'s as the %s.
-/// star:
-/// 0 = Place of the Sun
-/// 1 = Place of the Moon
-/// 2 = Place of the Stars
-/// 1 = Star Gladiator %s's %s: <mapname>
-/// star:
-/// 0 = Place of the Sun
-/// 1 = Place of the Moon
-/// 2 = Place of the Stars
-/// 10 = Star Gladiator %s has designed <mapname>'s as the %s.
-/// star:
-/// 0 = Target of the Sun
-/// 1 = Target of the Moon
-/// 2 = Target of the Stars
-/// 11 = Star Gladiator %s's %s: <mapname used as monster name>
-/// star:
-/// 0 = Monster of the Sun
-/// 1 = Monster of the Moon
-/// 2 = Monster of the Stars
-/// 20 = [TaeKwon Mission] Target Monster : <mapname used as monster name> (<star>%)
-/// 21 = [Taming Mission] Target Monster : <mapname used as monster name>
-/// 22 = [Collector Rank] Target Item : <monster_id used as item id>
-/// 30 = [Sun, Moon and Stars Angel] Designed places and monsters have been reset.
-/// 40 = Target HP : <monster_id used as HP>
-void clif_starskill(struct map_session_data* sd, const char* mapname, int monster_id, unsigned char star, unsigned char result)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x20e));
- WFIFOW(fd,0) = 0x20e;
- safestrncpy((char*)WFIFOP(fd,2), mapname, NAME_LENGTH);
- WFIFOL(fd,26) = monster_id;
- WFIFOB(fd,30) = star;
- WFIFOB(fd,31) = result;
- WFIFOSET(fd,packet_len(0x20e));
-}
-
-/*==========================================
- * Info about Star Glaldiator save map [Komurka]
- * type: 1: Information, 0: Map registered
- *------------------------------------------*/
-void clif_feel_info(struct map_session_data* sd, unsigned char feel_level, unsigned char type)
-{
- char mapname[MAP_NAME_LENGTH_EXT];
-
- mapindex_getmapname_ext(mapindex_id2name(sd->feel_map[feel_level].index), mapname);
- clif_starskill(sd, mapname, 0, feel_level, type ? 1 : 0);
-}
-
-/*==========================================
- * Info about Star Glaldiator hate mob [Komurka]
- * type: 1: Register mob, 0: Information.
- *------------------------------------------*/
-void clif_hate_info(struct map_session_data *sd, unsigned char hate_level,int class_, unsigned char type)
-{
- if( pcdb_checkid(class_) )
- {
- clif_starskill(sd, job_name(class_), class_, hate_level, type ? 10 : 11);
- }
- else if( mobdb_checkid(class_) )
- {
- clif_starskill(sd, mob_db(class_)->jname, class_, hate_level, type ? 10 : 11);
- }
- else
- {
- ShowWarning("clif_hate_info: Received invalid class %d for this packet (char_id=%d, hate_level=%u, type=%u).\n", class_, sd->status.char_id, (unsigned int)hate_level, (unsigned int)type);
- }
-}
-
-/*==========================================
- * Info about TaeKwon Do TK_MISSION mob [Skotlex]
- *------------------------------------------*/
-void clif_mission_info(struct map_session_data *sd, int mob_id, unsigned char progress)
-{
- clif_starskill(sd, mob_db(mob_id)->jname, mob_id, progress, 20);
-}
-
-/*==========================================
- * Feel/Hate reset (thanks to Rayce) [Skotlex]
- *------------------------------------------*/
-void clif_feel_hate_reset(struct map_session_data *sd)
-{
- clif_starskill(sd, "", 0, 0, 30);
-}
-
-
-/// Equip window (un)tick ack (ZC_CONFIG).
-/// 02d9 <type>.L <value>.L
-/// type:
-/// 0 = open equip window
-/// value:
-/// 0 = disabled
-/// 1 = enabled
-void clif_equiptickack(struct map_session_data* sd, int flag)
-{
- int fd;
- nullpo_retv(sd);
- fd = sd->fd;
-
- WFIFOHEAD(fd, packet_len(0x2d9));
- WFIFOW(fd, 0) = 0x2d9;
- WFIFOL(fd, 2) = 0;
- WFIFOL(fd, 6) = flag;
- WFIFOSET(fd, packet_len(0x2d9));
-}
-
-
-/// The player's 'view equip' state, sent during login (ZC_CONFIG_NOTIFY).
-/// 02da <open equip window>.B
-/// open equip window:
-/// 0 = disabled
-/// 1 = enabled
-void clif_equipcheckbox(struct map_session_data* sd)
-{
- int fd;
- nullpo_retv(sd);
- fd = sd->fd;
-
- WFIFOHEAD(fd, packet_len(0x2da));
- WFIFOW(fd, 0) = 0x2da;
- WFIFOB(fd, 2) = (sd->status.show_equip ? 1 : 0);
- WFIFOSET(fd, packet_len(0x2da));
-}
-
-
-/// Sends info about a player's equipped items.
-/// 02d7 <packet len>.W <name>.24B <class>.W <hairstyle>.W <up-viewid>.W <mid-viewid>.W <low-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.26B* (ZC_EQUIPWIN_MICROSCOPE)
-/// 02d7 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE, PACKETVER >= 20100629)
-/// 0859 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE2, PACKETVER >= 20101124)
-/// 0859 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <robe>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE2, PACKETVER >= 20110111)
-void clif_viewequip_ack(struct map_session_data* sd, struct map_session_data* tsd)
-{
- uint8* buf;
- int i, n, fd, offset = 0;
-#if PACKETVER < 20100629
- const int s = 26;
-#else
- const int s = 28;
-#endif
- nullpo_retv(sd);
- nullpo_retv(tsd);
- fd = sd->fd;
-
- WFIFOHEAD(fd, MAX_INVENTORY * s + 43);
- buf = WFIFOP(fd,0);
-
-#if PACKETVER < 20101124
- WBUFW(buf, 0) = 0x2d7;
-#else
- WBUFW(buf, 0) = 0x859;
-#endif
- safestrncpy((char*)WBUFP(buf, 4), tsd->status.name, NAME_LENGTH);
- WBUFW(buf,28) = tsd->status.class_;
- WBUFW(buf,30) = tsd->vd.hair_style;
- WBUFW(buf,32) = tsd->vd.head_bottom;
- WBUFW(buf,34) = tsd->vd.head_mid;
- WBUFW(buf,36) = tsd->vd.head_top;
-#if PACKETVER >= 20110111
- WBUFW(buf,38) = tsd->vd.robe;
- offset+= 2;
- buf = WBUFP(buf,2);
-#endif
- WBUFW(buf,38) = tsd->vd.hair_color;
- WBUFW(buf,40) = tsd->vd.cloth_color;
- WBUFB(buf,42) = tsd->vd.sex;
-
- for(i=0,n=0; i < MAX_INVENTORY; i++)
- {
- if (tsd->status.inventory[i].nameid <= 0 || tsd->inventory_data[i] == NULL) // Item doesn't exist
- continue;
- if (!itemdb_isequip2(tsd->inventory_data[i])) // Is not equippable
- continue;
-
- // Inventory position
- WBUFW(buf, n*s+43) = i + 2;
- // Add refine, identify flag, element, etc.
- clif_item_sub(WBUFP(buf,0), n*s+45, &tsd->status.inventory[i], tsd->inventory_data[i], pc_equippoint(tsd, i));
- // Add cards
- clif_addcards(WBUFP(buf, n*s+55), &tsd->status.inventory[i]);
- // Expiration date stuff, if all of those are set to 0 then the client doesn't show anything related (6 bytes)
- WBUFL(buf, n*s+63) = tsd->status.inventory[i].expire_time;
- WBUFW(buf, n*s+67) = 0;
-#if PACKETVER >= 20100629
- if (tsd->inventory_data[i]->equip&EQP_VISIBLE)
- WBUFW(buf, n*s+69) = tsd->inventory_data[i]->look;
- else
- WBUFW(buf, n*s+69) = 0;
-#endif
- n++;
- }
-
- WFIFOW(fd, 2) = 43+offset+n*s; // Set length
- WFIFOSET(fd, WFIFOW(fd, 2));
-}
-
-
-/// Display msgstringtable.txt string (ZC_MSG).
-/// 0291 <message>.W
-void clif_msg(struct map_session_data* sd, unsigned short id)
-{
- int fd;
- nullpo_retv(sd);
- fd = sd->fd;
-
- WFIFOHEAD(fd, packet_len(0x291));
- WFIFOW(fd, 0) = 0x291;
- WFIFOW(fd, 2) = id; // zero-based msgstringtable.txt index
- WFIFOSET(fd, packet_len(0x291));
-}
-
-
-/// Display msgstringtable.txt string and fill in a valid for %d format (ZC_MSG_VALUE).
-/// 0x7e2 <message>.W <value>.L
-void clif_msg_value(struct map_session_data* sd, unsigned short id, int value)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd, packet_len(0x7e2));
- WFIFOW(fd,0) = 0x7e2;
- WFIFOW(fd,2) = id;
- WFIFOL(fd,4) = value;
- WFIFOSET(fd, packet_len(0x7e2));
-}
-
-
-/// Displays msgstringtable.txt string, prefixed with a skill name. (ZC_MSG_SKILL).
-/// 07e6 <skill id>.W <msg id>.L
-///
-/// NOTE: Message has following format and is printed in color 0xCDCDFF (purple):
-/// "[SkillName] Message"
-void clif_msg_skill(struct map_session_data* sd, uint16 skill_id, int msg_id)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd, packet_len(0x7e6));
- WFIFOW(fd,0) = 0x7e6;
- WFIFOW(fd,2) = skill_id;
- WFIFOL(fd,4) = msg_id;
- WFIFOSET(fd, packet_len(0x7e6));
-}
-
-
-/// View player equip request denied
-void clif_viewequip_fail(struct map_session_data* sd)
-{
- clif_msg(sd, 0x54d);
-}
-
-
-/// Validates one global/guild/party/whisper message packet and tries to recognize its components.
-/// Returns true if the packet was parsed successfully.
-/// Formats: 0 - <packet id>.w <packet len>.w (<name> : <message>).?B 00
-/// 1 - <packet id>.w <packet len>.w <name>.24B <message>.?B 00
-static bool clif_process_message(struct map_session_data* sd, int format, char** name_, int* namelen_, char** message_, int* messagelen_)
-{
- char *text, *name, *message;
- unsigned int packetlen, textlen, namelen, messagelen;
- int fd = sd->fd;
-
- *name_ = NULL;
- *namelen_ = 0;
- *message_ = NULL;
- *messagelen_ = 0;
-
- packetlen = RFIFOW(fd,2);
- // basic structure checks
- if( packetlen < 4 + 1 )
- { // 4-byte header and at least an empty string is expected
- ShowWarning("clif_process_message: Received malformed packet from player '%s' (no message data)!\n", sd->status.name);
- return false;
- }
-
- text = (char*)RFIFOP(fd,4);
- textlen = packetlen - 4;
-
- // process <name> part of the packet
- if( format == 0 )
- {// name and message are separated by ' : '
- // validate name
- name = text;
- namelen = strnlen(sd->status.name, NAME_LENGTH-1); // name length (w/o zero byte)
-
- if( strncmp(name, sd->status.name, namelen) || // the text must start with the speaker's name
- name[namelen] != ' ' || name[namelen+1] != ':' || name[namelen+2] != ' ' ) // followed by ' : '
- {
- //Hacked message, or infamous "client desynch" issue where they pick one char while loading another.
- ShowWarning("clif_process_message: Player '%s' sent a message using an incorrect name! Forcing a relog...\n", sd->status.name);
- set_eof(fd); // Just kick them out to correct it.
- return false;
- }
-
- message = name + namelen + 3;
- messagelen = textlen - namelen - 3; // this should be the message length (w/ zero byte included)
- }
- else
- {// name has fixed width
- if( textlen < NAME_LENGTH + 1 )
- {
- ShowWarning("clif_process_message: Received malformed packet from player '%s' (packet length is incorrect)!\n", sd->status.name);
- return false;
- }
-
- // validate name
- name = text;
- namelen = strnlen(name, NAME_LENGTH-1); // name length (w/o zero byte)
-
- if( name[namelen] != '\0' )
- { // only restriction is that the name must be zero-terminated
- ShowWarning("clif_process_message: Player '%s' sent an unterminated name!\n", sd->status.name);
- return false;
- }
-
- message = name + NAME_LENGTH;
- messagelen = textlen - NAME_LENGTH; // this should be the message length (w/ zero byte included)
- }
-
- if( messagelen != strnlen(message, messagelen)+1 )
- { // the declared length must match real length
- ShowWarning("clif_process_message: Received malformed packet from player '%s' (length is incorrect)!\n", sd->status.name);
- return false;
- }
- // verify <message> part of the packet
- if( message[messagelen-1] != '\0' )
- { // message must be zero-terminated
- ShowWarning("clif_process_message: Player '%s' sent an unterminated message string!\n", sd->status.name);
- return false;
- }
- if( messagelen > CHAT_SIZE_MAX-1 )
- { // messages mustn't be too long
- // Normally you can only enter CHATBOX_SIZE-1 letters into the chat box, but Frost Joke / Dazzler's text can be longer.
- // Also, the physical size of strings that use multibyte encoding can go multiple times over the chatbox capacity.
- // Neither the official client nor server place any restriction on the length of the data in the packet,
- // but we'll only allow reasonably long strings here. This also makes sure that they fit into the `chatlog` table.
- ShowWarning("clif_process_message: Player '%s' sent a message too long ('%.*s')!\n", sd->status.name, CHAT_SIZE_MAX-1, message);
- return false;
- }
-
- *name_ = name;
- *namelen_ = namelen;
- *message_ = message;
- *messagelen_ = messagelen;
- return true;
-}
-
-// ---------------------
-// clif_guess_PacketVer
-// ---------------------
-// Parses a WantToConnection packet to try to identify which is the packet version used. [Skotlex]
-// error codes:
-// 0 - Success
-// 1 - Unknown packet_ver
-// 2 - Invalid account_id
-// 3 - Invalid char_id
-// 4 - Invalid login_id1 (reserved)
-// 5 - Invalid client_tick (reserved)
-// 6 - Invalid sex
-// Only the first 'invalid' error that appears is used.
-static int clif_guess_PacketVer(int fd, int get_previous, int *error)
-{
- static int err = 1;
- static int packet_ver = -1;
- int cmd, packet_len, value; //Value is used to temporarily store account/char_id/sex
-
- if (get_previous)
- {//For quick reruns, since the normal code flow is to fetch this once to identify the packet version, then again in the wanttoconnect function. [Skotlex]
- if( error )
- *error = err;
- return packet_ver;
- }
-
- //By default, start searching on the default one.
- err = 1;
- packet_ver = clif_config.packet_db_ver;
- cmd = RFIFOW(fd,0);
- packet_len = RFIFOREST(fd);
-
-#define SET_ERROR(n) \
- if( err == 1 )\
- err = n;\
-//define SET_ERROR
-
- // FIXME: If the packet is not received at once, this will FAIL.
- // Figure out, when it happens, that only part of the packet is
- // received, or fix the function to be able to deal with that
- // case.
-#define CHECK_PACKET_VER() \
- if( cmd != clif_config.connect_cmd[packet_ver] || packet_len != packet_db[packet_ver][cmd].len )\
- ;/* not wanttoconnection or wrong length */\
- else if( (value=(int)RFIFOL(fd, packet_db[packet_ver][cmd].pos[0])) < START_ACCOUNT_NUM || value > END_ACCOUNT_NUM )\
- { SET_ERROR(2); }/* invalid account_id */\
- else if( (value=(int)RFIFOL(fd, packet_db[packet_ver][cmd].pos[1])) <= 0 )\
- { SET_ERROR(3); }/* invalid char_id */\
- /* RFIFOL(fd, packet_db[packet_ver][cmd].pos[2]) - don't care about login_id1 */\
- /* RFIFOL(fd, packet_db[packet_ver][cmd].pos[3]) - don't care about client_tick */\
- else if( (value=(int)RFIFOB(fd, packet_db[packet_ver][cmd].pos[4])) != 0 && value != 1 )\
- { SET_ERROR(6); }/* invalid sex */\
- else\
- {\
- err = 0;\
- if( error )\
- *error = 0;\
- return packet_ver;\
- }\
-//define CHECK_PACKET_VER
-
- CHECK_PACKET_VER();//Default packet version found.
-
- for (packet_ver = MAX_PACKET_VER; packet_ver > 0; packet_ver--)
- { //Start guessing the version, giving priority to the newer ones. [Skotlex]
- CHECK_PACKET_VER();
- }
- if( error )
- *error = err;
- packet_ver = -1;
- return -1;
-#undef SET_ERROR
-#undef CHECK_PACKET_VER
-}
-
-// ------------
-// clif_parse_*
-// ------------
-// Parses incoming (player) connection
-
-
-/// Request to connect to map-server.
-/// 0072 <account id>.L <char id>.L <auth code>.L <client time>.L <gender>.B (CZ_ENTER)
-/// 0436 <account id>.L <char id>.L <auth code>.L <client time>.L <gender>.B (CZ_ENTER2)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_WantToConnection(int fd, TBL_PC* sd)
-{
- struct block_list* bl;
- struct auth_node* node;
- int cmd, account_id, char_id, login_id1, sex;
- unsigned int client_tick; //The client tick is a tick, therefore it needs be unsigned. [Skotlex]
- int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 (by [Yor])
-
- if (sd) {
- ShowError("clif_parse_WantToConnection : invalid request (character already logged in)\n");
- return;
- }
-
- // Only valid packet version get here
- packet_ver = clif_guess_PacketVer(fd, 1, NULL);
-
- cmd = RFIFOW(fd,0);
- account_id = RFIFOL(fd, packet_db[packet_ver][cmd].pos[0]);
- char_id = RFIFOL(fd, packet_db[packet_ver][cmd].pos[1]);
- login_id1 = RFIFOL(fd, packet_db[packet_ver][cmd].pos[2]);
- client_tick = RFIFOL(fd, packet_db[packet_ver][cmd].pos[3]);
- sex = RFIFOB(fd, packet_db[packet_ver][cmd].pos[4]);
-
- if( packet_ver < 5 || // reject really old client versions
- (packet_ver <= 9 && (battle_config.packet_ver_flag & 1) == 0) || // older than 6sept04
- (packet_ver > 9 && (battle_config.packet_ver_flag & 1<<(packet_ver-9)) == 0)) // version not allowed
- {// packet version rejected
- ShowInfo("Rejected connection attempt, forbidden packet version (AID/CID: '"CL_WHITE"%d/%d"CL_RESET"', Packet Ver: '"CL_WHITE"%d"CL_RESET"', IP: '"CL_WHITE"%s"CL_RESET"').\n", account_id, char_id, packet_ver, ip2str(session[fd]->client_addr, NULL));
- WFIFOHEAD(fd,packet_len(0x6a));
- WFIFOW(fd,0) = 0x6a;
- WFIFOB(fd,2) = 5; // Your Game's EXE file is not the latest version
- WFIFOSET(fd,packet_len(0x6a));
- set_eof(fd);
- return;
- }
-
- if( runflag != MAPSERVER_ST_RUNNING )
- {// not allowed
- clif_authfail_fd(fd,1);// server closed
- return;
- }
-
- //Check for double login.
- bl = map_id2bl(account_id);
- if(bl && bl->type != BL_PC) {
- ShowError("clif_parse_WantToConnection: a non-player object already has id %d, please increase the starting account number\n", account_id);
- WFIFOHEAD(fd,packet_len(0x6a));
- WFIFOW(fd,0) = 0x6a;
- WFIFOB(fd,2) = 3; // Rejected by server
- WFIFOSET(fd,packet_len(0x6a));
- set_eof(fd);
- return;
- }
-
- if (bl ||
- ((node=chrif_search(account_id)) && //An already existing node is valid only if it is for this login.
- !(node->account_id == account_id && node->char_id == char_id && node->state == ST_LOGIN)))
- {
- clif_authfail_fd(fd, 8); //Still recognizes last connection
- return;
- }
-
- CREATE(sd, TBL_PC, 1);
- sd->fd = fd;
- sd->packet_ver = packet_ver;
- session[fd]->session_data = sd;
-
- pc_setnewpc(sd, account_id, char_id, login_id1, client_tick, sex, fd);
-
-#if PACKETVER < 20070521
- WFIFOHEAD(fd,4);
- WFIFOL(fd,0) = sd->bl.id;
- WFIFOSET(fd,4);
-#else
- WFIFOHEAD(fd,packet_len(0x283));
- WFIFOW(fd,0) = 0x283;
- WFIFOL(fd,2) = sd->bl.id;
- WFIFOSET(fd,packet_len(0x283));
-#endif
-
- chrif_authreq(sd);
-}
-
-
-/// Notification from the client, that it has finished map loading and is about to display player's character (CZ_NOTIFY_ACTORINIT).
-/// 007d
-void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
-{
- if(sd->bl.prev != NULL)
- return;
-
- if (!sd->state.active)
- { //Character loading is not complete yet!
- //Let pc_reg_received reinvoke this when ready.
- sd->state.connect_new = 0;
- return;
- }
-
- if (sd->state.rewarp)
- { //Rewarp player.
- sd->state.rewarp = 0;
- clif_changemap(sd, sd->mapindex, sd->bl.x, sd->bl.y);
- return;
- }
-
- sd->state.warping = 0;
-
- // look
-#if PACKETVER < 4
- clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
- clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
-#else
- clif_changelook(&sd->bl,LOOK_WEAPON,0);
-#endif
-
- if(sd->vd.cloth_color)
- clif_refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF);
-
- // item
- clif_inventorylist(sd); // inventory list first, otherwise deleted items in pc_checkitem show up as 'unknown item'
- pc_checkitem(sd);
-
- // cart
- if(pc_iscarton(sd)) {
- clif_cartlist(sd);
- clif_updatestatus(sd,SP_CARTINFO);
- }
-
- // weight
- clif_updatestatus(sd,SP_WEIGHT);
- clif_updatestatus(sd,SP_MAXWEIGHT);
-
- // guild
- // (needs to go before clif_spawn() to show guild emblems correctly)
- if(sd->status.guild_id)
- guild_send_memberinfoshort(sd,1);
-
- if(battle_config.pc_invincible_time > 0) {
- if(map_flag_gvg(sd->bl.m))
- pc_setinvincibletimer(sd,battle_config.pc_invincible_time<<1);
- else
- pc_setinvincibletimer(sd,battle_config.pc_invincible_time);
- }
-
- if( map[sd->bl.m].users++ == 0 && battle_config.dynamic_mobs )
- map_spawnmobs(sd->bl.m);
- if( !(sd->sc.option&OPTION_INVISIBLE) )
- {// increment the number of pvp players on the map
- map[sd->bl.m].users_pvp++;
- }
- if( map[sd->bl.m].instance_id )
- {
- instance[map[sd->bl.m].instance_id].users++;
- instance_check_idle(map[sd->bl.m].instance_id);
- }
- sd->state.debug_remove_map = 0; // temporary state to track double remove_map's [FlavioJS]
-
- // reset the callshop flag if the player changes map
- sd->state.callshop = 0;
-
- map_addblock(&sd->bl);
- clif_spawn(&sd->bl);
-
- // Party
- // (needs to go after clif_spawn() to show hp bars correctly)
- if(sd->status.party_id) {
- party_send_movemap(sd);
- clif_party_hp(sd); // Show hp after displacement [LuzZza]
- }
-
- if( sd->bg_id ) clif_bg_hp(sd); // BattleGround System
-
- if(map[sd->bl.m].flag.pvp && !(sd->sc.option&OPTION_INVISIBLE)) {
- if(!battle_config.pk_mode) { // remove pvp stuff for pk_mode [Valaris]
- if (!map[sd->bl.m].flag.pvp_nocalcrank)
- sd->pvp_timer = add_timer(gettick()+200, pc_calc_pvprank_timer, sd->bl.id, 0);
- sd->pvp_rank = 0;
- sd->pvp_lastusers = 0;
- sd->pvp_point = 5;
- sd->pvp_won = 0;
- sd->pvp_lost = 0;
- }
- clif_map_property(sd, MAPPROPERTY_FREEPVPZONE);
- } else
- // set flag, if it's a duel [LuzZza]
- if(sd->duel_group)
- clif_map_property(sd, MAPPROPERTY_FREEPVPZONE);
-
- if (map[sd->bl.m].flag.gvg_dungeon)
- clif_map_property(sd, MAPPROPERTY_FREEPVPZONE); //TODO: Figure out the real packet to send here.
-
- if( map_flag_gvg(sd->bl.m) )
- clif_map_property(sd, MAPPROPERTY_AGITZONE);
-
- // info about nearby objects
- // must use foreachinarea (CIRCULAR_AREA interferes with foreachinrange)
- map_foreachinarea(clif_getareachar, sd->bl.m, sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE, sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_ALL, sd);
-
- // pet
- if( sd->pd )
- {
- if( battle_config.pet_no_gvg && map_flag_gvg(sd->bl.m) )
- { //Return the pet to egg. [Skotlex]
- clif_displaymessage(sd->fd, msg_txt(666));
- pet_menu(sd, 3); //Option 3 is return to egg.
- }
- else
- {
- map_addblock(&sd->pd->bl);
- clif_spawn(&sd->pd->bl);
- clif_send_petdata(sd,sd->pd,0,0);
- clif_send_petstatus(sd);
-// skill_unit_move(&sd->pd->bl,gettick(),1);
- }
- }
-
- //homunculus [blackhole89]
- if( merc_is_hom_active(sd->hd) )
- {
- map_addblock(&sd->hd->bl);
- clif_spawn(&sd->hd->bl);
- clif_send_homdata(sd,SP_ACK,0);
- clif_hominfo(sd,sd->hd,1);
- clif_hominfo(sd,sd->hd,0); //for some reason, at least older clients want this sent twice
- clif_homskillinfoblock(sd);
- if( battle_config.hom_setting&0x8 )
- status_calc_bl(&sd->hd->bl, SCB_SPEED); //Homunc mimic their master's speed on each map change
- if( !(battle_config.hom_setting&0x2) )
- skill_unit_move(&sd->hd->bl,gettick(),1); // apply land skills immediately
- }
-
- if( sd->md ) {
- map_addblock(&sd->md->bl);
- clif_spawn(&sd->md->bl);
- clif_mercenary_info(sd);
- clif_mercenary_skillblock(sd);
- status_calc_bl(&sd->md->bl, SCB_SPEED); // Mercenary mimic their master's speed on each map change
- }
-
- if( sd->ed ) {
- map_addblock(&sd->ed->bl);
- clif_spawn(&sd->ed->bl);
- clif_elemental_info(sd);
- clif_elemental_updatestatus(sd,SP_HP);
- clif_hpmeter_single(sd->fd,sd->ed->bl.id,sd->ed->battle_status.hp,sd->ed->battle_status.max_hp);
- clif_elemental_updatestatus(sd,SP_SP);
- status_calc_bl(&sd->ed->bl, SCB_SPEED); //Elemental mimic their master's speed on each map change
- }
-
- if(sd->state.connect_new) {
- int lv;
- sd->state.connect_new = 0;
- clif_skillinfoblock(sd);
- clif_hotkeys_send(sd);
- clif_updatestatus(sd,SP_BASEEXP);
- clif_updatestatus(sd,SP_NEXTBASEEXP);
- clif_updatestatus(sd,SP_JOBEXP);
- clif_updatestatus(sd,SP_NEXTJOBEXP);
- clif_updatestatus(sd,SP_SKILLPOINT);
- clif_initialstatus(sd);
-
- if (sd->sc.option&OPTION_FALCON)
- clif_status_load(&sd->bl, SI_FALCON, 1);
-
- if (sd->sc.option&OPTION_RIDING)
- clif_status_load(&sd->bl, SI_RIDING, 1);
- else if (sd->sc.option&OPTION_WUGRIDER)
- clif_status_load(&sd->bl, SI_WUGRIDER, 1);
-
- if(sd->status.manner < 0)
- sc_start(&sd->bl,SC_NOCHAT,100,0,0);
-
- //Auron reported that This skill only triggers when you logon on the map o.O [Skotlex]
- if ((lv = pc_checkskill(sd,SG_KNOWLEDGE)) > 0) {
- if(sd->bl.m == sd->feel_map[0].m
- || sd->bl.m == sd->feel_map[1].m
- || sd->bl.m == sd->feel_map[2].m)
- sc_start(&sd->bl, SC_KNOWLEDGE, 100, lv, skill_get_time(SG_KNOWLEDGE, lv));
- }
-
- if(sd->pd && sd->pd->pet.intimate > 900)
- clif_pet_emotion(sd->pd,(sd->pd->pet.class_ - 100)*100 + 50 + pet_hungry_val(sd->pd));
-
- if(merc_is_hom_active(sd->hd))
- merc_hom_init_timers(sd->hd);
-
- if (night_flag && map[sd->bl.m].flag.nightenabled) {
- sd->state.night = 1;
- clif_status_load(&sd->bl, SI_NIGHT, 1);
- }
-
- // Notify everyone that this char logged in [Skotlex].
- map_foreachpc(clif_friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 1);
-
- //Login Event
- npc_script_event(sd, NPCE_LOGIN);
- } else {
- //For some reason the client "loses" these on warp/map-change.
- clif_updatestatus(sd,SP_STR);
- clif_updatestatus(sd,SP_AGI);
- clif_updatestatus(sd,SP_VIT);
- clif_updatestatus(sd,SP_INT);
- clif_updatestatus(sd,SP_DEX);
- clif_updatestatus(sd,SP_LUK);
-
- // abort currently running script
- sd->state.using_fake_npc = 0;
- sd->state.menu_or_input = 0;
- sd->npc_menu = 0;
-
- if(sd->npc_id)
- npc_event_dequeue(sd);
- }
-
- if( sd->state.changemap )
- {// restore information that gets lost on map-change
-#if PACKETVER >= 20070918
- clif_partyinvitationstate(sd);
- clif_equipcheckbox(sd);
-#endif
- if( (battle_config.bg_flee_penalty != 100 || battle_config.gvg_flee_penalty != 100) &&
- (map_flag_gvg(sd->state.pmap) || map_flag_gvg(sd->bl.m) || map[sd->state.pmap].flag.battleground || map[sd->bl.m].flag.battleground) )
- status_calc_bl(&sd->bl, SCB_FLEE); //Refresh flee penalty
-
- if( night_flag && map[sd->bl.m].flag.nightenabled )
- { //Display night.
- if( !sd->state.night )
- {
- sd->state.night = 1;
- clif_status_load(&sd->bl, SI_NIGHT, 1);
- }
- }
- else if( sd->state.night )
- { //Clear night display.
- sd->state.night = 0;
- clif_status_load(&sd->bl, SI_NIGHT, 0);
- }
-
- if( map[sd->bl.m].flag.battleground )
- {
- clif_map_type(sd, MAPTYPE_BATTLEFIELD); // Battleground Mode
- if( map[sd->bl.m].flag.battleground == 2 )
- clif_bg_updatescore_single(sd);
- }
-
- if( map[sd->bl.m].flag.allowks && !map_flag_ks(sd->bl.m) )
- {
- char output[128];
- sprintf(output, "[ Kill Steal Protection Disable. KS is allowed in this map ]");
- clif_broadcast(&sd->bl, output, strlen(output) + 1, 0x10, SELF);
- }
-
- map_iwall_get(sd); // Updates Walls Info on this Map to Client
- sd->state.changemap = false;
- }
-
- mail_clear(sd);
-
- /* Guild Aura Init */
- if( sd->state.gmaster_flag ) {
- guild_guildaura_refresh(sd,GD_LEADERSHIP,guild_checkskill(sd->state.gmaster_flag,GD_LEADERSHIP));
- guild_guildaura_refresh(sd,GD_GLORYWOUNDS,guild_checkskill(sd->state.gmaster_flag,GD_GLORYWOUNDS));
- guild_guildaura_refresh(sd,GD_SOULCOLD,guild_checkskill(sd->state.gmaster_flag,GD_SOULCOLD));
- guild_guildaura_refresh(sd,GD_HAWKEYES,guild_checkskill(sd->state.gmaster_flag,GD_HAWKEYES));
- }
-
- if( sd->state.vending ) { /* show we have a vending */
- clif_openvending(sd,sd->bl.id,sd->vending);
- clif_showvendingboard(&sd->bl,sd->message,0);
- }
-
- if(map[sd->bl.m].flag.loadevent) // Lance
- npc_script_event(sd, NPCE_LOADMAP);
-
- if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobexp(sd))
- clif_status_load(&sd->bl, SI_DEVIL, 1); //blindness [Komurka]
-
- if (sd->sc.opt2) //Client loses these on warp.
- clif_changeoption(&sd->bl);
-
- clif_weather_check(sd);
-
- // For automatic triggering of NPCs after map loading (so you don't need to walk 1 step first)
- if (map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNPC))
- npc_touch_areanpc(sd,sd->bl.m,sd->bl.x,sd->bl.y);
- else
- sd->areanpc_id = 0;
-
- /* it broke at some point (e.g. during a crash), so we make it visibly dead again. */
- if( !sd->status.hp && !pc_isdead(sd) && status_isdead(&sd->bl) )
- pc_setdead(sd);
-
- // If player is dead, and is spawned (such as @refresh) send death packet. [Valaris]
- if(pc_isdead(sd))
- clif_clearunit_area(&sd->bl, CLR_DEAD);
- else {
- skill_usave_trigger(sd);
- clif_changed_dir(&sd->bl, SELF);
- }
-
-// Trigger skill effects if you appear standing on them
- if(!battle_config.pc_invincible_time)
- skill_unit_move(&sd->bl,gettick(),1);
-}
-
-
-/// Server's tick (ZC_NOTIFY_TIME).
-/// 007f <time>.L
-void clif_notify_time(struct map_session_data* sd, unsigned long time)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x7f));
- WFIFOW(fd,0) = 0x7f;
- WFIFOL(fd,2) = time;
- WFIFOSET(fd,packet_len(0x7f));
-}
-
-
-/// Request for server's tick.
-/// 007e <client tick>.L (CZ_REQUEST_TIME)
-/// 0360 <client tick>.L (CZ_REQUEST_TIME2)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_TickSend(int fd, struct map_session_data *sd)
-{
- sd->client_tick = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
-
- clif_notify_time(sd, gettick());
-}
-
-
-/// Sends hotkey bar.
-/// 02b9 { <is skill>.B <id>.L <count>.W }*27 (ZC_SHORTCUT_KEY_LIST)
-/// 07d9 { <is skill>.B <id>.L <count>.W }*36 (ZC_SHORTCUT_KEY_LIST_V2, PACKETVER >= 20090603)
-/// 07d9 { <is skill>.B <id>.L <count>.W }*38 (ZC_SHORTCUT_KEY_LIST_V2, PACKETVER >= 20090617)
-void clif_hotkeys_send(struct map_session_data *sd) {
-#ifdef HOTKEY_SAVING
- const int fd = sd->fd;
- int i;
-#if PACKETVER < 20090603
- const int cmd = 0x2b9;
-#else
- const int cmd = 0x7d9;
-#endif
- if (!fd) return;
- WFIFOHEAD(fd, 2+MAX_HOTKEYS*7);
- WFIFOW(fd, 0) = cmd;
- for(i = 0; i < MAX_HOTKEYS; i++) {
- WFIFOB(fd, 2 + 0 + i * 7) = sd->status.hotkeys[i].type; // type: 0: item, 1: skill
- WFIFOL(fd, 2 + 1 + i * 7) = sd->status.hotkeys[i].id; // item or skill ID
- WFIFOW(fd, 2 + 5 + i * 7) = sd->status.hotkeys[i].lv; // skill level
- }
- WFIFOSET(fd, packet_len(cmd));
-#endif
-}
-
-
-/// Request to update a position on the hotkey bar (CZ_SHORTCUT_KEY_CHANGE).
-/// 02ba <index>.W <is skill>.B <id>.L <count>.W
-void clif_parse_Hotkey(int fd, struct map_session_data *sd) {
-#ifdef HOTKEY_SAVING
- unsigned short idx;
- int cmd;
-
- cmd = RFIFOW(fd, 0);
- idx = RFIFOW(fd, packet_db[sd->packet_ver][cmd].pos[0]);
- if (idx >= MAX_HOTKEYS) return;
-
- sd->status.hotkeys[idx].type = RFIFOB(fd, packet_db[sd->packet_ver][cmd].pos[1]);
- sd->status.hotkeys[idx].id = RFIFOL(fd, packet_db[sd->packet_ver][cmd].pos[2]);
- sd->status.hotkeys[idx].lv = RFIFOW(fd, packet_db[sd->packet_ver][cmd].pos[3]);
-#endif
-}
-
-
-/// Displays cast-like progress bar (ZC_PROGRESS).
-/// 02f0 <color>.L <time>.L
-void clif_progressbar(struct map_session_data * sd, unsigned long color, unsigned int second)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x2f0));
- WFIFOW(fd,0) = 0x2f0;
- WFIFOL(fd,2) = color;
- WFIFOL(fd,6) = second;
- WFIFOSET(fd,packet_len(0x2f0));
-}
-
-
-/// Removes an ongoing progress bar (ZC_PROGRESS_CANCEL).
-/// 02f2
-void clif_progressbar_abort(struct map_session_data * sd)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x2f2));
- WFIFOW(fd,0) = 0x2f2;
- WFIFOSET(fd,packet_len(0x2f2));
-}
-
-
-/// Notification from the client, that the progress bar has reached 100% (CZ_PROGRESS).
-/// 02f1
-void clif_parse_progressbar(int fd, struct map_session_data * sd)
-{
- int npc_id = sd->progressbar.npc_id;
-
- if( gettick() < sd->progressbar.timeout && sd->st )
- sd->st->state = END;
-
- sd->progressbar.npc_id = sd->progressbar.timeout = 0;
- npc_scriptcont(sd, npc_id);
-}
-
-
-/// Request to walk to a certain position on the current map.
-/// 0085 <dest>.3B (CZ_REQUEST_MOVE)
-/// 035f <dest>.3B (CZ_REQUEST_MOVE2)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_WalkToXY(int fd, struct map_session_data *sd)
-{
- short x, y;
-
- if (pc_isdead(sd)) {
- clif_clearunit_area(&sd->bl, CLR_DEAD);
- return;
- }
-
- if (sd->sc.opt1 && ( sd->sc.opt1 == OPT1_STONEWAIT || sd->sc.opt1 == OPT1_BURNING ))
- ; //You CAN walk on this OPT1 value.
- else if( sd->progressbar.npc_id )
- clif_progressbar_abort(sd);
- else if (pc_cant_act(sd))
- return;
-
- if(sd->sc.data[SC_RUN] || sd->sc.data[SC_WUGDASH])
- return;
-
- pc_delinvincibletimer(sd);
-
- RFIFOPOS(fd, packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0], &x, &y, NULL);
-
- //Set last idle time... [Skotlex]
- sd->idletime = last_tick;
-
- unit_walktoxy(&sd->bl, x, y, 4);
-}
-
-
-/// Notification about the result of a disconnect request (ZC_ACK_REQ_DISCONNECT).
-/// 018b <result>.W
-/// result:
-/// 0 = disconnect (quit)
-/// 1 = cannot disconnect (wait 10 seconds)
-/// ? = ignored
-void clif_disconnect_ack(struct map_session_data* sd, short result)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x18b));
- WFIFOW(fd,0) = 0x18b;
- WFIFOW(fd,2) = result;
- WFIFOSET(fd,packet_len(0x18b));
-}
-
-
-/// Request to disconnect from server (CZ_REQ_DISCONNECT).
-/// 018a <type>.W
-/// type:
-/// 0 = quit
-void clif_parse_QuitGame(int fd, struct map_session_data *sd)
-{
- /* Rovert's prevent logout option fixed [Valaris] */
- if( !sd->sc.data[SC_CLOAKING] && !sd->sc.data[SC_HIDING] && !sd->sc.data[SC_CHASEWALK] && !sd->sc.data[SC_CLOAKINGEXCEED] &&
- (!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout) )
- {
- set_eof(fd);
- clif_disconnect_ack(sd, 0);
- } else {
- clif_disconnect_ack(sd, 1);
- }
-}
-
-
-/// Requesting unit's name.
-/// 0094 <id>.L (CZ_REQNAME)
-/// 0368 <id>.L (CZ_REQNAME2)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_GetCharNameRequest(int fd, struct map_session_data *sd)
-{
- int id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
- struct block_list* bl;
- //struct status_change *sc;
-
- if( id < 0 && -id == sd->bl.id ) // for disguises [Valaris]
- id = sd->bl.id;
-
- bl = map_id2bl(id);
- if( bl == NULL )
- return; // Lagged clients could request names of already gone mobs/players. [Skotlex]
-
- if( sd->bl.m != bl->m || !check_distance_bl(&sd->bl, bl, AREA_SIZE) )
- return; // Block namerequests past view range
-
- // 'see people in GM hide' cheat detection
- /* disabled due to false positives (network lag + request name of char that's about to hide = race condition)
- sc = status_get_sc(bl);
- if (sc && sc->option&OPTION_INVISIBLE && !disguised(bl) &&
- bl->type != BL_NPC && //Skip hidden NPCs which can be seen using Maya Purple
- pc_get_group_level(sd) < battle_config.hack_info_GM_level
- ) {
- char gm_msg[256];
- sprintf(gm_msg, "Hack on NameRequest: character '%s' (account: %d) requested the name of an invisible target (id: %d).\n", sd->status.name, sd->status.account_id, id);
- ShowWarning(gm_msg);
- // information is sent to all online GMs
- intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, gm_msg);
- return;
- }
- */
-
- clif_charnameack(fd, bl);
-}
-
-
-/// Validates and processes global messages
-/// 008c <packet len>.W <text>.?B (<name> : <message>) 00 (CZ_REQUEST_CHAT)
-/// There are various variants of this packet.
-void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
-{
- const char* text = (char*)RFIFOP(fd,4);
- int textlen = RFIFOW(fd,2) - 4;
-
- char *name, *message, *fakename = NULL;
- int namelen, messagelen;
-
- bool is_fake;
-
- // validate packet and retrieve name and message
- if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
- return;
-
- if( is_atcommand(fd, sd, message, 1) )
- return;
-
- if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) )
- return;
-
- if( battle_config.min_chat_delay )
- { //[Skotlex]
- if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0)
- return;
- sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
- }
- /**
- * Fake Name Design by FatalEror (bug report #9)
- **/
- if( ( is_fake = ( sd->fakename[0] ) ) ) {
- fakename = (char*) aMalloc(strlen(sd->fakename)+messagelen+3);
- strcpy(fakename, sd->fakename);
- strcat(fakename, " : ");
- strcat(fakename, message);
- textlen = strlen(fakename) + 1;
- }
- // send message to others (using the send buffer for temp. storage)
- WFIFOHEAD(fd, 8 + textlen);
- WFIFOW(fd,0) = 0x8d;
- WFIFOW(fd,2) = 8 + textlen;
- WFIFOL(fd,4) = sd->bl.id;
- safestrncpy((char*)WFIFOP(fd,8), is_fake ? fakename : text, textlen);
- //FIXME: chat has range of 9 only
- clif_send(WFIFOP(fd,0), WFIFOW(fd,2), &sd->bl, sd->chatID ? CHAT_WOS : AREA_CHAT_WOC);
-
- // send back message to the speaker
- if( is_fake ) {
- WFIFOW(fd,0) = 0x8e;
- WFIFOW(fd,2) = textlen + 4;
- safestrncpy((char*)WFIFOP(fd,4), fakename, textlen);
- aFree(fakename);
- } else {
- memcpy(WFIFOP(fd,0), RFIFOP(fd,0), RFIFOW(fd,2));
- WFIFOW(fd,0) = 0x8e;
- }
- WFIFOSET(fd, WFIFOW(fd,2));
-#ifdef PCRE_SUPPORT
- // trigger listening npcs
- map_foreachinrange(npc_chat_sub, &sd->bl, AREA_SIZE, BL_NPC, text, textlen, &sd->bl);
-#endif
-
- // Chat logging type 'O' / Global Chat
- log_chat(LOG_CHAT_GLOBAL, 0, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, message);
-}
-
-
-/// /mm /mapmove (as @rura GM command) (CZ_MOVETO_MAP).
-/// Request to warp to a map on given coordinates.
-/// 0140 <map name>.16B <x>.W <y>.W
-void clif_parse_MapMove(int fd, struct map_session_data *sd)
-{
- char command[MAP_NAME_LENGTH_EXT+25];
- char* map_name;
-
- map_name = (char*)RFIFOP(fd,2);
- map_name[MAP_NAME_LENGTH_EXT-1]='\0';
- sprintf(command, "%cmapmove %s %d %d", atcommand_symbol, map_name, RFIFOW(fd,18), RFIFOW(fd,20));
- is_atcommand(fd, sd, command, 1);
-}
-
-
-/// Updates body and head direction of an object (ZC_CHANGE_DIRECTION).
-/// 009c <id>.L <head dir>.W <dir>.B
-/// head dir:
-/// 0 = straight
-/// 1 = turned CW
-/// 2 = turned CCW
-/// dir:
-/// 0 = north
-/// 1 = northwest
-/// 2 = west
-/// 3 = southwest
-/// 4 = south
-/// 5 = southeast
-/// 6 = east
-/// 7 = northeast
-void clif_changed_dir(struct block_list *bl, enum send_target target)
-{
- unsigned char buf[64];
-
- WBUFW(buf,0) = 0x9c;
- WBUFL(buf,2) = bl->id;
- WBUFW(buf,6) = bl->type==BL_PC?((TBL_PC*)bl)->head_dir:0;
- WBUFB(buf,8) = unit_getdir(bl);
-
- clif_send(buf, packet_len(0x9c), bl, target);
-
- if (disguised(bl)) {
- WBUFL(buf,2) = -bl->id;
- WBUFW(buf,6) = 0;
- clif_send(buf, packet_len(0x9c), bl, SELF);
- }
-}
-
-
-/// Request to change own body and head direction.
-/// 009b <head dir>.W <dir>.B (CZ_CHANGE_DIRECTION)
-/// 0361 <head dir>.W <dir>.B (CZ_CHANGE_DIRECTION2)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_ChangeDir(int fd, struct map_session_data *sd)
-{
- unsigned char headdir, dir;
-
- headdir = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
- dir = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
- pc_setdir(sd, dir, headdir);
-
- clif_changed_dir(&sd->bl, AREA_WOS);
-}
-
-
-/// Request to show an emotion (CZ_REQ_EMOTION).
-/// 00bf <type>.B
-/// type:
-/// @see enum emotion_type
-void clif_parse_Emotion(int fd, struct map_session_data *sd)
-{
- int emoticon = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
-
- if (battle_config.basic_skill_check == 0 || pc_checkskill(sd, NV_BASIC) >= 2) {
- if (emoticon == E_MUTE) {// prevent use of the mute emote [Valaris]
- clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 1);
- return;
- }
- // fix flood of emotion icon (ro-proxy): flood only the hacker player
- if (sd->emotionlasttime + 1 >= time(NULL)) { // not more than 1 per second
- sd->emotionlasttime = time(NULL);
- clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 1);
- return;
- }
- sd->emotionlasttime = time(NULL);
-
- if(battle_config.client_reshuffle_dice && emoticon>=E_DICE1 && emoticon<=E_DICE6)
- {// re-roll dice
- emoticon = rnd()%6+E_DICE1;
- }
-
- clif_emotion(&sd->bl, emoticon);
- } else
- clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 1);
-}
-
-
-/// Amount of currently online players, reply to /w /who (ZC_USER_COUNT).
-/// 00c2 <count>.L
-void clif_user_count(struct map_session_data* sd, int count)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0xc2));
- WFIFOW(fd,0) = 0xc2;
- WFIFOL(fd,2) = count;
- WFIFOSET(fd,packet_len(0xc2));
-}
-
-
-/// /w /who (CZ_REQ_USER_COUNT).
-/// Request to display amount of currently connected players.
-/// 00c1
-void clif_parse_HowManyConnections(int fd, struct map_session_data *sd)
-{
- clif_user_count(sd, map_getusers());
-}
-
-
-void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type, int target_id, unsigned int tick)
-{
- if (pc_isdead(sd)) {
- clif_clearunit_area(&sd->bl, CLR_DEAD);
- return;
- }
-
- if (sd->sc.count &&
- (sd->sc.data[SC_TRICKDEAD] ||
- sd->sc.data[SC_AUTOCOUNTER] ||
- sd->sc.data[SC_BLADESTOP] ||
- sd->sc.data[SC__MANHOLE] ||
- sd->sc.data[SC_CURSEDCIRCLE_ATKER] ||
- sd->sc.data[SC_CURSEDCIRCLE_TARGET] ))
- return;
-
- pc_stop_walking(sd, 1);
- pc_stop_attack(sd);
-
- if(target_id<0 && -target_id == sd->bl.id) // for disguises [Valaris]
- target_id = sd->bl.id;
-
- switch(action_type)
- {
- case 0x00: // once attack
- case 0x07: // continuous attack
-
- if( pc_cant_act(sd) || sd->sc.option&OPTION_HIDE )
- return;
-
- if( sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER) )
- return;
-
- if( sd->sc.data[SC_BASILICA] || sd->sc.data[SC__SHADOWFORM] )
- return;
-
- if (!battle_config.sdelay_attack_enable && pc_checkskill(sd, SA_FREECAST) <= 0) {
- if (DIFF_TICK(tick, sd->ud.canact_tick) < 0) {
- clif_skill_fail(sd, 1, USESKILL_FAIL_SKILLINTERVAL, 0);
- return;
- }
- }
-
- pc_delinvincibletimer(sd);
- sd->idletime = last_tick;
- unit_attack(&sd->bl, target_id, action_type != 0);
- break;
- case 0x02: // sitdown
- if (battle_config.basic_skill_check && pc_checkskill(sd, NV_BASIC) < 3) {
- clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 2);
- break;
- }
-
- if(pc_issit(sd)) {
- //Bugged client? Just refresh them.
- clif_sitting(&sd->bl);
- return;
- }
-
- if (sd->ud.skilltimer != INVALID_TIMER || (sd->sc.opt1 && sd->sc.opt1 != OPT1_BURNING ))
- break;
-
- if (sd->sc.count && (
- sd->sc.data[SC_DANCING] ||
- (sd->sc.data[SC_GRAVITATION] && sd->sc.data[SC_GRAVITATION]->val3 == BCT_SELF)
- )) //No sitting during these states either.
- break;
-
- pc_setsit(sd);
- skill_sit(sd,1);
- clif_sitting(&sd->bl);
- break;
- case 0x03: // standup
- if (!pc_issit(sd)) {
- //Bugged client? Just refresh them.
- clif_standing(&sd->bl);
- return;
- }
- pc_setstand(sd);
- skill_sit(sd,0);
- clif_standing(&sd->bl);
- break;
- }
-}
-
-
-/// Request for an action.
-/// 0089 <target id>.L <action>.B (CZ_REQUEST_ACT)
-/// 0437 <target id>.L <action>.B (CZ_REQUEST_ACT2)
-/// action:
-/// 0 = attack
-/// 1 = pick up item
-/// 2 = sit down
-/// 3 = stand up
-/// 7 = continous attack
-/// 12 = (touch skill?)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_ActionRequest(int fd, struct map_session_data *sd)
-{
- clif_parse_ActionRequest_sub(sd,
- RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]),
- RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]),
- gettick()
- );
-}
-
-
-/// Response to the death/system menu (CZ_RESTART).
-/// 00b2 <type>.B
-/// type:
-/// 0 = restart (respawn)
-/// 1 = char-select (disconnect)
-void clif_parse_Restart(int fd, struct map_session_data *sd)
-{
- switch(RFIFOB(fd,2)) {
- case 0x00:
- pc_respawn(sd,CLR_RESPAWN);
- break;
- case 0x01:
- /* Rovert's Prevent logout option - Fixed [Valaris] */
- if( !sd->sc.data[SC_CLOAKING] && !sd->sc.data[SC_HIDING] && !sd->sc.data[SC_CHASEWALK] && !sd->sc.data[SC_CLOAKINGEXCEED] &&
- (!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout) )
- { //Send to char-server for character selection.
- chrif_charselectreq(sd, session[fd]->client_addr);
- } else {
- clif_disconnect_ack(sd, 1);
- }
- break;
- }
-}
-
-
-/// Validates and processes whispered messages (CZ_WHISPER).
-/// 0096 <packet len>.W <nick>.24B <message>.?B
-void clif_parse_WisMessage(int fd, struct map_session_data* sd)
-{
- struct map_session_data* dstsd;
- int i;
-
- char *target, *message;
- int namelen, messagelen;
-
- // validate packet and retrieve name and message
- if( !clif_process_message(sd, 1, &target, &namelen, &message, &messagelen) )
- return;
-
- if ( is_atcommand(fd, sd, message, 1) )
- return;
-
- if (sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT))
- return;
-
- if (battle_config.min_chat_delay) { //[Skotlex]
- if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0) {
- return;
- }
- sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
- }
-
- // Chat logging type 'W' / Whisper
- log_chat(LOG_CHAT_WHISPER, 0, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, target, message);
-
- //-------------------------------------------------------//
- // Lordalfa - Paperboy - To whisper NPC commands //
- //-------------------------------------------------------//
- if (target[0] && (strncasecmp(target,"NPC:",4) == 0) && (strlen(target) > 4))
- {
- char* str = target+4; //Skip the NPC: string part.
- struct npc_data* npc;
- if ((npc = npc_name2id(str))) {
- char split_data[NUM_WHISPER_VAR][CHAT_SIZE_MAX];
- char *split;
- char output[256];
-
- str = message;
- // skip codepage indicator, if detected
- if( str[0] == '|' && strlen(str) >= 4 )
- str += 3;
- for( i = 0; i < NUM_WHISPER_VAR; ++i ) {// Splits the message using '#' as separators
- split = strchr(str,'#');
- if( split == NULL ) { // use the remaining string
- safestrncpy(split_data[i], str, ARRAYLENGTH(split_data[i]));
- for( ++i; i < NUM_WHISPER_VAR; ++i )
- split_data[i][0] = '\0';
- break;
- }
- *split = '\0';
- safestrncpy(split_data[i], str, ARRAYLENGTH(split_data[i]));
- str = split+1;
- }
-
- for( i = 0; i < NUM_WHISPER_VAR; ++i ) {
- sprintf(output, "@whispervar%d$", i);
- set_var(sd,output,(char *) split_data[i]);
- }
-
- sprintf(output, "%s::OnWhisperGlobal", npc->exname);
- npc_event(sd,output,0); // Calls the NPC label
-
- return;
- }
- } else if(strcmpi(target, main_chat_nick) == 0) { // Main chat [LuzZza]
- if(!sd->state.mainchat)
- clif_displaymessage(fd, msg_txt(388)); // You should enable main chat with "@main on" command.
- else {
- // send the main message using inter-server system
- intif_main_message( sd, message );
- }
-
- return;
- }
-
- // searching destination character
- dstsd = map_nick2sd(target);
-
- if (dstsd == NULL || strcmp(dstsd->status.name, target) != 0) {
- // player is not on this map-server
- // At this point, don't send wisp/page if it's not exactly the same name, because (example)
- // if there are 'Test' player on an other map-server and 'test' player on this map-server,
- // and if we ask for 'Test', we must not contact 'test' player
- // so, we send information to inter-server, which is the only one which decide (and copy correct name).
- intif_wis_message(sd, target, message, messagelen);
- return;
- }
-
- // if player ignores everyone
- if (dstsd->state.ignoreAll) {
- if (dstsd->sc.option & OPTION_INVISIBLE && pc_get_group_level(sd) < pc_get_group_level(dstsd))
- clif_wis_end(fd, 1); // 1: target character is not loged in
- else
- clif_wis_end(fd, 3); // 3: everyone ignored by target
- return;
- }
-
- // if player is autotrading
- if( dstsd->state.autotrade == 1 ) {
- char output[256];
- sprintf(output, "%s is in autotrade mode and cannot receive whispered messages.", dstsd->status.name);
- clif_wis_message(fd, wisp_server_name, output, strlen(output) + 1);
- return;
- }
-
- // if player ignores the source character
- ARR_FIND(0, MAX_IGNORE_LIST, i, dstsd->ignore[i].name[0] == '\0' || strcmp(dstsd->ignore[i].name, sd->status.name) == 0);
- if(i < MAX_IGNORE_LIST && dstsd->ignore[i].name[0] != '\0') { // source char present in ignore list
- clif_wis_end(fd, 2); // 2: ignored by target
- return;
- }
-
- // notify sender of success
- clif_wis_end(fd, 0); // 0: success to send wisper
-
- // Normal message
- clif_wis_message(dstsd->fd, sd->status.name, message, messagelen);
-}
-
-
-/// /b /nb (CZ_BROADCAST).
-/// Request to broadcast a message on whole server.
-/// 0099 <packet len>.W <text>.?B 00
-void clif_parse_Broadcast(int fd, struct map_session_data* sd) {
- char command[CHAT_SIZE_MAX+11];
- char* msg = (char*)RFIFOP(fd,4);
- unsigned int len = RFIFOW(fd,2)-4;
-
- // as the length varies depending on the command used, just block unreasonably long strings
- mes_len_check(msg, len, CHAT_SIZE_MAX);
-
- sprintf(command, "%ckami %s", atcommand_symbol, msg);
- is_atcommand(fd, sd, command, 1);
-}
-
-
-/// Request to pick up an item.
-/// 009f <id>.L (CZ_ITEM_PICKUP)
-/// 0362 <id>.L (CZ_ITEM_PICKUP2)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_TakeItem(int fd, struct map_session_data *sd)
-{
- struct flooritem_data *fitem;
- int map_object_id;
-
- map_object_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
-
- fitem = (struct flooritem_data*)map_id2bl(map_object_id);
-
- do {
- if (pc_isdead(sd)) {
- clif_clearunit_area(&sd->bl, CLR_DEAD);
- break;
- }
-
- if (fitem == NULL || fitem->bl.type != BL_ITEM || fitem->bl.m != sd->bl.m)
- break;
-
- if( sd->sc.cant.pickup )
- break;
-
- if (pc_cant_act(sd))
- break;
-
- if (!pc_takeitem(sd, fitem))
- break;
-
- return;
- } while (0);
- // Client REQUIRES a fail packet or you can no longer pick items.
- clif_additem(sd,0,0,6);
-}
-
-
-/// Request to drop an item.
-/// 00a2 <index>.W <amount>.W (CZ_ITEM_THROW)
-/// 0363 <index>.W <amount>.W (CZ_ITEM_THROW2)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_DropItem(int fd, struct map_session_data *sd)
-{
- int item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
- int item_amount = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
-
- for(;;) {
- if (pc_isdead(sd))
- break;
-
- if (pc_cant_act(sd))
- break;
-
- if (sd->sc.count && (
- sd->sc.data[SC_AUTOCOUNTER] ||
- sd->sc.data[SC_BLADESTOP] ||
- (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOITEM)
- ))
- break;
-
- if (!pc_dropitem(sd, item_index, item_amount))
- break;
-
- return;
- }
-
- //Because the client does not like being ignored.
- clif_dropitem(sd, item_index,0);
-}
-
-
-/// Request to use an item.
-/// 00a7 <index>.W <account id>.L (CZ_USE_ITEM)
-/// 0439 <index>.W <account id>.L (CZ_USE_ITEM2)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_UseItem(int fd, struct map_session_data *sd)
-{
- int n;
-
- if (pc_isdead(sd)) {
- clif_clearunit_area(&sd->bl, CLR_DEAD);
- return;
- }
-
- //This flag enables you to use items while in an NPC. [Skotlex]
- if (sd->npc_id) {
- if (sd->npc_id != sd->npc_item_flag)
- return;
- }
- else if (pc_istrading(sd))
- return;
-
- //Whether the item is used or not is irrelevant, the char ain't idle. [Skotlex]
- sd->idletime = last_tick;
- n = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
-
- if(n <0 || n >= MAX_INVENTORY)
- return;
- if (!pc_useitem(sd,n))
- clif_useitemack(sd,n,0,false); //Send an empty ack packet or the client gets stuck.
-}
-
-
-/// Request to equip an item (CZ_REQ_WEAR_EQUIP).
-/// 00a9 <index>.W <position>.W
-void clif_parse_EquipItem(int fd,struct map_session_data *sd)
-{
- int index;
-
- if(pc_isdead(sd)) {
- clif_clearunit_area(&sd->bl,CLR_DEAD);
- return;
- }
- index = RFIFOW(fd,2)-2;
- if (index < 0 || index >= MAX_INVENTORY)
- return; //Out of bounds check.
-
- if(sd->npc_id) {
- if (sd->npc_id != sd->npc_item_flag)
- return;
- } else if (sd->state.storage_flag || sd->sc.opt1)
- ; //You can equip/unequip stuff while storage is open/under status changes
- else if (pc_cant_act(sd))
- return;
-
- if(!sd->status.inventory[index].identify) {
- clif_equipitemack(sd,index,0,0); // fail
- return;
- }
-
- if(!sd->inventory_data[index])
- return;
-
- if(sd->inventory_data[index]->type == IT_PETARMOR){
- pet_equipitem(sd,index);
- return;
- }
-
- //Client doesn't send the position for ammo.
- if(sd->inventory_data[index]->type == IT_AMMO)
- pc_equipitem(sd,index,EQP_AMMO);
- else
- pc_equipitem(sd,index,RFIFOW(fd,4));
-}
-
-
-/// Request to take off an equip (CZ_REQ_TAKEOFF_EQUIP).
-/// 00ab <index>.W
-void clif_parse_UnequipItem(int fd,struct map_session_data *sd)
-{
- int index;
-
- if(pc_isdead(sd)) {
- clif_clearunit_area(&sd->bl,CLR_DEAD);
- return;
- }
-
- if (sd->state.storage_flag || sd->sc.opt1)
- ; //You can equip/unequip stuff while storage is open/under status changes
- else if (pc_cant_act(sd))
- return;
-
- index = RFIFOW(fd,2)-2;
-
- pc_unequipitem(sd,index,1);
-}
-
-
-/// Request to start a conversation with an NPC (CZ_CONTACTNPC).
-/// 0090 <id>.L <type>.B
-/// type:
-/// 1 = click
-void clif_parse_NpcClicked(int fd,struct map_session_data *sd)
-{
- struct block_list *bl;
-
- if(pc_isdead(sd)) {
- clif_clearunit_area(&sd->bl,CLR_DEAD);
- return;
- }
-
- if (pc_cant_act(sd))
- return;
-
- bl = map_id2bl(RFIFOL(fd,2));
- if (!bl) return;
- switch (bl->type) {
- case BL_MOB:
- case BL_PC:
- clif_parse_ActionRequest_sub(sd, 0x07, bl->id, gettick());
- break;
- case BL_NPC:
- if( bl->m != -1 )// the user can't click floating npcs directly (hack attempt)
- npc_click(sd,(TBL_NPC*)bl);
- break;
- }
-}
-
-
-/// Selection between buy/sell was made (CZ_ACK_SELECT_DEALTYPE).
-/// 00c5 <id>.L <type>.B
-/// type:
-/// 0 = buy
-/// 1 = sell
-void clif_parse_NpcBuySellSelected(int fd,struct map_session_data *sd)
-{
- if (sd->state.trading)
- return;
- npc_buysellsel(sd,RFIFOL(fd,2),RFIFOB(fd,6));
-}
-
-
-/// Notification about the result of a purchase attempt from an NPC shop (ZC_PC_PURCHASE_RESULT).
-/// 00ca <result>.B
-/// result:
-/// 0 = "The deal has successfully completed."
-/// 1 = "You do not have enough zeny."
-/// 2 = "You are over your Weight Limit."
-/// 3 = "Out of the maximum capacity, you have too many items."
-void clif_npc_buy_result(struct map_session_data* sd, unsigned char result)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0xca));
- WFIFOW(fd,0) = 0xca;
- WFIFOB(fd,2) = result;
- WFIFOSET(fd,packet_len(0xca));
-}
-
-
-/// Request to buy chosen items from npc shop (CZ_PC_PURCHASE_ITEMLIST).
-/// 00c8 <packet len>.W { <amount>.W <name id>.W }*
-void clif_parse_NpcBuyListSend(int fd, struct map_session_data* sd)
-{
- int n = (RFIFOW(fd,2)-4) /4;
- unsigned short* item_list = (unsigned short*)RFIFOP(fd,4);
- int result;
-
- if( sd->state.trading || !sd->npc_shopid )
- result = 1;
- else
- result = npc_buylist(sd,n,item_list);
-
- sd->npc_shopid = 0; //Clear shop data.
-
- clif_npc_buy_result(sd, result);
-}
-
-
-/// Notification about the result of a sell attempt to an NPC shop (ZC_PC_SELL_RESULT).
-/// 00cb <result>.B
-/// result:
-/// 0 = "The deal has successfully completed."
-/// 1 = "The deal has failed."
-void clif_npc_sell_result(struct map_session_data* sd, unsigned char result)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0xcb));
- WFIFOW(fd,0) = 0xcb;
- WFIFOB(fd,2) = result;
- WFIFOSET(fd,packet_len(0xcb));
-}
-
-
-/// Request to sell chosen items to npc shop (CZ_PC_SELL_ITEMLIST).
-/// 00c9 <packet len>.W { <index>.W <amount>.W }*
-void clif_parse_NpcSellListSend(int fd,struct map_session_data *sd)
-{
- int fail=0,n;
- unsigned short *item_list;
-
- n = (RFIFOW(fd,2)-4) /4;
- item_list = (unsigned short*)RFIFOP(fd,4);
-
- if (sd->state.trading || !sd->npc_shopid)
- fail = 1;
- else
- fail = npc_selllist(sd,n,item_list);
-
- sd->npc_shopid = 0; //Clear shop data.
-
- clif_npc_sell_result(sd, fail);
-}
-
-
-/// Chatroom creation request (CZ_CREATE_CHATROOM).
-/// 00d5 <packet len>.W <limit>.W <type>.B <passwd>.8B <title>.?B
-/// type:
-/// 0 = private
-/// 1 = public
-void clif_parse_CreateChatRoom(int fd, struct map_session_data* sd)
-{
- int len = RFIFOW(fd,2)-15;
- int limit = RFIFOW(fd,4);
- bool pub = (RFIFOB(fd,6) != 0);
- const char* password = (char*)RFIFOP(fd,7); //not zero-terminated
- const char* title = (char*)RFIFOP(fd,15); // not zero-terminated
- char s_password[CHATROOM_PASS_SIZE];
- char s_title[CHATROOM_TITLE_SIZE];
-
- if (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM)
- return;
- if(battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 4) {
- clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,3);
- return;
- }
-
- if( len <= 0 )
- return; // invalid input
-
- safestrncpy(s_password, password, CHATROOM_PASS_SIZE);
- safestrncpy(s_title, title, min(len+1,CHATROOM_TITLE_SIZE)); //NOTE: assumes that safestrncpy will not access the len+1'th byte
-
- chat_createpcchat(sd, s_title, s_password, limit, pub);
-}
-
-
-/// Chatroom join request (CZ_REQ_ENTER_ROOM).
-/// 00d9 <chat ID>.L <passwd>.8B
-void clif_parse_ChatAddMember(int fd, struct map_session_data* sd)
-{
- int chatid = RFIFOL(fd,2);
- const char* password = (char*)RFIFOP(fd,6); // not zero-terminated
-
- chat_joinchat(sd,chatid,password);
-}
-
-
-/// Chatroom properties adjustment request (CZ_CHANGE_CHATROOM).
-/// 00de <packet len>.W <limit>.W <type>.B <passwd>.8B <title>.?B
-/// type:
-/// 0 = private
-/// 1 = public
-void clif_parse_ChatRoomStatusChange(int fd, struct map_session_data* sd)
-{
- int len = RFIFOW(fd,2)-15;
- int limit = RFIFOW(fd,4);
- bool pub = (RFIFOB(fd,6) != 0);
- const char* password = (char*)RFIFOP(fd,7); // not zero-terminated
- const char* title = (char*)RFIFOP(fd,15); // not zero-terminated
- char s_password[CHATROOM_PASS_SIZE];
- char s_title[CHATROOM_TITLE_SIZE];
-
- if( len <= 0 )
- return; // invalid input
-
- safestrncpy(s_password, password, CHATROOM_PASS_SIZE);
- safestrncpy(s_title, title, min(len+1,CHATROOM_TITLE_SIZE)); //NOTE: assumes that safestrncpy will not access the len+1'th byte
-
- chat_changechatstatus(sd, s_title, s_password, limit, pub);
-}
-
-
-/// Request to change the chat room ownership (CZ_REQ_ROLE_CHANGE).
-/// 00e0 <role>.L <nick>.24B
-/// role:
-/// 0 = owner
-/// 1 = normal
-void clif_parse_ChangeChatOwner(int fd, struct map_session_data* sd)
-{
- chat_changechatowner(sd,(char*)RFIFOP(fd,6));
-}
-
-
-/// Request to expel a player from chat room (CZ_REQ_EXPEL_MEMBER).
-/// 00e2 <name>.24B
-void clif_parse_KickFromChat(int fd,struct map_session_data *sd)
-{
- chat_kickchat(sd,(char*)RFIFOP(fd,2));
-}
-
-
-/// Request to leave the current chatroom (CZ_EXIT_ROOM).
-/// 00e3
-void clif_parse_ChatLeave(int fd, struct map_session_data* sd)
-{
- chat_leavechat(sd,0);
-}
-
-
-//Handles notifying asker and rejecter of what has just ocurred.
-//Type is used to determine the correct msg_txt to use:
-//0:
-static void clif_noask_sub(struct map_session_data *src, struct map_session_data *target, int type)
-{
- const char* msg;
- char output[256];
- // Your request has been rejected by autoreject option.
- msg = msg_txt(392);
- clif_disp_onlyself(src, msg, strlen(msg));
- //Notice that a request was rejected.
- snprintf(output, 256, msg_txt(393+type), src->status.name, 256);
- clif_disp_onlyself(target, output, strlen(output));
-}
-
-
-/// Request to begin a trade (CZ_REQ_EXCHANGE_ITEM).
-/// 00e4 <account id>.L
-void clif_parse_TradeRequest(int fd,struct map_session_data *sd)
-{
- struct map_session_data *t_sd;
-
- t_sd = map_id2sd(RFIFOL(fd,2));
-
- if(!sd->chatID && pc_cant_act(sd))
- return; //You can trade while in a chatroom.
-
- // @noask [LuzZza]
- if(t_sd && t_sd->state.noask) {
- clif_noask_sub(sd, t_sd, 0);
- return;
- }
-
- if( battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 1)
- {
- clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,0);
- return;
- }
-
- trade_traderequest(sd,t_sd);
-}
-
-
-/// Answer to a trade request (CZ_ACK_EXCHANGE_ITEM).
-/// 00e6 <result>.B
-/// result:
-/// 3 = accepted
-/// 4 = rejected
-void clif_parse_TradeAck(int fd,struct map_session_data *sd)
-{
- trade_tradeack(sd,RFIFOB(fd,2));
-}
-
-
-/// Request to add an item to current trade (CZ_ADD_EXCHANGE_ITEM).
-/// 00e8 <index>.W <amount>.L
-void clif_parse_TradeAddItem(int fd,struct map_session_data *sd)
-{
- short index = RFIFOW(fd,2);
- int amount = RFIFOL(fd,4);
-
- if( index == 0 )
- trade_tradeaddzeny(sd, amount);
- else
- trade_tradeadditem(sd, index, (short)amount);
-}
-
-
-/// Request to lock items in current trade (CZ_CONCLUDE_EXCHANGE_ITEM).
-/// 00eb
-void clif_parse_TradeOk(int fd,struct map_session_data *sd)
-{
- trade_tradeok(sd);
-}
-
-
-/// Request to cancel current trade (CZ_CANCEL_EXCHANGE_ITEM).
-/// 00ed
-void clif_parse_TradeCancel(int fd,struct map_session_data *sd)
-{
- trade_tradecancel(sd);
-}
-
-
-/// Request to commit current trade (CZ_EXEC_EXCHANGE_ITEM).
-/// 00ef
-void clif_parse_TradeCommit(int fd,struct map_session_data *sd)
-{
- trade_tradecommit(sd);
-}
-
-
-/// Request to stop chasing/attacking an unit (CZ_CANCEL_LOCKON).
-/// 0118
-void clif_parse_StopAttack(int fd,struct map_session_data *sd)
-{
- pc_stop_attack(sd);
-}
-
-
-/// Request to move an item from inventory to cart (CZ_MOVE_ITEM_FROM_BODY_TO_CART).
-/// 0126 <index>.W <amount>.L
-void clif_parse_PutItemToCart(int fd,struct map_session_data *sd)
-{
- if (pc_istrading(sd))
- return;
- if (!pc_iscarton(sd))
- return;
- pc_putitemtocart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4));
-}
-
-
-/// Request to move an item from cart to inventory (CZ_MOVE_ITEM_FROM_CART_TO_BODY).
-/// 0127 <index>.W <amount>.L
-void clif_parse_GetItemFromCart(int fd,struct map_session_data *sd)
-{
- if (!pc_iscarton(sd))
- return;
- pc_getitemfromcart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4));
-}
-
-
-/// Request to remove cart/falcon/peco/dragon (CZ_REQ_CARTOFF).
-/// 012a
-void clif_parse_RemoveOption(int fd,struct map_session_data *sd)
-{
- /**
- * Attempts to remove these options when this function is called (will remove all available)
- **/
-#ifdef NEW_CARTS
- pc_setoption(sd,sd->sc.option&~(OPTION_RIDING|OPTION_FALCON|OPTION_DRAGON|OPTION_MADOGEAR));
- if( sd->sc.data[SC_PUSH_CART] )
- pc_setcart(sd,0);
-#else
- pc_setoption(sd,sd->sc.option&~(OPTION_CART|OPTION_RIDING|OPTION_FALCON|OPTION_DRAGON|OPTION_MADOGEAR));
-#endif
-}
-
-
-/// Request to change cart's visual look (CZ_REQ_CHANGECART).
-/// 01af <num>.W
-void clif_parse_ChangeCart(int fd,struct map_session_data *sd)
-{// TODO: State tracking?
- int type;
-
- if( sd && pc_checkskill(sd, MC_CHANGECART) < 1 )
- return;
-
- type = (int)RFIFOW(fd,2);
-#ifdef NEW_CARTS
- if( (type == 9 && sd->status.base_level > 131) ||
- (type == 8 && sd->status.base_level > 121) ||
- (type == 7 && sd->status.base_level > 111) ||
- (type == 6 && sd->status.base_level > 101) ||
- (type == 5 && sd->status.base_level > 90) ||
- (type == 4 && sd->status.base_level > 80) ||
- (type == 3 && sd->status.base_level > 65) ||
- (type == 2 && sd->status.base_level > 40) ||
- (type == 1))
-#else
- if( (type == 5 && sd->status.base_level > 90) ||
- (type == 4 && sd->status.base_level > 80) ||
- (type == 3 && sd->status.base_level > 65) ||
- (type == 2 && sd->status.base_level > 40) ||
- (type == 1))
-#endif
- pc_setcart(sd,type);
-}
-
-
-/// Request to increase status (CZ_STATUS_CHANGE).
-/// 00bb <status id>.W <amount>.B
-/// status id:
-/// SP_STR ~ SP_LUK
-/// amount:
-/// client sends always 1 for this, even when using /str+ and
-/// the like
-void clif_parse_StatusUp(int fd,struct map_session_data *sd)
-{
- pc_statusup(sd,RFIFOW(fd,2));
-}
-
-
-/// Request to increase level of a skill (CZ_UPGRADE_SKILLLEVEL).
-/// 0112 <skill id>.W
-void clif_parse_SkillUp(int fd,struct map_session_data *sd)
-{
- pc_skillup(sd,RFIFOW(fd,2));
-}
-
-static void clif_parse_UseSkillToId_homun(struct homun_data *hd, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, int target_id)
-{
- int lv;
-
- if( !hd )
- return;
- if( skillnotok_hom(skill_id, hd) )
- return;
- if( hd->bl.id != target_id && skill_get_inf(skill_id)&INF_SELF_SKILL )
- target_id = hd->bl.id;
- if( hd->ud.skilltimer != INVALID_TIMER )
- {
- if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) return;
- }
- else if( DIFF_TICK(tick, hd->ud.canact_tick) < 0 )
- return;
-
- lv = merc_hom_checkskill(hd, skill_id);
- if( skill_lv > lv )
- skill_lv = lv;
- if( skill_lv )
- unit_skilluse_id(&hd->bl, target_id, skill_id, skill_lv);
-}
-
-static void clif_parse_UseSkillToPos_homun(struct homun_data *hd, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, short x, short y, int skillmoreinfo)
-{
- int lv;
- if( !hd )
- return;
- if( skillnotok_hom(skill_id, hd) )
- return;
- if( hd->ud.skilltimer != INVALID_TIMER ) {
- if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) return;
- } else if( DIFF_TICK(tick, hd->ud.canact_tick) < 0 )
- return;
-
- if( hd->sc.data[SC_BASILICA] )
- return;
- lv = merc_hom_checkskill(hd, skill_id);
- if( skill_lv > lv )
- skill_lv = lv;
- if( skill_lv )
- unit_skilluse_pos(&hd->bl, x, y, skill_id, skill_lv);
-}
-
-static void clif_parse_UseSkillToId_mercenary(struct mercenary_data *md, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, int target_id)
-{
- int lv;
-
- if( !md )
- return;
- if( skillnotok_mercenary(skill_id, md) )
- return;
- if( md->bl.id != target_id && skill_get_inf(skill_id)&INF_SELF_SKILL )
- target_id = md->bl.id;
- if( md->ud.skilltimer != INVALID_TIMER )
- {
- if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) return;
- }
- else if( DIFF_TICK(tick, md->ud.canact_tick) < 0 )
- return;
-
- lv = mercenary_checkskill(md, skill_id);
- if( skill_lv > lv )
- skill_lv = lv;
- if( skill_lv )
- unit_skilluse_id(&md->bl, target_id, skill_id, skill_lv);
-}
-
-static void clif_parse_UseSkillToPos_mercenary(struct mercenary_data *md, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, short x, short y, int skillmoreinfo)
-{
- int lv;
- if( !md )
- return;
- if( skillnotok_mercenary(skill_id, md) )
- return;
- if( md->ud.skilltimer != INVALID_TIMER )
- return;
- if( DIFF_TICK(tick, md->ud.canact_tick) < 0 )
- {
- clif_skill_fail(md->master, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0);
- return;
- }
-
- if( md->sc.data[SC_BASILICA] )
- return;
- lv = mercenary_checkskill(md, skill_id);
- if( skill_lv > lv )
- skill_lv = lv;
- if( skill_lv )
- unit_skilluse_pos(&md->bl, x, y, skill_id, skill_lv);
-}
-
-
-/// Request to use a targeted skill.
-/// 0113 <skill lv>.W <skill id>.W <target id>.L (CZ_USE_SKILL)
-/// 0438 <skill lv>.W <skill id>.W <target id>.L (CZ_USE_SKILL2)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
-{
- uint16 skill_id, skill_lv;
- int tmp, target_id;
- unsigned int tick = gettick();
-
- skill_lv = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
- skill_id = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
- target_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]);
-
- if( skill_lv < 1 ) skill_lv = 1; //No clue, I have seen the client do this with guild skills :/ [Skotlex]
-
- tmp = skill_get_inf(skill_id);
- if (tmp&INF_GROUND_SKILL || !tmp)
- return; //Using a ground/passive skill on a target? WRONG.
-
- if( skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE + MAX_HOMUNSKILL )
- {
- clif_parse_UseSkillToId_homun(sd->hd, sd, tick, skill_id, skill_lv, target_id);
- return;
- }
-
- if( skill_id >= MC_SKILLBASE && skill_id < MC_SKILLBASE + MAX_MERCSKILL )
- {
- clif_parse_UseSkillToId_mercenary(sd->md, sd, tick, skill_id, skill_lv, target_id);
- return;
- }
-
- // Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
- sd->idletime = last_tick;
-
- if( pc_cant_act(sd) && skill_id != RK_REFRESH && !(skill_id == SR_GENTLETOUCH_CURE && (sd->sc.opt1 == OPT1_STONE || sd->sc.opt1 == OPT1_FREEZE || sd->sc.opt1 == OPT1_STUN)) )
- return;
- if( pc_issit(sd) )
- return;
-
- if( skillnotok(skill_id, sd) )
- return;
-
- if( sd->bl.id != target_id && tmp&INF_SELF_SKILL )
- target_id = sd->bl.id; // never trust the client
-
- if( target_id < 0 && -target_id == sd->bl.id ) // for disguises [Valaris]
- target_id = sd->bl.id;
-
- if( sd->ud.skilltimer != INVALID_TIMER )
- {
- if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST )
- return;
- }
- else if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 )
- {
- if( sd->skillitem != skill_id )
- {
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0);
- return;
- }
- }
-
- if( sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER) )
- return;
-
- if( sd->sc.data[SC_BASILICA] && (skill_id != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) )
- return; // On basilica only caster can use Basilica again to stop it.
-
- if( sd->menuskill_id ) {
- if( sd->menuskill_id == SA_TAMINGMONSTER ) {
- clif_menuskill_clear(sd); //Cancel pet capture.
- } else if( sd->menuskill_id != SA_AUTOSPELL )
- return; //Can't use skills while a menu is open.
- }
- if( sd->skillitem == skill_id ) {
- if( skill_lv != sd->skillitemlv )
- skill_lv = sd->skillitemlv;
- if( !(tmp&INF_SELF_SKILL) )
- pc_delinvincibletimer(sd); // Target skills thru items cancel invincibility. [Inkfish]
- unit_skilluse_id(&sd->bl, target_id, skill_id, skill_lv);
- return;
- }
-
- sd->skillitem = sd->skillitemlv = 0;
-
- if( skill_id >= GD_SKILLBASE ) {
- if( sd->state.gmaster_flag )
- skill_lv = guild_checkskill(sd->state.gmaster_flag, skill_id);
- else
- skill_lv = 0;
- } else {
- tmp = pc_checkskill(sd, skill_id);
- if( skill_lv > tmp )
- skill_lv = tmp;
- }
-
- pc_delinvincibletimer(sd);
-
- if( skill_lv )
- unit_skilluse_id(&sd->bl, target_id, skill_id, skill_lv);
-}
-
-/*==========================================
- * Client tells server he'd like to use AoE skill id 'skill_id' of level 'skill_lv' on 'x','y' location
- *------------------------------------------*/
-static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, uint16 skill_lv, uint16 skill_id, short x, short y, int skillmoreinfo)
-{
- unsigned int tick = gettick();
-
- if( !(skill_get_inf(skill_id)&INF_GROUND_SKILL) )
- return; //Using a target skill on the ground? WRONG.
-
- if( skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE + MAX_HOMUNSKILL ) {
- clif_parse_UseSkillToPos_homun(sd->hd, sd, tick, skill_id, skill_lv, x, y, skillmoreinfo);
- return;
- }
-
- if( skill_id >= MC_SKILLBASE && skill_id < MC_SKILLBASE + MAX_MERCSKILL )
- {
- clif_parse_UseSkillToPos_mercenary(sd->md, sd, tick, skill_id, skill_lv, x, y, skillmoreinfo);
- return;
- }
-
- //Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
- sd->idletime = last_tick;
-
- if( skillnotok(skill_id, sd) )
- return;
- if( skillmoreinfo != -1 )
- {
- if( pc_issit(sd) )
- {
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
- return;
- }
- //You can't use Graffiti/TalkieBox AND have a vending open, so this is safe.
- safestrncpy(sd->message, (char*)RFIFOP(fd,skillmoreinfo), MESSAGE_SIZE);
- }
-
- if( sd->ud.skilltimer != INVALID_TIMER )
- return;
-
- if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 ) {
- if( sd->skillitem != skill_id ) {
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0);
- return;
- }
- }
-
- if( sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER) )
- return;
-
- if( sd->sc.data[SC_BASILICA] && (skill_id != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) )
- return; // On basilica only caster can use Basilica again to stop it.
-
- if( sd->menuskill_id ) {
- if( sd->menuskill_id == SA_TAMINGMONSTER ) {
- clif_menuskill_clear(sd); //Cancel pet capture.
- } else if( sd->menuskill_id != SA_AUTOSPELL )
- return; //Can't use skills while a menu is open.
- }
-
- pc_delinvincibletimer(sd);
-
- if( sd->skillitem == skill_id ) {
- if( skill_lv != sd->skillitemlv )
- skill_lv = sd->skillitemlv;
- unit_skilluse_pos(&sd->bl, x, y, skill_id, skill_lv);
- } else {
- int lv;
- sd->skillitem = sd->skillitemlv = 0;
- if( (lv = pc_checkskill(sd, skill_id)) > 0 ) {
- if( skill_lv > lv )
- skill_lv = lv;
- unit_skilluse_pos(&sd->bl, x, y, skill_id,skill_lv);
- }
- }
-}
-
-
-/// Request to use a ground skill.
-/// 0116 <skill lv>.W <skill id>.W <x>.W <y>.W (CZ_USE_SKILL_TOGROUND)
-/// 0366 <skill lv>.W <skill id>.W <x>.W <y>.W (CZ_USE_SKILL_TOGROUND2)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_UseSkillToPos(int fd, struct map_session_data *sd)
-{
- if (pc_cant_act(sd))
- return;
- if (pc_issit(sd))
- return;
-
- clif_parse_UseSkillToPosSub(fd, sd,
- RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), //skill lv
- RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]), //skill num
- RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]), //pos x
- RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[3]), //pos y
- -1 //Skill more info.
- );
-}
-
-
-/// Request to use a ground skill with text.
-/// 0190 <skill lv>.W <skill id>.W <x>.W <y>.W <contents>.80B (CZ_USE_SKILL_TOGROUND_WITHTALKBOX)
-/// 0367 <skill lv>.W <skill id>.W <x>.W <y>.W <contents>.80B (CZ_USE_SKILL_TOGROUND_WITHTALKBOX2)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_UseSkillToPosMoreInfo(int fd, struct map_session_data *sd)
-{
- if (pc_cant_act(sd))
- return;
- if (pc_issit(sd))
- return;
-
- clif_parse_UseSkillToPosSub(fd, sd,
- RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), //Skill lv
- RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]), //Skill num
- RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]), //pos x
- RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[3]), //pos y
- packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[4] //skill more info
- );
-}
-
-
-/// Answer to map selection dialog (CZ_SELECT_WARPPOINT).
-/// 011b <skill id>.W <map name>.16B
-void clif_parse_UseSkillMap(int fd, struct map_session_data* sd)
-{
- uint16 skill_id = RFIFOW(fd,2);
- char map_name[MAP_NAME_LENGTH];
- mapindex_getmapname((char*)RFIFOP(fd,4), map_name);
-
- if(skill_id != sd->menuskill_id)
- return;
-
- if( pc_cant_act(sd) ) {
- clif_menuskill_clear(sd);
- return;
- }
-
- pc_delinvincibletimer(sd);
- skill_castend_map(sd,skill_id,map_name);
-}
-
-
-/// Request to set a memo on current map (CZ_REMEMBER_WARPPOINT).
-/// 011d
-void clif_parse_RequestMemo(int fd,struct map_session_data *sd)
-{
- if (!pc_isdead(sd))
- pc_memo(sd,-1);
-}
-
-
-/// Answer to pharmacy item selection dialog (CZ_REQMAKINGITEM).
-/// 018e <name id>.W { <material id>.W }*3
-void clif_parse_ProduceMix(int fd,struct map_session_data *sd)
-{
- switch( sd->menuskill_id ) {
- case -1:
- case AM_PHARMACY:
- case RK_RUNEMASTERY:
- case GC_RESEARCHNEWPOISON:
- break;
- default:
- return;
- }
- if (pc_istrading(sd)) {
- //Make it fail to avoid shop exploits where you sell something different than you see.
- clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0);
- clif_menuskill_clear(sd);
- return;
- }
- if( skill_can_produce_mix(sd,RFIFOW(fd,2),sd->menuskill_val, 1) )
- skill_produce_mix(sd,0,RFIFOW(fd,2),RFIFOW(fd,4),RFIFOW(fd,6),RFIFOW(fd,8), 1);
- clif_menuskill_clear(sd);
-}
-
-
-/// Answer to mixing item selection dialog (CZ_REQ_MAKINGITEM).
-/// 025b <mk type>.W <name id>.W
-/// mk type:
-/// 1 = cooking
-/// 2 = arrow
-/// 3 = elemental
-/// 4 = GN_MIX_COOKING
-/// 5 = GN_MAKEBOMB
-/// 6 = GN_S_PHARMACY
-void clif_parse_Cooking(int fd,struct map_session_data *sd) {
- int type = RFIFOW(fd,2);
- int nameid = RFIFOW(fd,4);
- int amount = sd->menuskill_val2?sd->menuskill_val2:1;
- if( type == 6 && sd->menuskill_id != GN_MIX_COOKING && sd->menuskill_id != GN_S_PHARMACY )
- return;
-
- if (pc_istrading(sd)) {
- //Make it fail to avoid shop exploits where you sell something different than you see.
- clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0);
- clif_menuskill_clear(sd);
- return;
- }
- if( skill_can_produce_mix(sd,nameid,sd->menuskill_val, amount) )
- skill_produce_mix(sd,sd->menuskill_id,nameid,0,0,0,amount);
- clif_menuskill_clear(sd);
-}
-
-
-/// Answer to repair weapon item selection dialog (CZ_REQ_ITEMREPAIR).
-/// 01fd <index>.W <name id>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W
-void clif_parse_RepairItem(int fd, struct map_session_data *sd)
-{
- if (sd->menuskill_id != BS_REPAIRWEAPON)
- return;
- if (pc_istrading(sd)) {
- //Make it fail to avoid shop exploits where you sell something different than you see.
- clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0);
- clif_menuskill_clear(sd);
- return;
- }
- skill_repairweapon(sd,RFIFOW(fd,2));
- clif_menuskill_clear(sd);
-}
-
-
-/// Answer to refine weapon item selection dialog (CZ_REQ_WEAPONREFINE).
-/// 0222 <index>.L
-void clif_parse_WeaponRefine(int fd, struct map_session_data *sd)
-{
- int idx;
-
- if (sd->menuskill_id != WS_WEAPONREFINE) //Packet exploit?
- return;
- if (pc_istrading(sd)) {
- //Make it fail to avoid shop exploits where you sell something different than you see.
- clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0);
- clif_menuskill_clear(sd);
- return;
- }
- idx = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
- skill_weaponrefine(sd, idx-2);
- clif_menuskill_clear(sd);
-}
-
-
-/// Answer to script menu dialog (CZ_CHOOSE_MENU).
-/// 00b8 <npc id>.L <choice>.B
-/// choice:
-/// 1~254 = menu item
-/// 255 = cancel
-/// NOTE: If there were more than 254 items in the list, choice
-/// overflows to choice%256.
-void clif_parse_NpcSelectMenu(int fd,struct map_session_data *sd)
-{
- int npc_id = RFIFOL(fd,2);
- uint8 select = RFIFOB(fd,6);
-
- if( (select > sd->npc_menu && select != 0xff) || select == 0 )
- {
- TBL_NPC* nd = map_id2nd(npc_id);
- ShowWarning("Invalid menu selection on npc %d:'%s' - got %d, valid range is [%d..%d] (player AID:%d, CID:%d, name:'%s')!\n", npc_id, (nd)?nd->name:"invalid npc id", select, 1, sd->npc_menu, sd->bl.id, sd->status.char_id, sd->status.name);
- clif_GM_kick(NULL,sd);
- return;
- }
-
- sd->npc_menu = select;
- npc_scriptcont(sd,npc_id);
-}
-
-
-/// NPC dialog 'next' click (CZ_REQ_NEXT_SCRIPT).
-/// 00b9 <npc id>.L
-void clif_parse_NpcNextClicked(int fd,struct map_session_data *sd)
-{
- npc_scriptcont(sd,RFIFOL(fd,2));
-}
-
-
-/// NPC numeric input dialog value (CZ_INPUT_EDITDLG).
-/// 0143 <npc id>.L <value>.L
-void clif_parse_NpcAmountInput(int fd,struct map_session_data *sd)
-{
- int npcid = RFIFOL(fd,2);
- int amount = (int)RFIFOL(fd,6);
-
- sd->npc_amount = amount;
- npc_scriptcont(sd, npcid);
-}
-
-
-/// NPC text input dialog value (CZ_INPUT_EDITDLGSTR).
-/// 01d5 <packet len>.W <npc id>.L <string>.?B
-void clif_parse_NpcStringInput(int fd, struct map_session_data* sd)
-{
- int message_len = RFIFOW(fd,2)-8;
- int npcid = RFIFOL(fd,4);
- const char* message = (char*)RFIFOP(fd,8);
-
- if( message_len <= 0 )
- return; // invalid input
-
- safestrncpy(sd->npc_str, message, min(message_len,CHATBOX_SIZE));
- npc_scriptcont(sd, npcid);
-}
-
-
-/// NPC dialog 'close' click (CZ_CLOSE_DIALOG).
-/// 0146 <npc id>.L
-void clif_parse_NpcCloseClicked(int fd,struct map_session_data *sd)
-{
- if (!sd->npc_id) //Avoid parsing anything when the script was done with. [Skotlex]
- return;
- npc_scriptcont(sd,RFIFOL(fd,2));
-}
-
-
-/// Answer to identify item selection dialog (CZ_REQ_ITEMIDENTIFY).
-/// 0178 <index>.W
-/// index:
-/// -1 = cancel
-void clif_parse_ItemIdentify(int fd,struct map_session_data *sd)
-{
- short idx = RFIFOW(fd,2);
-
- if (sd->menuskill_id != MC_IDENTIFY)
- return;
- if( idx == -1 ) {// cancel pressed
- clif_menuskill_clear(sd);
- return;
- }
- skill_identify(sd,idx-2);
- clif_menuskill_clear(sd);
-}
-
-
-/// Answer to arrow crafting item selection dialog (CZ_REQ_MAKINGARROW).
-/// 01ae <name id>.W
-void clif_parse_SelectArrow(int fd,struct map_session_data *sd)
-{
- if (pc_istrading(sd)) {
- //Make it fail to avoid shop exploits where you sell something different than you see.
- clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0);
- clif_menuskill_clear(sd);
- return;
- }
- switch( sd->menuskill_id ) {
- case AC_MAKINGARROW:
- skill_arrow_create(sd,RFIFOW(fd,2));
- break;
- case SA_CREATECON:
- skill_produce_mix(sd,SA_CREATECON,RFIFOW(fd,2),0,0,0, 1);
- break;
- case WL_READING_SB:
- skill_spellbook(sd,RFIFOW(fd,2));
- break;
- case GC_POISONINGWEAPON:
- skill_poisoningweapon(sd,RFIFOW(fd,2));
- break;
- case NC_MAGICDECOY:
- skill_magicdecoy(sd,RFIFOW(fd,2));
- break;
- }
-
- clif_menuskill_clear(sd);
-}
-
-
-/// Answer to SA_AUTOSPELL skill selection dialog (CZ_SELECTAUTOSPELL).
-/// 01ce <skill id>.L
-void clif_parse_AutoSpell(int fd,struct map_session_data *sd)
-{
- if (sd->menuskill_id != SA_AUTOSPELL)
- return;
- skill_autospell(sd,RFIFOL(fd,2));
- clif_menuskill_clear(sd);
-}
-
-
-/// Request to display item carding/composition list (CZ_REQ_ITEMCOMPOSITION_LIST).
-/// 017a <card index>.W
-void clif_parse_UseCard(int fd,struct map_session_data *sd)
-{
- if (sd->state.trading != 0)
- return;
- clif_use_card(sd,RFIFOW(fd,2)-2);
-}
-
-
-/// Answer to carding/composing item selection dialog (CZ_REQ_ITEMCOMPOSITION).
-/// 017c <card index>.W <equip index>.W
-void clif_parse_InsertCard(int fd,struct map_session_data *sd)
-{
- if (sd->state.trading != 0)
- return;
- pc_insert_card(sd,RFIFOW(fd,2)-2,RFIFOW(fd,4)-2);
-}
-
-
-/// Request of character's name by char ID.
-/// 0193 <char id>.L (CZ_REQNAME_BYGID)
-/// 0369 <char id>.L (CZ_REQNAME_BYGID2)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_SolveCharName(int fd, struct map_session_data *sd)
-{
- int charid;
-
- charid = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
- map_reqnickdb(sd, charid);
-}
-
-
-/// /resetskill /resetstate (CZ_RESET).
-/// Request to reset stats or skills.
-/// 0197 <type>.W
-/// type:
-/// 0 = state
-/// 1 = skill
-void clif_parse_ResetChar(int fd, struct map_session_data *sd) {
- char cmd[15];
-
- if( RFIFOW(fd,2) )
- sprintf(cmd,"%cresetskill",atcommand_symbol);
- else
- sprintf(cmd,"%cresetstat",atcommand_symbol);
-
- is_atcommand(fd, sd, cmd, 1);
-}
-
-
-/// /lb /nlb (CZ_LOCALBROADCAST).
-/// Request to broadcast a message on current map.
-/// 019c <packet len>.W <text>.?B
-void clif_parse_LocalBroadcast(int fd, struct map_session_data* sd)
-{
- char command[CHAT_SIZE_MAX+16];
- char* msg = (char*)RFIFOP(fd,4);
- unsigned int len = RFIFOW(fd,2)-4;
-
- // as the length varies depending on the command used, just block unreasonably long strings
- mes_len_check(msg, len, CHAT_SIZE_MAX);
-
- sprintf(command, "%clkami %s", atcommand_symbol, msg);
- is_atcommand(fd, sd, command, 1);
-}
-
-
-/// Request to move an item from inventory to storage.
-/// 00f3 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_BODY_TO_STORE)
-/// 0364 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_BODY_TO_STORE2)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_MoveToKafra(int fd, struct map_session_data *sd)
-{
- int item_index, item_amount;
-
- if (pc_istrading(sd))
- return;
-
- item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
- item_amount = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
- if (item_index < 0 || item_index >= MAX_INVENTORY || item_amount < 1)
- return;
-
- if (sd->state.storage_flag == 1)
- storage_storageadd(sd, item_index, item_amount);
- else
- if (sd->state.storage_flag == 2)
- storage_guild_storageadd(sd, item_index, item_amount);
-}
-
-
-/// Request to move an item from storage to inventory.
-/// 00f5 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_STORE_TO_BODY)
-/// 0365 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_STORE_TO_BODY2)
-/// There are various variants of this packet, some of them have padding between fields.
-void clif_parse_MoveFromKafra(int fd,struct map_session_data *sd)
-{
- int item_index, item_amount;
-
- item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-1;
- item_amount = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
-
- if (sd->state.storage_flag == 1)
- storage_storageget(sd, item_index, item_amount);
- else
- if(sd->state.storage_flag == 2)
- storage_guild_storageget(sd, item_index, item_amount);
-}
-
-
-/// Request to move an item from cart to storage (CZ_MOVE_ITEM_FROM_CART_TO_STORE).
-/// 0129 <index>.W <amount>.L
-void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd)
-{
- if( sd->state.vending )
- return;
- if (!pc_iscarton(sd))
- return;
-
- if (sd->state.storage_flag == 1)
- storage_storageaddfromcart(sd, RFIFOW(fd,2) - 2, RFIFOL(fd,4));
- else
- if (sd->state.storage_flag == 2)
- storage_guild_storageaddfromcart(sd, RFIFOW(fd,2) - 2, RFIFOL(fd,4));
-}
-
-
-/// Request to move an item from storage to cart (CZ_MOVE_ITEM_FROM_STORE_TO_CART).
-/// 0128 <index>.W <amount>.L
-void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd)
-{
- if( sd->state.vending )
- return;
- if (!pc_iscarton(sd))
- return;
-
- if (sd->state.storage_flag == 1)
- storage_storagegettocart(sd, RFIFOW(fd,2)-1, RFIFOL(fd,4));
- else
- if (sd->state.storage_flag == 2)
- storage_guild_storagegettocart(sd, RFIFOW(fd,2)-1, RFIFOL(fd,4));
-}
-
-
-/// Request to close storage (CZ_CLOSE_STORE).
-/// 00f7
-void clif_parse_CloseKafra(int fd, struct map_session_data *sd)
-{
- if( sd->state.storage_flag == 1 )
- storage_storageclose(sd);
- else
- if( sd->state.storage_flag == 2 )
- storage_guild_storageclose(sd);
-}
-
-
-/// Displays kafra storage password dialog (ZC_REQ_STORE_PASSWORD).
-/// 023a <info>.W
-/// info:
-/// 0 = password has not been set yet
-/// 1 = storage is password-protected
-/// 8 = too many wrong passwords
-/// ? = ignored
-/// NOTE: This packet is only available on certain non-kRO clients.
-void clif_storagepassword(struct map_session_data* sd, short info)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x23a));
- WFIFOW(fd,0) = 0x23a;
- WFIFOW(fd,2) = info;
- WFIFOSET(fd,packet_len(0x23a));
-}
-
-
-/// Answer to the kafra storage password dialog (CZ_ACK_STORE_PASSWORD).
-/// 023b <type>.W <password>.16B <new password>.16B
-/// type:
-/// 2 = change password
-/// 3 = check password
-/// NOTE: This packet is only available on certain non-kRO clients.
-void clif_parse_StoragePassword(int fd, struct map_session_data *sd)
-{
- //TODO
-}
-
-
-/// Result of kafra storage password validation (ZC_RESULT_STORE_PASSWORD).
-/// 023c <result>.W <error count>.W
-/// result:
-/// 4 = password change success
-/// 5 = password change failure
-/// 6 = password check success
-/// 7 = password check failure
-/// 8 = too many wrong passwords
-/// ? = ignored
-/// NOTE: This packet is only available on certain non-kRO clients.
-void clif_storagepassword_result(struct map_session_data* sd, short result, short error_count)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x23c));
- WFIFOW(fd,0) = 0x23c;
- WFIFOW(fd,2) = result;
- WFIFOW(fd,4) = error_count;
- WFIFOSET(fd,packet_len(0x23c));
-}
-
-
-/// Party creation request
-/// 00f9 <party name>.24B (CZ_MAKE_GROUP)
-/// 01e8 <party name>.24B <item pickup rule>.B <item share rule>.B (CZ_MAKE_GROUP2)
-void clif_parse_CreateParty(int fd, struct map_session_data *sd)
-{
- char* name = (char*)RFIFOP(fd,2);
- name[NAME_LENGTH-1] = '\0';
-
- if( map[sd->bl.m].flag.partylock )
- {// Party locked.
- clif_displaymessage(fd, msg_txt(227));
- return;
- }
- if( battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 7 )
- {
- clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,4);
- return;
- }
-
- party_create(sd,name,0,0);
-}
-
-void clif_parse_CreateParty2(int fd, struct map_session_data *sd)
-{
- char* name = (char*)RFIFOP(fd,2);
- int item1 = RFIFOB(fd,26);
- int item2 = RFIFOB(fd,27);
- name[NAME_LENGTH-1] = '\0';
-
- if( map[sd->bl.m].flag.partylock )
- {// Party locked.
- clif_displaymessage(fd, msg_txt(227));
- return;
- }
- if( battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 7 )
- {
- clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,4);
- return;
- }
-
- party_create(sd,name,item1,item2);
-}
-
-
-/// Party invitation request
-/// 00fc <account id>.L (CZ_REQ_JOIN_GROUP)
-/// 02c4 <char name>.24B (CZ_PARTY_JOIN_REQ)
-void clif_parse_PartyInvite(int fd, struct map_session_data *sd)
-{
- struct map_session_data *t_sd;
-
- if(map[sd->bl.m].flag.partylock)
- {// Party locked.
- clif_displaymessage(fd, msg_txt(227));
- return;
- }
-
- t_sd = map_id2sd(RFIFOL(fd,2));
-
- if(t_sd && t_sd->state.noask)
- {// @noask [LuzZza]
- clif_noask_sub(sd, t_sd, 1);
- return;
- }
-
- party_invite(sd, t_sd);
-}
-
-void clif_parse_PartyInvite2(int fd, struct map_session_data *sd)
-{
- struct map_session_data *t_sd;
- char *name = (char*)RFIFOP(fd,2);
- name[NAME_LENGTH-1] = '\0';
-
- if(map[sd->bl.m].flag.partylock)
- {// Party locked.
- clif_displaymessage(fd, msg_txt(227));
- return;
- }
-
- t_sd = map_nick2sd(name);
-
- if(t_sd && t_sd->state.noask)
- {// @noask [LuzZza]
- clif_noask_sub(sd, t_sd, 1);
- return;
- }
-
- party_invite(sd, t_sd);
-}
-
-
-/// Party invitation reply
-/// 00ff <party id>.L <flag>.L (CZ_JOIN_GROUP)
-/// 02c7 <party id>.L <flag>.B (CZ_PARTY_JOIN_REQ_ACK)
-/// flag:
-/// 0 = reject
-/// 1 = accept
-void clif_parse_ReplyPartyInvite(int fd,struct map_session_data *sd)
-{
- party_reply_invite(sd,RFIFOL(fd,2),RFIFOL(fd,6));
-}
-
-void clif_parse_ReplyPartyInvite2(int fd,struct map_session_data *sd)
-{
- party_reply_invite(sd,RFIFOL(fd,2),RFIFOB(fd,6));
-}
-
-
-/// Request to leave party (CZ_REQ_LEAVE_GROUP).
-/// 0100
-void clif_parse_LeaveParty(int fd, struct map_session_data *sd)
-{
- if(map[sd->bl.m].flag.partylock)
- { //Guild locked.
- clif_displaymessage(fd, msg_txt(227));
- return;
- }
- party_leave(sd);
-}
-
-
-/// Request to expel a party member (CZ_REQ_EXPEL_GROUP_MEMBER).
-/// 0103 <account id>.L <char name>.24B
-void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd)
-{
- if(map[sd->bl.m].flag.partylock)
- { //Guild locked.
- clif_displaymessage(fd, msg_txt(227));
- return;
- }
- party_removemember(sd,RFIFOL(fd,2),(char*)RFIFOP(fd,6));
-}
-
-
-/// Request to change party options.
-/// 0102 <exp share rule>.L (CZ_CHANGE_GROUPEXPOPTION)
-/// 07d7 <exp share rule>.L <item pickup rule>.B <item share rule>.B (CZ_GROUPINFO_CHANGE_V2)
-void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd)
-{
- struct party_data *p;
- int i;
-
- if( !sd->status.party_id )
- return;
-
- p = party_search(sd->status.party_id);
- if( p == NULL )
- return;
-
- ARR_FIND( 0, MAX_PARTY, i, p->data[i].sd == sd );
- if( i == MAX_PARTY )
- return; //Shouldn't happen
-
- if( !p->party.member[i].leader )
- return;
-
-#if PACKETVER < 20090603
- //Client can't change the item-field
- party_changeoption(sd, RFIFOL(fd,2), p->party.item);
-#else
- party_changeoption(sd, RFIFOL(fd,2), ((RFIFOB(fd,6)?1:0)|(RFIFOB(fd,7)?2:0)));
-#endif
-}
-
-
-/// Validates and processes party messages (CZ_REQUEST_CHAT_PARTY).
-/// 0108 <packet len>.W <text>.?B (<name> : <message>) 00
-void clif_parse_PartyMessage(int fd, struct map_session_data* sd)
-{
- const char* text = (char*)RFIFOP(fd,4);
- int textlen = RFIFOW(fd,2) - 4;
-
- char *name, *message;
- int namelen, messagelen;
-
- // validate packet and retrieve name and message
- if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
- return;
-
- if( is_atcommand(fd, sd, message, 1) )
- return;
-
- if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) )
- return;
-
- if( battle_config.min_chat_delay )
- { //[Skotlex]
- if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0)
- return;
- sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
- }
-
- party_send_message(sd, text, textlen);
-}
-
-
-/// Changes Party Leader (CZ_CHANGE_GROUP_MASTER).
-/// 07da <account id>.L
-void clif_parse_PartyChangeLeader(int fd, struct map_session_data* sd)
-{
- party_changeleader(sd, map_id2sd(RFIFOL(fd,2)));
-}
-
-
-/// Party Booking in KRO [Spiria]
-///
-
-/// Request to register a party booking advertisment (CZ_PARTY_BOOKING_REQ_REGISTER).
-/// 0802 <level>.W <map id>.W { <job>.W }*6
-void clif_parse_PartyBookingRegisterReq(int fd, struct map_session_data* sd)
-{
- short level = RFIFOW(fd,2);
- short mapid = RFIFOW(fd,4);
- short job[PARTY_BOOKING_JOBS];
- int i;
-
- for(i=0; i<PARTY_BOOKING_JOBS; i++)
- job[i] = RFIFOB(fd,6+i*2);
-
- party_booking_register(sd, level, mapid, job);
-}
-
-
-/// Result of request to register a party booking advertisment (ZC_PARTY_BOOKING_ACK_REGISTER).
-/// 0803 <result>.W
-/// result:
-/// 0 = success
-/// 1 = failure
-/// 2 = already registered
-void clif_PartyBookingRegisterAck(struct map_session_data *sd, int flag)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x803));
- WFIFOW(fd,0) = 0x803;
- WFIFOW(fd,2) = flag;
- WFIFOSET(fd,packet_len(0x803));
-}
-
-
-/// Request to search for party booking advertisments (CZ_PARTY_BOOKING_REQ_SEARCH).
-/// 0804 <level>.W <map id>.W <job>.W <last index>.L <result count>.W
-void clif_parse_PartyBookingSearchReq(int fd, struct map_session_data* sd)
-{
- short level = RFIFOW(fd,2);
- short mapid = RFIFOW(fd,4);
- short job = RFIFOW(fd,6);
- unsigned long lastindex = RFIFOL(fd,8);
- short resultcount = RFIFOW(fd,12);
-
- party_booking_search(sd, level, mapid, job, lastindex, resultcount);
-}
-
-
-/// Party booking search results (ZC_PARTY_BOOKING_ACK_SEARCH).
-/// 0805 <packet len>.W <more results>.B { <index>.L <char name>.24B <expire time>.L <level>.W <map id>.W { <job>.W }*6 }*
-/// more results:
-/// 0 = no
-/// 1 = yes
-void clif_PartyBookingSearchAck(int fd, struct party_booking_ad_info** results, int count, bool more_result)
-{
- int i, j;
- int size = sizeof(struct party_booking_ad_info); // structure size (48)
- struct party_booking_ad_info *pb_ad;
- WFIFOHEAD(fd,size*count + 5);
- WFIFOW(fd,0) = 0x805;
- WFIFOW(fd,2) = size*count + 5;
- WFIFOB(fd,4) = more_result;
- for(i=0; i<count; i++)
- {
- pb_ad = results[i];
- WFIFOL(fd,i*size+5) = pb_ad->index;
- memcpy(WFIFOP(fd,i*size+9),pb_ad->charname,NAME_LENGTH);
- WFIFOL(fd,i*size+33) = pb_ad->starttime; // FIXME: This is expire time
- WFIFOW(fd,i*size+37) = pb_ad->p_detail.level;
- WFIFOW(fd,i*size+39) = pb_ad->p_detail.mapid;
- for(j=0; j<PARTY_BOOKING_JOBS; j++)
- WFIFOW(fd,i*size+41+j*2) = pb_ad->p_detail.job[j];
- }
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Request to delete own party booking advertisment (CZ_PARTY_BOOKING_REQ_DELETE).
-/// 0806
-void clif_parse_PartyBookingDeleteReq(int fd, struct map_session_data* sd)
-{
- if(party_booking_delete(sd))
- clif_PartyBookingDeleteAck(sd, 0);
-}
-
-
-/// Result of request to delete own party booking advertisment (ZC_PARTY_BOOKING_ACK_DELETE).
-/// 0807 <result>.W
-/// result:
-/// 0 = success
-/// 1 = success (auto-removed expired ad)
-/// 2 = failure
-/// 3 = nothing registered
-void clif_PartyBookingDeleteAck(struct map_session_data* sd, int flag)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x807));
- WFIFOW(fd,0) = 0x807;
- WFIFOW(fd,2) = flag;
- WFIFOSET(fd,packet_len(0x807));
-}
-
-
-/// Request to update party booking advertisment (CZ_PARTY_BOOKING_REQ_UPDATE).
-/// 0808 { <job>.W }*6
-void clif_parse_PartyBookingUpdateReq(int fd, struct map_session_data* sd)
-{
- short job[PARTY_BOOKING_JOBS];
- int i;
-
- for(i=0; i<PARTY_BOOKING_JOBS; i++)
- job[i] = RFIFOW(fd,2+i*2);
-
- party_booking_update(sd, job);
-}
-
-
-/// Notification about new party booking advertisment (ZC_PARTY_BOOKING_NOTIFY_INSERT).
-/// 0809 <index>.L <char name>.24B <expire time>.L <level>.W <map id>.W { <job>.W }*6
-void clif_PartyBookingInsertNotify(struct map_session_data* sd, struct party_booking_ad_info* pb_ad)
-{
- int i;
- uint8 buf[38+PARTY_BOOKING_JOBS*2];
-
- if(pb_ad == NULL) return;
-
- WBUFW(buf,0) = 0x809;
- WBUFL(buf,2) = pb_ad->index;
- memcpy(WBUFP(buf,6),pb_ad->charname,NAME_LENGTH);
- WBUFL(buf,30) = pb_ad->starttime; // FIXME: This is expire time
- WBUFW(buf,34) = pb_ad->p_detail.level;
- WBUFW(buf,36) = pb_ad->p_detail.mapid;
- for(i=0; i<PARTY_BOOKING_JOBS; i++)
- WBUFW(buf,38+i*2) = pb_ad->p_detail.job[i];
-
- clif_send(buf, packet_len(0x809), &sd->bl, ALL_CLIENT);
-}
-
-
-/// Notification about updated party booking advertisment (ZC_PARTY_BOOKING_NOTIFY_UPDATE).
-/// 080a <index>.L { <job>.W }*6
-void clif_PartyBookingUpdateNotify(struct map_session_data* sd, struct party_booking_ad_info* pb_ad)
-{
- int i;
- uint8 buf[6+PARTY_BOOKING_JOBS*2];
-
- if(pb_ad == NULL) return;
-
- WBUFW(buf,0) = 0x80a;
- WBUFL(buf,2) = pb_ad->index;
- for(i=0; i<PARTY_BOOKING_JOBS; i++)
- WBUFW(buf,6+i*2) = pb_ad->p_detail.job[i];
- clif_send(buf,packet_len(0x80a),&sd->bl,ALL_CLIENT); // Now UPDATE all client.
-}
-
-
-/// Notification about deleted party booking advertisment (ZC_PARTY_BOOKING_NOTIFY_DELETE).
-/// 080b <index>.L
-void clif_PartyBookingDeleteNotify(struct map_session_data* sd, int index)
-{
- uint8 buf[6];
-
- WBUFW(buf,0) = 0x80b;
- WBUFL(buf,2) = index;
-
- clif_send(buf, packet_len(0x80b), &sd->bl, ALL_CLIENT); // Now UPDATE all client.
-}
-
-
-/// Request to close own vending (CZ_REQ_CLOSESTORE).
-/// 012e
-void clif_parse_CloseVending(int fd, struct map_session_data* sd)
-{
- vending_closevending(sd);
-}
-
-
-/// Request to open a vending shop (CZ_REQ_BUY_FROMMC).
-/// 0130 <account id>.L
-void clif_parse_VendingListReq(int fd, struct map_session_data* sd)
-{
- if( sd->npc_id )
- {// using an NPC
- return;
- }
- vending_vendinglistreq(sd,RFIFOL(fd,2));
-}
-
-
-/// Shop item(s) purchase request (CZ_PC_PURCHASE_ITEMLIST_FROMMC).
-/// 0134 <packet len>.W <account id>.L { <amount>.W <index>.W }*
-void clif_parse_PurchaseReq(int fd, struct map_session_data* sd)
-{
- int len = (int)RFIFOW(fd,2) - 8;
- int id = (int)RFIFOL(fd,4);
- const uint8* data = (uint8*)RFIFOP(fd,8);
-
- vending_purchasereq(sd, id, sd->vended_id, data, len/4);
-
- // whether it fails or not, the buy window is closed
- sd->vended_id = 0;
-}
-
-
-/// Shop item(s) purchase request (CZ_PC_PURCHASE_ITEMLIST_FROMMC2).
-/// 0801 <packet len>.W <account id>.L <unique id>.L { <amount>.W <index>.W }*
-void clif_parse_PurchaseReq2(int fd, struct map_session_data* sd)
-{
- int len = (int)RFIFOW(fd,2) - 12;
- int aid = (int)RFIFOL(fd,4);
- int uid = (int)RFIFOL(fd,8);
- const uint8* data = (uint8*)RFIFOP(fd,12);
-
- vending_purchasereq(sd, aid, uid, data, len/4);
-
- // whether it fails or not, the buy window is closed
- sd->vended_id = 0;
-}
-
-
-/// Confirm or cancel the shop preparation window.
-/// 012f <packet len>.W <shop name>.80B { <index>.W <amount>.W <price>.L }* (CZ_REQ_OPENSTORE)
-/// 01b2 <packet len>.W <shop name>.80B <result>.B { <index>.W <amount>.W <price>.L }* (CZ_REQ_OPENSTORE2)
-/// result:
-/// 0 = canceled
-/// 1 = open
-void clif_parse_OpenVending(int fd, struct map_session_data* sd)
-{
- short len = (short)RFIFOW(fd,2) - 85;
- const char* message = (char*)RFIFOP(fd,4);
- bool flag = (bool)RFIFOB(fd,84);
- const uint8* data = (uint8*)RFIFOP(fd,85);
-
- if( sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM )
- return;
- if( map[sd->bl.m].flag.novending ) {
- clif_displaymessage (sd->fd, msg_txt(276)); // "You can't open a shop on this map"
- return;
- }
- if( map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNOVENDING) ) {
- clif_displaymessage (sd->fd, msg_txt(204)); // "You can't open a shop on this cell."
- return;
- }
-
- if( vending_checknearnpc(&sd->bl) ) {
- char output[150];
- sprintf(output, msg_txt(662), battle_config.min_npc_vending_distance);
- clif_displaymessage(sd->fd, output);
- clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0);
- return;
- }
-
- if( message[0] == '\0' ) // invalid input
- return;
-
- vending_openvending(sd, message, flag, data, len/8);
-}
-
-
-/// Guild creation request (CZ_REQ_MAKE_GUILD).
-/// 0165 <char id>.L <guild name>.24B
-void clif_parse_CreateGuild(int fd,struct map_session_data *sd)
-{
- char* name = (char*)RFIFOP(fd,6);
- name[NAME_LENGTH-1] = '\0';
-
- if(map[sd->bl.m].flag.guildlock)
- { //Guild locked.
- clif_displaymessage(fd, msg_txt(228));
- return;
- }
-
- guild_create(sd, name);
-}
-
-
-/// Request for guild window interface permissions (CZ_REQ_GUILD_MENUINTERFACE).
-/// 014d
-void clif_parse_GuildCheckMaster(int fd, struct map_session_data *sd)
-{
- clif_guild_masterormember(sd);
-}
-
-
-/// Request for guild window information (CZ_REQ_GUILD_MENU).
-/// 014f <type>.L
-/// type:
-/// 0 = basic info
-/// 1 = member manager
-/// 2 = positions
-/// 3 = skills
-/// 4 = expulsion list
-/// 5 = unknown (GM_ALLGUILDLIST)
-/// 6 = notice
-void clif_parse_GuildRequestInfo(int fd, struct map_session_data *sd)
-{
- if( !sd->status.guild_id && !sd->bg_id )
- return;
-
- switch( RFIFOL(fd,2) )
- {
- case 0: // Basic Information Guild, hostile alliance information
- clif_guild_basicinfo(sd);
- clif_guild_allianceinfo(sd);
- break;
- case 1: // Members list, list job title
- clif_guild_positionnamelist(sd);
- clif_guild_memberlist(sd);
- break;
- case 2: // List job title, title information list
- clif_guild_positionnamelist(sd);
- clif_guild_positioninfolist(sd);
- break;
- case 3: // Skill list
- clif_guild_skillinfo(sd);
- break;
- case 4: // Expulsion list
- clif_guild_expulsionlist(sd);
- break;
- default:
- ShowError("clif: guild request info: unknown type %d\n", RFIFOL(fd,2));
- break;
- }
-}
-
-
-/// Request to update guild positions (CZ_REG_CHANGE_GUILD_POSITIONINFO).
-/// 0161 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L <name>.24B }*
-void clif_parse_GuildChangePositionInfo(int fd, struct map_session_data *sd)
-{
- int i;
-
- if(!sd->state.gmaster_flag)
- return;
-
- for(i = 4; i < RFIFOW(fd,2); i += 40 ){
- guild_change_position(sd->status.guild_id, RFIFOL(fd,i), RFIFOL(fd,i+4), RFIFOL(fd,i+12), (char*)RFIFOP(fd,i+16));
- }
-}
-
-
-/// Request to update the position of guild members (CZ_REQ_CHANGE_MEMBERPOS).
-/// 0155 <packet len>.W { <account id>.L <char id>.L <position id>.L }*
-void clif_parse_GuildChangeMemberPosition(int fd, struct map_session_data *sd)
-{
- int i;
-
- if(!sd->state.gmaster_flag)
- return;
-
- for(i=4;i<RFIFOW(fd,2);i+=12){
- guild_change_memberposition(sd->status.guild_id,
- RFIFOL(fd,i),RFIFOL(fd,i+4),RFIFOL(fd,i+8));
- }
-}
-
-
-/// Request for guild emblem data (CZ_REQ_GUILD_EMBLEM_IMG).
-/// 0151 <guild id>.L
-void clif_parse_GuildRequestEmblem(int fd,struct map_session_data *sd)
-{
- struct guild* g;
- int guild_id = RFIFOL(fd,2);
-
- if( (g = guild_search(guild_id)) != NULL )
- clif_guild_emblem(sd,g);
-}
-
-
-/// Validates data of a guild emblem (compressed bitmap)
-static bool clif_validate_emblem(const uint8* emblem, unsigned long emblem_len)
-{
- bool success;
- uint8 buf[1800]; // no well-formed emblem bitmap is larger than 1782 (24 bit) / 1654 (8 bit) bytes
- unsigned long buf_len = sizeof(buf);
-
- success = ( decode_zip(buf, &buf_len, emblem, emblem_len) == 0 && buf_len >= 18 ) // sizeof(BITMAPFILEHEADER) + sizeof(biSize) of the following info header struct
- && RBUFW(buf,0) == 0x4d42 // BITMAPFILEHEADER.bfType (signature)
- && RBUFL(buf,2) == buf_len // BITMAPFILEHEADER.bfSize (file size)
- && RBUFL(buf,10) < buf_len // BITMAPFILEHEADER.bfOffBits (offset to bitmap bits)
- ;
-
- return success;
-}
-
-
-/// Request to update the guild emblem (CZ_REGISTER_GUILD_EMBLEM_IMG).
-/// 0153 <packet len>.W <emblem data>.?B
-void clif_parse_GuildChangeEmblem(int fd,struct map_session_data *sd)
-{
- unsigned long emblem_len = RFIFOW(fd,2)-4;
- const uint8* emblem = RFIFOP(fd,4);
-
- if( !emblem_len || !sd->state.gmaster_flag )
- return;
-
- if( !clif_validate_emblem(emblem, emblem_len) )
- {
- ShowWarning("clif_parse_GuildChangeEmblem: Rejected malformed guild emblem (size=%lu, accound_id=%d, char_id=%d, guild_id=%d).\n", emblem_len, sd->status.account_id, sd->status.char_id, sd->status.guild_id);
- return;
- }
-
- guild_change_emblem(sd, emblem_len, (const char*)emblem);
-}
-
-
-/// Guild notice update request (CZ_GUILD_NOTICE).
-/// 016e <guild id>.L <msg1>.60B <msg2>.120B
-void clif_parse_GuildChangeNotice(int fd, struct map_session_data* sd)
-{
- int guild_id = RFIFOL(fd,2);
- char* msg1 = (char*)RFIFOP(fd,6);
- char* msg2 = (char*)RFIFOP(fd,66);
-
- if(!sd->state.gmaster_flag)
- return;
-
- // compensate for some client defects when using multilanguage mode
- if (msg1[0] == '|' && msg1[3] == '|') msg1+= 3; // skip duplicate marker
- if (msg2[0] == '|' && msg2[3] == '|') msg2+= 3; // skip duplicate marker
- if (msg2[0] == '|') msg2[strnlen(msg2, MAX_GUILDMES2)-1] = '\0'; // delete extra space at the end of string
-
- guild_change_notice(sd, guild_id, msg1, msg2);
-}
-
-
-/// Guild invite request (CZ_REQ_JOIN_GUILD).
-/// 0168 <account id>.L <inviter account id>.L <inviter char id>.L
-void clif_parse_GuildInvite(int fd,struct map_session_data *sd)
-{
- struct map_session_data *t_sd;
-
- if(map[sd->bl.m].flag.guildlock)
- { //Guild locked.
- clif_displaymessage(fd, msg_txt(228));
- return;
- }
-
- t_sd = map_id2sd(RFIFOL(fd,2));
-
- // @noask [LuzZza]
- if(t_sd && t_sd->state.noask) {
- clif_noask_sub(sd, t_sd, 2);
- return;
- }
-
- guild_invite(sd,t_sd);
-}
-
-
-/// Answer to guild invitation (CZ_JOIN_GUILD).
-/// 016b <guild id>.L <answer>.L
-/// answer:
-/// 0 = refuse
-/// 1 = accept
-void clif_parse_GuildReplyInvite(int fd,struct map_session_data *sd)
-{
- guild_reply_invite(sd,RFIFOL(fd,2),RFIFOL(fd,6));
-}
-
-
-/// Request to leave guild (CZ_REQ_LEAVE_GUILD).
-/// 0159 <guild id>.L <account id>.L <char id>.L <reason>.40B
-void clif_parse_GuildLeave(int fd,struct map_session_data *sd)
-{
- if(map[sd->bl.m].flag.guildlock)
- { //Guild locked.
- clif_displaymessage(fd, msg_txt(228));
- return;
- }
- if( sd->bg_id )
- {
- clif_displaymessage(fd, msg_txt(670)); //"You can't leave battleground guilds."
- return;
- }
-
- guild_leave(sd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),(char*)RFIFOP(fd,14));
-}
-
-
-/// Request to expel a member of a guild (CZ_REQ_BAN_GUILD).
-/// 015b <guild id>.L <account id>.L <char id>.L <reason>.40B
-void clif_parse_GuildExpulsion(int fd,struct map_session_data *sd)
-{
- if( map[sd->bl.m].flag.guildlock || sd->bg_id )
- { // Guild locked.
- clif_displaymessage(fd, msg_txt(228));
- return;
- }
- guild_expulsion(sd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),(char*)RFIFOP(fd,14));
-}
-
-
-/// Validates and processes guild messages (CZ_GUILD_CHAT).
-/// 017e <packet len>.W <text>.?B (<name> : <message>) 00
-void clif_parse_GuildMessage(int fd, struct map_session_data* sd)
-{
- const char* text = (char*)RFIFOP(fd,4);
- int textlen = RFIFOW(fd,2) - 4;
-
- char *name, *message;
- int namelen, messagelen;
-
- // validate packet and retrieve name and message
- if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
- return;
-
- if( is_atcommand(fd, sd, message, 1) )
- return;
-
- if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) )
- return;
-
- if( battle_config.min_chat_delay )
- { //[Skotlex]
- if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0)
- return;
- sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
- }
-
- if( sd->bg_id )
- bg_send_message(sd, text, textlen);
- else
- guild_send_message(sd, text, textlen);
-}
-
-
-/// Guild alliance request (CZ_REQ_ALLY_GUILD).
-/// 0170 <account id>.L <inviter account id>.L <inviter char id>.L
-void clif_parse_GuildRequestAlliance(int fd, struct map_session_data *sd)
-{
- struct map_session_data *t_sd;
-
- if(!sd->state.gmaster_flag)
- return;
-
- if(map[sd->bl.m].flag.guildlock)
- { //Guild locked.
- clif_displaymessage(fd, msg_txt(228));
- return;
- }
-
- t_sd = map_id2sd(RFIFOL(fd,2));
-
- // @noask [LuzZza]
- if(t_sd && t_sd->state.noask) {
- clif_noask_sub(sd, t_sd, 3);
- return;
- }
-
- guild_reqalliance(sd,t_sd);
-}
-
-
-/// Answer to a guild alliance request (CZ_ALLY_GUILD).
-/// 0172 <inviter account id>.L <answer>.L
-/// answer:
-/// 0 = refuse
-/// 1 = accept
-void clif_parse_GuildReplyAlliance(int fd, struct map_session_data *sd)
-{
- guild_reply_reqalliance(sd,RFIFOL(fd,2),RFIFOL(fd,6));
-}
-
-
-/// Request to delete a guild alliance or opposition (CZ_REQ_DELETE_RELATED_GUILD).
-/// 0183 <opponent guild id>.L <relation>.L
-/// relation:
-/// 0 = Ally
-/// 1 = Enemy
-void clif_parse_GuildDelAlliance(int fd, struct map_session_data *sd)
-{
- if(!sd->state.gmaster_flag)
- return;
-
- if(map[sd->bl.m].flag.guildlock)
- { //Guild locked.
- clif_displaymessage(fd, msg_txt(228));
- return;
- }
- guild_delalliance(sd,RFIFOL(fd,2),RFIFOL(fd,6));
-}
-
-
-/// Request to set a guild as opposition (CZ_REQ_HOSTILE_GUILD).
-/// 0180 <account id>.L
-void clif_parse_GuildOpposition(int fd, struct map_session_data *sd)
-{
- struct map_session_data *t_sd;
-
- if(!sd->state.gmaster_flag)
- return;
-
- if(map[sd->bl.m].flag.guildlock)
- { //Guild locked.
- clif_displaymessage(fd, msg_txt(228));
- return;
- }
-
- t_sd = map_id2sd(RFIFOL(fd,2));
-
- // @noask [LuzZza]
- if(t_sd && t_sd->state.noask) {
- clif_noask_sub(sd, t_sd, 4);
- return;
- }
-
- guild_opposition(sd,t_sd);
-}
-
-
-/// Request to delete own guild (CZ_REQ_DISORGANIZE_GUILD).
-/// 015d <key>.40B
-/// key:
-/// now guild name; might have been (intended) email, since the
-/// field name and size is same as the one in CH_DELETE_CHAR.
-void clif_parse_GuildBreak(int fd, struct map_session_data *sd)
-{
- if( map[sd->bl.m].flag.guildlock )
- { //Guild locked.
- clif_displaymessage(fd, msg_txt(228));
- return;
- }
- guild_break(sd,(char*)RFIFOP(fd,2));
-}
-
-
-/// Pet
-///
-
-/// Request to invoke a pet menu action (CZ_COMMAND_PET).
-/// 01a1 <type>.B
-/// type:
-/// 0 = pet information
-/// 1 = feed
-/// 2 = performance
-/// 3 = return to egg
-/// 4 = unequip accessory
-void clif_parse_PetMenu(int fd, struct map_session_data *sd)
-{
- pet_menu(sd,RFIFOB(fd,2));
-}
-
-
-/// Attempt to tame a monster (CZ_TRYCAPTURE_MONSTER).
-/// 019f <id>.L
-void clif_parse_CatchPet(int fd, struct map_session_data *sd)
-{
- pet_catch_process2(sd,RFIFOL(fd,2));
-}
-
-
-/// Answer to pet incubator egg selection dialog (CZ_SELECT_PETEGG).
-/// 01a7 <index>.W
-void clif_parse_SelectEgg(int fd, struct map_session_data *sd)
-{
- if (sd->menuskill_id != SA_TAMINGMONSTER || sd->menuskill_val != -1)
- {
- //Forged packet, disconnect them [Kevin]
- clif_authfail_fd(fd, 0);
- return;
- }
- pet_select_egg(sd,RFIFOW(fd,2)-2);
- clif_menuskill_clear(sd);
-}
-
-
-/// Request to display pet's emotion/talk (CZ_PET_ACT).
-/// 01a9 <data>.L
-/// data:
-/// is either emotion (@see enum emotion_type) or a compound value
-/// (((mob id)-100)*100+(act id)*10+(hunger)) that describes an
-/// entry (given in parentheses) in data\pettalktable.xml
-/// act id:
-/// 0 = feeding
-/// 1 = hunting
-/// 2 = danger
-/// 3 = dead
-/// 4 = normal (stand)
-/// 5 = special performance (perfor_s)
-/// 6 = level up (levelup)
-/// 7 = performance 1 (perfor_1)
-/// 8 = performance 2 (perfor_2)
-/// 9 = performance 3 (perfor_3)
-/// 10 = log-in greeting (connect)
-/// hungry value:
-/// 0 = very hungry (hungry)
-/// 1 = hungry (bit_hungry)
-/// 2 = satisfied (noting)
-/// 3 = stuffed (full)
-/// 4 = full (so_full)
-void clif_parse_SendEmotion(int fd, struct map_session_data *sd)
-{
- if(sd->pd)
- clif_pet_emotion(sd->pd,RFIFOL(fd,2));
-}
-
-
-/// Request to change pet's name (CZ_RENAME_PET).
-/// 01a5 <name>.24B
-void clif_parse_ChangePetName(int fd, struct map_session_data *sd)
-{
- pet_change_name(sd,(char*)RFIFOP(fd,2));
-}
-
-
-/// /kill (CZ_DISCONNECT_CHARACTER).
-/// Request to disconnect a character.
-/// 00cc <account id>.L
-/// NOTE: Also sent when using GM right click menu "(name) force to quit"
-void clif_parse_GMKick(int fd, struct map_session_data *sd)
-{
- struct block_list *target;
- int tid;
-
- tid = RFIFOL(fd,2);
- target = map_id2bl(tid);
- if (!target) {
- clif_GM_kickack(sd, 0);
- return;
- }
-
- switch (target->type) {
- case BL_PC:
- {
- char command[NAME_LENGTH+6];
- sprintf(command, "%ckick %s", atcommand_symbol, status_get_name(target));
- is_atcommand(fd, sd, command, 1);
- }
- break;
-
- /**
- * This one does not invoke any atcommand, so we need to check for permissions.
- */
- case BL_MOB:
- {
- char command[100];
- if( !pc_can_use_command(sd, "killmonster", COMMAND_ATCOMMAND)) {
- clif_GM_kickack(sd, 0);
- return;
- }
- sprintf(command, "/kick %s (%d)", status_get_name(target), status_get_class(target));
- log_atcommand(sd, command);
- status_percent_damage(&sd->bl, target, 100, 0, true); // can invalidate 'target'
- }
- break;
-
- case BL_NPC:
- {
- char command[NAME_LENGTH+11];
- sprintf(command, "%cunloadnpc %s", atcommand_symbol, status_get_name(target));
- is_atcommand(fd, sd, command, 1);
- }
- break;
-
- default:
- clif_GM_kickack(sd, 0);
- }
-}
-
-
-/// /killall (CZ_DISCONNECT_ALL_CHARACTER).
-/// Request to disconnect all characters.
-/// 00ce
-void clif_parse_GMKickAll(int fd, struct map_session_data* sd) {
- char cmd[15];
- sprintf(cmd,"%ckickall",atcommand_symbol);
- is_atcommand(fd, sd, cmd, 1);
-}
-
-
-/// /remove (CZ_REMOVE_AID).
-/// Request to warp to a character with given login ID.
-/// 01ba <account name>.24B
-
-/// /shift (CZ_SHIFT).
-/// Request to warp to a character with given name.
-/// 01bb <char name>.24B
-void clif_parse_GMShift(int fd, struct map_session_data *sd)
-{// FIXME: remove is supposed to receive account name for clients prior 20100803RE
- char *player_name;
- char command[NAME_LENGTH+8];
-
- player_name = (char*)RFIFOP(fd,2);
- player_name[NAME_LENGTH-1] = '\0';
-
- sprintf(command, "%cjumpto %s", atcommand_symbol, player_name);
- is_atcommand(fd, sd, command, 1);
-}
-
-
-/// /remove (CZ_REMOVE_AID_SSO).
-/// Request to warp to a character with given account ID.
-/// 0843 <account id>.L
-void clif_parse_GMRemove2(int fd, struct map_session_data* sd)
-{
- int account_id;
- struct map_session_data* pl_sd;
-
- account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
- if( (pl_sd = map_id2sd(account_id)) != NULL )
- {
- char command[NAME_LENGTH+8];
- sprintf(command, "%cjumpto %s", atcommand_symbol, pl_sd->status.name);
- is_atcommand(fd, sd, command, 1);
- }
-}
-
-
-/// /recall (CZ_RECALL).
-/// Request to summon a player with given login ID to own position.
-/// 01bc <account name>.24B
-
-/// /summon (CZ_RECALL_GID).
-/// Request to summon a player with given name to own position.
-/// 01bd <char name>.24B
-void clif_parse_GMRecall(int fd, struct map_session_data *sd)
-{// FIXME: recall is supposed to receive account name for clients prior 20100803RE
- char *player_name;
- char command [NAME_LENGTH+8];
-
- player_name = (char*)RFIFOP(fd,2);
- player_name[NAME_LENGTH-1] = '\0';
-
- sprintf(command, "%crecall %s", atcommand_symbol, player_name);
- is_atcommand(fd, sd, command, 1);
-}
-
-
-/// /recall (CZ_RECALL_SSO).
-/// Request to summon a player with given account ID to own position.
-/// 0842 <account id>.L
-void clif_parse_GMRecall2(int fd, struct map_session_data* sd)
-{
- int account_id;
- struct map_session_data* pl_sd;
-
- account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
- if( (pl_sd = map_id2sd(account_id)) != NULL )
- {
- char command[NAME_LENGTH+8];
- sprintf(command, "%crecall %s", atcommand_symbol, pl_sd->status.name);
- is_atcommand(fd, sd, command, 1);
- }
-}
-
-
-/// /item /monster (CZ_ITEM_CREATE).
-/// Request to make items or spawn monsters.
-/// 013f <item/mob name>.24B
-void clif_parse_GM_Monster_Item(int fd, struct map_session_data *sd)
-{
- char *monster_item_name;
- char command[NAME_LENGTH+10];
-
- monster_item_name = (char*)RFIFOP(fd,2);
- monster_item_name[NAME_LENGTH-1] = '\0';
-
- // FIXME: Should look for item first, then for monster.
- // FIXME: /monster takes mob_db Sprite_Name as argument
- if( mobdb_searchname(monster_item_name) ) {
- snprintf(command, sizeof(command)-1, "%cmonster %s", atcommand_symbol, monster_item_name);
- is_atcommand(fd, sd, command, 1);
- return;
- }
- // FIXME: Stackables have a quantity of 20.
- // FIXME: Equips are supposed to be unidentified.
-
- if( itemdb_searchname(monster_item_name) ) {
- snprintf(command, sizeof(command)-1, "%citem %s", atcommand_symbol, monster_item_name);
- is_atcommand(fd, sd, command, 1);
- return;
- }
-}
-
-
-/// /hide (CZ_CHANGE_EFFECTSTATE).
-/// 019d <effect state>.L
-/// effect state:
-/// TODO: Any OPTION_* ?
-void clif_parse_GMHide(int fd, struct map_session_data *sd) {
- char cmd[6];
-
- sprintf(cmd,"%chide",atcommand_symbol);
-
- is_atcommand(fd, sd, cmd, 1);
-}
-
-
-/// Request to adjust player's manner points (CZ_REQ_GIVE_MANNER_POINT).
-/// 0149 <account id>.L <type>.B <value>.W
-/// type:
-/// 0 = positive points
-/// 1 = negative points
-/// 2 = self mute (+10 minutes)
-void clif_parse_GMReqNoChat(int fd,struct map_session_data *sd)
-{
- int id, type, value;
- struct map_session_data *dstsd;
- char command[NAME_LENGTH+15];
-
- id = RFIFOL(fd,2);
- type = RFIFOB(fd,6);
- value = RFIFOW(fd,7);
-
- if( type == 0 )
- value = -value;
-
- //If type is 2 and the ids don't match, this is a crafted hacked packet!
- //Disabled because clients keep self-muting when you give players public @ commands... [Skotlex]
- if (type == 2 /* && (pc_get_group_level(sd) > 0 || sd->bl.id != id)*/)
- return;
-
- dstsd = map_id2sd(id);
- if( dstsd == NULL )
- return;
-
- sprintf(command, "%cmute %d %s", atcommand_symbol, value, dstsd->status.name);
- is_atcommand(fd, sd, command, 1);
-}
-
-
-/// /rc (CZ_REQ_GIVE_MANNER_BYNAME).
-/// GM adjustment of a player's manner value by -60.
-/// 0212 <char name>.24B
-void clif_parse_GMRc(int fd, struct map_session_data* sd)
-{
- char command[NAME_LENGTH+15];
- char *name = (char*)RFIFOP(fd,2);
-
- name[NAME_LENGTH-1] = '\0';
- sprintf(command, "%cmute %d %s", atcommand_symbol, 60, name);
- is_atcommand(fd, sd, command, 1);
-}
-
-
-/// Result of request to resolve account name (ZC_ACK_ACCOUNTNAME).
-/// 01e0 <account id>.L <account name>.24B
-void clif_account_name(struct map_session_data* sd, int account_id, const char* accname)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x1e0));
- WFIFOW(fd,0) = 0x1e0;
- WFIFOL(fd,2) = account_id;
- safestrncpy((char*)WFIFOP(fd,6), accname, NAME_LENGTH);
- WFIFOSET(fd,packet_len(0x1e0));
-}
-
-
-/// GM requesting account name (for right-click gm menu) (CZ_REQ_ACCOUNTNAME).
-/// 01df <account id>.L
-void clif_parse_GMReqAccountName(int fd, struct map_session_data *sd)
-{
- int account_id = RFIFOL(fd,2);
-
- //TODO: find out if this works for any player or only for authorized GMs
- clif_account_name(sd, account_id, ""); // insert account name here >_<
-}
-
-
-/// /changemaptype <x> <y> <type> (CZ_CHANGE_MAPTYPE).
-/// GM single cell type change request.
-/// 0198 <x>.W <y>.W <type>.W
-/// type:
-/// 0 = not walkable
-/// 1 = walkable
-void clif_parse_GMChangeMapType(int fd, struct map_session_data *sd)
-{
- int x,y,type;
-
- if( pc_has_permission(sd, PC_PERM_USE_CHANGEMAPTYPE) )
- return;
-
- x = RFIFOW(fd,2);
- y = RFIFOW(fd,4);
- type = RFIFOW(fd,6);
-
- map_setgatcell(sd->bl.m,x,y,type);
- clif_changemapcell(0,sd->bl.m,x,y,type,ALL_SAMEMAP);
- //FIXME: once players leave the map, the client 'forgets' this information.
-}
-
-
-/// /in /ex (CZ_SETTING_WHISPER_PC).
-/// Request to allow/deny whispers from a nick.
-/// 00cf <nick>.24B <type>.B
-/// type:
-/// 0 = (/ex nick) deny speech from nick
-/// 1 = (/in nick) allow speech from nick
-void clif_parse_PMIgnore(int fd, struct map_session_data* sd)
-{
- char* nick;
- uint8 type;
- int i;
-
- nick = (char*)RFIFOP(fd,2); // speed up
- nick[NAME_LENGTH-1] = '\0'; // to be sure that the player name has at most 23 characters
- type = RFIFOB(fd,26);
-
- if( type == 0 )
- { // Add name to ignore list (block)
- if (strcmp(wisp_server_name, nick) == 0) {
- clif_wisexin(sd, type, 1); // fail
- return;
- }
-
- // try to find a free spot, while checking for duplicates at the same time
- ARR_FIND( 0, MAX_IGNORE_LIST, i, sd->ignore[i].name[0] == '\0' || strcmp(sd->ignore[i].name, nick) == 0 );
- if( i == MAX_IGNORE_LIST )
- {// no space for new entry
- clif_wisexin(sd, type, 2); // too many blocks
- return;
- }
- if( sd->ignore[i].name[0] != '\0' )
- {// name already exists
- clif_wisexin(sd, type, 0); // Aegis reports success.
- return;
- }
-
- //Insert in position i
- safestrncpy(sd->ignore[i].name, nick, NAME_LENGTH);
- }
- else
- { // Remove name from ignore list (unblock)
-
- // find entry
- ARR_FIND( 0, MAX_IGNORE_LIST, i, sd->ignore[i].name[0] == '\0' || strcmp(sd->ignore[i].name, nick) == 0 );
- if( i == MAX_IGNORE_LIST || sd->ignore[i].name[i] == '\0' )
- { //Not found
- clif_wisexin(sd, type, 1); // fail
- return;
- }
- // move everything one place down to overwrite removed entry
- memmove(sd->ignore[i].name, sd->ignore[i+1].name, (MAX_IGNORE_LIST-i-1)*sizeof(sd->ignore[0].name));
- // wipe last entry
- memset(sd->ignore[MAX_IGNORE_LIST-1].name, 0, sizeof(sd->ignore[0].name));
- }
-
- clif_wisexin(sd, type, 0); // success
-}
-
-
-/// /inall /exall (CZ_SETTING_WHISPER_STATE).
-/// Request to allow/deny all whispers.
-/// 00d0 <type>.B
-/// type:
-/// 0 = (/exall) deny all speech
-/// 1 = (/inall) allow all speech
-void clif_parse_PMIgnoreAll(int fd, struct map_session_data *sd)
-{
- int type = RFIFOB(fd,2), flag;
-
- if( type == 0 )
- {// Deny all
- if( sd->state.ignoreAll ) {
- flag = 1; // fail
- } else {
- sd->state.ignoreAll = 1;
- flag = 0; // success
- }
- }
- else
- {//Unblock everyone
- if( sd->state.ignoreAll ) {
- sd->state.ignoreAll = 0;
- flag = 0; // success
- } else {
- if (sd->ignore[0].name[0] != '\0')
- { //Wipe the ignore list.
- memset(sd->ignore, 0, sizeof(sd->ignore));
- flag = 0; // success
- } else {
- flag = 1; // fail
- }
- }
- }
-
- clif_wisall(sd, type, flag);
-}
-
-
-/// Whisper ignore list (ZC_WHISPER_LIST).
-/// 00d4 <packet len>.W { <char name>.24B }*
-void clif_PMIgnoreList(struct map_session_data* sd)
-{
- int i, fd = sd->fd;
-
- WFIFOHEAD(fd,4+ARRAYLENGTH(sd->ignore)*NAME_LENGTH);
- WFIFOW(fd,0) = 0xd4;
-
- for( i = 0; i < ARRAYLENGTH(sd->ignore) && sd->ignore[i].name[0]; i++ )
- {
- memcpy(WFIFOP(fd,4+i*NAME_LENGTH), sd->ignore[i].name, NAME_LENGTH);
- }
-
- WFIFOW(fd,2) = 4+i*NAME_LENGTH;
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Whisper ignore list request (CZ_REQ_WHISPER_LIST).
-/// 00d3
-void clif_parse_PMIgnoreList(int fd,struct map_session_data *sd)
-{
- clif_PMIgnoreList(sd);
-}
-
-
-/// Request to invoke the /doridori recovery bonus (CZ_DORIDORI).
-/// 01e7
-void clif_parse_NoviceDoriDori(int fd, struct map_session_data *sd)
-{
- if (sd->state.doridori) return;
-
- switch (sd->class_&MAPID_UPPERMASK)
- {
- case MAPID_SOUL_LINKER:
- case MAPID_STAR_GLADIATOR:
- case MAPID_TAEKWON:
- if (!sd->state.rest)
- break;
- case MAPID_SUPER_NOVICE:
- sd->state.doridori=1;
- break;
- }
-}
-
-
-/// Request to invoke the effect of super novice's guardian angel prayer (CZ_CHOPOKGI).
-/// 01ed
-/// Note: This packet is caused by 7 lines of any text, followed by
-/// the prayer and an another line of any text. The prayer is
-/// defined by lines 790~793 in data\msgstringtable.txt
-/// "Dear angel, can you hear my voice?"
-/// "I am" (space separated player name) "Super Novice~"
-/// "Help me out~ Please~ T_T"
-void clif_parse_NoviceExplosionSpirits(int fd, struct map_session_data *sd)
-{
- if( ( sd->class_&MAPID_UPPERMASK ) == MAPID_SUPER_NOVICE )
- {
- unsigned int next = pc_nextbaseexp(sd);
- if( next == 0 ) next = pc_thisbaseexp(sd);
- if( next )
- {
- int percent = (int)( ( (float)sd->status.base_exp/(float)next )*1000. );
-
- if( percent && ( percent%100 ) == 0 )
- {// 10.0%, 20.0%, ..., 90.0%
- sc_start(&sd->bl, status_skill2sc(MO_EXPLOSIONSPIRITS), 100, 17, skill_get_time(MO_EXPLOSIONSPIRITS, 5)); //Lv17-> +50 critical (noted by Poki) [Skotlex]
- clif_skill_nodamage(&sd->bl, &sd->bl, MO_EXPLOSIONSPIRITS, 5, 1); // prayer always shows successful Lv5 cast and disregards noskill restrictions
- }
- }
- }
-}
-
-
-/// Friends List
-///
-
-/// Toggles a single friend online/offline [Skotlex] (ZC_FRIENDS_STATE).
-/// 0206 <account id>.L <char id>.L <state>.B
-/// state:
-/// 0 = online
-/// 1 = offline
-void clif_friendslist_toggle(struct map_session_data *sd,int account_id, int char_id, int online)
-{
- int i, fd = sd->fd;
-
- //Seek friend.
- for (i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id &&
- (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++);
-
- if(i == MAX_FRIENDS || sd->status.friends[i].char_id == 0)
- return; //Not found
-
- WFIFOHEAD(fd,packet_len(0x206));
- WFIFOW(fd, 0) = 0x206;
- WFIFOL(fd, 2) = sd->status.friends[i].account_id;
- WFIFOL(fd, 6) = sd->status.friends[i].char_id;
- WFIFOB(fd,10) = !online; //Yeah, a 1 here means "logged off", go figure...
- WFIFOSET(fd, packet_len(0x206));
-}
-
-
-//Subfunction called from clif_foreachclient to toggle friends on/off [Skotlex]
-int clif_friendslist_toggle_sub(struct map_session_data *sd,va_list ap)
-{
- int account_id, char_id, online;
- account_id = va_arg(ap, int);
- char_id = va_arg(ap, int);
- online = va_arg(ap, int);
- clif_friendslist_toggle(sd, account_id, char_id, online);
- return 0;
-}
-
-
-/// Sends the whole friends list (ZC_FRIENDS_LIST).
-/// 0201 <packet len>.W { <account id>.L <char id>.L <name>.24B }*
-void clif_friendslist_send(struct map_session_data *sd)
-{
- int i = 0, n, fd = sd->fd;
-
- // Send friends list
- WFIFOHEAD(fd, MAX_FRIENDS * 32 + 4);
- WFIFOW(fd, 0) = 0x201;
- for(i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id; i++)
- {
- WFIFOL(fd, 4 + 32 * i + 0) = sd->status.friends[i].account_id;
- WFIFOL(fd, 4 + 32 * i + 4) = sd->status.friends[i].char_id;
- memcpy(WFIFOP(fd, 4 + 32 * i + 8), &sd->status.friends[i].name, NAME_LENGTH);
- }
-
- if (i) {
- WFIFOW(fd,2) = 4 + 32 * i;
- WFIFOSET(fd, WFIFOW(fd,2));
- }
-
- for (n = 0; n < i; n++)
- { //Sending the online players
- if (map_charid2sd(sd->status.friends[n].char_id))
- clif_friendslist_toggle(sd, sd->status.friends[n].account_id, sd->status.friends[n].char_id, 1);
- }
-}
-
-
-/// Notification about the result of a friend add request (ZC_ADD_FRIENDS_LIST).
-/// 0209 <result>.W <account id>.L <char id>.L <name>.24B
-/// result:
-/// 0 = MsgStringTable[821]="You have become friends with (%s)."
-/// 1 = MsgStringTable[822]="(%s) does not want to be friends with you."
-/// 2 = MsgStringTable[819]="Your Friend List is full."
-/// 3 = MsgStringTable[820]="(%s)'s Friend List is full."
-void clif_friendslist_reqack(struct map_session_data *sd, struct map_session_data *f_sd, int type)
-{
- int fd;
- nullpo_retv(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0x209));
- WFIFOW(fd,0) = 0x209;
- WFIFOW(fd,2) = type;
- if (f_sd)
- {
- WFIFOL(fd,4) = f_sd->status.account_id;
- WFIFOL(fd,8) = f_sd->status.char_id;
- memcpy(WFIFOP(fd, 12), f_sd->status.name,NAME_LENGTH);
- }
- WFIFOSET(fd, packet_len(0x209));
-}
-
-
-/// Asks a player for permission to be added as friend (ZC_REQ_ADD_FRIENDS).
-/// 0207 <req account id>.L <req char id>.L <req char name>.24B
-void clif_friendlist_req(struct map_session_data* sd, int account_id, int char_id, const char* name)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x207));
- WFIFOW(fd,0) = 0x207;
- WFIFOL(fd,2) = account_id;
- WFIFOL(fd,6) = char_id;
- memcpy(WFIFOP(fd,10), name, NAME_LENGTH);
- WFIFOSET(fd,packet_len(0x207));
-}
-
-
-/// Request to add a player as friend (CZ_ADD_FRIENDS).
-/// 0202 <name>.24B
-void clif_parse_FriendsListAdd(int fd, struct map_session_data *sd)
-{
- struct map_session_data *f_sd;
- int i;
-
- f_sd = map_nick2sd((char*)RFIFOP(fd,2));
-
- // ensure that the request player's friend list is not full
- ARR_FIND(0, MAX_FRIENDS, i, sd->status.friends[i].char_id == 0);
-
- if( i == MAX_FRIENDS ) {
- clif_friendslist_reqack(sd, f_sd, 2);
- return;
- }
-
- // Friend doesn't exist (no player with this name)
- if (f_sd == NULL) {
- clif_displaymessage(fd, msg_txt(3));
- return;
- }
-
- if( sd->bl.id == f_sd->bl.id )
- {// adding oneself as friend
- return;
- }
-
- // @noask [LuzZza]
- if(f_sd->state.noask) {
- clif_noask_sub(sd, f_sd, 5);
- return;
- }
-
- // Friend already exists
- for (i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id != 0; i++) {
- if (sd->status.friends[i].char_id == f_sd->status.char_id) {
- clif_displaymessage(fd, msg_txt(671)); //"Friend already exists."
- return;
- }
- }
-
- f_sd->friend_req = sd->status.char_id;
- sd->friend_req = f_sd->status.char_id;
-
- clif_friendlist_req(f_sd, sd->status.account_id, sd->status.char_id, sd->status.name);
-}
-
-
-/// Answer to a friend add request (CZ_ACK_REQ_ADD_FRIENDS).
-/// 0208 <inviter account id>.L <inviter char id>.L <result>.B
-/// 0208 <inviter account id>.L <inviter char id>.L <result>.L (PACKETVER >= 6)
-/// result:
-/// 0 = rejected
-/// 1 = accepted
-void clif_parse_FriendsListReply(int fd, struct map_session_data *sd)
-{
- struct map_session_data *f_sd;
- int account_id;
- char reply;
-
- account_id = RFIFOL(fd,2);
- //char_id = RFIFOL(fd,6);
-#if PACKETVER < 6
- reply = RFIFOB(fd,10);
-#else
- reply = RFIFOL(fd,10);
-#endif
-
- if( sd->bl.id == account_id )
- {// adding oneself as friend
- return;
- }
-
- f_sd = map_id2sd(account_id); //The account id is the same as the bl.id of players.
- if (f_sd == NULL)
- return;
-
- if (reply == 0 || !( sd->friend_req == f_sd->status.char_id && f_sd->friend_req == sd->status.char_id ) )
- clif_friendslist_reqack(f_sd, sd, 1);
- else {
- int i;
- // Find an empty slot
- for (i = 0; i < MAX_FRIENDS; i++)
- if (f_sd->status.friends[i].char_id == 0)
- break;
- if (i == MAX_FRIENDS) {
- clif_friendslist_reqack(f_sd, sd, 2);
- return;
- }
-
- f_sd->status.friends[i].account_id = sd->status.account_id;
- f_sd->status.friends[i].char_id = sd->status.char_id;
- memcpy(f_sd->status.friends[i].name, sd->status.name, NAME_LENGTH);
- clif_friendslist_reqack(f_sd, sd, 0);
-
- if (battle_config.friend_auto_add) {
- // Also add f_sd to sd's friendlist.
- for (i = 0; i < MAX_FRIENDS; i++) {
- if (sd->status.friends[i].char_id == f_sd->status.char_id)
- return; //No need to add anything.
- if (sd->status.friends[i].char_id == 0)
- break;
- }
- if (i == MAX_FRIENDS) {
- clif_friendslist_reqack(sd, f_sd, 2);
- return;
- }
-
- sd->status.friends[i].account_id = f_sd->status.account_id;
- sd->status.friends[i].char_id = f_sd->status.char_id;
- memcpy(sd->status.friends[i].name, f_sd->status.name, NAME_LENGTH);
- clif_friendslist_reqack(sd, f_sd, 0);
- }
- }
-}
-
-
-/// Request to delete a friend (CZ_DELETE_FRIENDS).
-/// 0203 <account id>.L <char id>.L
-void clif_parse_FriendsListRemove(int fd, struct map_session_data *sd)
-{
- struct map_session_data *f_sd = NULL;
- int account_id, char_id;
- int i, j;
-
- account_id = RFIFOL(fd,2);
- char_id = RFIFOL(fd,6);
-
- // Search friend
- for (i = 0; i < MAX_FRIENDS &&
- (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++);
-
- if (i == MAX_FRIENDS) {
- clif_displaymessage(fd, msg_txt(672)); //"Name not found in list."
- return;
- }
-
- //remove from friend's list first
- if( (f_sd = map_id2sd(account_id)) && f_sd->status.char_id == char_id) {
- for (i = 0; i < MAX_FRIENDS &&
- (f_sd->status.friends[i].char_id != sd->status.char_id || f_sd->status.friends[i].account_id != sd->status.account_id); i++);
-
- if (i != MAX_FRIENDS) {
- // move all chars up
- for(j = i + 1; j < MAX_FRIENDS; j++)
- memcpy(&f_sd->status.friends[j-1], &f_sd->status.friends[j], sizeof(f_sd->status.friends[0]));
-
- memset(&f_sd->status.friends[MAX_FRIENDS-1], 0, sizeof(f_sd->status.friends[MAX_FRIENDS-1]));
- //should the guy be notified of some message? we should add it here if so
- WFIFOHEAD(f_sd->fd,packet_len(0x20a));
- WFIFOW(f_sd->fd,0) = 0x20a;
- WFIFOL(f_sd->fd,2) = sd->status.account_id;
- WFIFOL(f_sd->fd,6) = sd->status.char_id;
- WFIFOSET(f_sd->fd, packet_len(0x20a));
- }
-
- } else { //friend not online -- ask char server to delete from his friendlist
- if(chrif_removefriend(char_id,sd->status.char_id)) { // char-server offline, abort
- clif_displaymessage(fd, msg_txt(673)); //"This action can't be performed at the moment. Please try again later."
- return;
- }
- }
-
- // We can now delete from original requester
- for (i = 0; i < MAX_FRIENDS &&
- (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++);
- // move all chars up
- for(j = i + 1; j < MAX_FRIENDS; j++)
- memcpy(&sd->status.friends[j-1], &sd->status.friends[j], sizeof(sd->status.friends[0]));
-
- memset(&sd->status.friends[MAX_FRIENDS-1], 0, sizeof(sd->status.friends[MAX_FRIENDS-1]));
- clif_displaymessage(fd, msg_txt(674)); //"Friend removed"
-
- WFIFOHEAD(fd,packet_len(0x20a));
- WFIFOW(fd,0) = 0x20a;
- WFIFOL(fd,2) = account_id;
- WFIFOL(fd,6) = char_id;
- WFIFOSET(fd, packet_len(0x20a));
-}
-
-
-/// /pvpinfo list (ZC_ACK_PVPPOINT).
-/// 0210 <char id>.L <account id>.L <win point>.L <lose point>.L <point>.L
-void clif_PVPInfo(struct map_session_data* sd)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x210));
- WFIFOW(fd,0) = 0x210;
- WFIFOL(fd,2) = sd->status.char_id;
- WFIFOL(fd,6) = sd->status.account_id;
- WFIFOL(fd,10) = sd->pvp_won; // times won
- WFIFOL(fd,14) = sd->pvp_lost; // times lost
- WFIFOL(fd,18) = sd->pvp_point;
- WFIFOSET(fd, packet_len(0x210));
-}
-
-
-/// /pvpinfo (CZ_REQ_PVPPOINT).
-/// 020f <char id>.L <account id>.L
-void clif_parse_PVPInfo(int fd,struct map_session_data *sd)
-{
- // TODO: Is there a way to use this on an another player (char/acc id)?
- clif_PVPInfo(sd);
-}
-
-
-/// /blacksmith list (ZC_BLACKSMITH_RANK).
-/// 0219 { <name>.24B }*10 { <point>.L }*10
-void clif_blacksmith(struct map_session_data* sd)
-{
- int i, fd = sd->fd;
- const char* name;
-
- WFIFOHEAD(fd,packet_len(0x219));
- WFIFOW(fd,0) = 0x219;
- //Packet size limits this list to 10 elements. [Skotlex]
- for (i = 0; i < 10 && i < MAX_FAME_LIST; i++) {
- if (smith_fame_list[i].id > 0) {
- if (strcmp(smith_fame_list[i].name, "-") == 0 &&
- (name = map_charid2nick(smith_fame_list[i].id)) != NULL)
- {
- strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), name, NAME_LENGTH);
- } else
- strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), smith_fame_list[i].name, NAME_LENGTH);
- } else
- strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), "None", 5);
- WFIFOL(fd, 242 + i * 4) = smith_fame_list[i].fame;
- }
- for(;i < 10; i++) { //In case the MAX is less than 10.
- strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), "Unavailable", 12);
- WFIFOL(fd, 242 + i * 4) = 0;
- }
-
- WFIFOSET(fd, packet_len(0x219));
-}
-
-
-/// /blacksmith (CZ_BLACKSMITH_RANK).
-/// 0217
-void clif_parse_Blacksmith(int fd,struct map_session_data *sd)
-{
- clif_blacksmith(sd);
-}
-
-
-/// Notification about backsmith points (ZC_BLACKSMITH_POINT).
-/// 021b <points>.L <total points>.L
-void clif_fame_blacksmith(struct map_session_data *sd, int points)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x21b));
- WFIFOW(fd,0) = 0x21b;
- WFIFOL(fd,2) = points;
- WFIFOL(fd,6) = sd->status.fame;
- WFIFOSET(fd, packet_len(0x21b));
-}
-
-
-/// /alchemist list (ZC_ALCHEMIST_RANK).
-/// 021a { <name>.24B }*10 { <point>.L }*10
-void clif_alchemist(struct map_session_data* sd)
-{
- int i, fd = sd->fd;
- const char* name;
-
- WFIFOHEAD(fd,packet_len(0x21a));
- WFIFOW(fd,0) = 0x21a;
- //Packet size limits this list to 10 elements. [Skotlex]
- for (i = 0; i < 10 && i < MAX_FAME_LIST; i++) {
- if (chemist_fame_list[i].id > 0) {
- if (strcmp(chemist_fame_list[i].name, "-") == 0 &&
- (name = map_charid2nick(chemist_fame_list[i].id)) != NULL)
- {
- memcpy(WFIFOP(fd, 2 + 24 * i), name, NAME_LENGTH);
- } else
- memcpy(WFIFOP(fd, 2 + 24 * i), chemist_fame_list[i].name, NAME_LENGTH);
- } else
- memcpy(WFIFOP(fd, 2 + 24 * i), "None", NAME_LENGTH);
- WFIFOL(fd, 242 + i * 4) = chemist_fame_list[i].fame;
- }
- for(;i < 10; i++) { //In case the MAX is less than 10.
- memcpy(WFIFOP(fd, 2 + 24 * i), "Unavailable", NAME_LENGTH);
- WFIFOL(fd, 242 + i * 4) = 0;
- }
-
- WFIFOSET(fd, packet_len(0x21a));
-}
-
-
-/// /alchemist (CZ_ALCHEMIST_RANK).
-/// 0218
-void clif_parse_Alchemist(int fd,struct map_session_data *sd)
-{
- clif_alchemist(sd);
-}
-
-
-/// Notification about alchemist points (ZC_ALCHEMIST_POINT).
-/// 021c <points>.L <total points>.L
-void clif_fame_alchemist(struct map_session_data *sd, int points)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x21c));
- WFIFOW(fd,0) = 0x21c;
- WFIFOL(fd,2) = points;
- WFIFOL(fd,6) = sd->status.fame;
- WFIFOSET(fd, packet_len(0x21c));
-}
-
-
-/// /taekwon list (ZC_TAEKWON_RANK).
-/// 0226 { <name>.24B }*10 { <point>.L }*10
-void clif_taekwon(struct map_session_data* sd)
-{
- int i, fd = sd->fd;
- const char* name;
-
- WFIFOHEAD(fd,packet_len(0x226));
- WFIFOW(fd,0) = 0x226;
- //Packet size limits this list to 10 elements. [Skotlex]
- for (i = 0; i < 10 && i < MAX_FAME_LIST; i++) {
- if (taekwon_fame_list[i].id > 0) {
- if (strcmp(taekwon_fame_list[i].name, "-") == 0 &&
- (name = map_charid2nick(taekwon_fame_list[i].id)) != NULL)
- {
- memcpy(WFIFOP(fd, 2 + 24 * i), name, NAME_LENGTH);
- } else
- memcpy(WFIFOP(fd, 2 + 24 * i), taekwon_fame_list[i].name, NAME_LENGTH);
- } else
- memcpy(WFIFOP(fd, 2 + 24 * i), "None", NAME_LENGTH);
- WFIFOL(fd, 242 + i * 4) = taekwon_fame_list[i].fame;
- }
- for(;i < 10; i++) { //In case the MAX is less than 10.
- memcpy(WFIFOP(fd, 2 + 24 * i), "Unavailable", NAME_LENGTH);
- WFIFOL(fd, 242 + i * 4) = 0;
- }
- WFIFOSET(fd, packet_len(0x226));
-}
-
-
-/// /taekwon (CZ_TAEKWON_RANK).
-/// 0225
-void clif_parse_Taekwon(int fd,struct map_session_data *sd)
-{
- clif_taekwon(sd);
-}
-
-
-/// Notification about taekwon points (ZC_TAEKWON_POINT).
-/// 0224 <points>.L <total points>.L
-void clif_fame_taekwon(struct map_session_data *sd, int points)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x224));
- WFIFOW(fd,0) = 0x224;
- WFIFOL(fd,2) = points;
- WFIFOL(fd,6) = sd->status.fame;
- WFIFOSET(fd, packet_len(0x224));
-}
-
-
-/// /pk list (ZC_KILLER_RANK).
-/// 0238 { <name>.24B }*10 { <point>.L }*10
-void clif_ranking_pk(struct map_session_data* sd)
-{
- int i, fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x238));
- WFIFOW(fd,0) = 0x238;
- for(i=0;i<10;i++){
- memcpy(WFIFOP(fd,i*24+2), "Unknown", NAME_LENGTH);
- WFIFOL(fd,i*4+242) = 0;
- }
- WFIFOSET(fd, packet_len(0x238));
-}
-
-
-/// /pk (CZ_KILLER_RANK).
-/// 0237
-void clif_parse_RankingPk(int fd,struct map_session_data *sd)
-{
- clif_ranking_pk(sd);
-}
-
-
-/// SG Feel save OK [Komurka] (CZ_AGREE_STARPLACE).
-/// 0254 <which>.B
-/// which:
-/// 0 = sun
-/// 1 = moon
-/// 2 = star
-void clif_parse_FeelSaveOk(int fd,struct map_session_data *sd)
-{
- int i;
- if (sd->menuskill_id != SG_FEEL)
- return;
- i = sd->menuskill_val-1;
- if (i<0 || i >= MAX_PC_FEELHATE) return; //Bug?
-
- sd->feel_map[i].index = map_id2index(sd->bl.m);
- sd->feel_map[i].m = sd->bl.m;
- pc_setglobalreg(sd,sg_info[i].feel_var,sd->feel_map[i].index);
-
-//Are these really needed? Shouldn't they show up automatically from the feel save packet?
-// clif_misceffect2(&sd->bl, 0x1b0);
-// clif_misceffect2(&sd->bl, 0x21f);
- clif_feel_info(sd, i, 0);
- clif_menuskill_clear(sd);
-}
-
-
-/// Star Gladiator's Feeling map confirmation prompt (ZC_STARPLACE).
-/// 0253 <which>.B
-/// which:
-/// 0 = sun
-/// 1 = moon
-/// 2 = star
-void clif_feel_req(int fd, struct map_session_data *sd, uint16 skill_lv)
-{
- WFIFOHEAD(fd,packet_len(0x253));
- WFIFOW(fd,0)=0x253;
- WFIFOB(fd,2)=TOB(skill_lv-1);
- WFIFOSET(fd, packet_len(0x253));
- sd->menuskill_id = SG_FEEL;
- sd->menuskill_val = skill_lv;
-}
-
-
-/// Request to change homunculus' name (CZ_RENAME_MER).
-/// 0231 <name>.24B
-void clif_parse_ChangeHomunculusName(int fd, struct map_session_data *sd)
-{
- merc_hom_change_name(sd,(char*)RFIFOP(fd,2));
-}
-
-
-/// Request to warp/move homunculus/mercenary to it's owner (CZ_REQUEST_MOVETOOWNER).
-/// 0234 <id>.L
-void clif_parse_HomMoveToMaster(int fd, struct map_session_data *sd)
-{
- int id = RFIFOL(fd,2); // Mercenary or Homunculus
- struct block_list *bl = NULL;
- struct unit_data *ud = NULL;
-
- if( sd->md && sd->md->bl.id == id )
- bl = &sd->md->bl;
- else if( merc_is_hom_active(sd->hd) && sd->hd->bl.id == id )
- bl = &sd->hd->bl; // Moving Homunculus
- else
- return;
-
- unit_calc_pos(bl, sd->bl.x, sd->bl.y, sd->ud.dir);
- ud = unit_bl2ud(bl);
- unit_walktoxy(bl, ud->to_x, ud->to_y, 4);
-}
-
-
-/// Request to move homunculus/mercenary (CZ_REQUEST_MOVENPC).
-/// 0232 <id>.L <position data>.3B
-void clif_parse_HomMoveTo(int fd, struct map_session_data *sd)
-{
- int id = RFIFOL(fd,2); // Mercenary or Homunculus
- struct block_list *bl = NULL;
- short x, y;
-
- RFIFOPOS(fd, packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1], &x, &y, NULL);
-
- if( sd->md && sd->md->bl.id == id )
- bl = &sd->md->bl; // Moving Mercenary
- else if( merc_is_hom_active(sd->hd) && sd->hd->bl.id == id )
- bl = &sd->hd->bl; // Moving Homunculus
- else
- return;
-
- unit_walktoxy(bl, x, y, 4);
-}
-
-
-/// Request to do an action with homunculus/mercenary (CZ_REQUEST_ACTNPC).
-/// 0233 <id>.L <target id>.L <action>.B
-/// action:
-/// always 0
-void clif_parse_HomAttack(int fd,struct map_session_data *sd)
-{
- struct block_list *bl = NULL;
- int id = RFIFOL(fd,2),
- target_id = RFIFOL(fd,6),
- action_type = RFIFOB(fd,10);
-
- if( merc_is_hom_active(sd->hd) && sd->hd->bl.id == id )
- bl = &sd->hd->bl;
- else if( sd->md && sd->md->bl.id == id )
- bl = &sd->md->bl;
- else return;
-
- unit_stop_attack(bl);
- unit_attack(bl, target_id, action_type != 0);
-}
-
-
-/// Request to invoke a homunculus menu action (CZ_COMMAND_MER).
-/// 022d <type>.W <command>.B
-/// type:
-/// always 0
-/// command:
-/// 0 = homunculus information
-/// 1 = feed
-/// 2 = delete
-void clif_parse_HomMenu(int fd, struct map_session_data *sd)
-{ //[orn]
- int cmd;
-
- cmd = RFIFOW(fd,0);
-
- if(!merc_is_hom_active(sd->hd))
- return;
-
- merc_menu(sd,RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[1]));
-}
-
-
-/// Request to resurrect oneself using Token of Siegfried (CZ_STANDING_RESURRECTION).
-/// 0292
-void clif_parse_AutoRevive(int fd, struct map_session_data *sd)
-{
- int item_position = pc_search_inventory(sd, ITEMID_TOKEN_OF_SIEGFRIED);
-
- if (item_position < 0)
- return;
-
- if (sd->sc.data[SC_HELLPOWER]) //Cannot res while under the effect of SC_HELLPOWER.
- return;
-
- if (!status_revive(&sd->bl, 100, 100))
- return;
-
- clif_skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,4,1);
- pc_delitem(sd, item_position, 1, 0, 1, LOG_TYPE_CONSUME);
-}
-
-
-/// Information about character's status values (ZC_ACK_STATUS_GM).
-/// 0214 <str>.B <standardStr>.B <agi>.B <standardAgi>.B <vit>.B <standardVit>.B
-/// <int>.B <standardInt>.B <dex>.B <standardDex>.B <luk>.B <standardLuk>.B
-/// <attPower>.W <refiningPower>.W <max_mattPower>.W <min_mattPower>.W
-/// <itemdefPower>.W <plusdefPower>.W <mdefPower>.W <plusmdefPower>.W
-/// <hitSuccessValue>.W <avoidSuccessValue>.W <plusAvoidSuccessValue>.W
-/// <criticalSuccessValue>.W <ASPD>.W <plusASPD>.W
-void clif_check(int fd, struct map_session_data* pl_sd)
-{
- WFIFOHEAD(fd,packet_len(0x214));
- WFIFOW(fd, 0) = 0x214;
- WFIFOB(fd, 2) = min(pl_sd->status.str, UINT8_MAX);
- WFIFOB(fd, 3) = pc_need_status_point(pl_sd, SP_STR, 1);
- WFIFOB(fd, 4) = min(pl_sd->status.agi, UINT8_MAX);
- WFIFOB(fd, 5) = pc_need_status_point(pl_sd, SP_AGI, 1);
- WFIFOB(fd, 6) = min(pl_sd->status.vit, UINT8_MAX);
- WFIFOB(fd, 7) = pc_need_status_point(pl_sd, SP_VIT, 1);
- WFIFOB(fd, 8) = min(pl_sd->status.int_, UINT8_MAX);
- WFIFOB(fd, 9) = pc_need_status_point(pl_sd, SP_INT, 1);
- WFIFOB(fd,10) = min(pl_sd->status.dex, UINT8_MAX);
- WFIFOB(fd,11) = pc_need_status_point(pl_sd, SP_DEX, 1);
- WFIFOB(fd,12) = min(pl_sd->status.luk, UINT8_MAX);
- WFIFOB(fd,13) = pc_need_status_point(pl_sd, SP_LUK, 1);
- WFIFOW(fd,14) = pl_sd->battle_status.batk+pl_sd->battle_status.rhw.atk+pl_sd->battle_status.lhw.atk;
- WFIFOW(fd,16) = pl_sd->battle_status.rhw.atk2+pl_sd->battle_status.lhw.atk2;
- WFIFOW(fd,18) = pl_sd->battle_status.matk_max;
- WFIFOW(fd,20) = pl_sd->battle_status.matk_min;
- WFIFOW(fd,22) = pl_sd->battle_status.def;
- WFIFOW(fd,24) = pl_sd->battle_status.def2;
- WFIFOW(fd,26) = pl_sd->battle_status.mdef;
- WFIFOW(fd,28) = pl_sd->battle_status.mdef2;
- WFIFOW(fd,30) = pl_sd->battle_status.hit;
- WFIFOW(fd,32) = pl_sd->battle_status.flee;
- WFIFOW(fd,34) = pl_sd->battle_status.flee2/10;
- WFIFOW(fd,36) = pl_sd->battle_status.cri/10;
- WFIFOW(fd,38) = (2000-pl_sd->battle_status.amotion)/10; // aspd
- WFIFOW(fd,40) = 0; // FIXME: What is 'plusASPD' supposed to be? Maybe adelay?
- WFIFOSET(fd,packet_len(0x214));
-}
-
-
-/// /check (CZ_REQ_STATUS_GM).
-/// Request character's status values.
-/// 0213 <char name>.24B
-void clif_parse_Check(int fd, struct map_session_data *sd)
-{
- char charname[NAME_LENGTH];
- struct map_session_data* pl_sd;
-
- if(!pc_has_permission(sd, PC_PERM_USE_CHECK))
- return;
-
- safestrncpy(charname, (const char*)RFIFOP(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), sizeof(charname));
-
- if( ( pl_sd = map_nick2sd(charname) ) == NULL || pc_get_group_level(sd) < pc_get_group_level(pl_sd) )
- {
- return;
- }
-
- clif_check(fd, pl_sd);
-}
-
-
-
-/// MAIL SYSTEM
-/// By Zephyrus
-///
-
-/// Notification about the result of adding an item to mail (ZC_ACK_MAIL_ADD_ITEM).
-/// 0255 <index>.W <result>.B
-/// result:
-/// 0 = success
-/// 1 = failure
-void clif_Mail_setattachment(int fd, int index, uint8 flag)
-{
- WFIFOHEAD(fd,packet_len(0x255));
- WFIFOW(fd,0) = 0x255;
- WFIFOW(fd,2) = index;
- WFIFOB(fd,4) = flag;
- WFIFOSET(fd,packet_len(0x255));
-}
-
-
-/// Notification about the result of retrieving a mail attachment (ZC_MAIL_REQ_GET_ITEM).
-/// 0245 <result>.B
-/// result:
-/// 0 = success
-/// 1 = failure
-/// 2 = too many items
-void clif_Mail_getattachment(int fd, uint8 flag)
-{
- WFIFOHEAD(fd,packet_len(0x245));
- WFIFOW(fd,0) = 0x245;
- WFIFOB(fd,2) = flag;
- WFIFOSET(fd,packet_len(0x245));
-}
-
-
-/// Notification about the result of sending a mail (ZC_MAIL_REQ_SEND).
-/// 0249 <result>.B
-/// result:
-/// 0 = success
-/// 1 = recipinent does not exist
-void clif_Mail_send(int fd, bool fail)
-{
- WFIFOHEAD(fd,packet_len(0x249));
- WFIFOW(fd,0) = 0x249;
- WFIFOB(fd,2) = fail;
- WFIFOSET(fd,packet_len(0x249));
-}
-
-
-/// Notification about the result of deleting a mail (ZC_ACK_MAIL_DELETE).
-/// 0257 <mail id>.L <result>.W
-/// result:
-/// 0 = success
-/// 1 = failure
-void clif_Mail_delete(int fd, int mail_id, short fail)
-{
- WFIFOHEAD(fd, packet_len(0x257));
- WFIFOW(fd,0) = 0x257;
- WFIFOL(fd,2) = mail_id;
- WFIFOW(fd,6) = fail;
- WFIFOSET(fd, packet_len(0x257));
-}
-
-
-/// Notification about the result of returning a mail (ZC_ACK_MAIL_RETURN).
-/// 0274 <mail id>.L <result>.W
-/// result:
-/// 0 = success
-/// 1 = failure
-void clif_Mail_return(int fd, int mail_id, short fail)
-{
- WFIFOHEAD(fd,packet_len(0x274));
- WFIFOW(fd,0) = 0x274;
- WFIFOL(fd,2) = mail_id;
- WFIFOW(fd,6) = fail;
- WFIFOSET(fd,packet_len(0x274));
-}
-
-
-/// Notification about new mail (ZC_MAIL_RECEIVE).
-/// 024a <mail id>.L <title>.40B <sender>.24B
-void clif_Mail_new(int fd, int mail_id, const char *sender, const char *title)
-{
- WFIFOHEAD(fd,packet_len(0x24a));
- WFIFOW(fd,0) = 0x24a;
- WFIFOL(fd,2) = mail_id;
- safestrncpy((char*)WFIFOP(fd,6), title, MAIL_TITLE_LENGTH);
- safestrncpy((char*)WFIFOP(fd,46), sender, NAME_LENGTH);
- WFIFOSET(fd,packet_len(0x24a));
-}
-
-
-/// Opens/closes the mail window (ZC_MAIL_WINDOWS).
-/// 0260 <type>.L
-/// type:
-/// 0 = open
-/// 1 = close
-void clif_Mail_window(int fd, int flag)
-{
- WFIFOHEAD(fd,packet_len(0x260));
- WFIFOW(fd,0) = 0x260;
- WFIFOL(fd,2) = flag;
- WFIFOSET(fd,packet_len(0x260));
-}
-
-
-/// Lists mails stored in inbox (ZC_MAIL_REQ_GET_LIST).
-/// 0240 <packet len>.W <amount>.L { <mail id>.L <title>.40B <read>.B <sender>.24B <time>.L }*amount
-/// read:
-/// 0 = unread
-/// 1 = read
-void clif_Mail_refreshinbox(struct map_session_data *sd)
-{
- int fd = sd->fd;
- struct mail_data *md = &sd->mail.inbox;
- struct mail_message *msg;
- int len, i, j;
-
- len = 8 + (73 * md->amount);
-
- WFIFOHEAD(fd,len);
- WFIFOW(fd,0) = 0x240;
- WFIFOW(fd,2) = len;
- WFIFOL(fd,4) = md->amount;
- for( i = j = 0; i < MAIL_MAX_INBOX && j < md->amount; i++ )
- {
- msg = &md->msg[i];
- if (msg->id < 1)
- continue;
-
- WFIFOL(fd,8+73*j) = msg->id;
- memcpy(WFIFOP(fd,12+73*j), msg->title, MAIL_TITLE_LENGTH);
- WFIFOB(fd,52+73*j) = (msg->status != MAIL_UNREAD);
- memcpy(WFIFOP(fd,53+73*j), msg->send_name, NAME_LENGTH);
- WFIFOL(fd,77+73*j) = (uint32)msg->timestamp;
- j++;
- }
- WFIFOSET(fd,len);
-
- if( md->full )
- {// TODO: is this official?
- char output[100];
- sprintf(output, "Inbox is full (Max %d). Delete some mails.", MAIL_MAX_INBOX);
- clif_disp_onlyself(sd, output, strlen(output));
- }
-}
-
-
-/// Mail inbox list request (CZ_MAIL_GET_LIST).
-/// 023f
-void clif_parse_Mail_refreshinbox(int fd, struct map_session_data *sd)
-{
- struct mail_data* md = &sd->mail.inbox;
-
- if( md->amount < MAIL_MAX_INBOX && (md->full || sd->mail.changed) )
- intif_Mail_requestinbox(sd->status.char_id, 1);
- else
- clif_Mail_refreshinbox(sd);
-
- mail_removeitem(sd, 0);
- mail_removezeny(sd, 0);
-}
-
-
-/// Opens a mail (ZC_MAIL_REQ_OPEN).
-/// 0242 <packet len>.W <mail id>.L <title>.40B <sender>.24B <time>.L <zeny>.L
-/// <amount>.L <name id>.W <item type>.W <identified>.B <damaged>.B <refine>.B
-/// <card1>.W <card2>.W <card3>.W <card4>.W <message>.?B
-void clif_Mail_read(struct map_session_data *sd, int mail_id)
-{
- int i, fd = sd->fd;
-
- ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
- if( i == MAIL_MAX_INBOX )
- {
- clif_Mail_return(sd->fd, mail_id, 1); // Mail doesn't exist
- ShowWarning("clif_parse_Mail_read: char '%s' trying to read a message not the inbox.\n", sd->status.name);
- return;
- }
- else
- {
- struct mail_message *msg = &sd->mail.inbox.msg[i];
- struct item *item = &msg->item;
- struct item_data *data;
- int msg_len = strlen(msg->body), len;
-
- if( msg_len == 0 ) {
- strcpy(msg->body, "(no message)");
- msg_len = strlen(msg->body);
- }
-
- len = 101 + msg_len;
-
- WFIFOHEAD(fd,len);
- WFIFOW(fd,0) = 0x242;
- WFIFOW(fd,2) = len;
- WFIFOL(fd,4) = msg->id;
- safestrncpy((char*)WFIFOP(fd,8), msg->title, MAIL_TITLE_LENGTH + 1);
- safestrncpy((char*)WFIFOP(fd,48), msg->send_name, NAME_LENGTH + 1);
- WFIFOL(fd,72) = 0;
- WFIFOL(fd,76) = msg->zeny;
-
- if( item->nameid && (data = itemdb_exists(item->nameid)) != NULL )
- {
- WFIFOL(fd,80) = item->amount;
- WFIFOW(fd,84) = (data->view_id)?data->view_id:item->nameid;
- WFIFOW(fd,86) = data->type;
- WFIFOB(fd,88) = item->identify;
- WFIFOB(fd,89) = item->attribute;
- WFIFOB(fd,90) = item->refine;
- WFIFOW(fd,91) = item->card[0];
- WFIFOW(fd,93) = item->card[1];
- WFIFOW(fd,95) = item->card[2];
- WFIFOW(fd,97) = item->card[3];
- }
- else // no item, set all to zero
- memset(WFIFOP(fd,80), 0x00, 19);
-
- WFIFOB(fd,99) = (unsigned char)msg_len;
- safestrncpy((char*)WFIFOP(fd,100), msg->body, msg_len + 1);
- WFIFOSET(fd,len);
-
- if (msg->status == MAIL_UNREAD) {
- msg->status = MAIL_READ;
- intif_Mail_read(mail_id);
- clif_parse_Mail_refreshinbox(fd, sd);
- }
- }
-}
-
-
-/// Request to open a mail (CZ_MAIL_OPEN).
-/// 0241 <mail id>.L
-void clif_parse_Mail_read(int fd, struct map_session_data *sd)
-{
- int mail_id = RFIFOL(fd,2);
-
- if( mail_id <= 0 )
- return;
- if( mail_invalid_operation(sd) )
- return;
-
- clif_Mail_read(sd, RFIFOL(fd,2));
-}
-
-
-/// Request to receive mail's attachment (CZ_MAIL_GET_ITEM).
-/// 0244 <mail id>.L
-void clif_parse_Mail_getattach(int fd, struct map_session_data *sd)
-{
- int mail_id = RFIFOL(fd,2);
- int i;
- bool fail = false;
-
- if( !chrif_isconnected() )
- return;
- if( mail_id <= 0 )
- return;
- if( mail_invalid_operation(sd) )
- return;
-
- ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
- if( i == MAIL_MAX_INBOX )
- return;
-
- if( sd->mail.inbox.msg[i].zeny < 1 && (sd->mail.inbox.msg[i].item.nameid < 1 || sd->mail.inbox.msg[i].item.amount < 1) )
- return;
-
- if( sd->mail.inbox.msg[i].zeny + sd->status.zeny > MAX_ZENY )
- {
- clif_Mail_getattachment(fd, 1);
- return;
- }
-
- if( sd->mail.inbox.msg[i].item.nameid > 0 )
- {
- struct item_data *data;
- unsigned int weight;
-
- if ((data = itemdb_exists(sd->mail.inbox.msg[i].item.nameid)) == NULL)
- return;
-
- switch( pc_checkadditem(sd, data->nameid, sd->mail.inbox.msg[i].item.amount) )
- {
- case ADDITEM_NEW:
- fail = ( pc_inventoryblank(sd) == 0 );
- break;
- case ADDITEM_OVERAMOUNT:
- fail = true;
- }
-
- if( fail )
- {
- clif_Mail_getattachment(fd, 1);
- return;
- }
-
- weight = data->weight * sd->mail.inbox.msg[i].item.amount;
- if( sd->weight + weight > sd->max_weight )
- {
- clif_Mail_getattachment(fd, 2);
- return;
- }
- }
-
- sd->mail.inbox.msg[i].zeny = 0;
- memset(&sd->mail.inbox.msg[i].item, 0, sizeof(struct item));
- clif_Mail_read(sd, mail_id);
-
- intif_Mail_getattach(sd->status.char_id, mail_id);
-}
-
-
-/// Request to delete a mail (CZ_MAIL_DELETE).
-/// 0243 <mail id>.L
-void clif_parse_Mail_delete(int fd, struct map_session_data *sd)
-{
- int mail_id = RFIFOL(fd,2);
- int i;
-
- if( !chrif_isconnected() )
- return;
- if( mail_id <= 0 )
- return;
- if( mail_invalid_operation(sd) )
- return;
-
- ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
- if (i < MAIL_MAX_INBOX)
- {
- struct mail_message *msg = &sd->mail.inbox.msg[i];
-
- if( (msg->item.nameid > 0 && msg->item.amount > 0) || msg->zeny > 0 )
- {// can't delete mail without removing attachment first
- clif_Mail_delete(sd->fd, mail_id, 1);
- return;
- }
-
- intif_Mail_delete(sd->status.char_id, mail_id);
- }
-}
-
-
-/// Request to return a mail (CZ_REQ_MAIL_RETURN).
-/// 0273 <mail id>.L <receive name>.24B
-void clif_parse_Mail_return(int fd, struct map_session_data *sd)
-{
- int mail_id = RFIFOL(fd,2);
- int i;
-
- if( mail_id <= 0 )
- return;
- if( mail_invalid_operation(sd) )
- return;
-
- ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
- if( i < MAIL_MAX_INBOX && sd->mail.inbox.msg[i].send_id != 0 )
- intif_Mail_return(sd->status.char_id, mail_id);
- else
- clif_Mail_return(sd->fd, mail_id, 1);
-}
-
-
-/// Request to add an item or Zeny to mail (CZ_MAIL_ADD_ITEM).
-/// 0247 <index>.W <amount>.L
-void clif_parse_Mail_setattach(int fd, struct map_session_data *sd)
-{
- int idx = RFIFOW(fd,2);
- int amount = RFIFOL(fd,4);
- unsigned char flag;
-
- if( !chrif_isconnected() )
- return;
- if (idx < 0 || amount < 0)
- return;
-
- flag = mail_setitem(sd, idx, amount);
- clif_Mail_setattachment(fd,idx,flag);
-}
-
-
-/// Request to reset mail item and/or Zeny (CZ_MAIL_RESET_ITEM).
-/// 0246 <type>.W
-/// type:
-/// 0 = reset all
-/// 1 = remove item
-/// 2 = remove zeny
-void clif_parse_Mail_winopen(int fd, struct map_session_data *sd)
-{
- int flag = RFIFOW(fd,2);
-
- if (flag == 0 || flag == 1)
- mail_removeitem(sd, 0);
- if (flag == 0 || flag == 2)
- mail_removezeny(sd, 0);
-}
-
-
-/// Request to send mail (CZ_MAIL_SEND).
-/// 0248 <packet len>.W <recipient>.24B <title>.40B <body len>.B <body>.?B
-void clif_parse_Mail_send(int fd, struct map_session_data *sd)
-{
- struct mail_message msg;
- int body_len;
-
- if( !chrif_isconnected() )
- return;
- if( sd->state.trading )
- return;
-
- if( RFIFOW(fd,2) < 69 ) {
- ShowWarning("Invalid Msg Len from account %d.\n", sd->status.account_id);
- return;
- }
-
- if( DIFF_TICK(sd->cansendmail_tick, gettick()) > 0 )
- {
- clif_displaymessage(sd->fd,msg_txt(675)); //"Cannot send mails too fast!!."
- clif_Mail_send(fd, true); // fail
- return;
- }
-
- body_len = RFIFOB(fd,68);
-
- if (body_len > MAIL_BODY_LENGTH)
- body_len = MAIL_BODY_LENGTH;
-
- if( !mail_setattachment(sd, &msg) )
- { // Invalid Append condition
- clif_Mail_send(sd->fd, true); // fail
- mail_removeitem(sd,0);
- mail_removezeny(sd,0);
- return;
- }
-
- msg.id = 0; // id will be assigned by charserver
- msg.send_id = sd->status.char_id;
- msg.dest_id = 0; // will attempt to resolve name
- safestrncpy(msg.send_name, sd->status.name, NAME_LENGTH);
- safestrncpy(msg.dest_name, (char*)RFIFOP(fd,4), NAME_LENGTH);
- safestrncpy(msg.title, (char*)RFIFOP(fd,28), MAIL_TITLE_LENGTH);
-
- if (msg.title[0] == '\0') {
- return; // Message has no length and somehow client verification was skipped.
- }
-
- if (body_len)
- safestrncpy(msg.body, (char*)RFIFOP(fd,69), body_len + 1);
- else
- memset(msg.body, 0x00, MAIL_BODY_LENGTH);
-
- msg.timestamp = time(NULL);
- if( !intif_Mail_send(sd->status.account_id, &msg) )
- mail_deliveryfail(sd, &msg);
-
- sd->cansendmail_tick = gettick() + 1000; // 1 Second flood Protection
-}
-
-
-/// AUCTION SYSTEM
-/// By Zephyrus
-///
-
-/// Opens/closes the auction window (ZC_AUCTION_WINDOWS).
-/// 025f <type>.L
-/// type:
-/// 0 = open
-/// 1 = close
-void clif_Auction_openwindow(struct map_session_data *sd)
-{
- int fd = sd->fd;
-
- if( sd->state.storage_flag || sd->state.vending || sd->state.buyingstore || sd->state.trading )
- return;
-
- WFIFOHEAD(fd,packet_len(0x25f));
- WFIFOW(fd,0) = 0x25f;
- WFIFOL(fd,2) = 0;
- WFIFOSET(fd,packet_len(0x25f));
-}
-
-
-/// Returns auction item search results (ZC_AUCTION_ITEM_REQ_SEARCH).
-/// 0252 <packet len>.W <pages>.L <count>.L { <auction id>.L <seller name>.24B <name id>.W <type>.L <amount>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <now price>.L <max price>.L <buyer name>.24B <delete time>.L }*
-void clif_Auction_results(struct map_session_data *sd, short count, short pages, uint8 *buf)
-{
- int i, fd = sd->fd, len = sizeof(struct auction_data);
- struct auction_data auction;
- struct item_data *item;
- int k;
-
- WFIFOHEAD(fd,12 + (count * 83));
- WFIFOW(fd,0) = 0x252;
- WFIFOW(fd,2) = 12 + (count * 83);
- WFIFOL(fd,4) = pages;
- WFIFOL(fd,8) = count;
-
- for( i = 0; i < count; i++ )
- {
- memcpy(&auction, RBUFP(buf,i * len), len);
- k = 12 + (i * 83);
-
- WFIFOL(fd,k) = auction.auction_id;
- safestrncpy((char*)WFIFOP(fd,4+k), auction.seller_name, NAME_LENGTH);
-
- if( (item = itemdb_exists(auction.item.nameid)) != NULL && item->view_id > 0 )
- WFIFOW(fd,28+k) = item->view_id;
- else
- WFIFOW(fd,28+k) = auction.item.nameid;
-
- WFIFOL(fd,30+k) = auction.type;
- WFIFOW(fd,34+k) = auction.item.amount; // Always 1
- WFIFOB(fd,36+k) = auction.item.identify;
- WFIFOB(fd,37+k) = auction.item.attribute;
- WFIFOB(fd,38+k) = auction.item.refine;
- WFIFOW(fd,39+k) = auction.item.card[0];
- WFIFOW(fd,41+k) = auction.item.card[1];
- WFIFOW(fd,43+k) = auction.item.card[2];
- WFIFOW(fd,45+k) = auction.item.card[3];
- WFIFOL(fd,47+k) = auction.price;
- WFIFOL(fd,51+k) = auction.buynow;
- safestrncpy((char*)WFIFOP(fd,55+k), auction.buyer_name, NAME_LENGTH);
- WFIFOL(fd,79+k) = (uint32)auction.timestamp;
- }
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Result from request to add an item (ZC_ACK_AUCTION_ADD_ITEM).
-/// 0256 <index>.W <result>.B
-/// result:
-/// 0 = success
-/// 1 = failure
-static void clif_Auction_setitem(int fd, int index, bool fail)
-{
- WFIFOHEAD(fd,packet_len(0x256));
- WFIFOW(fd,0) = 0x256;
- WFIFOW(fd,2) = index;
- WFIFOB(fd,4) = fail;
- WFIFOSET(fd,packet_len(0x256));
-}
-
-
-/// Request to initialize 'new auction' data (CZ_AUCTION_CREATE).
-/// 024b <type>.W
-/// type:
-/// 0 = create (any other action in auction window)
-/// 1 = cancel (cancel pressed on register tab)
-/// ? = junk, uninitialized value (ex. when switching between list filters)
-void clif_parse_Auction_cancelreg(int fd, struct map_session_data *sd)
-{
- if( sd->auction.amount > 0 )
- clif_additem(sd, sd->auction.index, sd->auction.amount, 0);
-
- sd->auction.amount = 0;
-}
-
-
-/// Request to add an item to the action (CZ_AUCTION_ADD_ITEM).
-/// 024c <index>.W <count>.L
-void clif_parse_Auction_setitem(int fd, struct map_session_data *sd)
-{
- int idx = RFIFOW(fd,2) - 2;
- int amount = RFIFOL(fd,4); // Always 1
- struct item_data *item;
-
- if( sd->auction.amount > 0 )
- sd->auction.amount = 0;
-
- if( idx < 0 || idx >= MAX_INVENTORY )
- {
- ShowWarning("Character %s trying to set invalid item index in auctions.\n", sd->status.name);
- return;
- }
-
- if( amount != 1 || amount > sd->status.inventory[idx].amount )
- { // By client, amount is always set to 1. Maybe this is a future implementation.
- ShowWarning("Character %s trying to set invalid amount in auctions.\n", sd->status.name);
- return;
- }
-
- if( (item = itemdb_exists(sd->status.inventory[idx].nameid)) != NULL && !(item->type == IT_ARMOR || item->type == IT_PETARMOR || item->type == IT_WEAPON || item->type == IT_CARD || item->type == IT_ETC) )
- { // Consumable or pets are not allowed
- clif_Auction_setitem(sd->fd, idx, true);
- return;
- }
-
- if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time ||
- !sd->status.inventory[idx].identify ||
- !itemdb_canauction(&sd->status.inventory[idx],pc_get_group_level(sd)) ) { // Quest Item or something else
- clif_Auction_setitem(sd->fd, idx, true);
- return;
- }
-
- sd->auction.index = idx;
- sd->auction.amount = amount;
- clif_Auction_setitem(fd, idx + 2, false);
-}
-
-/// Result from an auction action (ZC_AUCTION_RESULT).
-/// 0250 <result>.B
-/// result:
-/// 0 = You have failed to bid into the auction
-/// 1 = You have successfully bid in the auction
-/// 2 = The auction has been canceled
-/// 3 = An auction with at least one bidder cannot be canceled
-/// 4 = You cannot register more than 5 items in an auction at a time
-/// 5 = You do not have enough Zeny to pay the Auction Fee
-/// 6 = You have won the auction
-/// 7 = You have failed to win the auction
-/// 8 = You do not have enough Zeny
-/// 9 = You cannot place more than 5 bids at a time
-void clif_Auction_message(int fd, unsigned char flag)
-{
- WFIFOHEAD(fd,packet_len(0x250));
- WFIFOW(fd,0) = 0x250;
- WFIFOB(fd,2) = flag;
- WFIFOSET(fd,packet_len(0x250));
-}
-
-
-/// Result of the auction close request (ZC_AUCTION_ACK_MY_SELL_STOP).
-/// 025e <result>.W
-/// result:
-/// 0 = You have ended the auction
-/// 1 = You cannot end the auction
-/// 2 = Auction ID is incorrect
-void clif_Auction_close(int fd, unsigned char flag)
-{
- WFIFOHEAD(fd,packet_len(0x25e));
- WFIFOW(fd,0) = 0x25d; // BUG: The client identifies this packet as 0x25d (CZ_AUCTION_REQ_MY_SELL_STOP)
- WFIFOW(fd,2) = flag;
- WFIFOSET(fd,packet_len(0x25e));
-}
-
-
-/// Request to add an auction (CZ_AUCTION_ADD).
-/// 024d <now money>.L <max money>.L <delete hour>.W
-void clif_parse_Auction_register(int fd, struct map_session_data *sd)
-{
- struct auction_data auction;
- struct item_data *item;
-
- auction.price = RFIFOL(fd,2);
- auction.buynow = RFIFOL(fd,6);
- auction.hours = RFIFOW(fd,10);
-
- // Invalid Situations...
- if( sd->auction.amount < 1 )
- {
- ShowWarning("Character %s trying to register auction without item.\n", sd->status.name);
- return;
- }
-
- if( auction.price >= auction.buynow )
- {
- ShowWarning("Character %s trying to alter auction prices.\n", sd->status.name);
- return;
- }
-
- if( auction.hours < 1 || auction.hours > 48 )
- {
- ShowWarning("Character %s trying to enter an invalid time for auction.\n", sd->status.name);
- return;
- }
-
- // Auction checks...
- if( sd->status.zeny < (auction.hours * battle_config.auction_feeperhour) )
- {
- clif_Auction_message(fd, 5); // You do not have enough zeny to pay the Auction Fee.
- return;
- }
-
- if( auction.buynow > battle_config.auction_maximumprice )
- { // Zeny Limits
- auction.buynow = battle_config.auction_maximumprice;
- if( auction.price >= auction.buynow )
- auction.price = auction.buynow - 1;
- }
-
- auction.auction_id = 0;
- auction.seller_id = sd->status.char_id;
- safestrncpy(auction.seller_name, sd->status.name, sizeof(auction.seller_name));
- auction.buyer_id = 0;
- memset(auction.buyer_name, '\0', sizeof(auction.buyer_name));
-
- if( sd->status.inventory[sd->auction.index].nameid == 0 || sd->status.inventory[sd->auction.index].amount < sd->auction.amount )
- {
- clif_Auction_message(fd, 2); // The auction has been canceled
- return;
- }
-
- if( (item = itemdb_exists(sd->status.inventory[sd->auction.index].nameid)) == NULL )
- { // Just in case
- clif_Auction_message(fd, 2); // The auction has been canceled
- return;
- }
-
- safestrncpy(auction.item_name, item->jname, sizeof(auction.item_name));
- auction.type = item->type;
- memcpy(&auction.item, &sd->status.inventory[sd->auction.index], sizeof(struct item));
- auction.item.amount = 1;
- auction.timestamp = 0;
-
- if( !intif_Auction_register(&auction) )
- clif_Auction_message(fd, 4); // No Char Server? lets say something to the client
- else
- {
- int zeny = auction.hours*battle_config.auction_feeperhour;
-
- pc_delitem(sd, sd->auction.index, sd->auction.amount, 1, 6, LOG_TYPE_AUCTION);
- sd->auction.amount = 0;
-
- pc_payzeny(sd, zeny, LOG_TYPE_AUCTION, NULL);
- }
-}
-
-
-/// Cancels an auction (CZ_AUCTION_ADD_CANCEL).
-/// 024e <auction id>.L
-void clif_parse_Auction_cancel(int fd, struct map_session_data *sd)
-{
- unsigned int auction_id = RFIFOL(fd,2);
-
- intif_Auction_cancel(sd->status.char_id, auction_id);
-}
-
-
-/// Closes an auction (CZ_AUCTION_REQ_MY_SELL_STOP).
-/// 025d <auction id>.L
-void clif_parse_Auction_close(int fd, struct map_session_data *sd)
-{
- unsigned int auction_id = RFIFOL(fd,2);
-
- intif_Auction_close(sd->status.char_id, auction_id);
-}
-
-
-/// Places a bid on an auction (CZ_AUCTION_BUY).
-/// 024f <auction id>.L <money>.L
-void clif_parse_Auction_bid(int fd, struct map_session_data *sd)
-{
- unsigned int auction_id = RFIFOL(fd,2);
- int bid = RFIFOL(fd,6);
-
- if( !pc_can_give_items(sd) ) { //They aren't supposed to give zeny [Inkfish]
- clif_displaymessage(sd->fd, msg_txt(246));
- return;
- }
-
- if( bid <= 0 )
- clif_Auction_message(fd, 0); // You have failed to bid into the auction
- else if( bid > sd->status.zeny )
- clif_Auction_message(fd, 8); // You do not have enough zeny
- else if ( CheckForCharServer() ) // char server is down (bugreport:1138)
- clif_Auction_message(fd, 0); // You have failed to bid into the auction
- else {
- pc_payzeny(sd, bid, LOG_TYPE_AUCTION, NULL);
- intif_Auction_bid(sd->status.char_id, sd->status.name, auction_id, bid);
- }
-}
-
-
-/// Auction Search (CZ_AUCTION_ITEM_SEARCH).
-/// 0251 <search type>.W <auction id>.L <search text>.24B <page number>.W
-/// search type:
-/// 0 = armor
-/// 1 = weapon
-/// 2 = card
-/// 3 = misc
-/// 4 = name search
-/// 5 = auction id search
-void clif_parse_Auction_search(int fd, struct map_session_data* sd)
-{
- char search_text[NAME_LENGTH];
- short type = RFIFOW(fd,2), page = RFIFOW(fd,32);
- int price = RFIFOL(fd,4); // FIXME: bug #5071
-
- clif_parse_Auction_cancelreg(fd, sd);
-
- safestrncpy(search_text, (char*)RFIFOP(fd,8), sizeof(search_text));
- intif_Auction_requestlist(sd->status.char_id, type, price, search_text, page);
-}
-
-
-/// Requests list of own currently active bids or auctions (CZ_AUCTION_REQ_MY_INFO).
-/// 025c <type>.W
-/// type:
-/// 0 = sell (own auctions)
-/// 1 = buy (own bids)
-void clif_parse_Auction_buysell(int fd, struct map_session_data* sd)
-{
- short type = RFIFOW(fd,2) + 6;
- clif_parse_Auction_cancelreg(fd, sd);
-
- intif_Auction_requestlist(sd->status.char_id, type, 0, "", 1);
-}
-
-
-/// CASH/POINT SHOP
-///
-
-/// List of items offered in a cash shop (ZC_PC_CASH_POINT_ITEMLIST).
-/// 0287 <packet len>.W <cash point>.L { <sell price>.L <discount price>.L <item type>.B <name id>.W }*
-/// 0287 <packet len>.W <cash point>.L <kafra point>.L { <sell price>.L <discount price>.L <item type>.B <name id>.W }* (PACKETVER >= 20070711)
-void clif_cashshop_show(struct map_session_data *sd, struct npc_data *nd)
-{
- int fd,i;
-#if PACKETVER < 20070711
- const int offset = 8;
-#else
- const int offset = 12;
-#endif
-
- nullpo_retv(sd);
- nullpo_retv(nd);
-
- fd = sd->fd;
- sd->npc_shopid = nd->bl.id;
- WFIFOHEAD(fd,offset+nd->u.shop.count*11);
- WFIFOW(fd,0) = 0x287;
- WFIFOW(fd,2) = offset+nd->u.shop.count*11;
- WFIFOL(fd,4) = sd->cashPoints; // Cash Points
-#if PACKETVER >= 20070711
- WFIFOL(fd,8) = sd->kafraPoints; // Kafra Points
-#endif
-
- for( i = 0; i < nd->u.shop.count; i++ )
- {
- struct item_data* id = itemdb_search(nd->u.shop.shop_item[i].nameid);
- WFIFOL(fd,offset+0+i*11) = nd->u.shop.shop_item[i].value;
- WFIFOL(fd,offset+4+i*11) = nd->u.shop.shop_item[i].value; // Discount Price
- WFIFOB(fd,offset+8+i*11) = itemtype(id->type);
- WFIFOW(fd,offset+9+i*11) = ( id->view_id > 0 ) ? id->view_id : id->nameid;
- }
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Cashshop Buy Ack (ZC_PC_CASH_POINT_UPDATE).
-/// 0289 <cash point>.L <error>.W
-/// 0289 <cash point>.L <kafra point>.L <error>.W (PACKETVER >= 20070711)
-/// error:
-/// 0 = The deal has successfully completed. (ERROR_TYPE_NONE)
-/// 1 = The Purchase has failed because the NPC does not exist. (ERROR_TYPE_NPC)
-/// 2 = The Purchase has failed because the Kafra Shop System is not working correctly. (ERROR_TYPE_SYSTEM)
-/// 3 = You are over your Weight Limit. (ERROR_TYPE_INVENTORY_WEIGHT)
-/// 4 = You cannot purchase items while you are in a trade. (ERROR_TYPE_EXCHANGE)
-/// 5 = The Purchase has failed because the Item Information was incorrect. (ERROR_TYPE_ITEM_ID)
-/// 6 = You do not have enough Kafra Credit Points. (ERROR_TYPE_MONEY)
-/// 7 = You can purchase up to 10 items.
-/// 8 = Some items could not be purchased.
-void clif_cashshop_ack(struct map_session_data* sd, int error)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd, packet_len(0x289));
- WFIFOW(fd,0) = 0x289;
- WFIFOL(fd,2) = sd->cashPoints;
-#if PACKETVER < 20070711
- WFIFOW(fd,6) = TOW(error);
-#else
- WFIFOL(fd,6) = sd->kafraPoints;
- WFIFOW(fd,10) = TOW(error);
-#endif
- WFIFOSET(fd, packet_len(0x289));
-}
-
-
-/// Request to buy item(s) from cash shop (CZ_PC_BUY_CASH_POINT_ITEM).
-/// 0288 <name id>.W <amount>.W
-/// 0288 <name id>.W <amount>.W <kafra points>.L (PACKETVER >= 20070711)
-/// 0288 <packet len>.W <kafra points>.L <count>.W { <amount>.W <name id>.W }.4B*count (PACKETVER >= 20100803)
-void clif_parse_cashshop_buy(int fd, struct map_session_data *sd)
-{
- int fail = 0;
- nullpo_retv(sd);
-
- if( sd->state.trading || !sd->npc_shopid )
- fail = 1;
- else
- {
-#if PACKETVER < 20101116
- short nameid = RFIFOW(fd,2);
- short amount = RFIFOW(fd,4);
- int points = RFIFOL(fd,6);
-
- fail = npc_cashshop_buy(sd, nameid, amount, points);
-#else
- int len = RFIFOW(fd,2);
- int points = RFIFOL(fd,4);
- int count = RFIFOW(fd,8);
- unsigned short* item_list = (unsigned short*)RFIFOP(fd,10);
-
- if( len < 10 || len != 10 + count * 4)
- {
- ShowWarning("Player %u sent incorrect cash shop buy packet (len %u:%u)!\n", sd->status.char_id, len, 10 + count * 4);
- return;
- }
- fail = npc_cashshop_buylist(sd,points,count,item_list);
-#endif
- }
-
- clif_cashshop_ack(sd,fail);
-}
-
-
-/// Adoption System
-///
-
-/// Adoption message (ZC_BABYMSG).
-/// 0216 <msg>.L
-/// msg:
-/// 0 = "You cannot adopt more than 1 child."
-/// 1 = "You must be at least character level 70 in order to adopt someone."
-/// 2 = "You cannot adopt a married person."
-void clif_Adopt_reply(struct map_session_data *sd, int type)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,6);
- WFIFOW(fd,0) = 0x216;
- WFIFOL(fd,2) = type;
- WFIFOSET(fd,6);
-}
-
-
-/// Adoption confirmation (ZC_REQ_BABY).
-/// 01f6 <account id>.L <char id>.L <name>.B
-void clif_Adopt_request(struct map_session_data *sd, struct map_session_data *src, int p_id)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,34);
- WFIFOW(fd,0) = 0x1f6;
- WFIFOL(fd,2) = src->status.account_id;
- WFIFOL(fd,6) = p_id;
- memcpy(WFIFOP(fd,10), src->status.name, NAME_LENGTH);
- WFIFOSET(fd,34);
-}
-
-
-/// Request to adopt a player (CZ_REQ_JOIN_BABY).
-/// 01f9 <account id>.L
-void clif_parse_Adopt_request(int fd, struct map_session_data *sd)
-{
- struct map_session_data *tsd = map_id2sd(RFIFOL(fd,2)), *p_sd = map_charid2sd(sd->status.partner_id);
-
- if( pc_can_Adopt(sd, p_sd, tsd) )
- {
- tsd->adopt_invite = sd->status.account_id;
- clif_Adopt_request(tsd, sd, p_sd->status.account_id);
- }
-}
-
-
-/// Answer to adopt confirmation (CZ_JOIN_BABY).
-/// 01f7 <account id>.L <char id>.L <answer>.L
-/// answer:
-/// 0 = rejected
-/// 1 = accepted
-void clif_parse_Adopt_reply(int fd, struct map_session_data *sd)
-{
- int p1_id = RFIFOL(fd,2);
- int p2_id = RFIFOL(fd,6);
- int result = RFIFOL(fd,10);
- struct map_session_data* p1_sd = map_id2sd(p1_id);
- struct map_session_data* p2_sd = map_id2sd(p2_id);
-
- int pid = sd->adopt_invite;
- sd->adopt_invite = 0;
-
- if( p1_sd == NULL || p2_sd == NULL )
- return; // Both players need to be online
-
- if( pid != p1_sd->status.account_id )
- return; // Incorrect values
-
- if( result == 0 )
- return; // Rejected
-
- pc_adoption(p1_sd, p2_sd, sd);
-}
-
-
-/// Convex Mirror (ZC_BOSS_INFO).
-/// 0293 <infoType>.B <x>.L <y>.L <minHours>.W <minMinutes>.W <maxHours>.W <maxMinutes>.W <monster name>.51B
-/// infoType:
-/// 0 = No boss on this map (BOSS_INFO_NOT).
-/// 1 = Boss is alive (position update) (BOSS_INFO_ALIVE).
-/// 2 = Boss is alive (initial announce) (BOSS_INFO_ALIVE_WITHMSG).
-/// 3 = Boss is dead (BOSS_INFO_DEAD).
-void clif_bossmapinfo(int fd, struct mob_data *md, short flag)
-{
- WFIFOHEAD(fd,70);
- memset(WFIFOP(fd,0),0,70);
- WFIFOW(fd,0) = 0x293;
-
- if( md != NULL )
- {
- if( md->bl.prev != NULL )
- { // Boss on This Map
- if( flag )
- {
- WFIFOB(fd,2) = 1;
- WFIFOL(fd,3) = md->bl.x;
- WFIFOL(fd,7) = md->bl.y;
- }
- else
- WFIFOB(fd,2) = 2; // First Time
- }
- else if (md->spawn_timer != INVALID_TIMER)
- { // Boss is Dead
- const struct TimerData * timer_data = get_timer(md->spawn_timer);
- unsigned int seconds;
- int hours, minutes;
-
- seconds = DIFF_TICK(timer_data->tick, gettick()) / 1000 + 60;
- hours = seconds / (60 * 60);
- seconds = seconds - (60 * 60 * hours);
- minutes = seconds / 60;
-
- WFIFOB(fd,2) = 3;
- WFIFOW(fd,11) = hours; // Hours
- WFIFOW(fd,13) = minutes; // Minutes
- }
- safestrncpy((char*)WFIFOP(fd,19), md->db->jname, NAME_LENGTH);
- }
-
- WFIFOSET(fd,70);
-}
-
-
-/// Requesting equip of a player (CZ_EQUIPWIN_MICROSCOPE).
-/// 02d6 <account id>.L
-void clif_parse_ViewPlayerEquip(int fd, struct map_session_data* sd)
-{
- int charid = RFIFOL(fd, 2);
- struct map_session_data* tsd = map_id2sd(charid);
-
- if (!tsd)
- return;
-
- if( tsd->status.show_equip || pc_has_permission(sd, PC_PERM_VIEW_EQUIPMENT) )
- clif_viewequip_ack(sd, tsd);
- else
- clif_viewequip_fail(sd);
-}
-
-
-/// Request to change equip window tick (CZ_CONFIG).
-/// 02d8 <type>.L <value>.L
-/// type:
-/// 0 = open equip window
-/// value:
-/// 0 = disabled
-/// 1 = enabled
-void clif_parse_EquipTick(int fd, struct map_session_data* sd)
-{
- bool flag = (bool)RFIFOL(fd,6);
- sd->status.show_equip = flag;
- clif_equiptickack(sd, flag);
-}
-
-
-/// Questlog System [Kevin] [Inkfish]
-///
-
-/// Sends list of all quest states (ZC_ALL_QUEST_LIST).
-/// 02b1 <packet len>.W <num>.L { <quest id>.L <active>.B }*num
-void clif_quest_send_list(struct map_session_data * sd)
-{
- int fd = sd->fd;
- int i;
- int len = sd->avail_quests*5+8;
-
- WFIFOHEAD(fd,len);
- WFIFOW(fd, 0) = 0x2b1;
- WFIFOW(fd, 2) = len;
- WFIFOL(fd, 4) = sd->avail_quests;
-
- for( i = 0; i < sd->avail_quests; i++ )
- {
- WFIFOL(fd, i*5+8) = sd->quest_log[i].quest_id;
- WFIFOB(fd, i*5+12) = sd->quest_log[i].state;
- }
-
- WFIFOSET(fd, len);
-}
-
-
-/// Sends list of all quest missions (ZC_ALL_QUEST_MISSION).
-/// 02b2 <packet len>.W <num>.L { <quest id>.L <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3 }*num
-void clif_quest_send_mission(struct map_session_data * sd)
-{
- int fd = sd->fd;
- int i, j;
- int len = sd->avail_quests*104+8;
- struct mob_db *mob;
-
- WFIFOHEAD(fd, len);
- WFIFOW(fd, 0) = 0x2b2;
- WFIFOW(fd, 2) = len;
- WFIFOL(fd, 4) = sd->avail_quests;
-
- for( i = 0; i < sd->avail_quests; i++ )
- {
- WFIFOL(fd, i*104+8) = sd->quest_log[i].quest_id;
- WFIFOL(fd, i*104+12) = sd->quest_log[i].time - quest_db[sd->quest_index[i]].time;
- WFIFOL(fd, i*104+16) = sd->quest_log[i].time;
- WFIFOW(fd, i*104+20) = quest_db[sd->quest_index[i]].num_objectives;
-
- for( j = 0 ; j < quest_db[sd->quest_index[i]].num_objectives; j++ )
- {
- WFIFOL(fd, i*104+22+j*30) = quest_db[sd->quest_index[i]].mob[j];
- WFIFOW(fd, i*104+26+j*30) = sd->quest_log[i].count[j];
- mob = mob_db(quest_db[sd->quest_index[i]].mob[j]);
- memcpy(WFIFOP(fd, i*104+28+j*30), mob?mob->jname:"NULL", NAME_LENGTH);
- }
- }
-
- WFIFOSET(fd, len);
-}
-
-
-/// Notification about a new quest (ZC_ADD_QUEST).
-/// 02b3 <quest id>.L <active>.B <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3
-void clif_quest_add(struct map_session_data * sd, struct quest * qd, int index)
-{
- int fd = sd->fd;
- int i;
- struct mob_db *mob;
-
- WFIFOHEAD(fd, packet_len(0x2b3));
- WFIFOW(fd, 0) = 0x2b3;
- WFIFOL(fd, 2) = qd->quest_id;
- WFIFOB(fd, 6) = qd->state;
- WFIFOB(fd, 7) = qd->time - quest_db[index].time;
- WFIFOL(fd, 11) = qd->time;
- WFIFOW(fd, 15) = quest_db[index].num_objectives;
-
- for( i = 0; i < quest_db[index].num_objectives; i++ )
- {
- WFIFOL(fd, i*30+17) = quest_db[index].mob[i];
- WFIFOW(fd, i*30+21) = qd->count[i];
- mob = mob_db(quest_db[index].mob[i]);
- memcpy(WFIFOP(fd, i*30+23), mob?mob->jname:"NULL", NAME_LENGTH);
- }
-
- WFIFOSET(fd, packet_len(0x2b3));
-}
-
-
-/// Notification about a quest being removed (ZC_DEL_QUEST).
-/// 02b4 <quest id>.L
-void clif_quest_delete(struct map_session_data * sd, int quest_id)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd, packet_len(0x2b4));
- WFIFOW(fd, 0) = 0x2b4;
- WFIFOL(fd, 2) = quest_id;
- WFIFOSET(fd, packet_len(0x2b4));
-}
-
-
-/// Notification of an update to the hunting mission counter (ZC_UPDATE_MISSION_HUNT).
-/// 02b5 <packet len>.W <mobs>.W { <quest id>.L <mob id>.L <total count>.W <current count>.W }*3
-void clif_quest_update_objective(struct map_session_data * sd, struct quest * qd, int index)
-{
- int fd = sd->fd;
- int i;
- int len = quest_db[index].num_objectives*12+6;
-
- WFIFOHEAD(fd, len);
- WFIFOW(fd, 0) = 0x2b5;
- WFIFOW(fd, 2) = len;
- WFIFOW(fd, 4) = quest_db[index].num_objectives;
-
- for( i = 0; i < quest_db[index].num_objectives; i++ )
- {
- WFIFOL(fd, i*12+6) = qd->quest_id;
- WFIFOL(fd, i*12+10) = quest_db[index].mob[i];
- WFIFOW(fd, i*12+14) = quest_db[index].count[i];
- WFIFOW(fd, i*12+16) = qd->count[i];
- }
-
- WFIFOSET(fd, len);
-}
-
-
-/// Request to change the state of a quest (CZ_ACTIVE_QUEST).
-/// 02b6 <quest id>.L <active>.B
-void clif_parse_questStateAck(int fd, struct map_session_data * sd)
-{
- quest_update_status(sd, RFIFOL(fd,2), RFIFOB(fd,6)?Q_ACTIVE:Q_INACTIVE);
-}
-
-
-/// Notification about the change of a quest state (ZC_ACTIVE_QUEST).
-/// 02b7 <quest id>.L <active>.B
-void clif_quest_update_status(struct map_session_data * sd, int quest_id, bool active)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd, packet_len(0x2b7));
- WFIFOW(fd, 0) = 0x2b7;
- WFIFOL(fd, 2) = quest_id;
- WFIFOB(fd, 6) = active;
- WFIFOSET(fd, packet_len(0x2b7));
-}
-
-
-/// Notification about an NPC's quest state (ZC_QUEST_NOTIFY_EFFECT).
-/// 0446 <npc id>.L <x>.W <y>.W <effect>.W <type>.W
-/// effect:
-/// 0 = none
-/// 1 = exclamation mark icon
-/// 2 = question mark icon
-/// type:
-/// 0 = yellow
-/// 1 = orange
-/// 2 = green
-/// 3 = purple
-void clif_quest_show_event(struct map_session_data *sd, struct block_list *bl, short state, short color)
-{
-#if PACKETVER >= 20090218
- int fd = sd->fd;
-
- WFIFOHEAD(fd, packet_len(0x446));
- WFIFOW(fd, 0) = 0x446;
- WFIFOL(fd, 2) = bl->id;
- WFIFOW(fd, 6) = bl->x;
- WFIFOW(fd, 8) = bl->y;
- WFIFOW(fd, 10) = state;
- WFIFOW(fd, 12) = color;
- WFIFOSET(fd, packet_len(0x446));
-#endif
-}
-
-
-/// Mercenary System
-///
-
-/// Notification about a mercenary status parameter change (ZC_MER_PAR_CHANGE).
-/// 02a2 <var id>.W <value>.L
-void clif_mercenary_updatestatus(struct map_session_data *sd, int type)
-{
- struct mercenary_data *md;
- struct status_data *status;
- int fd;
- if( sd == NULL || (md = sd->md) == NULL )
- return;
-
- fd = sd->fd;
- status = &md->battle_status;
- WFIFOHEAD(fd,packet_len(0x2a2));
- WFIFOW(fd,0) = 0x2a2;
- WFIFOW(fd,2) = type;
- switch( type )
- {
- case SP_ATK1:
- {
- int atk = rnd()%(status->rhw.atk2 - status->rhw.atk + 1) + status->rhw.atk;
- WFIFOL(fd,4) = cap_value(atk, 0, INT16_MAX);
- }
- break;
- case SP_MATK1:
- WFIFOL(fd,4) = cap_value(status->matk_max, 0, INT16_MAX);
- break;
- case SP_HIT:
- WFIFOL(fd,4) = status->hit;
- break;
- case SP_CRITICAL:
- WFIFOL(fd,4) = status->cri/10;
- break;
- case SP_DEF1:
- WFIFOL(fd,4) = status->def;
- break;
- case SP_MDEF1:
- WFIFOL(fd,4) = status->mdef;
- break;
- case SP_MERCFLEE:
- WFIFOL(fd,4) = status->flee;
- break;
- case SP_ASPD:
- WFIFOL(fd,4) = status->amotion;
- break;
- case SP_HP:
- WFIFOL(fd,4) = status->hp;
- break;
- case SP_MAXHP:
- WFIFOL(fd,4) = status->max_hp;
- break;
- case SP_SP:
- WFIFOL(fd,4) = status->sp;
- break;
- case SP_MAXSP:
- WFIFOL(fd,4) = status->max_sp;
- break;
- case SP_MERCKILLS:
- WFIFOL(fd,4) = md->mercenary.kill_count;
- break;
- case SP_MERCFAITH:
- WFIFOL(fd,4) = mercenary_get_faith(md);
- break;
- }
- WFIFOSET(fd,packet_len(0x2a2));
-}
-
-
-/// Mercenary base status data (ZC_MER_INIT).
-/// 029b <id>.L <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W
-/// <name>.24B <level>.W <hp>.L <maxhp>.L <sp>.L <maxsp>.L <expire time>.L <faith>.W
-/// <calls>.L <kills>.L <atk range>.W
-void clif_mercenary_info(struct map_session_data *sd)
-{
- int fd;
- struct mercenary_data *md;
- struct status_data *status;
- int atk;
-
- if( sd == NULL || (md = sd->md) == NULL )
- return;
-
- fd = sd->fd;
- status = &md->battle_status;
-
- WFIFOHEAD(fd,packet_len(0x29b));
- WFIFOW(fd,0) = 0x29b;
- WFIFOL(fd,2) = md->bl.id;
-
- // Mercenary shows ATK as a random value between ATK ~ ATK2
- atk = rnd()%(status->rhw.atk2 - status->rhw.atk + 1) + status->rhw.atk;
- WFIFOW(fd,6) = cap_value(atk, 0, INT16_MAX);
- WFIFOW(fd,8) = cap_value(status->matk_max, 0, INT16_MAX);
- WFIFOW(fd,10) = status->hit;
- WFIFOW(fd,12) = status->cri/10;
- WFIFOW(fd,14) = status->def;
- WFIFOW(fd,16) = status->mdef;
- WFIFOW(fd,18) = status->flee;
- WFIFOW(fd,20) = status->amotion;
- safestrncpy((char*)WFIFOP(fd,22), md->db->name, NAME_LENGTH);
- WFIFOW(fd,46) = md->db->lv;
- WFIFOL(fd,48) = status->hp;
- WFIFOL(fd,52) = status->max_hp;
- WFIFOL(fd,56) = status->sp;
- WFIFOL(fd,60) = status->max_sp;
- WFIFOL(fd,64) = (int)time(NULL) + (mercenary_get_lifetime(md) / 1000);
- WFIFOW(fd,68) = mercenary_get_faith(md);
- WFIFOL(fd,70) = mercenary_get_calls(md);
- WFIFOL(fd,74) = md->mercenary.kill_count;
- WFIFOW(fd,78) = md->battle_status.rhw.range;
- WFIFOSET(fd,packet_len(0x29b));
-}
-
-
-/// Mercenary skill tree (ZC_MER_SKILLINFO_LIST).
-/// 029d <packet len>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <skill name>.24B <upgradable>.B }*
-void clif_mercenary_skillblock(struct map_session_data *sd)
-{
- struct mercenary_data *md;
- int fd, i, len = 4, id, j;
-
- if( sd == NULL || (md = sd->md) == NULL )
- return;
-
- fd = sd->fd;
- WFIFOHEAD(fd,4+37*MAX_MERCSKILL);
- WFIFOW(fd,0) = 0x29d;
- for( i = 0; i < MAX_MERCSKILL; i++ )
- {
- if( (id = md->db->skill[i].id) == 0 )
- continue;
- j = id - MC_SKILLBASE;
- WFIFOW(fd,len) = id;
- WFIFOL(fd,len+2) = skill_get_inf(id);
- WFIFOW(fd,len+6) = md->db->skill[j].lv;
- WFIFOW(fd,len+8) = skill_get_sp(id, md->db->skill[j].lv);
- WFIFOW(fd,len+10) = skill_get_range2(&md->bl, id, md->db->skill[j].lv);
- safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
- WFIFOB(fd,len+36) = 0; // Skillable for Mercenary?
- len += 37;
- }
-
- WFIFOW(fd,2) = len;
- WFIFOSET(fd,len);
-}
-
-
-/// Request to invoke a mercenary menu action (CZ_MER_COMMAND).
-/// 029f <command>.B
-/// 1 = mercenary information
-/// 2 = delete
-void clif_parse_mercenary_action(int fd, struct map_session_data* sd)
-{
- int option = RFIFOB(fd,2);
- if( sd->md == NULL )
- return;
-
- if( option == 2 ) merc_delete(sd->md, 2);
-}
-
-
-/// Mercenary Message
-/// message:
-/// 0 = Mercenary soldier's duty hour is over.
-/// 1 = Your mercenary soldier has been killed.
-/// 2 = Your mercenary soldier has been fired.
-/// 3 = Your mercenary soldier has ran away.
-void clif_mercenary_message(struct map_session_data* sd, int message)
-{
- clif_msg(sd, 1266 + message);
-}
-
-
-/// Notification about the remaining time of a rental item (ZC_CASH_TIME_COUNTER).
-/// 0298 <name id>.W <seconds>.L
-void clif_rental_time(int fd, int nameid, int seconds)
-{ // '<ItemName>' item will disappear in <seconds/60> minutes.
- WFIFOHEAD(fd,packet_len(0x298));
- WFIFOW(fd,0) = 0x298;
- WFIFOW(fd,2) = nameid;
- WFIFOL(fd,4) = seconds;
- WFIFOSET(fd,packet_len(0x298));
-}
-
-
-/// Deletes a rental item from client's inventory (ZC_CASH_ITEM_DELETE).
-/// 0299 <index>.W <name id>.W
-void clif_rental_expired(int fd, int index, int nameid)
-{ // '<ItemName>' item has been deleted from the Inventory
- WFIFOHEAD(fd,packet_len(0x299));
- WFIFOW(fd,0) = 0x299;
- WFIFOW(fd,2) = index+2;
- WFIFOW(fd,4) = nameid;
- WFIFOSET(fd,packet_len(0x299));
-}
-
-
-/// Book Reading (ZC_READ_BOOK).
-/// 0294 <book id>.L <page>.L
-void clif_readbook(int fd, int book_id, int page)
-{
- WFIFOHEAD(fd,packet_len(0x294));
- WFIFOW(fd,0) = 0x294;
- WFIFOL(fd,2) = book_id;
- WFIFOL(fd,6) = page;
- WFIFOSET(fd,packet_len(0x294));
-}
-
-
-/// Battlegrounds
-///
-
-/// Updates HP bar of a camp member (ZC_BATTLEFIELD_NOTIFY_HP).
-/// 02e0 <account id>.L <name>.24B <hp>.W <max hp>.W
-void clif_bg_hp(struct map_session_data *sd)
-{
- unsigned char buf[34];
- const int cmd = 0x2e0;
- nullpo_retv(sd);
-
- WBUFW(buf,0) = cmd;
- WBUFL(buf,2) = sd->status.account_id;
- memcpy(WBUFP(buf,6), sd->status.name, NAME_LENGTH);
-
- if( sd->battle_status.max_hp > INT16_MAX )
- { // To correctly display the %hp bar. [Skotlex]
- WBUFW(buf,30) = sd->battle_status.hp/(sd->battle_status.max_hp/100);
- WBUFW(buf,32) = 100;
- }
- else
- {
- WBUFW(buf,30) = sd->battle_status.hp;
- WBUFW(buf,32) = sd->battle_status.max_hp;
- }
-
- clif_send(buf, packet_len(cmd), &sd->bl, BG_AREA_WOS);
-}
-
-
-/// Updates the position of a camp member on the minimap (ZC_BATTLEFIELD_NOTIFY_POSITION).
-/// 02df <account id>.L <name>.24B <class>.W <x>.W <y>.W
-void clif_bg_xy(struct map_session_data *sd)
-{
- unsigned char buf[36];
- nullpo_retv(sd);
-
- WBUFW(buf,0)=0x2df;
- WBUFL(buf,2)=sd->status.account_id;
- memcpy(WBUFP(buf,6), sd->status.name, NAME_LENGTH);
- WBUFW(buf,30)=sd->status.class_;
- WBUFW(buf,32)=sd->bl.x;
- WBUFW(buf,34)=sd->bl.y;
-
- clif_send(buf, packet_len(0x2df), &sd->bl, BG_SAMEMAP_WOS);
-}
-
-void clif_bg_xy_remove(struct map_session_data *sd)
-{
- unsigned char buf[36];
- nullpo_retv(sd);
-
- WBUFW(buf,0)=0x2df;
- WBUFL(buf,2)=sd->status.account_id;
- memset(WBUFP(buf,6), 0, NAME_LENGTH);
- WBUFW(buf,30)=0;
- WBUFW(buf,32)=-1;
- WBUFW(buf,34)=-1;
-
- clif_send(buf, packet_len(0x2df), &sd->bl, BG_SAMEMAP_WOS);
-}
-
-
-/// Notifies clients of a battleground message (ZC_BATTLEFIELD_CHAT).
-/// 02dc <packet len>.W <account id>.L <name>.24B <message>.?B
-void clif_bg_message(struct battleground_data *bg, int src_id, const char *name, const char *mes, int len)
-{
- struct map_session_data *sd;
- unsigned char *buf;
- if( (sd = bg_getavailablesd(bg)) == NULL )
- return;
-
- buf = (unsigned char*)aMalloc((len + NAME_LENGTH + 8)*sizeof(unsigned char));
-
- WBUFW(buf,0) = 0x2dc;
- WBUFW(buf,2) = len + NAME_LENGTH + 8;
- WBUFL(buf,4) = src_id;
- memcpy(WBUFP(buf,8), name, NAME_LENGTH);
- memcpy(WBUFP(buf,32), mes, len);
- clif_send(buf,WBUFW(buf,2), &sd->bl, BG);
-
- if( buf )
- aFree(buf);
-}
-
-
-/// Validates and processes battlechat messages [pakpil] (CZ_BATTLEFIELD_CHAT).
-/// 0x2db <packet len>.W <text>.?B (<name> : <message>) 00
-void clif_parse_BattleChat(int fd, struct map_session_data* sd)
-{
- const char* text = (char*)RFIFOP(fd,4);
- int textlen = RFIFOW(fd,2) - 4;
-
- char *name, *message;
- int namelen, messagelen;
-
- if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
- return;
-
- if( is_atcommand(fd, sd, message, 1) )
- return;
-
- if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) )
- return;
-
- if( battle_config.min_chat_delay )
- {
- if( DIFF_TICK(sd->cantalk_tick, gettick()) > 0 )
- return;
- sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
- }
-
- bg_send_message(sd, text, textlen);
-}
-
-
-/// Notifies client of a battleground score change (ZC_BATTLEFIELD_NOTIFY_POINT).
-/// 02de <camp A points>.W <camp B points>.W
-void clif_bg_updatescore(int16 m)
-{
- struct block_list bl;
- unsigned char buf[6];
-
- bl.id = 0;
- bl.type = BL_NUL;
- bl.m = m;
-
- WBUFW(buf,0) = 0x2de;
- WBUFW(buf,2) = map[m].bgscore_lion;
- WBUFW(buf,4) = map[m].bgscore_eagle;
- clif_send(buf,packet_len(0x2de),&bl,ALL_SAMEMAP);
-}
-
-void clif_bg_updatescore_single(struct map_session_data *sd)
-{
- int fd;
- nullpo_retv(sd);
- fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x2de));
- WFIFOW(fd,0) = 0x2de;
- WFIFOW(fd,2) = map[sd->bl.m].bgscore_lion;
- WFIFOW(fd,4) = map[sd->bl.m].bgscore_eagle;
- WFIFOSET(fd,packet_len(0x2de));
-}
-
-
-/// Battleground camp belong-information (ZC_BATTLEFIELD_NOTIFY_CAMPINFO).
-/// 02dd <account id>.L <name>.24B <camp>.W
-void clif_sendbgemblem_area(struct map_session_data *sd)
-{
- unsigned char buf[33];
- nullpo_retv(sd);
-
- WBUFW(buf, 0) = 0x2dd;
- WBUFL(buf,2) = sd->bl.id;
- safestrncpy((char*)WBUFP(buf,6), sd->status.name, NAME_LENGTH); // name don't show in screen.
- WBUFW(buf,30) = sd->bg_id;
- clif_send(buf,packet_len(0x2dd), &sd->bl, AREA);
-}
-
-void clif_sendbgemblem_single(int fd, struct map_session_data *sd)
-{
- nullpo_retv(sd);
- WFIFOHEAD(fd,32);
- WFIFOW(fd,0) = 0x2dd;
- WFIFOL(fd,2) = sd->bl.id;
- safestrncpy((char*)WFIFOP(fd,6), sd->status.name, NAME_LENGTH);
- WFIFOW(fd,30) = sd->bg_id;
- WFIFOSET(fd,packet_len(0x2dd));
-}
-
-
-/// Custom Fonts (ZC_NOTIFY_FONT).
-/// 02ef <account_id>.L <font id>.W
-void clif_font(struct map_session_data *sd)
-{
-#if PACKETVER >= 20080102
- unsigned char buf[8];
- nullpo_retv(sd);
- WBUFW(buf,0) = 0x2ef;
- WBUFL(buf,2) = sd->bl.id;
- WBUFW(buf,6) = sd->user_font;
- clif_send(buf, packet_len(0x2ef), &sd->bl, AREA);
-#endif
-}
-
-
-/*==========================================
- * Instancing Window
- *------------------------------------------*/
-int clif_instance(int instance_id, int type, int flag)
-{
- struct map_session_data *sd;
- struct party_data *p;
- unsigned char buf[255];
-
- if( (p = party_search(instance[instance_id].party_id)) == NULL || (sd = party_getavailablesd(p)) == NULL )
- return 0;
-
- switch( type )
- {
- case 1:
- // S 0x2cb <Instance name>.61B <Standby Position>.W
- // Required to start the instancing information window on Client
- // This window re-appear each "refresh" of client automatically until type 4 is send to client.
- WBUFW(buf,0) = 0x02CB;
- memcpy(WBUFP(buf,2),instance[instance_id].name,INSTANCE_NAME_LENGTH);
- WBUFW(buf,63) = flag;
- clif_send(buf,packet_len(0x02CB),&sd->bl,PARTY);
- break;
- case 2:
- // S 0x2cc <Standby Position>.W
- // To announce Instancing queue creation if no maps available
- WBUFW(buf,0) = 0x02CC;
- WBUFW(buf,2) = flag;
- clif_send(buf,packet_len(0x02CC),&sd->bl,PARTY);
- break;
- case 3:
- case 4:
- // S 0x2cd <Instance Name>.61B <Instance Remaining Time>.L <Instance Noplayers close time>.L
- WBUFW(buf,0) = 0x02CD;
- memcpy(WBUFP(buf,2),instance[instance_id].name,61);
- if( type == 3 )
- {
- WBUFL(buf,63) = (uint32)instance[instance_id].progress_timeout;
- WBUFL(buf,67) = 0;
- }
- else
- {
- WBUFL(buf,63) = 0;
- WBUFL(buf,67) = (uint32)instance[instance_id].idle_timeout;
- }
- clif_send(buf,packet_len(0x02CD),&sd->bl,PARTY);
- break;
- case 5:
- // S 0x2ce <Message ID>.L
- // 0 = Notification (EnterLimitDate update?)
- // 1 = The Memorial Dungeon expired; it has been destroyed
- // 2 = The Memorial Dungeon's entry time limit expired; it has been destroyed
- // 3 = The Memorial Dungeon has been removed.
- // 4 = Create failure (removes the instance window)
- WBUFW(buf,0) = 0x02CE;
- WBUFL(buf,2) = flag;
- //WBUFL(buf,6) = EnterLimitDate;
- clif_send(buf,packet_len(0x02CE),&sd->bl,PARTY);
- break;
- }
- return 0;
-}
-
-void clif_instance_join(int fd, int instance_id)
-{
- if( instance[instance_id].idle_timer != INVALID_TIMER )
- {
- WFIFOHEAD(fd,packet_len(0x02CD));
- WFIFOW(fd,0) = 0x02CD;
- memcpy(WFIFOP(fd,2),instance[instance_id].name,61);
- WFIFOL(fd,63) = 0;
- WFIFOL(fd,67) = (uint32)instance[instance_id].idle_timeout;
- WFIFOSET(fd,packet_len(0x02CD));
- }
- else if( instance[instance_id].progress_timer != INVALID_TIMER )
- {
- WFIFOHEAD(fd,packet_len(0x02CD));
- WFIFOW(fd,0) = 0x02CD;
- memcpy(WFIFOP(fd,2),instance[instance_id].name,61);
- WFIFOL(fd,63) = (uint32)instance[instance_id].progress_timeout;;
- WFIFOL(fd,67) = 0;
- WFIFOSET(fd,packet_len(0x02CD));
- }
- else
- {
- WFIFOHEAD(fd,packet_len(0x02CB));
- WFIFOW(fd,0) = 0x02CB;
- memcpy(WFIFOP(fd,2),instance[instance_id].name,61);
- WFIFOW(fd,63) = 0;
- WFIFOSET(fd,packet_len(0x02CB));
- }
-}
-
-void clif_instance_leave(int fd)
-{
- WFIFOHEAD(fd,packet_len(0x02CE));
- WFIFOW(fd,0) = 0x02ce;
- WFIFOL(fd,2) = 4;
- WFIFOSET(fd,packet_len(0x02CE));
-}
-
-
-/// Notifies clients about item picked up by a party member (ZC_ITEM_PICKUP_PARTY).
-/// 02b8 <account id>.L <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B
-void clif_party_show_picker(struct map_session_data * sd, struct item * item_data)
-{
-#if PACKETVER >= 20071002
- unsigned char buf[22];
- struct item_data* id = itemdb_search(item_data->nameid);
-
- WBUFW(buf,0) = 0x2b8;
- WBUFL(buf,2) = sd->status.account_id;
- WBUFW(buf,6) = item_data->nameid;
- WBUFB(buf,8) = item_data->identify;
- WBUFB(buf,9) = item_data->attribute;
- WBUFB(buf,10) = item_data->refine;
- clif_addcards(WBUFP(buf,11), item_data);
- WBUFW(buf,19) = id->equip; // equip location
- WBUFB(buf,21) = itemtype(id->type); // item type
- clif_send(buf, packet_len(0x2b8), &sd->bl, PARTY_SAMEMAP_WOS);
-#endif
-}
-
-
-/// Display gained exp (ZC_NOTIFY_EXP).
-/// 07f6 <account id>.L <amount>.L <var id>.W <exp type>.W
-/// var id:
-/// SP_BASEEXP, SP_JOBEXP
-/// exp type:
-/// 0 = normal exp gain/loss
-/// 1 = quest exp gain/loss
-void clif_displayexp(struct map_session_data *sd, unsigned int exp, char type, bool quest)
-{
- int fd;
-
- nullpo_retv(sd);
-
- fd = sd->fd;
-
- WFIFOHEAD(fd, packet_len(0x7f6));
- WFIFOW(fd,0) = 0x7f6;
- WFIFOL(fd,2) = sd->bl.id;
- WFIFOL(fd,6) = exp;
- WFIFOW(fd,10) = type;
- WFIFOW(fd,12) = quest?1:0;// Normal exp is shown in yellow, quest exp is shown in purple.
- WFIFOSET(fd,packet_len(0x7f6));
-}
-
-
-/// Displays digital clock digits on top of the screen (ZC_SHOWDIGIT).
-/// type:
-/// 0 = Displays 'value' for 5 seconds.
-/// 1 = Incremental counter (1 tick/second), negated 'value' specifies start value (e.g. using -10 lets the counter start at 10).
-/// 2 = Decremental counter (1 tick/second), negated 'value' specifies start value (does not stop when reaching 0, but overflows).
-/// 3 = Decremental counter (1 tick/second), 'value' specifies start value (stops when reaching 0, displays at most 2 digits).
-/// value:
-/// Except for type 3 it is interpreted as seconds for displaying as DD:HH:MM:SS, HH:MM:SS, MM:SS or SS (leftmost '00' is not displayed).
-void clif_showdigit(struct map_session_data* sd, unsigned char type, int value)
-{
- WFIFOHEAD(sd->fd, packet_len(0x1b1));
- WFIFOW(sd->fd,0) = 0x1b1;
- WFIFOB(sd->fd,2) = type;
- WFIFOL(sd->fd,3) = value;
- WFIFOSET(sd->fd, packet_len(0x1b1));
-}
-
-
-/// Notification of the state of client command /effect (CZ_LESSEFFECT).
-/// 021d <state>.L
-/// state:
-/// 0 = Full effects
-/// 1 = Reduced effects
-///
-/// NOTE: The state is used on Aegis for sending skill unit packet
-/// 0x11f (ZC_SKILL_ENTRY) instead of 0x1c9 (ZC_SKILL_ENTRY2)
-/// whenever possible. Due to the way the decision check is
-/// constructed, this state tracking was rendered useless,
-/// as the only skill unit, that is sent with 0x1c9 is
-/// Graffiti.
-void clif_parse_LessEffect(int fd, struct map_session_data* sd)
-{
- int isLess = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
-
- sd->state.lesseffect = ( isLess != 0 );
-}
-
-/// S 07e4 <length>.w <option>.l <val>.l {<index>.w <amount>.w).4b*
-void clif_parse_ItemListWindowSelected(int fd, struct map_session_data* sd) {
- int n = (RFIFOW(fd,2)-12) / 4;
- int type = RFIFOL(fd,4);
- int flag = RFIFOL(fd,8); // Button clicked: 0 = Cancel, 1 = OK
- unsigned short* item_list = (unsigned short*)RFIFOP(fd,12);
-
- if( sd->state.trading || sd->npc_shopid )
- return;
-
- if( flag == 0 || n == 0) {
- clif_menuskill_clear(sd);
- return; // Canceled by player.
- }
-
- if( sd->menuskill_id != SO_EL_ANALYSIS && sd->menuskill_id != GN_CHANGEMATERIAL ) {
- clif_menuskill_clear(sd);
- return; // Prevent hacking.
- }
-
- switch( type ) {
- case 0: // Change Material
- skill_changematerial(sd,n,item_list);
- break;
- case 1: // Level 1: Pure to Rough
- case 2: // Level 2: Rough to Pure
- skill_elementalanalysis(sd,n,type,item_list);
- break;
- }
- clif_menuskill_clear(sd);
-
- return;
-}
-
-/*==========================================
- * Elemental System
- *==========================================*/
-void clif_elemental_updatestatus(struct map_session_data *sd, int type) {
- struct elemental_data *ed;
- struct status_data *status;
- int fd;
-
- if( sd == NULL || (ed = sd->ed) == NULL )
- return;
-
- fd = sd->fd;
- status = &ed->battle_status;
- WFIFOHEAD(fd,8);
- WFIFOW(fd,0) = 0x81e;
- WFIFOW(fd,2) = type;
- switch( type ) {
- case SP_HP:
- WFIFOL(fd,4) = status->hp;
- break;
- case SP_MAXHP:
- WFIFOL(fd,4) = status->max_hp;
- break;
- case SP_SP:
- WFIFOL(fd,4) = status->sp;
- break;
- case SP_MAXSP:
- WFIFOL(fd,4) = status->max_sp;
- break;
- }
- WFIFOSET(fd,8);
-}
-
-void clif_elemental_info(struct map_session_data *sd) {
- int fd;
- struct elemental_data *ed;
- struct status_data *status;
-
- if( sd == NULL || (ed = sd->ed) == NULL )
- return;
-
- fd = sd->fd;
- status = &ed->battle_status;
-
- WFIFOHEAD(fd,22);
- WFIFOW(fd, 0) = 0x81d;
- WFIFOL(fd, 2) = ed->bl.id;
- WFIFOL(fd, 6) = status->hp;
- WFIFOL(fd,10) = status->max_hp;
- WFIFOL(fd,14) = status->sp;
- WFIFOL(fd,18) = status->max_sp;
- WFIFOSET(fd,22);
-}
-
-
-/// Buying Store System
-///
-
-/// Opens preparation window for buying store (ZC_OPEN_BUYING_STORE).
-/// 0810 <slots>.B
-void clif_buyingstore_open(struct map_session_data* sd)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x810));
- WFIFOW(fd,0) = 0x810;
- WFIFOB(fd,2) = sd->buyingstore.slots;
- WFIFOSET(fd,packet_len(0x810));
-}
-
-
-/// Request to create a buying store (CZ_REQ_OPEN_BUYING_STORE).
-/// 0811 <packet len>.W <limit zeny>.L <result>.B <store name>.80B { <name id>.W <amount>.W <price>.L }*
-/// result:
-/// 0 = cancel
-/// 1 = open
-static void clif_parse_ReqOpenBuyingStore(int fd, struct map_session_data* sd)
-{
- const unsigned int blocksize = 8;
- uint8* itemlist;
- char storename[MESSAGE_SIZE];
- unsigned char result;
- int zenylimit;
- unsigned int count, packet_len;
- struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
-
- packet_len = RFIFOW(fd,info->pos[0]);
-
- // TODO: Make this check global for all variable length packets.
- if( packet_len < 89 )
- {// minimum packet length
- ShowError("clif_parse_ReqOpenBuyingStore: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 89, packet_len, sd->bl.id);
- return;
- }
-
- zenylimit = RFIFOL(fd,info->pos[1]);
- result = RFIFOL(fd,info->pos[2]);
- safestrncpy(storename, (const char*)RFIFOP(fd,info->pos[3]), sizeof(storename));
- itemlist = RFIFOP(fd,info->pos[4]);
-
- // so that buyingstore_create knows, how many elements it has access to
- packet_len-= info->pos[4];
-
- if( packet_len%blocksize )
- {
- ShowError("clif_parse_ReqOpenBuyingStore: Unexpected item list size %u (account_id=%d, block size=%u)\n", packet_len, sd->bl.id, blocksize);
- return;
- }
- count = packet_len/blocksize;
-
- buyingstore_create(sd, zenylimit, result, storename, itemlist, count);
-}
-
-
-/// Notification, that the requested buying store could not be created (ZC_FAILED_OPEN_BUYING_STORE_TO_BUYER).
-/// 0812 <result>.W <total weight>.L
-/// result:
-/// 1 = "Failed to open buying store." (0x6cd, MSI_BUYINGSTORE_OPEN_FAILED)
-/// 2 = "Total amount of then possessed items exceeds the weight limit by <weight/10-maxweight*90%>. Please re-enter." (0x6ce, MSI_BUYINGSTORE_OVERWEIGHT)
-/// 8 = "No sale (purchase) information available." (0x705)
-/// ? = nothing
-void clif_buyingstore_open_failed(struct map_session_data* sd, unsigned short result, unsigned int weight)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x812));
- WFIFOW(fd,0) = 0x812;
- WFIFOW(fd,2) = result;
- WFIFOL(fd,4) = weight;
- WFIFOSET(fd,packet_len(0x812));
-}
-
-
-/// Notification, that the requested buying store was created (ZC_MYITEMLIST_BUYING_STORE).
-/// 0813 <packet len>.W <account id>.L <limit zeny>.L { <price>.L <count>.W <type>.B <name id>.W }*
-void clif_buyingstore_myitemlist(struct map_session_data* sd)
-{
- int fd = sd->fd;
- unsigned int i;
-
- WFIFOHEAD(fd,12+sd->buyingstore.slots*9);
- WFIFOW(fd,0) = 0x813;
- WFIFOW(fd,2) = 12+sd->buyingstore.slots*9;
- WFIFOL(fd,4) = sd->bl.id;
- WFIFOL(fd,8) = sd->buyingstore.zenylimit;
-
- for( i = 0; i < sd->buyingstore.slots; i++ )
- {
- WFIFOL(fd,12+i*9) = sd->buyingstore.items[i].price;
- WFIFOW(fd,16+i*9) = sd->buyingstore.items[i].amount;
- WFIFOB(fd,18+i*9) = itemtype(itemdb_type(sd->buyingstore.items[i].nameid));
- WFIFOW(fd,19+i*9) = sd->buyingstore.items[i].nameid;
- }
-
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Notifies clients in area of a buying store (ZC_BUYING_STORE_ENTRY).
-/// 0814 <account id>.L <store name>.80B
-void clif_buyingstore_entry(struct map_session_data* sd)
-{
- uint8 buf[86];
-
- WBUFW(buf,0) = 0x814;
- WBUFL(buf,2) = sd->bl.id;
- memcpy(WBUFP(buf,6), sd->message, MESSAGE_SIZE);
-
- clif_send(buf, packet_len(0x814), &sd->bl, AREA_WOS);
-}
-void clif_buyingstore_entry_single(struct map_session_data* sd, struct map_session_data* pl_sd)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x814));
- WFIFOW(fd,0) = 0x814;
- WFIFOL(fd,2) = pl_sd->bl.id;
- memcpy(WFIFOP(fd,6), pl_sd->message, MESSAGE_SIZE);
- WFIFOSET(fd,packet_len(0x814));
-}
-
-
-/// Request to close own buying store (CZ_REQ_CLOSE_BUYING_STORE).
-/// 0815
-static void clif_parse_ReqCloseBuyingStore(int fd, struct map_session_data* sd)
-{
- buyingstore_close(sd);
-}
-
-
-/// Notifies clients in area that a buying store was closed (ZC_DISAPPEAR_BUYING_STORE_ENTRY).
-/// 0816 <account id>.L
-void clif_buyingstore_disappear_entry(struct map_session_data* sd)
-{
- uint8 buf[6];
-
- WBUFW(buf,0) = 0x816;
- WBUFL(buf,2) = sd->bl.id;
-
- clif_send(buf, packet_len(0x816), &sd->bl, AREA_WOS);
-}
-void clif_buyingstore_disappear_entry_single(struct map_session_data* sd, struct map_session_data* pl_sd)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x816));
- WFIFOW(fd,0) = 0x816;
- WFIFOL(fd,2) = pl_sd->bl.id;
- WFIFOSET(fd,packet_len(0x816));
-}
-
-
-/// Request to open someone else's buying store (CZ_REQ_CLICK_TO_BUYING_STORE).
-/// 0817 <account id>.L
-static void clif_parse_ReqClickBuyingStore(int fd, struct map_session_data* sd)
-{
- int account_id;
-
- account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
-
- buyingstore_open(sd, account_id);
-}
-
-
-/// Sends buying store item list (ZC_ACK_ITEMLIST_BUYING_STORE).
-/// 0818 <packet len>.W <account id>.L <store id>.L <limit zeny>.L { <price>.L <amount>.W <type>.B <name id>.W }*
-void clif_buyingstore_itemlist(struct map_session_data* sd, struct map_session_data* pl_sd)
-{
- int fd = sd->fd;
- unsigned int i;
-
- WFIFOHEAD(fd,16+pl_sd->buyingstore.slots*9);
- WFIFOW(fd,0) = 0x818;
- WFIFOW(fd,2) = 16+pl_sd->buyingstore.slots*9;
- WFIFOL(fd,4) = pl_sd->bl.id;
- WFIFOL(fd,8) = pl_sd->buyer_id;
- WFIFOL(fd,12) = pl_sd->buyingstore.zenylimit;
-
- for( i = 0; i < pl_sd->buyingstore.slots; i++ )
- {
- WFIFOL(fd,16+i*9) = pl_sd->buyingstore.items[i].price;
- WFIFOW(fd,20+i*9) = pl_sd->buyingstore.items[i].amount; // TODO: Figure out, if no longer needed items (amount == 0) are listed on official.
- WFIFOB(fd,22+i*9) = itemtype(itemdb_type(pl_sd->buyingstore.items[i].nameid));
- WFIFOW(fd,23+i*9) = pl_sd->buyingstore.items[i].nameid;
- }
-
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Request to sell items to a buying store (CZ_REQ_TRADE_BUYING_STORE).
-/// 0819 <packet len>.W <account id>.L <store id>.L { <index>.W <name id>.W <amount>.W }*
-static void clif_parse_ReqTradeBuyingStore(int fd, struct map_session_data* sd)
-{
- const unsigned int blocksize = 6;
- uint8* itemlist;
- int account_id;
- unsigned int count, packet_len, buyer_id;
- struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
-
- packet_len = RFIFOW(fd,info->pos[0]);
-
- if( packet_len < 12 )
- {// minimum packet length
- ShowError("clif_parse_ReqTradeBuyingStore: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 12, packet_len, sd->bl.id);
- return;
- }
-
- account_id = RFIFOL(fd,info->pos[1]);
- buyer_id = RFIFOL(fd,info->pos[2]);
- itemlist = RFIFOP(fd,info->pos[3]);
-
- // so that buyingstore_trade knows, how many elements it has access to
- packet_len-= info->pos[3];
-
- if( packet_len%blocksize )
- {
- ShowError("clif_parse_ReqTradeBuyingStore: Unexpected item list size %u (account_id=%d, buyer_id=%d, block size=%u)\n", packet_len, sd->bl.id, account_id, blocksize);
- return;
- }
- count = packet_len/blocksize;
-
- buyingstore_trade(sd, account_id, buyer_id, itemlist, count);
-}
-
-
-/// Notifies the buyer, that the buying store has been closed due to a post-trade condition (ZC_FAILED_TRADE_BUYING_STORE_TO_BUYER).
-/// 081a <result>.W
-/// result:
-/// 3 = "All items within the buy limit were purchased." (0x6cf, MSI_BUYINGSTORE_TRADE_OVERLIMITZENY)
-/// 4 = "All items were purchased." (0x6d0, MSI_BUYINGSTORE_TRADE_BUYCOMPLETE)
-/// ? = nothing
-void clif_buyingstore_trade_failed_buyer(struct map_session_data* sd, short result)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x81a));
- WFIFOW(fd,0) = 0x81a;
- WFIFOW(fd,2) = result;
- WFIFOSET(fd,packet_len(0x81a));
-}
-
-
-/// Updates the zeny limit and an item in the buying store item list (ZC_UPDATE_ITEM_FROM_BUYING_STORE).
-/// 081b <name id>.W <amount>.W <limit zeny>.L
-void clif_buyingstore_update_item(struct map_session_data* sd, unsigned short nameid, unsigned short amount)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x81b));
- WFIFOW(fd,0) = 0x81b;
- WFIFOW(fd,2) = nameid;
- WFIFOW(fd,4) = amount; // amount of nameid received
- WFIFOL(fd,6) = sd->buyingstore.zenylimit;
- WFIFOSET(fd,packet_len(0x81b));
-}
-
-
-/// Deletes item from inventory, that was sold to a buying store (ZC_ITEM_DELETE_BUYING_STORE).
-/// 081c <index>.W <amount>.W <price>.L
-/// message:
-/// "%s (%d) were sold at %dz." (0x6d2, MSI_BUYINGSTORE_TRADE_SELLCOMPLETE)
-///
-/// NOTE: This function has to be called _instead_ of clif_delitem/clif_dropitem.
-void clif_buyingstore_delete_item(struct map_session_data* sd, short index, unsigned short amount, int price)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x81c));
- WFIFOW(fd,0) = 0x81c;
- WFIFOW(fd,2) = index+2;
- WFIFOW(fd,4) = amount;
- WFIFOL(fd,6) = price; // price per item, client calculates total Zeny by itself
- WFIFOSET(fd,packet_len(0x81c));
-}
-
-
-/// Notifies the seller, that a buying store trade failed (ZC_FAILED_TRADE_BUYING_STORE_TO_SELLER).
-/// 0824 <result>.W <name id>.W
-/// result:
-/// 5 = "The deal has failed." (0x39, MSI_DEAL_FAIL)
-/// 6 = "The trade failed, because the entered amount of item %s is higher, than the buyer is willing to buy." (0x6d3, MSI_BUYINGSTORE_TRADE_OVERCOUNT)
-/// 7 = "The trade failed, because the buyer is lacking required balance." (0x6d1, MSI_BUYINGSTORE_TRADE_LACKBUYERZENY)
-/// ? = nothing
-void clif_buyingstore_trade_failed_seller(struct map_session_data* sd, short result, unsigned short nameid)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x824));
- WFIFOW(fd,0) = 0x824;
- WFIFOW(fd,2) = result;
- WFIFOW(fd,4) = nameid;
- WFIFOSET(fd,packet_len(0x824));
-}
-
-
-/// Search Store Info System
-///
-
-/// Request to search for stores (CZ_SEARCH_STORE_INFO).
-/// 0835 <packet len>.W <type>.B <max price>.L <min price>.L <name id count>.B <card count>.B { <name id>.W }* { <card>.W }*
-/// type:
-/// 0 = Vending
-/// 1 = Buying Store
-///
-/// NOTE: The client determines the item ids by specifying a name and optionally,
-/// amount of card slots. If the client does not know about the item it
-/// cannot be searched.
-static void clif_parse_SearchStoreInfo(int fd, struct map_session_data* sd)
-{
- const unsigned int blocksize = 2;
- const uint8* itemlist;
- const uint8* cardlist;
- unsigned char type;
- unsigned int min_price, max_price, packet_len, count, item_count, card_count;
- struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
-
- packet_len = RFIFOW(fd,info->pos[0]);
-
- if( packet_len < 15 )
- {// minimum packet length
- ShowError("clif_parse_SearchStoreInfo: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 15, packet_len, sd->bl.id);
- return;
- }
-
- type = RFIFOB(fd,info->pos[1]);
- max_price = RFIFOL(fd,info->pos[2]);
- min_price = RFIFOL(fd,info->pos[3]);
- item_count = RFIFOB(fd,info->pos[4]);
- card_count = RFIFOB(fd,info->pos[5]);
- itemlist = RFIFOP(fd,info->pos[6]);
- cardlist = RFIFOP(fd,info->pos[6]+blocksize*item_count);
-
- // check, if there is enough data for the claimed count of items
- packet_len-= info->pos[6];
-
- if( packet_len%blocksize )
- {
- ShowError("clif_parse_SearchStoreInfo: Unexpected item list size %u (account_id=%d, block size=%u)\n", packet_len, sd->bl.id, blocksize);
- return;
- }
- count = packet_len/blocksize;
-
- if( count < item_count+card_count )
- {
- ShowError("clif_parse_SearchStoreInfo: Malformed packet (expected count=%u, count=%u, account_id=%d).\n", item_count+card_count, count, sd->bl.id);
- return;
- }
-
- searchstore_query(sd, type, min_price, max_price, (const unsigned short*)itemlist, item_count, (const unsigned short*)cardlist, card_count);
-}
-
-
-/// Results for a store search request (ZC_SEARCH_STORE_INFO_ACK).
-/// 0836 <packet len>.W <is first page>.B <is next page>.B <remaining uses>.B { <store id>.L <account id>.L <shop name>.80B <nameid>.W <item type>.B <price>.L <amount>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }*
-/// is first page:
-/// 0 = appends to existing results
-/// 1 = clears previous results before displaying this result set
-/// is next page:
-/// 0 = no "next" label
-/// 1 = "next" label to retrieve more results
-void clif_search_store_info_ack(struct map_session_data* sd)
-{
- const unsigned int blocksize = MESSAGE_SIZE+26;
- int fd = sd->fd;
- unsigned int i, start, end;
-
- start = sd->searchstore.pages*SEARCHSTORE_RESULTS_PER_PAGE;
- end = min(sd->searchstore.count, start+SEARCHSTORE_RESULTS_PER_PAGE);
-
- WFIFOHEAD(fd,7+(end-start)*blocksize);
- WFIFOW(fd,0) = 0x836;
- WFIFOW(fd,2) = 7+(end-start)*blocksize;
- WFIFOB(fd,4) = !sd->searchstore.pages;
- WFIFOB(fd,5) = searchstore_querynext(sd);
- WFIFOB(fd,6) = (unsigned char)min(sd->searchstore.uses, UINT8_MAX);
-
- for( i = start; i < end; i++ )
- {
- struct s_search_store_info_item* ssitem = &sd->searchstore.items[i];
- struct item it;
-
- WFIFOL(fd,i*blocksize+ 7) = ssitem->store_id;
- WFIFOL(fd,i*blocksize+11) = ssitem->account_id;
- memcpy(WFIFOP(fd,i*blocksize+15), ssitem->store_name, MESSAGE_SIZE);
- WFIFOW(fd,i*blocksize+15+MESSAGE_SIZE) = ssitem->nameid;
- WFIFOB(fd,i*blocksize+17+MESSAGE_SIZE) = itemtype(itemdb_type(ssitem->nameid));
- WFIFOL(fd,i*blocksize+18+MESSAGE_SIZE) = ssitem->price;
- WFIFOW(fd,i*blocksize+22+MESSAGE_SIZE) = ssitem->amount;
- WFIFOB(fd,i*blocksize+24+MESSAGE_SIZE) = ssitem->refine;
-
- // make-up an item for clif_addcards
- memset(&it, 0, sizeof(it));
- memcpy(&it.card, &ssitem->card, sizeof(it.card));
- it.nameid = ssitem->nameid;
- it.amount = ssitem->amount;
-
- clif_addcards(WFIFOP(fd,i*blocksize+25+MESSAGE_SIZE), &it);
- }
-
- WFIFOSET(fd,WFIFOW(fd,2));
-}
-
-
-/// Notification of failure when searching for stores (ZC_SEARCH_STORE_INFO_FAILED).
-/// 0837 <reason>.B
-/// reason:
-/// 0 = "No matching stores were found." (0x70b)
-/// 1 = "There are too many results. Please enter more detailed search term." (0x6f8)
-/// 2 = "You cannot search anymore." (0x706)
-/// 3 = "You cannot search yet." (0x708)
-/// 4 = "No sale (purchase) information available." (0x705)
-void clif_search_store_info_failed(struct map_session_data* sd, unsigned char reason)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x837));
- WFIFOW(fd,0) = 0x837;
- WFIFOB(fd,2) = reason;
- WFIFOSET(fd,packet_len(0x837));
-}
-
-
-/// Request to display next page of results (CZ_SEARCH_STORE_INFO_NEXT_PAGE).
-/// 0838
-static void clif_parse_SearchStoreInfoNextPage(int fd, struct map_session_data* sd)
-{
- searchstore_next(sd);
-}
-
-
-/// Opens the search store window (ZC_OPEN_SEARCH_STORE_INFO).
-/// 083a <type>.W <remaining uses>.B
-/// type:
-/// 0 = Search Stores
-/// 1 = Search Stores (Cash), asks for confirmation, when clicking a store
-void clif_open_search_store_info(struct map_session_data* sd)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x83a));
- WFIFOW(fd,0) = 0x83a;
- WFIFOW(fd,2) = sd->searchstore.effect;
-#if PACKETVER > 20100701
- WFIFOB(fd,4) = (unsigned char)min(sd->searchstore.uses, UINT8_MAX);
-#endif
- WFIFOSET(fd,packet_len(0x83a));
-}
-
-
-/// Request to close the store search window (CZ_CLOSE_SEARCH_STORE_INFO).
-/// 083b
-static void clif_parse_CloseSearchStoreInfo(int fd, struct map_session_data* sd)
-{
- searchstore_close(sd);
-}
-
-
-/// Request to invoke catalog effect on a store from search results (CZ_SSILIST_ITEM_CLICK).
-/// 083c <account id>.L <store id>.L <nameid>.W
-static void clif_parse_SearchStoreInfoListItemClick(int fd, struct map_session_data* sd)
-{
- unsigned short nameid;
- int account_id, store_id;
- struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
-
- account_id = RFIFOL(fd,info->pos[0]);
- store_id = RFIFOL(fd,info->pos[1]);
- nameid = RFIFOW(fd,info->pos[2]);
-
- searchstore_click(sd, account_id, store_id, nameid);
-}
-
-
-/// Notification of the store position on current map (ZC_SSILIST_ITEM_CLICK_ACK).
-/// 083d <xPos>.W <yPos>.W
-void clif_search_store_info_click_ack(struct map_session_data* sd, short x, short y)
-{
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x83d));
- WFIFOW(fd,0) = 0x83d;
- WFIFOW(fd,2) = x;
- WFIFOW(fd,4) = y;
- WFIFOSET(fd,packet_len(0x83d));
-}
-
-
-/// Parse function for packet debugging.
-void clif_parse_debug(int fd,struct map_session_data *sd)
-{
- int cmd, packet_len;
-
- // clif_parse ensures, that there is at least 2 bytes of data
- cmd = RFIFOW(fd,0);
-
- if( sd )
- {
- packet_len = packet_db[sd->packet_ver][cmd].len;
-
- if( packet_len == 0 )
- {// unknown
- packet_len = RFIFOREST(fd);
- }
- else if( packet_len == -1 )
- {// variable length
- packet_len = RFIFOW(fd,2); // clif_parse ensures, that this amount of data is already received
- }
- ShowDebug("Packet debug of 0x%04X (length %d), %s session #%d, %d/%d (AID/CID)\n", cmd, packet_len, sd->state.active ? "authed" : "unauthed", fd, sd->status.account_id, sd->status.char_id);
- }
- else
- {
- packet_len = RFIFOREST(fd);
- ShowDebug("Packet debug of 0x%04X (length %d), session #%d\n", cmd, packet_len, fd);
- }
-
- ShowDump(RFIFOP(fd,0), packet_len);
-}
-/*==========================================
- * Server tells client to display a window similar to Magnifier (item) one
- * Server populates the window with avilable elemental converter options according to player's inventory
- *------------------------------------------*/
-int clif_elementalconverter_list(struct map_session_data *sd) {
- int i,c,view,fd;
-
- nullpo_ret(sd);
-
-
-/// Main client packet processing function
- fd=sd->fd;
- WFIFOHEAD(fd, MAX_SKILL_PRODUCE_DB *2+4);
- WFIFOW(fd, 0)=0x1ad;
-
- for(i=0,c=0;i<MAX_SKILL_PRODUCE_DB;i++){
- if( skill_can_produce_mix(sd,skill_produce_db[i].nameid,23, 1) ){
- if((view = itemdb_viewid(skill_produce_db[i].nameid)) > 0)
- WFIFOW(fd,c*2+ 4)= view;
- else
- WFIFOW(fd,c*2+ 4)= skill_produce_db[i].nameid;
- c++;
- }
- }
- WFIFOW(fd,2) = c*2+4;
- WFIFOSET(fd, WFIFOW(fd,2));
- if (c > 0) {
- sd->menuskill_id = SA_CREATECON;
- sd->menuskill_val = c;
- }
-
- return 0;
-}
-/**
- * Rune Knight
- **/
-void clif_millenniumshield(struct map_session_data *sd, short shields ) {
-#if PACKETVER >= 20081217
- unsigned char buf[10];
-
- WBUFW(buf,0) = 0x440;
- WBUFL(buf,2) = sd->bl.id;
- WBUFW(buf,6) = shields;
- WBUFW(buf,8) = 0;
- clif_send(buf,packet_len(0x440),&sd->bl,AREA);
-#endif
-}
-/**
- * Warlock
- **/
-/*==========================================
- * Spellbook list [LimitLine/3CeAM]
- *------------------------------------------*/
-int clif_spellbook_list(struct map_session_data *sd)
-{
- int i, c;
- int fd;
-
- nullpo_ret(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd, 8 * 8 + 8);
- WFIFOW(fd,0) = 0x1ad;
-
- for( i = 0, c = 0; i < MAX_INVENTORY; i ++ )
- {
- if( itemdb_is_spellbook(sd->status.inventory[i].nameid) )
- {
- WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid;
- c ++;
- }
- }
-
- if( c > 0 )
- {
- WFIFOW(fd,2) = c * 2 + 4;
- WFIFOSET(fd, WFIFOW(fd, 2));
- sd->menuskill_id = WL_READING_SB;
- sd->menuskill_val = c;
- }
- else{
- status_change_end(&sd->bl,SC_STOP,INVALID_TIMER);
- clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK, 0);
- }
-
- return 1;
-}
-/**
- * Mechanic
- **/
-/*==========================================
- * Magic Decoy Material List
- *------------------------------------------*/
-int clif_magicdecoy_list(struct map_session_data *sd, uint16 skill_lv, short x, short y) {
- int i, c;
- int fd;
-
- nullpo_ret(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd, 8 * 8 + 8);
- WFIFOW(fd,0) = 0x1ad; // This is the official packet. [pakpil]
-
- for( i = 0, c = 0; i < MAX_INVENTORY; i ++ ) {
- if( itemdb_is_element(sd->status.inventory[i].nameid) ) {
- WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid;
- c ++;
- }
- }
- if( c > 0 ) {
- sd->menuskill_id = NC_MAGICDECOY;
- sd->menuskill_val = skill_lv;
- sd->sc.comet_x = x;
- sd->sc.comet_y = y;
- WFIFOW(fd,2) = c * 2 + 4;
- WFIFOSET(fd, WFIFOW(fd, 2));
- } else {
- clif_skill_fail(sd,NC_MAGICDECOY,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
-
- return 1;
-}
-/**
- * Guilotine Cross
- **/
-/*==========================================
- * Guillotine Cross Poisons List
- *------------------------------------------*/
-int clif_poison_list(struct map_session_data *sd, uint16 skill_lv) {
- int i, c;
- int fd;
-
- nullpo_ret(sd);
-
- fd = sd->fd;
- WFIFOHEAD(fd, 8 * 8 + 8);
- WFIFOW(fd,0) = 0x1ad; // This is the official packet. [pakpil]
-
- for( i = 0, c = 0; i < MAX_INVENTORY; i ++ ) {
- if( itemdb_is_poison(sd->status.inventory[i].nameid) ) {
- WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid;
- c ++;
- }
- }
- if( c > 0 ) {
- sd->menuskill_id = GC_POISONINGWEAPON;
- sd->menuskill_val = skill_lv;
- WFIFOW(fd,2) = c * 2 + 4;
- WFIFOSET(fd, WFIFOW(fd, 2));
- } else {
- clif_skill_fail(sd,GC_POISONINGWEAPON,USESKILL_FAIL_GUILLONTINE_POISON,0);
- return 0;
- }
-
- return 1;
-}
-int clif_autoshadowspell_list(struct map_session_data *sd) {
- int fd, i, c;
- nullpo_ret(sd);
- fd = sd->fd;
- if( !fd ) return 0;
-
- if( sd->menuskill_id == SC_AUTOSHADOWSPELL )
- return 0;
-
- WFIFOHEAD(fd, 2 * 6 + 4);
- WFIFOW(fd,0) = 0x442;
- for( i = 0, c = 0; i < MAX_SKILL; i++ )
- if( sd->status.skill[i].flag == SKILL_FLAG_PLAGIARIZED && sd->status.skill[i].id > 0 &&
- sd->status.skill[i].id < GS_GLITTERING && skill_get_type(sd->status.skill[i].id) == BF_MAGIC )
- { // Can't auto cast both Extended class and 3rd class skills.
- WFIFOW(fd,8+c*2) = sd->status.skill[i].id;
- c++;
- }
-
- if( c > 0 ) {
- WFIFOW(fd,2) = 8 + c * 2;
- WFIFOL(fd,4) = c;
- WFIFOSET(fd,WFIFOW(fd,2));
- sd->menuskill_id = SC_AUTOSHADOWSPELL;
- sd->menuskill_val = c;
- } else {
- status_change_end(&sd->bl,SC_STOP,INVALID_TIMER);
- clif_skill_fail(sd,SC_AUTOSHADOWSPELL,USESKILL_FAIL_IMITATION_SKILL_NONE,0);
- }
-
- return 1;
-}
-/*===========================================
- * Skill list for Four Elemental Analysis
- * and Change Material skills.
- *------------------------------------------*/
-int clif_skill_itemlistwindow( struct map_session_data *sd, uint16 skill_id, uint16 skill_lv )
-{
-#if PACKETVER >= 20090922
- int fd;
-
- nullpo_ret(sd);
-
- sd->menuskill_id = skill_id; // To prevent hacking.
- sd->menuskill_val = skill_lv;
-
- if( skill_id == GN_CHANGEMATERIAL )
- skill_lv = 0; // Changematerial
-
- fd = sd->fd;
- WFIFOHEAD(fd,packet_len(0x7e3));
- WFIFOW(fd,0) = 0x7e3;
- WFIFOL(fd,2) = skill_lv;
- WFIFOL(fd,4) = 0;
- WFIFOSET(fd,packet_len(0x7e3));
-
-#endif
-
- return 1;
-
-}
-/**
- * Sends a new status without a tick (currently used by the new mounts)
- **/
-int clif_status_load_notick(struct block_list *bl,int type,int flag,int val1, int val2, int val3) {
- unsigned char buf[32];
-
- nullpo_ret(bl);
-
- WBUFW(buf,0)=0x043f;
- WBUFW(buf,2)=type;
- WBUFL(buf,4)=bl->id;
- WBUFB(buf,8)=flag;
- WBUFL(buf,9) = 0;
- WBUFL(buf,13) = val1;
- WBUFL(buf,17) = val2;
- WBUFL(buf,21) = val3;
-
- clif_send(buf,packet_len(0x043f),bl,AREA);
- return 0;
-}
-//Notifies FD of ID's type
-int clif_status_load_single(int fd, int id,int type,int flag,int val1, int val2, int val3) {
- WFIFOHEAD(fd, packet_len(0x043f));
- WFIFOW(fd,0)=0x043f;
- WFIFOW(fd,2)=type;
- WFIFOL(fd,4)=id;
- WFIFOB(fd,8)=flag;
- WFIFOL(fd,9) = 0;
- WFIFOL(fd,13) = val1;
- WFIFOL(fd,17) = val2;
- WFIFOL(fd,21) = val3;
- WFIFOSET(fd, packet_len(0x043f));
- return 0;
-}
-// msgstringtable.txt
-// 0x291 <line>.W
-void clif_msgtable(int fd, int line) {
- WFIFOHEAD(fd, packet_len(0x291));
- WFIFOW(fd, 0) = 0x291;
- WFIFOW(fd, 2) = line;
- WFIFOSET(fd, packet_len(0x291));
-}
-
-// msgstringtable.txt
-// 0x7e2 <line>.W <value>.L
-void clif_msgtable_num(int fd, int line, int num) {
-#if PACKETVER >= 20090805
- WFIFOHEAD(fd, packet_len(0x7e2));
- WFIFOW(fd, 0) = 0x7e2;
- WFIFOW(fd, 2) = line;
- WFIFOL(fd, 4) = num;
- WFIFOSET(fd, packet_len(0x7e2));
-#endif
-}
-/*==========================================
- * used by SC_AUTOSHADOWSPELL
- * RFIFOL(fd,2) - flag (currently not used)
- *------------------------------------------*/
-void clif_parse_SkillSelectMenu(int fd, struct map_session_data *sd) {
-
- if( sd->menuskill_id != SC_AUTOSHADOWSPELL )
- return;
-
- if( pc_istrading(sd) ) {
- clif_skill_fail(sd,sd->ud.skill_id,0,0);
- clif_menuskill_clear(sd);
- return;
- }
-
- skill_select_menu(sd,RFIFOW(fd,6));
-
- clif_menuskill_clear(sd);
-}
-/*==========================================
- * Kagerou/Oboro amulet spirit
- *------------------------------------------*/
-void clif_talisman(struct map_session_data *sd,short type)
-{
- unsigned char buf[10];
-
- nullpo_retv(sd);
-
- WBUFW(buf,0)=0x08cf;
- WBUFL(buf,2)=sd->bl.id;
- WBUFW(buf,6)=type;
- WBUFW(buf,8)=sd->talisman[type];
- clif_send(buf,packet_len(0x08cf),&sd->bl,AREA);
-}
-/// Move Item from or to Personal Tab (CZ_WHATSOEVER) [FE]
-/// 0907 <index>.W
-///
-/// R 0908 <index>.w <type>.b
-/// type:
-/// 0 = move item to personal tab
-/// 1 = move item to normal tab
-void clif_parse_MoveItem(int fd, struct map_session_data *sd) {
-#if PACKETVER >= 20111122
- int index;
-
- /* can't move while dead. */
- if(pc_isdead(sd)) {
- return;
- }
-
- index = RFIFOW(fd,2)-2;
-
- if (index < 0 || index >= MAX_INVENTORY)
- return;
-
- if ( sd->status.inventory[index].favorite && RFIFOB(fd, 4) == 1 )
- sd->status.inventory[index].favorite = 0;
- else if( RFIFOB(fd, 4) == 0 )
- sd->status.inventory[index].favorite = 1;
- else
- return;/* nothing to do. */
-
- clif_favorite_item(sd, index);
-#endif
-}
-
-
-/// Items that are in favorite tab of inventory (ZC_ITEM_FAVORITE).
-/// 0900 <index>.W <favorite>.B
-void clif_favorite_item(struct map_session_data* sd, unsigned short index) {
- int fd = sd->fd;
-
- WFIFOHEAD(fd,packet_len(0x908));
- WFIFOW(fd,0) = 0x908;
- WFIFOW(fd,2) = index+2;
- WFIFOL(fd,4) = (sd->status.inventory[index].favorite == 1) ? 0 : 1;
- WFIFOSET(fd,packet_len(0x908));
-}
-
-void clif_snap( struct block_list *bl, short x, short y ) {
- unsigned char buf[10];
-
- WBUFW(buf,0) = 0x8d2;
- WBUFL(buf,2) = bl->id;
- WBUFW(buf,6) = x;
- WBUFW(buf,8) = y;
-
- clif_send(buf,packet_len(0x8d2),bl,AREA);
-}
-
-void clif_monster_hp_bar( struct mob_data* md, int fd ) {
-#if PACKETVER >= 20120404
- WFIFOHEAD(fd,packet_len(0x977));
-
- WFIFOW(fd,0) = 0x977;
- WFIFOL(fd,2) = md->bl.id;
- WFIFOL(fd,6) = md->status.hp;
- WFIFOL(fd,10) = md->status.max_hp;
-
- WFIFOSET(fd,packet_len(0x977));
-#endif
-}
-
-/*==========================================
- * Main client packet processing function
- *------------------------------------------*/
-static int clif_parse(int fd)
-{
- int cmd, packet_ver, packet_len, err;
- TBL_PC* sd;
- int pnum;
-
- //TODO apply delays or disconnect based on packet throughput [FlavioJS]
- // Note: "click masters" can do 80+ clicks in 10 seconds
-
- for( pnum = 0; pnum < 3; ++pnum )// Limit max packets per cycle to 3 (delay packet spammers) [FlavioJS] -- This actually aids packet spammers, but stuff like /str+ gets slow without it [Ai4rei]
- { // begin main client packet processing loop
-
- sd = (TBL_PC *)session[fd]->session_data;
- if (session[fd]->flag.eof) {
- if (sd) {
- if (sd->state.autotrade) {
- //Disassociate character from the socket connection.
- session[fd]->session_data = NULL;
- sd->fd = 0;
- ShowInfo("Character '"CL_WHITE"%s"CL_RESET"' logged off (using @autotrade).\n", sd->status.name);
- } else
- if (sd->state.active) {
- // Player logout display [Valaris]
- ShowInfo("Character '"CL_WHITE"%s"CL_RESET"' logged off.\n", sd->status.name);
- clif_quitsave(fd, sd);
- } else {
- //Unusual logout (during log on/off/map-changer procedure)
- ShowInfo("Player AID:%d/CID:%d logged off.\n", sd->status.account_id, sd->status.char_id);
- map_quit(sd);
- }
- } else {
- ShowInfo("Closed connection from '"CL_WHITE"%s"CL_RESET"'.\n", ip2str(session[fd]->client_addr, NULL));
- }
- do_close(fd);
- return 0;
- }
-
- if (RFIFOREST(fd) < 2)
- return 0;
-
- cmd = RFIFOW(fd,0);
-
- // identify client's packet version
- if (sd) {
- packet_ver = sd->packet_ver;
- } else {
- // check authentification packet to know packet version
- packet_ver = clif_guess_PacketVer(fd, 0, &err);
- if( err ) {// failed to identify packet version
- ShowInfo("clif_parse: Disconnecting session #%d with unknown packet version%s (p:0x%04x,l:%d).\n", fd, (
- err == 1 ? "" :
- err == 2 ? ", possibly for having an invalid account_id" :
- err == 3 ? ", possibly for having an invalid char_id." :
- /* Uncomment when checks are added in clif_guess_PacketVer. [FlavioJS]
- err == 4 ? ", possibly for having an invalid login_id1." :
- err == 5 ? ", possibly for having an invalid client_tick." :
- */
- err == 6 ? ", possibly for having an invalid sex." :
- ". ERROR invalid error code"), cmd, RFIFOREST(fd));
- WFIFOHEAD(fd,packet_len(0x6a));
- WFIFOW(fd,0) = 0x6a;
- WFIFOB(fd,2) = 3; // Rejected from Server
- WFIFOSET(fd,packet_len(0x6a));
-
-#ifdef DUMP_INVALID_PACKET
- ShowDump(RFIFOP(fd,0), RFIFOREST(fd));
-#endif
-
- RFIFOSKIP(fd, RFIFOREST(fd));
- set_eof(fd);
- return 0;
- }
- }
-
- // filter out invalid / unsupported packets
- if (cmd > MAX_PACKET_DB || packet_db[packet_ver][cmd].len == 0) {
- ShowWarning("clif_parse: Received unsupported packet (packet 0x%04x, %d bytes received), disconnecting session #%d.\n", cmd, RFIFOREST(fd), fd);
-#ifdef DUMP_INVALID_PACKET
- ShowDump(RFIFOP(fd,0), RFIFOREST(fd));
-#endif
- set_eof(fd);
- return 0;
- }
-
- // determine real packet length
- packet_len = packet_db[packet_ver][cmd].len;
- if (packet_len == -1) { // variable-length packet
- if (RFIFOREST(fd) < 4)
- return 0;
-
- packet_len = RFIFOW(fd,2);
- if (packet_len < 4 || packet_len > 32768) {
- ShowWarning("clif_parse: Received packet 0x%04x specifies invalid packet_len (%d), disconnecting session #%d.\n", cmd, packet_len, fd);
-#ifdef DUMP_INVALID_PACKET
- ShowDump(RFIFOP(fd,0), RFIFOREST(fd));
-#endif
- set_eof(fd);
- return 0;
- }
- }
- if ((int)RFIFOREST(fd) < packet_len)
- return 0; // not enough data received to form the packet
-
- if( packet_db[packet_ver][cmd].func == clif_parse_debug )
- packet_db[packet_ver][cmd].func(fd, sd);
- else if( packet_db[packet_ver][cmd].func != NULL ) {
- if( !sd && packet_db[packet_ver][cmd].func != clif_parse_WantToConnection )
- ; //Only valid packet when there is no session
- else
- if( sd && sd->bl.prev == NULL && packet_db[packet_ver][cmd].func != clif_parse_LoadEndAck )
- ; //Only valid packet when player is not on a map
- else
- if( sd && session[sd->fd]->flag.eof )
- ; //No more packets accepted
- else
- packet_db[packet_ver][cmd].func(fd, sd);
- }
-#ifdef DUMP_UNKNOWN_PACKET
- else {
- const char* packet_txt = "save/packet.txt";
- FILE* fp;
-
- if( ( fp = fopen( packet_txt , "a" ) ) != NULL ) {
- if( sd ) {
- fprintf(fp, "Unknown packet 0x%04X (length %d), %s session #%d, %d/%d (AID/CID)\n", cmd, packet_len, sd->state.active ? "authed" : "unauthed", fd, sd->status.account_id, sd->status.char_id);
- } else {
- fprintf(fp, "Unknown packet 0x%04X (length %d), session #%d\n", cmd, packet_len, fd);
- }
-
- WriteDump(fp, RFIFOP(fd,0), packet_len);
- fprintf(fp, "\n");
- fclose(fp);
- } else {
- ShowError("Failed to write '%s'.\n", packet_txt);
-
- // Dump on console instead
- if( sd ) {
- ShowDebug("Unknown packet 0x%04X (length %d), %s session #%d, %d/%d (AID/CID)\n", cmd, packet_len, sd->state.active ? "authed" : "unauthed", fd, sd->status.account_id, sd->status.char_id);
- } else {
- ShowDebug("Unknown packet 0x%04X (length %d), session #%d\n", cmd, packet_len, fd);
- }
-
- ShowDump(RFIFOP(fd,0), packet_len);
- }
- }
-#endif
-
- RFIFOSKIP(fd, packet_len);
-
- }; // main loop end
-
- return 0;
-}
-
-/*==========================================
- * Reads packet_db.txt and setups its array reference
- *------------------------------------------*/
-static int packetdb_readdb(void)
-{
- FILE *fp;
- char line[1024];
- int ln=0;
- int cmd,i,j,packet_ver;
- int max_cmd=-1;
- int skip_ver = 0;
- int warned = 0;
- char *str[64],*p,*str2[64],*p2,w1[64],w2[64];
- int packet_len_table[MAX_PACKET_DB] = {
- 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0040
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-#if PACKETVER <= 20081217
- 0, 0, 0, 0, 55, 17, 3, 37, 46, -1, 23, -1, 3,110, 3, 2,
-#else
- 0, 0, 0, 0, 55, 17, 3, 37, 46, -1, 23, -1, 3,114, 3, 2,
-#endif
-#if PACKETVER < 2
- 3, 28, 19, 11, 3, -1, 9, 5, 52, 51, 56, 58, 41, 2, 6, 6,
-#elif PACKETVER < 20071106 // 78-7b Lv99 effect for later Kameshima
- 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6,
-#elif PACKETVER <= 20081217 // change in 0x78 and 0x7c
- 3, 28, 19, 11, 3, -1, 9, 5, 55, 53, 58, 60, 42, 2, 6, 6,
-#else
- 3, 28, 19, 11, 3, -1, 9, 5, 55, 53, 58, 60, 44, 2, 6, 6,
-#endif
- //#0x0080
- 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 2, -1, -1, -1, 0, // 0x8b changed to 2 (was 23)
- 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6,
-#if PACKETVER <= 20100622
- 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6,
-#else
- 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 9, 4, 7, 0, -1, 6, // 0xaa changed to 9 (was 7)
-#endif
- 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3,
- //#0x00C0
- 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 3, 2, 27, // 0xcd change to 3 (was 6)
- 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1,
- 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2,
- 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10,
- //#0x0100
- 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1,
- 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16,
- 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1,
- 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26,
- //#0x0140
- 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6,
- 110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42,
- -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14,186,182,
- 14, 30, 10, 3, -1, 6,106, -1, 4, 5, 4, -1, 6, 7, -1, -1,
- //#0x0180
- 6, 3,106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6,
-#if PACKETVER < 1
- 90, 86, 24, 6, 30,102, 8, 4, 8, 4, 14, 10, -1, 6, 2, 6,
-#else // 196 comodo icon status display for later
- 90, 86, 24, 6, 30,102, 9, 4, 8, 4, 14, 10, -1, 6, 2, 6,
-#endif
-#if PACKETVER < 20081126
- 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4,
-#else // 0x1a2 changed (35->37)
- 3, 3, 37, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4,
-#endif
- 11, 7, -1, 67, 12, 18,114, 6, 3, 6, 26, 26, 26, 26, 2, 3,
- //#0x01C0, Set 0x1d5=-1
- 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 3, 9, 9, 30, 6, 28,
- 8, 14, 10, 35, 6, -1, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6,
- 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1,
- -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10,
- //#0x0200
- 26, -1, 26, 10, 18, 26, 11, 34, 14, 36, 10, 0, 0, -1, 32, 10, // 0x20c change to 0 (was 19)
- 22, 0, 26, 26, 42, 6, 6, 2, 2,282,282, 10, 10, -1, -1, 66,
-#if PACKETVER < 20071106
- 10, -1, -1, 8, 10, 2,282, 18, 18, 15, 58, 57, 64, 5, 71, 5,
-#else // 0x22c changed
- 10, -1, -1, 8, 10, 2,282, 18, 18, 15, 58, 57, 65, 5, 71, 5,
-#endif
- 12, 26, 9, 11, -1, -1, 10, 2,282, 11, 4, 36, 6, -1, 4, 2,
- //#0x0240
- -1, -1, -1, -1, -1, 3, 4, 8, -1, 3, 70, 4, 8, 12, 4, 10,
- 3, 32, -1, 3, 3, 5, 5, 8, 2, 3, -1, 6, 4, 6, 4, 6,
- 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0280
-#if PACKETVER < 20070711
- 0, 0, 0, 6, 14, 0, 0, -1, 6, 8, 18, 0, 0, 0, 0, 0,
-#else
- 0, 0, 0, 6, 14, 0, 0, -1, 10, 12, 18, 0, 0, 0, 0, 0, // 0x288, 0x289 increase by 4 (kafra points)
-#endif
- 0, 4, 0, 70, 10, 0, 0, 0, 8, 6, 27, 80, 0, -1, 0, 0,
- 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 85, -1, -1,107, 6, -1, 7, 7, 22,191, 0, 8, 0, 0, 0, 0,
- //#0x02C0
- 0, -1, 0, 0, 0, 30, 30, 0, 0, 3, 0, 65, 4, 71, 10, 0,
- -1, -1, -1, 0, 29, 0, 6, -1, 10, 10, 3, 0, -1, 32, 6, 36,
- 34, 33, 0, 0, 0, 0, 0, 0, -1, -1, -1, 13, 67, 59, 60, 8,
- 10, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0300
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0340
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0380
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x03C0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0400
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 25,
- //#0x0440
- 10, 4, -1, 0, 0, 0, 14, 0, 0, 0, 6, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0480
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x04C0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0500
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
- //#0x0540
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0580
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x05C0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0600
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
- //#0x0640
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0680
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x06C0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0700
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
- //#0x0740
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0780
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x07C0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-#if PACKETVER < 20090617
- 6, 2, -1, 4, 4, 4, 4, 8, 8,254, 6, 8, 6, 54, 30, 54,
-#else // 0x7d9 changed
- 6, 2, -1, 4, 4, 4, 4, 8, 8,268, 6, 8, 6, 54, 30, 54,
-#endif
- 0, 15, 8, 6, -1, 8, 8, 32, -1, 5, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 14, -1, -1, -1, 8, 25, 0, 0, 26, 0,
- //#0x0800
-#if PACKETVER < 20091229
- -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 20,
-#else // for Party booking ( PACKETVER >= 20091229 )
- -1, -1, 18, 4, 8, 6, 2, 4, 14, 50, 18, 6, 2, 3, 14, 20,
-#endif
- 3, -1, 8, -1, 86, 2, 6, 6, -1, -1, 4, 10, 10, 0, 0, 0,
- 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, -1, -1, 3, 2, 66, 5, 2, 12, 6, 0, 0,
- //#0x0840
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0880
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x08C0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,
- 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0900
- 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- //#0x0940
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0,
-
- };
- struct {
- void (*func)(int, struct map_session_data *);
- char *name;
- } clif_parse_func[]={
- {clif_parse_WantToConnection,"wanttoconnection"},
- {clif_parse_LoadEndAck,"loadendack"},
- {clif_parse_TickSend,"ticksend"},
- {clif_parse_WalkToXY,"walktoxy"},
- {clif_parse_QuitGame,"quitgame"},
- {clif_parse_GetCharNameRequest,"getcharnamerequest"},
- {clif_parse_GlobalMessage,"globalmessage"},
- {clif_parse_MapMove,"mapmove"},
- {clif_parse_ChangeDir,"changedir"},
- {clif_parse_Emotion,"emotion"},
- {clif_parse_HowManyConnections,"howmanyconnections"},
- {clif_parse_ActionRequest,"actionrequest"},
- {clif_parse_Restart,"restart"},
- {clif_parse_WisMessage,"wis"},
- {clif_parse_Broadcast,"broadcast"},
- {clif_parse_TakeItem,"takeitem"},
- {clif_parse_DropItem,"dropitem"},
- {clif_parse_UseItem,"useitem"},
- {clif_parse_EquipItem,"equipitem"},
- {clif_parse_UnequipItem,"unequipitem"},
- {clif_parse_NpcClicked,"npcclicked"},
- {clif_parse_NpcBuySellSelected,"npcbuysellselected"},
- {clif_parse_NpcBuyListSend,"npcbuylistsend"},
- {clif_parse_NpcSellListSend,"npcselllistsend"},
- {clif_parse_CreateChatRoom,"createchatroom"},
- {clif_parse_ChatAddMember,"chataddmember"},
- {clif_parse_ChatRoomStatusChange,"chatroomstatuschange"},
- {clif_parse_ChangeChatOwner,"changechatowner"},
- {clif_parse_KickFromChat,"kickfromchat"},
- {clif_parse_ChatLeave,"chatleave"},
- {clif_parse_TradeRequest,"traderequest"},
- {clif_parse_TradeAck,"tradeack"},
- {clif_parse_TradeAddItem,"tradeadditem"},
- {clif_parse_TradeOk,"tradeok"},
- {clif_parse_TradeCancel,"tradecancel"},
- {clif_parse_TradeCommit,"tradecommit"},
- {clif_parse_StopAttack,"stopattack"},
- {clif_parse_PutItemToCart,"putitemtocart"},
- {clif_parse_GetItemFromCart,"getitemfromcart"},
- {clif_parse_RemoveOption,"removeoption"},
- {clif_parse_ChangeCart,"changecart"},
- {clif_parse_StatusUp,"statusup"},
- {clif_parse_SkillUp,"skillup"},
- {clif_parse_UseSkillToId,"useskilltoid"},
- {clif_parse_UseSkillToPos,"useskilltopos"},
- {clif_parse_UseSkillToPosMoreInfo,"useskilltoposinfo"},
- {clif_parse_UseSkillMap,"useskillmap"},
- {clif_parse_RequestMemo,"requestmemo"},
- {clif_parse_ProduceMix,"producemix"},
- {clif_parse_Cooking,"cooking"},
- {clif_parse_NpcSelectMenu,"npcselectmenu"},
- {clif_parse_NpcNextClicked,"npcnextclicked"},
- {clif_parse_NpcAmountInput,"npcamountinput"},
- {clif_parse_NpcStringInput,"npcstringinput"},
- {clif_parse_NpcCloseClicked,"npccloseclicked"},
- {clif_parse_ItemIdentify,"itemidentify"},
- {clif_parse_SelectArrow,"selectarrow"},
- {clif_parse_AutoSpell,"autospell"},
- {clif_parse_UseCard,"usecard"},
- {clif_parse_InsertCard,"insertcard"},
- {clif_parse_RepairItem,"repairitem"},
- {clif_parse_WeaponRefine,"weaponrefine"},
- {clif_parse_SolveCharName,"solvecharname"},
- {clif_parse_ResetChar,"resetchar"},
- {clif_parse_LocalBroadcast,"localbroadcast"},
- {clif_parse_MoveToKafra,"movetokafra"},
- {clif_parse_MoveFromKafra,"movefromkafra"},
- {clif_parse_MoveToKafraFromCart,"movetokafrafromcart"},
- {clif_parse_MoveFromKafraToCart,"movefromkafratocart"},
- {clif_parse_CloseKafra,"closekafra"},
- {clif_parse_CreateParty,"createparty"},
- {clif_parse_CreateParty2,"createparty2"},
- {clif_parse_PartyInvite,"partyinvite"},
- {clif_parse_PartyInvite2,"partyinvite2"},
- {clif_parse_ReplyPartyInvite,"replypartyinvite"},
- {clif_parse_ReplyPartyInvite2,"replypartyinvite2"},
- {clif_parse_LeaveParty,"leaveparty"},
- {clif_parse_RemovePartyMember,"removepartymember"},
- {clif_parse_PartyChangeOption,"partychangeoption"},
- {clif_parse_PartyMessage,"partymessage"},
- {clif_parse_PartyChangeLeader,"partychangeleader"},
- {clif_parse_CloseVending,"closevending"},
- {clif_parse_VendingListReq,"vendinglistreq"},
- {clif_parse_PurchaseReq,"purchasereq"},
- {clif_parse_PurchaseReq2,"purchasereq2"},
- {clif_parse_OpenVending,"openvending"},
- {clif_parse_CreateGuild,"createguild"},
- {clif_parse_GuildCheckMaster,"guildcheckmaster"},
- {clif_parse_GuildRequestInfo,"guildrequestinfo"},
- {clif_parse_GuildChangePositionInfo,"guildchangepositioninfo"},
- {clif_parse_GuildChangeMemberPosition,"guildchangememberposition"},
- {clif_parse_GuildRequestEmblem,"guildrequestemblem"},
- {clif_parse_GuildChangeEmblem,"guildchangeemblem"},
- {clif_parse_GuildChangeNotice,"guildchangenotice"},
- {clif_parse_GuildInvite,"guildinvite"},
- {clif_parse_GuildReplyInvite,"guildreplyinvite"},
- {clif_parse_GuildLeave,"guildleave"},
- {clif_parse_GuildExpulsion,"guildexpulsion"},
- {clif_parse_GuildMessage,"guildmessage"},
- {clif_parse_GuildRequestAlliance,"guildrequestalliance"},
- {clif_parse_GuildReplyAlliance,"guildreplyalliance"},
- {clif_parse_GuildDelAlliance,"guilddelalliance"},
- {clif_parse_GuildOpposition,"guildopposition"},
- {clif_parse_GuildBreak,"guildbreak"},
- {clif_parse_PetMenu,"petmenu"},
- {clif_parse_CatchPet,"catchpet"},
- {clif_parse_SelectEgg,"selectegg"},
- {clif_parse_SendEmotion,"sendemotion"},
- {clif_parse_ChangePetName,"changepetname"},
-
- {clif_parse_GMKick,"gmkick"},
- {clif_parse_GMHide,"gmhide"},
- {clif_parse_GMReqNoChat,"gmreqnochat"},
- {clif_parse_GMReqAccountName,"gmreqaccname"},
- {clif_parse_GMKickAll,"killall"},
- {clif_parse_GMRecall,"recall"},
- {clif_parse_GMRecall,"summon"},
- {clif_parse_GM_Monster_Item,"itemmonster"},
- {clif_parse_GMShift,"remove"},
- {clif_parse_GMShift,"shift"},
- {clif_parse_GMChangeMapType,"changemaptype"},
- {clif_parse_GMRc,"rc"},
- {clif_parse_GMRecall2,"recall2"},
- {clif_parse_GMRemove2,"remove2"},
-
- {clif_parse_NoviceDoriDori,"sndoridori"},
- {clif_parse_NoviceExplosionSpirits,"snexplosionspirits"},
- {clif_parse_PMIgnore,"wisexin"},
- {clif_parse_PMIgnoreList,"wisexlist"},
- {clif_parse_PMIgnoreAll,"wisall"},
- {clif_parse_FriendsListAdd,"friendslistadd"},
- {clif_parse_FriendsListRemove,"friendslistremove"},
- {clif_parse_FriendsListReply,"friendslistreply"},
- {clif_parse_Blacksmith,"blacksmith"},
- {clif_parse_Alchemist,"alchemist"},
- {clif_parse_Taekwon,"taekwon"},
- {clif_parse_RankingPk,"rankingpk"},
- {clif_parse_FeelSaveOk,"feelsaveok"},
- {clif_parse_debug,"debug"},
- {clif_parse_ChangeHomunculusName,"changehomunculusname"},
- {clif_parse_HomMoveToMaster,"hommovetomaster"},
- {clif_parse_HomMoveTo,"hommoveto"},
- {clif_parse_HomAttack,"homattack"},
- {clif_parse_HomMenu,"hommenu"},
- {clif_parse_StoragePassword,"storagepassword"},
- {clif_parse_Hotkey,"hotkey"},
- {clif_parse_AutoRevive,"autorevive"},
- {clif_parse_Check,"check"},
- {clif_parse_Adopt_request,"adoptrequest"},
- {clif_parse_Adopt_reply,"adoptreply"},
- // MAIL SYSTEM
- {clif_parse_Mail_refreshinbox,"mailrefresh"},
- {clif_parse_Mail_read,"mailread"},
- {clif_parse_Mail_getattach,"mailgetattach"},
- {clif_parse_Mail_delete,"maildelete"},
- {clif_parse_Mail_return,"mailreturn"},
- {clif_parse_Mail_setattach,"mailsetattach"},
- {clif_parse_Mail_winopen,"mailwinopen"},
- {clif_parse_Mail_send,"mailsend"},
- // AUCTION SYSTEM
- {clif_parse_Auction_search,"auctionsearch"},
- {clif_parse_Auction_buysell,"auctionbuysell"},
- {clif_parse_Auction_setitem,"auctionsetitem"},
- {clif_parse_Auction_cancelreg,"auctioncancelreg"},
- {clif_parse_Auction_register,"auctionregister"},
- {clif_parse_Auction_cancel,"auctioncancel"},
- {clif_parse_Auction_close,"auctionclose"},
- {clif_parse_Auction_bid,"auctionbid"},
- // Quest Log System
- {clif_parse_questStateAck,"queststate"},
- {clif_parse_cashshop_buy,"cashshopbuy"},
- {clif_parse_ViewPlayerEquip,"viewplayerequip"},
- {clif_parse_EquipTick,"equiptickbox"},
- {clif_parse_BattleChat,"battlechat"},
- {clif_parse_mercenary_action,"mermenu"},
- {clif_parse_progressbar,"progressbar"},
- {clif_parse_SkillSelectMenu,"skillselectmenu"},
- {clif_parse_ItemListWindowSelected,"itemlistwindowselected"},
-#if PACKETVER >= 20091229
- {clif_parse_PartyBookingRegisterReq,"bookingregreq"},
- {clif_parse_PartyBookingSearchReq,"bookingsearchreq"},
- {clif_parse_PartyBookingUpdateReq,"bookingupdatereq"},
- {clif_parse_PartyBookingDeleteReq,"bookingdelreq"},
-#endif
- {clif_parse_PVPInfo,"pvpinfo"},
- {clif_parse_LessEffect,"lesseffect"},
- // Buying Store
- {clif_parse_ReqOpenBuyingStore,"reqopenbuyingstore"},
- {clif_parse_ReqCloseBuyingStore,"reqclosebuyingstore"},
- {clif_parse_ReqClickBuyingStore,"reqclickbuyingstore"},
- {clif_parse_ReqTradeBuyingStore,"reqtradebuyingstore"},
- // Store Search
- {clif_parse_SearchStoreInfo,"searchstoreinfo"},
- {clif_parse_SearchStoreInfoNextPage,"searchstoreinfonextpage"},
- {clif_parse_CloseSearchStoreInfo,"closesearchstoreinfo"},
- {clif_parse_SearchStoreInfoListItemClick,"searchstoreinfolistitemclick"},
- /* */
- { clif_parse_MoveItem , "moveitem" },
- {NULL,NULL}
- };
-
- // initialize packet_db[SERVER] from hardcoded packet_len_table[] values
- memset(packet_db,0,sizeof(packet_db));
- for( i = 0; i < ARRAYLENGTH(packet_len_table); ++i )
- packet_len(i) = packet_len_table[i];
-
- sprintf(line, "%s/packet_db.txt", db_path);
- if( (fp=fopen(line,"r"))==NULL ){
- ShowFatalError("can't read %s\n", line);
- exit(EXIT_FAILURE);
- }
-
- clif_config.packet_db_ver = MAX_PACKET_VER;
- packet_ver = MAX_PACKET_VER; // read into packet_db's version by default
- while( fgets(line, sizeof(line), fp) )
- {
- ln++;
- if(line[0]=='/' && line[1]=='/')
- continue;
- if (sscanf(line,"%256[^:]: %256[^\r\n]",w1,w2) == 2)
- {
- if(strcmpi(w1,"packet_ver")==0) {
- int prev_ver = packet_ver;
- skip_ver = 0;
- packet_ver = atoi(w2);
- if ( packet_ver > MAX_PACKET_VER )
- { //Check to avoid overflowing. [Skotlex]
- if( (warned&1) == 0 )
- ShowWarning("The packet_db table only has support up to version %d.\n", MAX_PACKET_VER);
- warned &= 1;
- skip_ver = 1;
- }
- else if( packet_ver < 0 )
- {
- if( (warned&2) == 0 )
- ShowWarning("Negative packet versions are not supported.\n");
- warned &= 2;
- skip_ver = 1;
- }
- else if( packet_ver == SERVER )
- {
- if( (warned&4) == 0 )
- ShowWarning("Packet version %d is reserved for server use only.\n", SERVER);
- warned &= 4;
- skip_ver = 1;
- }
-
- if( skip_ver )
- {
- ShowWarning("Skipping packet version %d.\n", packet_ver);
- packet_ver = prev_ver;
- continue;
- }
- // copy from previous version into new version and continue
- // - indicating all following packets should be read into the newer version
- memcpy(&packet_db[packet_ver], &packet_db[prev_ver], sizeof(packet_db[0]));
- continue;
- } else if(strcmpi(w1,"packet_db_ver")==0) {
- if(strcmpi(w2,"default")==0) //This is the preferred version.
- clif_config.packet_db_ver = MAX_PACKET_VER;
- else // to manually set the packet DB version
- clif_config.packet_db_ver = cap_value(atoi(w2), 0, MAX_PACKET_VER);
-
- continue;
- }
- }
-
- if( skip_ver != 0 )
- continue; // Skipping current packet version
-
- memset(str,0,sizeof(str));
- for(j=0,p=line;j<4 && p; ++j)
- {
- str[j]=p;
- p=strchr(p,',');
- if(p) *p++=0;
- }
- if(str[0]==NULL)
- continue;
- cmd=strtol(str[0],(char **)NULL,0);
- if(max_cmd < cmd)
- max_cmd = cmd;
- if(cmd <= 0 || cmd > MAX_PACKET_DB)
- continue;
- if(str[1]==NULL){
- ShowError("packet_db: packet len error\n");
- continue;
- }
-
- packet_db[packet_ver][cmd].len = (short)atoi(str[1]);
-
- if(str[2]==NULL){
- packet_db[packet_ver][cmd].func = NULL;
- ln++;
- continue;
- }
-
- // look up processing function by name
- ARR_FIND( 0, ARRAYLENGTH(clif_parse_func), j, clif_parse_func[j].name != NULL && strcmp(str[2],clif_parse_func[j].name)==0 );
- if( j < ARRAYLENGTH(clif_parse_func) )
- packet_db[packet_ver][cmd].func = clif_parse_func[j].func;
-
- // set the identifying cmd for the packet_db version
- if (strcmp(str[2],"wanttoconnection")==0)
- clif_config.connect_cmd[packet_ver] = cmd;
-
- if(str[3]==NULL){
- ShowError("packet_db: packet error\n");
- exit(EXIT_FAILURE);
- }
- for(j=0,p2=str[3];p2;j++){
- short k;
- str2[j]=p2;
- p2=strchr(p2,':');
- if(p2) *p2++=0;
- k = atoi(str2[j]);
- // if (packet_db[packet_ver][cmd].pos[j] != k && clif_config.prefer_packet_db) // not used for now
-
- if( j >= MAX_PACKET_POS )
- {
- ShowError("Too many positions found for packet 0x%04x (max=%d).\n", cmd, MAX_PACKET_POS);
- break;
- }
-
- packet_db[packet_ver][cmd].pos[j] = k;
- }
- }
- fclose(fp);
- if(max_cmd > MAX_PACKET_DB)
- {
- ShowWarning("Found packets up to 0x%X, ignored 0x%X and above.\n", max_cmd, MAX_PACKET_DB);
- ShowWarning("Please increase MAX_PACKET_DB and recompile.\n");
- }
- if (!clif_config.connect_cmd[clif_config.packet_db_ver])
- { //Locate the nearest version that we still support. [Skotlex]
- for(j = clif_config.packet_db_ver; j >= 0 && !clif_config.connect_cmd[j]; j--);
-
- clif_config.packet_db_ver = j?j:MAX_PACKET_VER;
- }
- ShowStatus("Done reading packet database from '"CL_WHITE"%s"CL_RESET"'. Using default packet version: "CL_WHITE"%d"CL_RESET".\n", "packet_db.txt", clif_config.packet_db_ver);
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int do_init_clif(void) {
- const char* colors[COLOR_MAX] = { "0xFF0000" };
- int i;
- /**
- * Setup Color Table (saves unnecessary load of strtoul on every call)
- **/
- for(i = 0; i < COLOR_MAX; i++) {
- color_table[i] = strtoul(colors[i],NULL,0);
- color_table[i] = (color_table[i] & 0x0000FF) << 16 | (color_table[i] & 0x00FF00) | (color_table[i] & 0xFF0000) >> 16;//RGB to BGR
- }
-
- clif_config.packet_db_ver = -1; // the main packet version of the DB
- memset(clif_config.connect_cmd, 0, sizeof(clif_config.connect_cmd)); //The default connect command will be determined after reading the packet_db [Skotlex]
-
- memset(packet_db,0,sizeof(packet_db));
- //Using the packet_db file is the only way to set up packets now [Skotlex]
- packetdb_readdb();
-
- set_defaultparse(clif_parse);
- if( make_listen_bind(bind_ip,map_port) == -1 ) {
- ShowFatalError("can't bind game port\n");
- exit(EXIT_FAILURE);
- }
-
- add_timer_func_list(clif_clearunit_delayed_sub, "clif_clearunit_delayed_sub");
- add_timer_func_list(clif_delayquit, "clif_delayquit");
-
- delay_clearunit_ers = ers_new(sizeof(struct block_list),"clif.c::delay_clearunit_ers",ERS_OPT_CLEAR);
-
- return 0;
-}
-
-void do_final_clif(void) {
- ers_destroy(delay_clearunit_ers);
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "../common/cbasetypes.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/grfio.h"
+#include "../common/malloc.h"
+#include "../common/nullpo.h"
+#include "../common/random.h"
+#include "../common/showmsg.h"
+#include "../common/strlib.h"
+#include "../common/utils.h"
+#include "../common/ers.h"
+
+#include "map.h"
+#include "chrif.h"
+#include "pc.h"
+#include "status.h"
+#include "npc.h"
+#include "itemdb.h"
+#include "chat.h"
+#include "trade.h"
+#include "storage.h"
+#include "script.h"
+#include "skill.h"
+#include "atcommand.h"
+#include "intif.h"
+#include "battle.h"
+#include "battleground.h"
+#include "mob.h"
+#include "party.h"
+#include "unit.h"
+#include "guild.h"
+#include "vending.h"
+#include "pet.h"
+#include "homunculus.h"
+#include "instance.h"
+#include "mercenary.h"
+#include "elemental.h"
+#include "log.h"
+#include "clif.h"
+#include "mail.h"
+#include "quest.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+
+/* for clif_clearunit_delayed */
+static struct eri *delay_clearunit_ers;
+
+//#define DUMP_UNKNOWN_PACKET
+//#define DUMP_INVALID_PACKET
+
+struct Clif_Config {
+ int packet_db_ver; //Preferred packet version.
+ int connect_cmd[MAX_PACKET_VER + 1]; //Store the connect command for all versions. [Skotlex]
+} clif_config;
+
+struct s_packet_db packet_db[MAX_PACKET_VER + 1][MAX_PACKET_DB + 1];
+
+//Converts item type in case of pet eggs.
+static inline int itemtype(int type)
+{
+ return ( type == IT_PETEGG ) ? IT_WEAPON : type;
+}
+
+
+static inline void WBUFPOS(uint8* p, unsigned short pos, short x, short y, unsigned char dir)
+{
+ p += pos;
+ p[0] = (uint8)(x>>2);
+ p[1] = (uint8)((x<<6) | ((y>>4)&0x3f));
+ p[2] = (uint8)((y<<4) | (dir&0xf));
+}
+
+
+// client-side: x0+=sx0*0.0625-0.5 and y0+=sy0*0.0625-0.5
+static inline void WBUFPOS2(uint8* p, unsigned short pos, short x0, short y0, short x1, short y1, unsigned char sx0, unsigned char sy0)
+{
+ p += pos;
+ p[0] = (uint8)(x0>>2);
+ p[1] = (uint8)((x0<<6) | ((y0>>4)&0x3f));
+ p[2] = (uint8)((y0<<4) | ((x1>>6)&0x0f));
+ p[3] = (uint8)((x1<<2) | ((y1>>8)&0x03));
+ p[4] = (uint8)y1;
+ p[5] = (uint8)((sx0<<4) | (sy0&0x0f));
+}
+
+
+static inline void WFIFOPOS(int fd, unsigned short pos, short x, short y, unsigned char dir)
+{
+ WBUFPOS(WFIFOP(fd,pos), 0, x, y, dir);
+}
+
+
+static inline void WFIFOPOS2(int fd, unsigned short pos, short x0, short y0, short x1, short y1, unsigned char sx0, unsigned char sy0)
+{
+ WBUFPOS2(WFIFOP(fd,pos), 0, x0, y0, x1, y1, sx0, sy0);
+}
+
+
+static inline void RBUFPOS(const uint8* p, unsigned short pos, short* x, short* y, unsigned char* dir)
+{
+ p += pos;
+
+ if( x ) {
+ x[0] = ( ( p[0] & 0xff ) << 2 ) | ( p[1] >> 6 );
+ }
+
+ if( y ) {
+ y[0] = ( ( p[1] & 0x3f ) << 4 ) | ( p[2] >> 4 );
+ }
+
+ if( dir ) {
+ dir[0] = ( p[2] & 0x0f );
+ }
+}
+
+
+static inline void RBUFPOS2(const uint8* p, unsigned short pos, short* x0, short* y0, short* x1, short* y1, unsigned char* sx0, unsigned char* sy0)
+{
+ p += pos;
+
+ if( x0 ) {
+ x0[0] = ( ( p[0] & 0xff ) << 2 ) | ( p[1] >> 6 );
+ }
+
+ if( y0 ) {
+ y0[0] = ( ( p[1] & 0x3f ) << 4 ) | ( p[2] >> 4 );
+ }
+
+ if( x1 ) {
+ x1[0] = ( ( p[2] & 0x0f ) << 6 ) | ( p[3] >> 2 );
+ }
+
+ if( y1 ) {
+ y1[0] = ( ( p[3] & 0x03 ) << 8 ) | ( p[4] >> 0 );
+ }
+
+ if( sx0 ) {
+ sx0[0] = ( p[5] & 0xf0 ) >> 4;
+ }
+
+ if( sy0 ) {
+ sy0[0] = ( p[5] & 0x0f ) >> 0;
+ }
+}
+
+
+static inline void RFIFOPOS(int fd, unsigned short pos, short* x, short* y, unsigned char* dir)
+{
+ RBUFPOS(RFIFOP(fd,pos), 0, x, y, dir);
+}
+
+
+static inline void RFIFOPOS2(int fd, unsigned short pos, short* x0, short* y0, short* x1, short* y1, unsigned char* sx0, unsigned char* sy0)
+{
+ RBUFPOS2(WFIFOP(fd,pos), 0, x0, y0, x1, y1, sx0, sy0);
+}
+
+
+//To idenfity disguised characters.
+static inline bool disguised(struct block_list* bl)
+{
+ return (bool)( bl->type == BL_PC && ((TBL_PC*)bl)->disguise );
+}
+
+
+//Guarantees that the given string does not exceeds the allowed size, as well as making sure it's null terminated. [Skotlex]
+static inline unsigned int mes_len_check(char* mes, unsigned int len, unsigned int max)
+{
+ if( len > max )
+ len = max;
+
+ mes[len-1] = '\0';
+
+ return len;
+}
+
+
+static char map_ip_str[128];
+static uint32 map_ip;
+static uint32 bind_ip = INADDR_ANY;
+static uint16 map_port = 5121;
+int map_fd;
+
+static int clif_parse (int fd);
+
+/*==========================================
+ * Ip setting of map-server
+ *------------------------------------------*/
+int clif_setip(const char* ip)
+{
+ char ip_str[16];
+ map_ip = host2ip(ip);
+ if (!map_ip) {
+ ShowWarning("Failed to Resolve Map Server Address! (%s)\n", ip);
+ return 0;
+ }
+
+ strncpy(map_ip_str, ip, sizeof(map_ip_str));
+ ShowInfo("Map Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%s"CL_RESET"'.\n", ip, ip2str(map_ip, ip_str));
+ return 1;
+}
+
+void clif_setbindip(const char* ip)
+{
+ char ip_str[16];
+ bind_ip = host2ip(ip);
+ if (bind_ip) {
+ ShowInfo("Map Server Bind IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%s"CL_RESET"'.\n", ip, ip2str(bind_ip, ip_str));
+ } else {
+ ShowWarning("Failed to Resolve Map Server Address! (%s)\n", ip);
+ }
+}
+
+/*==========================================
+ * Sets map port to 'port'
+ * is run from map.c upon loading map server configuration
+ *------------------------------------------*/
+void clif_setport(uint16 port)
+{
+ map_port = port;
+}
+
+/*==========================================
+ * Returns map server IP
+ *------------------------------------------*/
+uint32 clif_getip(void)
+{
+ return map_ip;
+}
+
+//Refreshes map_server ip, returns the new ip if the ip changed, otherwise it returns 0.
+uint32 clif_refresh_ip(void)
+{
+ uint32 new_ip;
+
+ new_ip = host2ip(map_ip_str);
+ if (new_ip && new_ip != map_ip) {
+ map_ip = new_ip;
+ ShowInfo("Updating IP resolution of [%s].\n", map_ip_str);
+ return map_ip;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Returns map port which is set by clif_setport()
+ *------------------------------------------*/
+uint16 clif_getport(void)
+{
+ return map_port;
+}
+
+#if PACKETVER >= 20071106
+static inline unsigned char clif_bl_type(struct block_list *bl) {
+ switch (bl->type) {
+ case BL_PC: return disguised(bl)?0x1:0x0; //PC_TYPE
+ case BL_ITEM: return 0x2; //ITEM_TYPE
+ case BL_SKILL: return 0x3; //SKILL_TYPE
+ case BL_CHAT: return 0x4; //UNKNOWN_TYPE
+ case BL_MOB: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x5; //NPC_MOB_TYPE
+ case BL_NPC: return 0x6; //NPC_EVT_TYPE
+ case BL_PET: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x7; //NPC_PET_TYPE
+ case BL_HOM: return 0x8; //NPC_HOM_TYPE
+ case BL_MER: return 0x9; //NPC_MERSOL_TYPE
+ case BL_ELEM: return 0xa; //NPC_ELEMENTAL_TYPE
+ default: return 0x1; //NPC_TYPE
+ }
+}
+#endif
+
+/*==========================================
+ * sub process of clif_send
+ * Called from a map_foreachinarea (grabs all players in specific area and subjects them to this function)
+ * In order to send area-wise packets, such as:
+ * - AREA : everyone nearby your area
+ * - AREA_WOSC (AREA WITHOUT SAME CHAT) : Not run for people in the same chat as yours
+ * - AREA_WOC (AREA WITHOUT CHAT) : Not run for people inside a chat
+ * - AREA_WOS (AREA WITHOUT SELF) : Not run for self
+ * - AREA_CHAT_WOC : Everyone in the area of your chat without a chat
+ *------------------------------------------*/
+static int clif_send_sub(struct block_list *bl, va_list ap)
+{
+ struct block_list *src_bl;
+ struct map_session_data *sd;
+ unsigned char *buf;
+ int len, type, fd;
+
+ nullpo_ret(bl);
+ nullpo_ret(sd = (struct map_session_data *)bl);
+
+ fd = sd->fd;
+ if (!fd) //Don't send to disconnected clients.
+ return 0;
+
+ buf = va_arg(ap,unsigned char*);
+ len = va_arg(ap,int);
+ nullpo_ret(src_bl = va_arg(ap,struct block_list*));
+ type = va_arg(ap,int);
+
+ switch(type)
+ {
+ case AREA_WOS:
+ if (bl == src_bl)
+ return 0;
+ break;
+ case AREA_WOC:
+ if (sd->chatID || bl == src_bl)
+ return 0;
+ break;
+ case AREA_WOSC:
+ {
+ if(src_bl->type == BL_PC){
+ struct map_session_data *ssd = (struct map_session_data *)src_bl;
+ if (ssd && sd->chatID && (sd->chatID == ssd->chatID))
+ return 0;
+ }
+ else if(src_bl->type == BL_NPC) {
+ struct npc_data *nd = (struct npc_data *)src_bl;
+ if (nd && sd->chatID && (sd->chatID == nd->chat_id))
+ return 0;
+ }
+ }
+ break;
+ }
+
+ if (session[fd] == NULL)
+ return 0;
+
+ WFIFOHEAD(fd, len);
+ if (WFIFOP(fd,0) == buf) {
+ ShowError("WARNING: Invalid use of clif_send function\n");
+ ShowError(" Packet x%4x use a WFIFO of a player instead of to use a buffer.\n", WBUFW(buf,0));
+ ShowError(" Please correct your code.\n");
+ // don't send to not move the pointer of the packet for next sessions in the loop
+ //WFIFOSET(fd,0);//## TODO is this ok?
+ //NO. It is not ok. There is the chance WFIFOSET actually sends the buffer data, and shifts elements around, which will corrupt the buffer.
+ return 0;
+ }
+
+ if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Packet Delegation (called on all packets that require data to be sent to more than one client)
+ * functions that are sent solely to one use whose ID it posses use WFIFOSET
+ *------------------------------------------*/
+int clif_send(const uint8* buf, int len, struct block_list* bl, enum send_target type)
+{
+ int i;
+ struct map_session_data *sd, *tsd;
+ struct party_data *p = NULL;
+ struct guild *g = NULL;
+ struct battleground_data *bg = NULL;
+ int x0 = 0, x1 = 0, y0 = 0, y1 = 0, fd;
+ struct s_mapiterator* iter;
+
+ if( type != ALL_CLIENT && type != CHAT_MAINCHAT )
+ nullpo_ret(bl);
+
+ sd = BL_CAST(BL_PC, bl);
+
+ switch(type) {
+
+ case ALL_CLIENT: //All player clients.
+ iter = mapit_getallusers();
+ while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
+ {
+ if( packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(tsd->fd, len);
+ memcpy(WFIFOP(tsd->fd,0), buf, len);
+ WFIFOSET(tsd->fd,len);
+ }
+ }
+ mapit_free(iter);
+ break;
+
+ case ALL_SAMEMAP: //All players on the same map
+ iter = mapit_getallusers();
+ while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
+ {
+ if( bl->m == tsd->bl.m && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(tsd->fd, len);
+ memcpy(WFIFOP(tsd->fd,0), buf, len);
+ WFIFOSET(tsd->fd,len);
+ }
+ }
+ mapit_free(iter);
+ break;
+
+ case AREA:
+ case AREA_WOSC:
+ if (sd && bl->prev == NULL) //Otherwise source misses the packet.[Skotlex]
+ clif_send (buf, len, bl, SELF);
+ case AREA_WOC:
+ case AREA_WOS:
+ map_foreachinarea(clif_send_sub, bl->m, bl->x-AREA_SIZE, bl->y-AREA_SIZE, bl->x+AREA_SIZE, bl->y+AREA_SIZE,
+ BL_PC, buf, len, bl, type);
+ break;
+ case AREA_CHAT_WOC:
+ map_foreachinarea(clif_send_sub, bl->m, bl->x-(AREA_SIZE-5), bl->y-(AREA_SIZE-5),
+ bl->x+(AREA_SIZE-5), bl->y+(AREA_SIZE-5), BL_PC, buf, len, bl, AREA_WOC);
+ break;
+
+ case CHAT:
+ case CHAT_WOS:
+ {
+ struct chat_data *cd;
+ if (sd) {
+ cd = (struct chat_data*)map_id2bl(sd->chatID);
+ } else if (bl->type == BL_CHAT) {
+ cd = (struct chat_data*)bl;
+ } else break;
+ if (cd == NULL)
+ break;
+ for(i = 0; i < cd->users; i++) {
+ if (type == CHAT_WOS && cd->usersd[i] == sd)
+ continue;
+ if (packet_db[cd->usersd[i]->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ if ((fd=cd->usersd[i]->fd) >0 && session[fd]) // Added check to see if session exists [PoW]
+ {
+ WFIFOHEAD(fd,len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ }
+ }
+ }
+ }
+ break;
+
+ case CHAT_MAINCHAT: //[LuzZza]
+ iter = mapit_getallusers();
+ while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
+ {
+ if( tsd->state.mainchat && tsd->chatID == 0 && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(tsd->fd, len);
+ memcpy(WFIFOP(tsd->fd,0), buf, len);
+ WFIFOSET(tsd->fd,len);
+ }
+ }
+ mapit_free(iter);
+ break;
+
+ case PARTY_AREA:
+ case PARTY_AREA_WOS:
+ x0 = bl->x - AREA_SIZE;
+ y0 = bl->y - AREA_SIZE;
+ x1 = bl->x + AREA_SIZE;
+ y1 = bl->y + AREA_SIZE;
+ case PARTY:
+ case PARTY_WOS:
+ case PARTY_SAMEMAP:
+ case PARTY_SAMEMAP_WOS:
+ if (sd && sd->status.party_id)
+ p = party_search(sd->status.party_id);
+
+ if (p) {
+ for(i=0;i<MAX_PARTY;i++){
+ if( (sd = p->data[i].sd) == NULL )
+ continue;
+
+ if( !(fd=sd->fd) )
+ continue;
+
+ if( sd->bl.id == bl->id && (type == PARTY_WOS || type == PARTY_SAMEMAP_WOS || type == PARTY_AREA_WOS) )
+ continue;
+
+ if( type != PARTY && type != PARTY_WOS && bl->m != sd->bl.m )
+ continue;
+
+ if( (type == PARTY_AREA || type == PARTY_AREA_WOS) && (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1) )
+ continue;
+
+ if( packet_db[sd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(fd,len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ }
+ }
+ if (!enable_spy) //Skip unnecessary parsing. [Skotlex]
+ break;
+
+ iter = mapit_getallusers();
+ while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
+ {
+ if( tsd->partyspy == p->party.party_id && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(tsd->fd, len);
+ memcpy(WFIFOP(tsd->fd,0), buf, len);
+ WFIFOSET(tsd->fd,len);
+ }
+ }
+ mapit_free(iter);
+ }
+ break;
+
+ case DUEL:
+ case DUEL_WOS:
+ if (!sd || !sd->duel_group) break; //Invalid usage.
+
+ iter = mapit_getallusers();
+ while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
+ {
+ if( type == DUEL_WOS && bl->id == tsd->bl.id )
+ continue;
+ if( sd->duel_group == tsd->duel_group && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(tsd->fd, len);
+ memcpy(WFIFOP(tsd->fd,0), buf, len);
+ WFIFOSET(tsd->fd,len);
+ }
+ }
+ mapit_free(iter);
+ break;
+
+ case SELF:
+ if (sd && (fd=sd->fd) && packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version
+ WFIFOHEAD(fd,len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ }
+ break;
+
+ // New definitions for guilds [Valaris] - Cleaned up and reorganized by [Skotlex]
+ case GUILD_AREA:
+ case GUILD_AREA_WOS:
+ x0 = bl->x - AREA_SIZE;
+ y0 = bl->y - AREA_SIZE;
+ x1 = bl->x + AREA_SIZE;
+ y1 = bl->y + AREA_SIZE;
+ case GUILD_SAMEMAP:
+ case GUILD_SAMEMAP_WOS:
+ case GUILD:
+ case GUILD_WOS:
+ case GUILD_NOBG:
+ if (sd && sd->status.guild_id)
+ g = guild_search(sd->status.guild_id);
+
+ if (g) {
+ for(i = 0; i < g->max_member; i++) {
+ if( (sd = g->member[i].sd) != NULL )
+ {
+ if( !(fd=sd->fd) )
+ continue;
+
+ if( type == GUILD_NOBG && sd->bg_id )
+ continue;
+
+ if( sd->bl.id == bl->id && (type == GUILD_WOS || type == GUILD_SAMEMAP_WOS || type == GUILD_AREA_WOS) )
+ continue;
+
+ if( type != GUILD && type != GUILD_NOBG && type != GUILD_WOS && sd->bl.m != bl->m )
+ continue;
+
+ if( (type == GUILD_AREA || type == GUILD_AREA_WOS) && (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1) )
+ continue;
+
+ if( packet_db[sd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(fd,len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ }
+ }
+ }
+ if (!enable_spy) //Skip unnecessary parsing. [Skotlex]
+ break;
+
+ iter = mapit_getallusers();
+ while( (tsd = (TBL_PC*)mapit_next(iter)) != NULL )
+ {
+ if( tsd->guildspy == g->guild_id && packet_db[tsd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(tsd->fd, len);
+ memcpy(WFIFOP(tsd->fd,0), buf, len);
+ WFIFOSET(tsd->fd,len);
+ }
+ }
+ mapit_free(iter);
+ }
+ break;
+
+ case BG_AREA:
+ case BG_AREA_WOS:
+ x0 = bl->x - AREA_SIZE;
+ y0 = bl->y - AREA_SIZE;
+ x1 = bl->x + AREA_SIZE;
+ y1 = bl->y + AREA_SIZE;
+ case BG_SAMEMAP:
+ case BG_SAMEMAP_WOS:
+ case BG:
+ case BG_WOS:
+ if( sd && sd->bg_id && (bg = bg_team_search(sd->bg_id)) != NULL )
+ {
+ for( i = 0; i < MAX_BG_MEMBERS; i++ )
+ {
+ if( (sd = bg->members[i].sd) == NULL || !(fd = sd->fd) )
+ continue;
+ if( sd->bl.id == bl->id && (type == BG_WOS || type == BG_SAMEMAP_WOS || type == BG_AREA_WOS) )
+ continue;
+ if( type != BG && type != BG_WOS && sd->bl.m != bl->m )
+ continue;
+ if( (type == BG_AREA || type == BG_AREA_WOS) && (sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1) )
+ continue;
+ if( packet_db[sd->packet_ver][RBUFW(buf,0)].len )
+ { // packet must exist for the client version
+ WFIFOHEAD(fd,len);
+ memcpy(WFIFOP(fd,0), buf, len);
+ WFIFOSET(fd,len);
+ }
+ }
+ }
+ break;
+
+ default:
+ ShowError("clif_send: Unrecognized type %d\n",type);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/// Notifies the client, that it's connection attempt was accepted.
+/// 0073 <start time>.L <position>.3B <x size>.B <y size>.B (ZC_ACCEPT_ENTER)
+/// 02eb <start time>.L <position>.3B <x size>.B <y size>.B <font>.W (ZC_ACCEPT_ENTER2)
+void clif_authok(struct map_session_data *sd)
+{
+#if PACKETVER < 20080102
+ const int cmd = 0x73;
+#else
+ const int cmd = 0x2eb;
+#endif
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(cmd));
+ WFIFOW(fd, 0) = cmd;
+ WFIFOL(fd, 2) = gettick();
+ WFIFOPOS(fd, 6, sd->bl.x, sd->bl.y, sd->ud.dir);
+ WFIFOB(fd, 9) = 5; // ignored
+ WFIFOB(fd,10) = 5; // ignored
+#if PACKETVER >= 20080102
+ WFIFOW(fd,11) = sd->user_font; // FIXME: Font is currently not saved.
+#endif
+ WFIFOSET(fd,packet_len(cmd));
+}
+
+
+/// Notifies the client, that it's connection attempt was refused (ZC_REFUSE_ENTER).
+/// 0074 <error code>.B
+/// error code:
+/// 0 = client type mismatch
+/// 1 = ID mismatch
+/// 2 = mobile - out of available time
+/// 3 = mobile - already logged in
+/// 4 = mobile - waiting state
+void clif_authrefuse(int fd, uint8 error_code)
+{
+ WFIFOHEAD(fd,packet_len(0x74));
+ WFIFOW(fd,0) = 0x74;
+ WFIFOB(fd,2) = error_code;
+ WFIFOSET(fd,packet_len(0x74));
+}
+
+
+/// Notifies the client of a ban or forced disconnect (SC_NOTIFY_BAN).
+/// 0081 <error code>.B
+/// error code:
+/// 0 = BAN_UNFAIR
+/// 1 = server closed -> MsgStringTable[4]
+/// 2 = ID already logged in -> MsgStringTable[5]
+/// 3 = timeout/too much lag -> MsgStringTable[241]
+/// 4 = server full -> MsgStringTable[264]
+/// 5 = underaged -> MsgStringTable[305]
+/// 8 = Server sill recognizes last connection -> MsgStringTable[441]
+/// 9 = too many connections from this ip -> MsgStringTable[529]
+/// 10 = out of available time paid for -> MsgStringTable[530]
+/// 11 = BAN_PAY_SUSPEND
+/// 12 = BAN_PAY_CHANGE
+/// 13 = BAN_PAY_WRONGIP
+/// 14 = BAN_PAY_PNGAMEROOM
+/// 15 = disconnected by a GM -> if( servicetype == taiwan ) MsgStringTable[579]
+/// 16 = BAN_JAPAN_REFUSE1
+/// 17 = BAN_JAPAN_REFUSE2
+/// 18 = BAN_INFORMATION_REMAINED_ANOTHER_ACCOUNT
+/// 100 = BAN_PC_IP_UNFAIR
+/// 101 = BAN_PC_IP_COUNT_ALL
+/// 102 = BAN_PC_IP_COUNT
+/// 103 = BAN_GRAVITY_MEM_AGREE
+/// 104 = BAN_GAME_MEM_AGREE
+/// 105 = BAN_HAN_VALID
+/// 106 = BAN_PC_IP_LIMIT_ACCESS
+/// 107 = BAN_OVER_CHARACTER_LIST
+/// 108 = BAN_IP_BLOCK
+/// 109 = BAN_INVALID_PWD_CNT
+/// 110 = BAN_NOT_ALLOWED_JOBCLASS
+/// ? = disconnected -> MsgStringTable[3]
+void clif_authfail_fd(int fd, int type)
+{
+ if (!fd || !session[fd] || session[fd]->func_parse != clif_parse) //clif_authfail should only be invoked on players!
+ return;
+
+ WFIFOHEAD(fd, packet_len(0x81));
+ WFIFOW(fd,0) = 0x81;
+ WFIFOB(fd,2) = type;
+ WFIFOSET(fd,packet_len(0x81));
+ set_eof(fd);
+}
+
+
+/// Notifies the client, whether it can disconnect and change servers (ZC_RESTART_ACK).
+/// 00b3 <type>.B
+/// type:
+/// 1 = disconnect, char-select
+/// ? = nothing
+void clif_charselectok(int id, uint8 ok)
+{
+ struct map_session_data* sd;
+ int fd;
+
+ if ((sd = map_id2sd(id)) == NULL || !sd->fd)
+ return;
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0xb3));
+ WFIFOW(fd,0) = 0xb3;
+ WFIFOB(fd,2) = ok;
+ WFIFOSET(fd,packet_len(0xb3));
+}
+
+/// Makes an item appear on the ground.
+/// 009e <id>.L <name id>.W <identified>.B <x>.W <y>.W <subX>.B <subY>.B <amount>.W (ZC_ITEM_FALL_ENTRY)
+/// 084b (ZC_ITEM_FALL_ENTRY4)
+void clif_dropflooritem(struct flooritem_data* fitem)
+{
+ uint8 buf[17];
+ int view;
+
+ nullpo_retv(fitem);
+
+ if (fitem->item_data.nameid <= 0)
+ return;
+
+ WBUFW(buf, 0) = 0x9e;
+ WBUFL(buf, 2) = fitem->bl.id;
+ WBUFW(buf, 6) = ((view = itemdb_viewid(fitem->item_data.nameid)) > 0) ? view : fitem->item_data.nameid;
+ WBUFB(buf, 8) = fitem->item_data.identify;
+ WBUFW(buf, 9) = fitem->bl.x;
+ WBUFW(buf,11) = fitem->bl.y;
+ WBUFB(buf,13) = fitem->subx;
+ WBUFB(buf,14) = fitem->suby;
+ WBUFW(buf,15) = fitem->item_data.amount;
+
+ clif_send(buf, packet_len(0x9e), &fitem->bl, AREA);
+}
+
+
+
+/// Makes an item disappear from the ground.
+/// 00a1 <id>.L (ZC_ITEM_DISAPPEAR)
+void clif_clearflooritem(struct flooritem_data *fitem, int fd)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(fitem);
+
+ WBUFW(buf,0) = 0xa1;
+ WBUFL(buf,2) = fitem->bl.id;
+
+ if (fd == 0) {
+ clif_send(buf, packet_len(0xa1), &fitem->bl, AREA);
+ } else {
+ WFIFOHEAD(fd,packet_len(0xa1));
+ memcpy(WFIFOP(fd,0), buf, packet_len(0xa1));
+ WFIFOSET(fd,packet_len(0xa1));
+ }
+}
+
+
+/// Makes a unit (char, npc, mob, homun) disappear to one client (ZC_NOTIFY_VANISH).
+/// 0080 <id>.L <type>.B
+/// type:
+/// 0 = out of sight
+/// 1 = died
+/// 2 = logged out
+/// 3 = teleport
+/// 4 = trickdead
+void clif_clearunit_single(int id, clr_type type, int fd)
+{
+ WFIFOHEAD(fd, packet_len(0x80));
+ WFIFOW(fd,0) = 0x80;
+ WFIFOL(fd,2) = id;
+ WFIFOB(fd,6) = type;
+ WFIFOSET(fd, packet_len(0x80));
+}
+
+/// Makes a unit (char, npc, mob, homun) disappear to all clients in area (ZC_NOTIFY_VANISH).
+/// 0080 <id>.L <type>.B
+/// type:
+/// 0 = out of sight
+/// 1 = died
+/// 2 = logged out
+/// 3 = teleport
+/// 4 = trickdead
+void clif_clearunit_area(struct block_list* bl, clr_type type)
+{
+ unsigned char buf[8];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x80;
+ WBUFL(buf,2) = bl->id;
+ WBUFB(buf,6) = type;
+
+ clif_send(buf, packet_len(0x80), bl, type == CLR_DEAD ? AREA : AREA_WOS);
+
+ if(disguised(bl)) {
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf, packet_len(0x80), bl, SELF);
+ }
+}
+
+
+/// Used to make monsters with player-sprites disappear after dying
+/// like normal monsters, because the client does not remove those
+/// automatically.
+static int clif_clearunit_delayed_sub(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct block_list *bl = (struct block_list *)data;
+ clif_clearunit_area(bl, (clr_type) id);
+ ers_free(delay_clearunit_ers,bl);
+ return 0;
+}
+void clif_clearunit_delayed(struct block_list* bl, clr_type type, unsigned int tick)
+{
+ struct block_list *tbl = ers_alloc(delay_clearunit_ers, struct block_list);
+ memcpy (tbl, bl, sizeof (struct block_list));
+ add_timer(tick, clif_clearunit_delayed_sub, (int)type, (intptr_t)tbl);
+}
+
+void clif_get_weapon_view(struct map_session_data* sd, unsigned short *rhand, unsigned short *lhand)
+{
+ if(sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER))
+ {
+ *rhand = *lhand = 0;
+ return;
+ }
+
+#if PACKETVER < 4
+ *rhand = sd->status.weapon;
+ *lhand = sd->status.shield;
+#else
+ if (sd->equip_index[EQI_HAND_R] >= 0 &&
+ sd->inventory_data[sd->equip_index[EQI_HAND_R]])
+ {
+ struct item_data* id = sd->inventory_data[sd->equip_index[EQI_HAND_R]];
+ if (id->view_id > 0)
+ *rhand = id->view_id;
+ else
+ *rhand = id->nameid;
+ } else
+ *rhand = 0;
+
+ if (sd->equip_index[EQI_HAND_L] >= 0 &&
+ sd->equip_index[EQI_HAND_L] != sd->equip_index[EQI_HAND_R] &&
+ sd->inventory_data[sd->equip_index[EQI_HAND_L]])
+ {
+ struct item_data* id = sd->inventory_data[sd->equip_index[EQI_HAND_L]];
+ if (id->view_id > 0)
+ *lhand = id->view_id;
+ else
+ *lhand = id->nameid;
+ } else
+ *lhand = 0;
+#endif
+}
+
+//To make the assignation of the level based on limits clearer/easier. [Skotlex]
+static int clif_setlevel_sub(int lv)
+{
+ if( lv < battle_config.max_lv )
+ {
+ ;
+ }
+ else if( lv < battle_config.aura_lv )
+ {
+ lv = battle_config.max_lv - 1;
+ }
+ else
+ {
+ lv = battle_config.max_lv;
+ }
+
+ return lv;
+}
+
+static int clif_setlevel(struct block_list* bl)
+{
+ int lv = status_get_lv(bl);
+ if( battle_config.client_limit_unit_lv&bl->type )
+ return clif_setlevel_sub(lv);
+ switch( bl->type )
+ {
+ case BL_NPC:
+ case BL_PET:
+ // npcs and pets do not have level
+ return 0;
+ }
+ return lv;
+}
+
+/*==========================================
+ * Prepares 'unit standing/spawning' packet
+ *------------------------------------------*/
+static int clif_set_unit_idle(struct block_list* bl, unsigned char* buffer, bool spawn)
+{
+ struct map_session_data* sd;
+ struct status_change* sc = status_get_sc(bl);
+ struct view_data* vd = status_get_viewdata(bl);
+ unsigned char *buf = WBUFP(buffer,0);
+#if PACKETVER < 20091103
+ bool type = !pcdb_checkid(vd->class_);
+#endif
+ unsigned short offset = 0;
+#if PACKETVER >= 20091103
+ const char *name;
+#endif
+ sd = BL_CAST(BL_PC, bl);
+
+#if PACKETVER < 20091103
+ if(type)
+ WBUFW(buf,0) = spawn?0x7c:0x78;
+ else
+#endif
+#if PACKETVER < 4
+ WBUFW(buf,0) = spawn?0x79:0x78;
+#elif PACKETVER < 7
+ WBUFW(buf,0) = spawn?0x1d9:0x1d8;
+#elif PACKETVER < 20080102
+ WBUFW(buf,0) = spawn?0x22b:0x22a;
+#elif PACKETVER < 20091103
+ WBUFW(buf,0) = spawn?0x2ed:0x2ee;
+#elif PACKETVER < 20101124
+ WBUFW(buf,0) = spawn?0x7f8:0x7f9;
+#else
+ WBUFW(buf,0) = spawn?0x858:0x857;
+#endif
+
+#if PACKETVER >= 20091103
+ name = status_get_name(bl);
+#if PACKETVER < 20110111
+ WBUFW(buf,2) = (spawn?62:63)+strlen(name);
+#else
+ WBUFW(buf,2) = (spawn?64:65)+strlen(name);
+#endif
+ WBUFB(buf,4) = clif_bl_type(bl);
+ offset+=3;
+ buf = WBUFP(buffer,offset);
+#elif PACKETVER >= 20071106
+ if (type) { //Non-player packets
+ WBUFB(buf,2) = clif_bl_type(bl);
+ offset++;
+ buf = WBUFP(buffer,offset);
+ }
+#endif
+ WBUFL(buf, 2) = bl->id;
+ WBUFW(buf, 6) = status_get_speed(bl);
+ WBUFW(buf, 8) = (sc)? sc->opt1 : 0;
+ WBUFW(buf,10) = (sc)? sc->opt2 : 0;
+#if PACKETVER < 20091103
+ if (type&&spawn) { //uses an older and different packet structure
+ WBUFW(buf,12) = (sc)? sc->option : 0;
+ WBUFW(buf,14) = vd->hair_style;
+ WBUFW(buf,16) = vd->weapon;
+ WBUFW(buf,18) = vd->head_bottom;
+ WBUFW(buf,20) = vd->class_; //Pet armor (ignored by client)
+ WBUFW(buf,22) = vd->shield;
+ } else {
+#endif
+#if PACKETVER >= 20091103
+ WBUFL(buf,12) = (sc)? sc->option : 0;
+ offset+=2;
+ buf = WBUFP(buffer,offset);
+#elif PACKETVER >= 7
+ if (!type) {
+ WBUFL(buf,12) = (sc)? sc->option : 0;
+ offset+=2;
+ buf = WBUFP(buffer,offset);
+ } else
+ WBUFW(buf,12) = (sc)? sc->option : 0;
+#else
+ WBUFW(buf,12) = (sc)? sc->option : 0;
+#endif
+ WBUFW(buf,14) = vd->class_;
+ WBUFW(buf,16) = vd->hair_style;
+ WBUFW(buf,18) = vd->weapon;
+#if PACKETVER < 4
+ WBUFW(buf,20) = vd->head_bottom;
+ WBUFW(buf,22) = vd->shield;
+#else
+ WBUFW(buf,20) = vd->shield;
+ WBUFW(buf,22) = vd->head_bottom;
+#endif
+#if PACKETVER < 20091103
+ }
+#endif
+ WBUFW(buf,24) = vd->head_top;
+ WBUFW(buf,26) = vd->head_mid;
+
+ if( bl->type == BL_NPC && vd->class_ == FLAG_CLASS )
+ { //The hell, why flags work like this?
+ WBUFW(buf,22) = status_get_emblem_id(bl);
+ WBUFW(buf,24) = GetWord(status_get_guild_id(bl), 1);
+ WBUFW(buf,26) = GetWord(status_get_guild_id(bl), 0);
+ }
+
+ WBUFW(buf,28) = vd->hair_color;
+ WBUFW(buf,30) = vd->cloth_color;
+ WBUFW(buf,32) = (sd)? sd->head_dir : 0;
+#if PACKETVER < 20091103
+ if (type&&spawn) { //End of packet 0x7c
+ WBUFB(buf,34) = (sd)?sd->status.karma:0; // karma
+ WBUFB(buf,35) = vd->sex;
+ WBUFPOS(buf,36,bl->x,bl->y,unit_getdir(bl));
+ WBUFB(buf,39) = 0;
+ WBUFB(buf,40) = 0;
+ return packet_len(0x7c);
+ }
+#endif
+#if PACKETVER >= 20110111
+ WBUFW(buf,34) = vd->robe;
+ offset+= 2;
+ buf = WBUFP(buffer,offset);
+#endif
+ WBUFL(buf,34) = status_get_guild_id(bl);
+ WBUFW(buf,38) = status_get_emblem_id(bl);
+ WBUFW(buf,40) = (sd)? sd->status.manner : 0;
+#if PACKETVER >= 20091103
+ WBUFL(buf,42) = (sc)? sc->opt3 : 0;
+ offset+=2;
+ buf = WBUFP(buffer,offset);
+#elif PACKETVER >= 7
+ if (!type) {
+ WBUFL(buf,42) = (sc)? sc->opt3 : 0;
+ offset+=2;
+ buf = WBUFP(buffer,offset);
+ } else
+ WBUFW(buf,42) = (sc)? sc->opt3 : 0;
+#else
+ WBUFW(buf,42) = (sc)? sc->opt3 : 0;
+#endif
+ WBUFB(buf,44) = (sd)? sd->status.karma : 0;
+ WBUFB(buf,45) = vd->sex;
+ WBUFPOS(buf,46,bl->x,bl->y,unit_getdir(bl));
+ WBUFB(buf,49) = (sd)? 5 : 0;
+ WBUFB(buf,50) = (sd)? 5 : 0;
+ if (!spawn) {
+ WBUFB(buf,51) = vd->dead_sit;
+ offset++;
+ buf = WBUFP(buffer,offset);
+ }
+ WBUFW(buf,51) = clif_setlevel(bl);
+#if PACKETVER < 20091103
+ if (type) //End for non-player packet
+ return packet_len(WBUFW(buffer,0));
+#endif
+#if PACKETVER >= 20080102
+ WBUFW(buf,53) = sd?sd->user_font:0;
+#endif
+#if PACKETVER >= 20091103
+ memcpy((char*)WBUFP(buf,55), name, NAME_LENGTH);
+ return WBUFW(buffer,2);
+#else
+ return packet_len(WBUFW(buffer,0));
+#endif
+}
+
+/*==========================================
+ * Prepares 'unit walking' packet
+ *------------------------------------------*/
+static int clif_set_unit_walking(struct block_list* bl, struct unit_data* ud, unsigned char* buffer)
+{
+ struct map_session_data* sd;
+ struct status_change* sc = status_get_sc(bl);
+ struct view_data* vd = status_get_viewdata(bl);
+ unsigned char* buf = WBUFP(buffer,0);
+#if PACKETVER >= 7
+ unsigned short offset = 0;
+#endif
+#if PACKETVER >= 20091103
+ const char *name;
+#endif
+
+ sd = BL_CAST(BL_PC, bl);
+
+#if PACKETVER < 4
+ WBUFW(buf, 0) = 0x7b;
+#elif PACKETVER < 7
+ WBUFW(buf, 0) = 0x1da;
+#elif PACKETVER < 20080102
+ WBUFW(buf, 0) = 0x22c;
+#elif PACKETVER < 20091103
+ WBUFW(buf, 0) = 0x2ec;
+#elif PACKETVER < 20101124
+ WBUFW(buf, 0) = 0x7f7;
+#else
+ WBUFW(buf, 0) = 0x856;
+#endif
+
+#if PACKETVER >= 20091103
+ name = status_get_name(bl);
+#if PACKETVER < 20110111
+ WBUFW(buf, 2) = 69+strlen(name);
+#else
+ WBUFW(buf, 2) = 71+strlen(name);
+#endif
+ offset+=2;
+ buf = WBUFP(buffer,offset);
+#endif
+#if PACKETVER >= 20071106
+ WBUFB(buf, 2) = clif_bl_type(bl);
+ offset++;
+ buf = WBUFP(buffer,offset);
+#endif
+ WBUFL(buf, 2) = bl->id;
+ WBUFW(buf, 6) = status_get_speed(bl);
+ WBUFW(buf, 8) = (sc)? sc->opt1 : 0;
+ WBUFW(buf,10) = (sc)? sc->opt2 : 0;
+#if PACKETVER < 7
+ WBUFW(buf,12) = (sc)? sc->option : 0;
+#else
+ WBUFL(buf,12) = (sc)? sc->option : 0;
+ offset+=2; //Shift the rest of elements by 2 bytes.
+ buf = WBUFP(buffer,offset);
+#endif
+ WBUFW(buf,14) = vd->class_;
+ WBUFW(buf,16) = vd->hair_style;
+ WBUFW(buf,18) = vd->weapon;
+#if PACKETVER < 4
+ WBUFW(buf,20) = vd->head_bottom;
+ WBUFL(buf,22) = gettick();
+ WBUFW(buf,26) = vd->shield;
+#else
+ WBUFW(buf,20) = vd->shield;
+ WBUFW(buf,22) = vd->head_bottom;
+ WBUFL(buf,24) = gettick();
+#endif
+ WBUFW(buf,28) = vd->head_top;
+ WBUFW(buf,30) = vd->head_mid;
+ WBUFW(buf,32) = vd->hair_color;
+ WBUFW(buf,34) = vd->cloth_color;
+ WBUFW(buf,36) = (sd)? sd->head_dir : 0;
+#if PACKETVER >= 20110111
+ WBUFW(buf,38) = vd->robe;
+ offset+= 2;
+ buf = WBUFP(buffer,offset);
+#endif
+ WBUFL(buf,38) = status_get_guild_id(bl);
+ WBUFW(buf,42) = status_get_emblem_id(bl);
+ WBUFW(buf,44) = (sd)? sd->status.manner : 0;
+#if PACKETVER < 7
+ WBUFW(buf,46) = (sc)? sc->opt3 : 0;
+#else
+ WBUFL(buf,46) = (sc)? sc->opt3 : 0;
+ offset+=2; //Shift the rest of elements by 2 bytes.
+ buf = WBUFP(buffer,offset);
+#endif
+ WBUFB(buf,48) = (sd)? sd->status.karma : 0;
+ WBUFB(buf,49) = vd->sex;
+ WBUFPOS2(buf,50,bl->x,bl->y,ud->to_x,ud->to_y,8,8);
+ WBUFB(buf,56) = (sd)? 5 : 0;
+ WBUFB(buf,57) = (sd)? 5 : 0;
+ WBUFW(buf,58) = clif_setlevel(bl);
+#if PACKETVER >= 20080102
+ WBUFW(buf,60) = sd?sd->user_font:0;
+#endif
+#if PACKETVER >= 20091103
+ memcpy((char*)WBUFP(buf,62), name, NAME_LENGTH);
+ return WBUFW(buffer,2);
+#else
+ return packet_len(WBUFW(buffer,0));
+#endif
+}
+
+//Modifies the buffer for disguise characters and sends it to self.
+//Used for spawn/walk packets, where the ID offset changes for packetver >=9
+static void clif_setdisguise(struct block_list *bl, unsigned char *buf,int len)
+{
+#if PACKETVER >= 20091103
+ WBUFB(buf,4)= pcdb_checkid(status_get_viewdata(bl)->class_) ? 0x0 : 0x5; //PC_TYPE : NPC_MOB_TYPE
+ WBUFL(buf,5)=-bl->id;
+#elif PACKETVER >= 20071106
+ WBUFB(buf,2)= pcdb_checkid(status_get_viewdata(bl)->class_) ? 0x0 : 0x5; //PC_TYPE : NPC_MOB_TYPE
+ WBUFL(buf,3)=-bl->id;
+#else
+ WBUFL(buf,2)=-bl->id;
+#endif
+ clif_send(buf, len, bl, SELF);
+}
+
+
+/// Changes sprite of an NPC object (ZC_NPCSPRITE_CHANGE).
+/// 01b0 <id>.L <type>.B <value>.L
+/// type:
+/// unused
+void clif_class_change(struct block_list *bl,int class_,int type)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(bl);
+
+ if(!pcdb_checkid(class_))
+ {// player classes yield missing sprites
+ WBUFW(buf,0)=0x1b0;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ WBUFL(buf,7)=class_;
+ clif_send(buf,packet_len(0x1b0),bl,AREA);
+ }
+}
+
+
+/// Notifies the client of an object's spirits.
+/// 01d0 <id>.L <amount>.W (ZC_SPIRITS)
+/// 01e1 <id>.L <amount>.W (ZC_SPIRITS2)
+static void clif_spiritball_single(int fd, struct map_session_data *sd)
+{
+ WFIFOHEAD(fd, packet_len(0x1e1));
+ WFIFOW(fd,0)=0x1e1;
+ WFIFOL(fd,2)=sd->bl.id;
+ WFIFOW(fd,6)=sd->spiritball;
+ WFIFOSET(fd, packet_len(0x1e1));
+}
+
+/*==========================================
+ * Kagerou/Oboro amulet spirit
+ *------------------------------------------*/
+static void clif_talisman_single(int fd, struct map_session_data *sd, short type)
+{
+ WFIFOHEAD(fd, packet_len(0x08cf));
+ WFIFOW(fd,0)=0x08cf;
+ WFIFOL(fd,2)=sd->bl.id;
+ WFIFOW(fd,6)=type;
+ WFIFOW(fd,8)=sd->talisman[type];
+ WFIFOSET(fd, packet_len(0x08cf));
+}
+
+/*==========================================
+ * Run when player changes map / refreshes
+ * Tells its client to display all weather settings being used by this map
+ *------------------------------------------*/
+static void clif_weather_check(struct map_session_data *sd)
+{
+ int16 m = sd->bl.m;
+ int fd = sd->fd;
+
+ if (map[m].flag.snow
+ || map[m].flag.clouds
+ || map[m].flag.fog
+ || map[m].flag.fireworks
+ || map[m].flag.sakura
+ || map[m].flag.leaves
+ /**
+ * No longer available, keeping here just in case it's back someday. [Ind]
+ **/
+ //|| map[m].flag.rain
+ || map[m].flag.clouds2)
+ {
+ if (map[m].flag.snow)
+ clif_specialeffect_single(&sd->bl, 162, fd);
+ if (map[m].flag.clouds)
+ clif_specialeffect_single(&sd->bl, 233, fd);
+ if (map[m].flag.clouds2)
+ clif_specialeffect_single(&sd->bl, 516, fd);
+ if (map[m].flag.fog)
+ clif_specialeffect_single(&sd->bl, 515, fd);
+ if (map[m].flag.fireworks) {
+ clif_specialeffect_single(&sd->bl, 297, fd);
+ clif_specialeffect_single(&sd->bl, 299, fd);
+ clif_specialeffect_single(&sd->bl, 301, fd);
+ }
+ if (map[m].flag.sakura)
+ clif_specialeffect_single(&sd->bl, 163, fd);
+ if (map[m].flag.leaves)
+ clif_specialeffect_single(&sd->bl, 333, fd);
+ /**
+ * No longer available, keeping here just in case it's back someday. [Ind]
+ **/
+ //if (map[m].flag.rain)
+ // clif_specialeffect_single(&sd->bl, 161, fd);
+ }
+}
+/**
+ * Run when the weather on a map changes, throws all players in map id 'm' to clif_weather_check function
+ **/
+void clif_weather(int16 m)
+{
+ struct s_mapiterator* iter;
+ struct map_session_data *sd=NULL;
+
+ iter = mapit_getallusers();
+ for( sd = (struct map_session_data*)mapit_first(iter); mapit_exists(iter); sd = (struct map_session_data*)mapit_next(iter) )
+ {
+ if( sd->bl.m == m )
+ clif_weather_check(sd);
+ }
+ mapit_free(iter);
+}
+/**
+ * Main function to spawn a unit on the client (player/mob/pet/etc)
+ **/
+int clif_spawn(struct block_list *bl)
+{
+ unsigned char buf[128];
+ struct view_data *vd;
+ int len;
+
+ vd = status_get_viewdata(bl);
+ if( !vd || vd->class_ == INVISIBLE_CLASS )
+ return 0;
+
+ /**
+ * Hide NPC from maya purple card.
+ **/
+ if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE))
+ return 0;
+
+ len = clif_set_unit_idle(bl, buf,true);
+ clif_send(buf, len, bl, AREA_WOS);
+ if (disguised(bl))
+ clif_setdisguise(bl, buf, len);
+
+ if (vd->cloth_color)
+ clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS);
+
+ switch (bl->type)
+ {
+ case BL_PC:
+ {
+ TBL_PC *sd = ((TBL_PC*)bl);
+ int i;
+ if (sd->spiritball > 0)
+ clif_spiritball(&sd->bl);
+ if(sd->state.size==SZ_BIG) // tiny/big players [Valaris]
+ clif_specialeffect(bl,423,AREA);
+ else if(sd->state.size==SZ_MEDIUM)
+ clif_specialeffect(bl,421,AREA);
+ if( sd->bg_id && map[sd->bl.m].flag.battleground )
+ clif_sendbgemblem_area(sd);
+ if( sd->sc.option&OPTION_MOUNTING ) {
+ //New Mounts are not complaint to the original method, so we gotta tell this guy that he is mounting.
+ clif_status_load_notick(&sd->bl,SI_ALL_RIDING,2,1,0,0);
+ }
+ for(i = 1; i < 5; i++){
+ if( sd->talisman[i] > 0 )
+ clif_talisman(sd, i);
+ }
+ #ifdef NEW_CARTS
+ if( sd->sc.data[SC_PUSH_CART] )
+ clif_status_load_notick(&sd->bl, SI_ON_PUSH_CART, 2, sd->sc.data[SC_PUSH_CART]->val1, 0, 0);
+ #endif
+ #if PACKETVER <= 20120207
+ if (sd->status.robe)
+ clif_refreshlook(bl,bl->id,LOOK_ROBE,sd->status.robe,AREA);
+ #endif
+ }
+ break;
+ case BL_MOB:
+ {
+ TBL_MOB *md = ((TBL_MOB*)bl);
+ if(md->special_state.size==SZ_BIG) // tiny/big mobs [Valaris]
+ clif_specialeffect(&md->bl,423,AREA);
+ else if(md->special_state.size==SZ_MEDIUM)
+ clif_specialeffect(&md->bl,421,AREA);
+ }
+ break;
+ case BL_NPC:
+ {
+ TBL_NPC *nd = ((TBL_NPC*)bl);
+ if( nd->size == SZ_BIG )
+ clif_specialeffect(&nd->bl,423,AREA);
+ else if( nd->size == SZ_MEDIUM )
+ clif_specialeffect(&nd->bl,421,AREA);
+ }
+ break;
+ case BL_PET:
+ if (vd->head_bottom)
+ clif_pet_equip_area((TBL_PET*)bl); // needed to display pet equip properly
+ break;
+ }
+ return 0;
+}
+
+/// Sends information about owned homunculus to the client (ZC_PROPERTY_HOMUN). [orn]
+/// 022e <name>.24B <modified>.B <level>.W <hunger>.W <intimacy>.W <equip id>.W <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W <hp>.W <max hp>.W <sp>.W <max sp>.W <exp>.L <max exp>.L <skill points>.W <atk range>.W
+void clif_hominfo(struct map_session_data *sd, struct homun_data *hd, int flag)
+{
+ struct status_data *status;
+ unsigned char buf[128];
+ int m_class;
+
+ nullpo_retv(hd);
+
+ status = &hd->battle_status;
+ m_class = hom_class2mapid(hd->homunculus.class_);
+
+ memset(buf,0,packet_len(0x22e));
+ WBUFW(buf,0)=0x22e;
+ memcpy(WBUFP(buf,2),hd->homunculus.name,NAME_LENGTH);
+ // Bit field, bit 0 : rename_flag (1 = already renamed), bit 1 : homunc vaporized (1 = true), bit 2 : homunc dead (1 = true)
+ WBUFB(buf,26)=(battle_config.hom_rename?0:hd->homunculus.rename_flag) | (hd->homunculus.vaporize << 1) | (hd->homunculus.hp?0:4);
+ WBUFW(buf,27)=hd->homunculus.level;
+ WBUFW(buf,29)=hd->homunculus.hunger;
+ WBUFW(buf,31)=(unsigned short) (hd->homunculus.intimacy / 100) ;
+ WBUFW(buf,33)=0; // equip id
+ WBUFW(buf,35)=cap_value(status->rhw.atk2+status->batk, 0, INT16_MAX);
+ WBUFW(buf,37)=cap_value(status->matk_max, 0, INT16_MAX);
+ WBUFW(buf,39)=status->hit;
+ if (battle_config.hom_setting&0x10)
+ WBUFW(buf,41)=status->luk/3 + 1; //crit is a +1 decimal value! Just display purpose.[Vicious]
+ else
+ WBUFW(buf,41)=status->cri/10;
+ WBUFW(buf,43)=status->def + status->vit ;
+ WBUFW(buf,45)=status->mdef;
+ WBUFW(buf,47)=status->flee;
+ WBUFW(buf,49)=(flag)?0:status->amotion;
+ if (status->max_hp > INT16_MAX) {
+ WBUFW(buf,51) = status->hp/(status->max_hp/100);
+ WBUFW(buf,53) = 100;
+ } else {
+ WBUFW(buf,51)=status->hp;
+ WBUFW(buf,53)=status->max_hp;
+ }
+ if (status->max_sp > INT16_MAX) {
+ WBUFW(buf,55) = status->sp/(status->max_sp/100);
+ WBUFW(buf,57) = 100;
+ } else {
+ WBUFW(buf,55)=status->sp;
+ WBUFW(buf,57)=status->max_sp;
+ }
+ WBUFL(buf,59)=hd->homunculus.exp;
+ if( ((m_class&HOM_REG) && hd->homunculus.level >= battle_config.hom_max_level) || ((m_class&HOM_S) && hd->homunculus.level >= battle_config.hom_S_max_level) )
+ WBUFL(buf,63)=0;
+ else
+ WBUFL(buf,63)=hd->exp_next;
+ WBUFW(buf,67)=hd->homunculus.skillpts;
+ WBUFW(buf,69)=status_get_range(&hd->bl);
+ clif_send(buf,packet_len(0x22e),&sd->bl,SELF);
+}
+
+
+/// Notification about a change in homunuculus' state (ZC_CHANGESTATE_MER).
+/// 0230 <type>.B <state>.B <id>.L <data>.L
+/// type:
+/// unused
+/// state:
+/// 0 = pre-init
+/// 1 = intimacy
+/// 2 = hunger
+/// 3 = accessory?
+/// ? = ignored
+void clif_send_homdata(struct map_session_data *sd, int state, int param)
+{ //[orn]
+ int fd = sd->fd;
+
+ if ( (state == SP_INTIMATE) && (param >= 910) && (sd->hd->homunculus.class_ == sd->hd->homunculusDB->evo_class) )
+ merc_hom_calc_skilltree(sd->hd, 0);
+
+ WFIFOHEAD(fd, packet_len(0x230));
+ WFIFOW(fd,0)=0x230;
+ WFIFOB(fd,2)=0;
+ WFIFOB(fd,3)=state;
+ WFIFOL(fd,4)=sd->hd->bl.id;
+ WFIFOL(fd,8)=param;
+ WFIFOSET(fd,packet_len(0x230));
+}
+
+
+int clif_homskillinfoblock(struct map_session_data *sd)
+{ //[orn]
+ struct homun_data *hd;
+ int fd = sd->fd;
+ int i,j,len=4,id;
+ WFIFOHEAD(fd, 4+37*MAX_HOMUNSKILL);
+
+ hd = sd->hd;
+ if ( !hd )
+ return 0 ;
+
+ WFIFOW(fd,0)=0x235;
+ for ( i = 0; i < MAX_HOMUNSKILL; i++){
+ if( (id = hd->homunculus.hskill[i].id) != 0 ){
+ j = id - HM_SKILLBASE;
+ WFIFOW(fd,len ) = id;
+ WFIFOW(fd,len+2) = skill_get_inf(id);
+ WFIFOW(fd,len+4) = 0;
+ WFIFOW(fd,len+6) = hd->homunculus.hskill[j].lv;
+ WFIFOW(fd,len+8) = skill_get_sp(id,hd->homunculus.hskill[j].lv);
+ WFIFOW(fd,len+10)= skill_get_range2(&sd->hd->bl, id,hd->homunculus.hskill[j].lv);
+ safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
+ WFIFOB(fd,len+36) = (hd->homunculus.hskill[j].lv < merc_skill_tree_get_max(id, hd->homunculus.class_))?1:0;
+ len+=37;
+ }
+ }
+ WFIFOW(fd,2)=len;
+ WFIFOSET(fd,len);
+
+ return 0;
+}
+
+void clif_homskillup(struct map_session_data *sd, uint16 skill_id)
+{ //[orn]
+ struct homun_data *hd;
+ int fd, idx;
+ nullpo_retv(sd);
+ idx = skill_id - HM_SKILLBASE;
+
+ fd=sd->fd;
+ hd=sd->hd;
+
+ WFIFOHEAD(fd, packet_len(0x239));
+ WFIFOW(fd,0) = 0x239;
+ WFIFOW(fd,2) = skill_id;
+ WFIFOW(fd,4) = hd->homunculus.hskill[idx].lv;
+ WFIFOW(fd,6) = skill_get_sp(skill_id,hd->homunculus.hskill[idx].lv);
+ WFIFOW(fd,8) = skill_get_range2(&hd->bl, skill_id,hd->homunculus.hskill[idx].lv);
+ WFIFOB(fd,10) = (hd->homunculus.hskill[idx].lv < skill_get_max(hd->homunculus.hskill[idx].id)) ? 1 : 0;
+ WFIFOSET(fd,packet_len(0x239));
+}
+
+int clif_hom_food(struct map_session_data *sd,int foodid,int fail) //[orn]
+{
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x22f));
+ WFIFOW(fd,0)=0x22f;
+ WFIFOB(fd,2)=fail;
+ WFIFOW(fd,3)=foodid;
+ WFIFOSET(fd,packet_len(0x22f));
+
+ return 0;
+}
+
+
+/// Notifies the client, that it is walking (ZC_NOTIFY_PLAYERMOVE).
+/// 0087 <walk start time>.L <walk data>.6B
+void clif_walkok(struct map_session_data *sd)
+{
+ int fd=sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x87));
+ WFIFOW(fd,0)=0x87;
+ WFIFOL(fd,2)=gettick();
+ WFIFOPOS2(fd,6,sd->bl.x,sd->bl.y,sd->ud.to_x,sd->ud.to_y,8,8);
+ WFIFOSET(fd,packet_len(0x87));
+}
+
+
+static void clif_move2(struct block_list *bl, struct view_data *vd, struct unit_data *ud)
+{
+ uint8 buf[128];
+ int len;
+
+ len = clif_set_unit_walking(bl,ud,buf);
+ clif_send(buf,len,bl,AREA_WOS);
+ if (disguised(bl))
+ clif_setdisguise(bl, buf, len);
+
+ if(vd->cloth_color)
+ clif_refreshlook(bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,AREA_WOS);
+
+ switch(bl->type)
+ {
+ case BL_PC:
+ {
+ TBL_PC *sd = ((TBL_PC*)bl);
+// clif_movepc(sd);
+ if(sd->state.size==SZ_BIG) // tiny/big players [Valaris]
+ clif_specialeffect(&sd->bl,423,AREA);
+ else if(sd->state.size==SZ_MEDIUM)
+ clif_specialeffect(&sd->bl,421,AREA);
+ }
+ break;
+ case BL_MOB:
+ {
+ TBL_MOB *md = ((TBL_MOB*)bl);
+ if(md->special_state.size==SZ_BIG) // tiny/big mobs [Valaris]
+ clif_specialeffect(&md->bl,423,AREA);
+ else if(md->special_state.size==SZ_MEDIUM)
+ clif_specialeffect(&md->bl,421,AREA);
+ }
+ break;
+ case BL_PET:
+ if( vd->head_bottom )
+ {// needed to display pet equip properly
+ clif_pet_equip_area((TBL_PET*)bl);
+ }
+ break;
+ }
+}
+
+
+/// Notifies clients in an area, that an other visible object is walking (ZC_NOTIFY_PLAYERMOVE).
+/// 0086 <id>.L <walk data>.6B <walk start time>.L
+/// Note: unit must not be self
+void clif_move(struct unit_data *ud)
+{
+ unsigned char buf[16];
+ struct view_data* vd;
+ struct block_list* bl = ud->bl;
+
+ vd = status_get_viewdata(bl);
+ if (!vd || vd->class_ == INVISIBLE_CLASS)
+ return; //This performance check is needed to keep GM-hidden objects from being notified to bots.
+
+ /**
+ * Hide NPC from maya purple card.
+ **/
+ if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE))
+ return;
+
+ if (ud->state.speed_changed) {
+ // Since we don't know how to update the speed of other objects,
+ // use the old walk packet to update the data.
+ ud->state.speed_changed = 0;
+ clif_move2(bl, vd, ud);
+ return;
+ }
+
+ WBUFW(buf,0)=0x86;
+ WBUFL(buf,2)=bl->id;
+ WBUFPOS2(buf,6,bl->x,bl->y,ud->to_x,ud->to_y,8,8);
+ WBUFL(buf,12)=gettick();
+ clif_send(buf, packet_len(0x86), bl, AREA_WOS);
+ if (disguised(bl))
+ {
+ WBUFL(buf,2)=-bl->id;
+ clif_send(buf, packet_len(0x86), bl, SELF);
+ }
+}
+
+
+/*==========================================
+ * Delays the map_quit of a player after they are disconnected. [Skotlex]
+ *------------------------------------------*/
+static int clif_delayquit(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct map_session_data *sd = NULL;
+
+ //Remove player from map server
+ if ((sd = map_id2sd(id)) != NULL && sd->fd == 0) //Should be a disconnected player.
+ map_quit(sd);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+void clif_quitsave(int fd,struct map_session_data *sd)
+{
+ if (!battle_config.prevent_logout ||
+ DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout)
+ map_quit(sd);
+ else if (sd->fd)
+ { //Disassociate session from player (session is deleted after this function was called)
+ //And set a timer to make him quit later.
+ session[sd->fd]->session_data = NULL;
+ sd->fd = 0;
+ add_timer(gettick() + 10000, clif_delayquit, sd->bl.id, 0);
+ }
+}
+
+/// Notifies the client of a position change to coordinates on given map (ZC_NPCACK_MAPMOVE).
+/// 0091 <map name>.16B <x>.W <y>.W
+void clif_changemap(struct map_session_data *sd, short map, int x, int y)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x91));
+ WFIFOW(fd,0) = 0x91;
+ mapindex_getmapname_ext(mapindex_id2name(map), (char*)WFIFOP(fd,2));
+ WFIFOW(fd,18) = x;
+ WFIFOW(fd,20) = y;
+ WFIFOSET(fd,packet_len(0x91));
+}
+
+
+/// Notifies the client of a position change to coordinates on given map, which is on another map-server (ZC_NPCACK_SERVERMOVE).
+/// 0092 <map name>.16B <x>.W <y>.W <ip>.L <port>.W
+void clif_changemapserver(struct map_session_data* sd, unsigned short map_index, int x, int y, uint32 ip, uint16 port)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x92));
+ WFIFOW(fd,0) = 0x92;
+ mapindex_getmapname_ext(mapindex_id2name(map_index), (char*)WFIFOP(fd,2));
+ WFIFOW(fd,18) = x;
+ WFIFOW(fd,20) = y;
+ WFIFOL(fd,22) = htonl(ip);
+ WFIFOW(fd,26) = ntows(htons(port)); // [!] LE byte order here [!]
+ WFIFOSET(fd,packet_len(0x92));
+}
+
+
+void clif_blown(struct block_list *bl)
+{
+//Aegis packets says fixpos, but it's unsure whether slide works better or not.
+// clif_fixpos(bl);
+ clif_slide(bl, bl->x, bl->y);
+}
+
+
+/// Visually moves(slides) a character to x,y. If the target cell
+/// isn't walkable, the char doesn't move at all. If the char is
+/// sitting it will stand up (ZC_STOPMOVE).
+/// 0088 <id>.L <x>.W <y>.W
+void clif_fixpos(struct block_list *bl)
+{
+ unsigned char buf[10];
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x88;
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,6) = bl->x;
+ WBUFW(buf,8) = bl->y;
+ clif_send(buf, packet_len(0x88), bl, AREA);
+
+ if( disguised(bl) )
+ {
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf, packet_len(0x88), bl, SELF);
+ }
+}
+
+
+/// Displays the buy/sell dialog of an NPC shop (ZC_SELECT_DEALTYPE).
+/// 00c4 <shop id>.L
+void clif_npcbuysell(struct map_session_data* sd, int id)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0xc4));
+ WFIFOW(fd,0)=0xc4;
+ WFIFOL(fd,2)=id;
+ WFIFOSET(fd,packet_len(0xc4));
+}
+
+
+/// Presents list of items, that can be bought in an NPC shop (ZC_PC_PURCHASE_ITEMLIST).
+/// 00c6 <packet len>.W { <price>.L <discount price>.L <item type>.B <name id>.W }*
+void clif_buylist(struct map_session_data *sd, struct npc_data *nd)
+{
+ int fd,i,c;
+
+ nullpo_retv(sd);
+ nullpo_retv(nd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, 4 + nd->u.shop.count * 11);
+ WFIFOW(fd,0) = 0xc6;
+
+ c = 0;
+ for( i = 0; i < nd->u.shop.count; i++ )
+ {
+ struct item_data* id = itemdb_exists(nd->u.shop.shop_item[i].nameid);
+ int val = nd->u.shop.shop_item[i].value;
+ if( id == NULL )
+ continue;
+ WFIFOL(fd, 4+c*11) = val;
+ WFIFOL(fd, 8+c*11) = pc_modifybuyvalue(sd,val);
+ WFIFOB(fd,12+c*11) = itemtype(id->type);
+ WFIFOW(fd,13+c*11) = ( id->view_id > 0 ) ? id->view_id : id->nameid;
+ c++;
+ }
+
+ WFIFOW(fd,2) = 4 + c*11;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Presents list of items, that can be sold to an NPC shop (ZC_PC_SELL_ITEMLIST).
+/// 00c7 <packet len>.W { <index>.W <price>.L <overcharge price>.L }*
+void clif_selllist(struct map_session_data *sd)
+{
+ int fd,i,c=0,val;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_INVENTORY * 10 + 4);
+ WFIFOW(fd,0)=0xc7;
+ for( i = 0; i < MAX_INVENTORY; i++ )
+ {
+ if( sd->status.inventory[i].nameid > 0 && sd->inventory_data[i] )
+ {
+ if( !itemdb_cansell(&sd->status.inventory[i], pc_get_group_level(sd)) )
+ continue;
+
+ if( sd->status.inventory[i].expire_time )
+ continue; // Cannot Sell Rental Items
+
+ val=sd->inventory_data[i]->value_sell;
+ if( val < 0 )
+ continue;
+ WFIFOW(fd,4+c*10)=i+2;
+ WFIFOL(fd,6+c*10)=val;
+ WFIFOL(fd,10+c*10)=pc_modifysellvalue(sd,val);
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=c*10+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Displays an NPC dialog message (ZC_SAY_DIALOG).
+/// 00b4 <packet len>.W <npc id>.L <message>.?B
+/// Client behavior (dialog window):
+/// - disable mouse targeting
+/// - open the dialog window
+/// - set npcid of dialog window (0 by default)
+/// - if set to clear on next mes, clear contents
+/// - append this text
+void clif_scriptmes(struct map_session_data *sd, int npcid, const char *mes)
+{
+ int fd = sd->fd;
+ int slen = strlen(mes) + 9;
+
+ WFIFOHEAD(fd, slen);
+ WFIFOW(fd,0)=0xb4;
+ WFIFOW(fd,2)=slen;
+ WFIFOL(fd,4)=npcid;
+ memcpy((char*)WFIFOP(fd,8), mes, slen-8);
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Adds a 'next' button to an NPC dialog (ZC_WAIT_DIALOG).
+/// 00b5 <npc id>.L
+/// Client behavior (dialog window):
+/// - disable mouse targeting
+/// - open the dialog window
+/// - add 'next' button
+/// When 'next' is pressed:
+/// - 00B9 <npcid of dialog window>.L
+/// - set to clear on next mes
+/// - remove 'next' button
+void clif_scriptnext(struct map_session_data *sd,int npcid)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0xb5));
+ WFIFOW(fd,0)=0xb5;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len(0xb5));
+}
+
+
+/// Adds a 'close' button to an NPC dialog (ZC_CLOSE_DIALOG).
+/// 00b6 <npc id>.L
+/// Client behavior:
+/// - if dialog window is open:
+/// - remove 'next' button
+/// - add 'close' button
+/// - else:
+/// - enable mouse targeting
+/// - close the dialog window
+/// - close the menu window
+/// When 'close' is pressed:
+/// - enable mouse targeting
+/// - close the dialog window
+/// - close the menu window
+/// - 0146 <npcid of dialog window>.L
+void clif_scriptclose(struct map_session_data *sd, int npcid)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0xb6));
+ WFIFOW(fd,0)=0xb6;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len(0xb6));
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+void clif_sendfakenpc(struct map_session_data *sd, int npcid)
+{
+ unsigned char *buf;
+ int fd = sd->fd;
+ sd->state.using_fake_npc = 1;
+
+ WFIFOHEAD(fd, packet_len(0x78));
+ buf = WFIFOP(fd,0);
+ memset(WBUFP(buf,0), 0, packet_len(0x78));
+ WBUFW(buf,0)=0x78;
+#if PACKETVER >= 20071106
+ WBUFB(buf,2) = 0; // object type
+ buf = WFIFOP(fd,1);
+#endif
+ WBUFL(buf,2)=npcid;
+ WBUFW(buf,14)=111;
+ WBUFPOS(buf,46,sd->bl.x,sd->bl.y,sd->ud.dir);
+ WBUFB(buf,49)=5;
+ WBUFB(buf,50)=5;
+ WFIFOSET(fd, packet_len(0x78));
+}
+
+
+/// Displays an NPC dialog menu (ZC_MENU_LIST).
+/// 00b7 <packet len>.W <npc id>.L <menu items>.?B
+/// Client behavior:
+/// - disable mouse targeting
+/// - close the menu window
+/// - open the menu window
+/// - add options to the menu (separated in the text by ":")
+/// - set npcid of menu window
+/// - if dialog window is open:
+/// - remove 'next' button
+/// When 'ok' is pressed:
+/// - 00B8 <npcid of menu window>.L <selected option>.B
+/// - close the menu window
+/// When 'cancel' is pressed:
+/// - 00B8 <npcid of menu window>.L <-1>.B
+/// - enable mouse targeting
+/// - close a bunch of windows...
+/// WARNING: the 'cancel' button closes other windows besides the dialog window and the menu window.
+/// Which suggests their have intertwined behavior. (probably the mouse targeting)
+/// TODO investigate behavior of other windows [FlavioJS]
+void clif_scriptmenu(struct map_session_data* sd, int npcid, const char* mes)
+{
+ int fd = sd->fd;
+ int slen = strlen(mes) + 9;
+ struct block_list *bl = NULL;
+
+ if (!sd->state.using_fake_npc && (npcid == fake_nd->bl.id || ((bl = map_id2bl(npcid)) && (bl->m!=sd->bl.m ||
+ bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 ||
+ bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1))))
+ clif_sendfakenpc(sd, npcid);
+
+ WFIFOHEAD(fd, slen);
+ WFIFOW(fd,0)=0xb7;
+ WFIFOW(fd,2)=slen;
+ WFIFOL(fd,4)=npcid;
+ memcpy((char*)WFIFOP(fd,8), mes, slen-8);
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Displays an NPC dialog input box for numbers (ZC_OPEN_EDITDLG).
+/// 0142 <npc id>.L
+/// Client behavior (inputnum window):
+/// - if npcid exists in the client:
+/// - open the inputnum window
+/// - set npcid of inputnum window
+/// When 'ok' is pressed:
+/// - if inputnum window has text:
+/// - if npcid exists in the client:
+/// - 0143 <npcid of inputnum window>.L <atoi(text)>.L
+/// - close inputnum window
+void clif_scriptinput(struct map_session_data *sd, int npcid)
+{
+ int fd;
+ struct block_list *bl = NULL;
+
+ nullpo_retv(sd);
+
+ if (!sd->state.using_fake_npc && (npcid == fake_nd->bl.id || ((bl = map_id2bl(npcid)) && (bl->m!=sd->bl.m ||
+ bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 ||
+ bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1))))
+ clif_sendfakenpc(sd, npcid);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0x142));
+ WFIFOW(fd,0)=0x142;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len(0x142));
+}
+
+
+/// Displays an NPC dialog input box for numbers (ZC_OPEN_EDITDLGSTR).
+/// 01d4 <npc id>.L
+/// Client behavior (inputstr window):
+/// - if npcid is 0 or npcid exists in the client:
+/// - open the inputstr window
+/// - set npcid of inputstr window
+/// When 'ok' is pressed:
+/// - if inputstr window has text and isn't an insult(manner.txt):
+/// - if npcid is 0 or npcid exists in the client:
+/// - 01d5 <packetlen>.W <npcid of inputstr window>.L <text>.?B
+/// - close inputstr window
+void clif_scriptinputstr(struct map_session_data *sd, int npcid)
+{
+ int fd;
+ struct block_list *bl = NULL;
+
+ nullpo_retv(sd);
+
+ if (!sd->state.using_fake_npc && (npcid == fake_nd->bl.id || ((bl = map_id2bl(npcid)) && (bl->m!=sd->bl.m ||
+ bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 ||
+ bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1))))
+ clif_sendfakenpc(sd, npcid);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0x1d4));
+ WFIFOW(fd,0)=0x1d4;
+ WFIFOL(fd,2)=npcid;
+ WFIFOSET(fd,packet_len(0x1d4));
+}
+
+
+/// Marks a position on client's minimap (ZC_COMPASS).
+/// 0144 <npc id>.L <type>.L <x>.L <y>.L <id>.B <color>.L
+/// npc id:
+/// is ignored in the client
+/// type:
+/// 0 = display mark for 15 seconds
+/// 1 = display mark until dead or teleported
+/// 2 = remove mark
+/// color:
+/// 0x00RRGGBB
+void clif_viewpoint(struct map_session_data *sd, int npc_id, int type, int x, int y, int id, int color)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0x144));
+ WFIFOW(fd,0)=0x144;
+ WFIFOL(fd,2)=npc_id;
+ WFIFOL(fd,6)=type;
+ WFIFOL(fd,10)=x;
+ WFIFOL(fd,14)=y;
+ WFIFOB(fd,18)=id;
+ WFIFOL(fd,19)=color;
+ WFIFOSET(fd,packet_len(0x144));
+}
+
+
+/// Displays an illustration image.
+/// 0145 <image name>.16B <type>.B (ZC_SHOW_IMAGE)
+/// 01b3 <image name>.64B <type>.B (ZC_SHOW_IMAGE2)
+/// type:
+/// 0 = bottom left corner
+/// 1 = bottom middle
+/// 2 = bottom right corner
+/// 3 = middle of screen, inside a movable window
+/// 4 = middle of screen, movable with a close button, chrome-less
+void clif_cutin(struct map_session_data* sd, const char* image, int type)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0x1b3));
+ WFIFOW(fd,0)=0x1b3;
+ strncpy((char*)WFIFOP(fd,2),image,64);
+ WFIFOB(fd,66)=type;
+ WFIFOSET(fd,packet_len(0x1b3));
+}
+
+
+/*==========================================
+ * Fills in card data from the given item and into the buffer. [Skotlex]
+ *------------------------------------------*/
+static void clif_addcards(unsigned char* buf, struct item* item)
+{
+ int i=0,j;
+ if( item == NULL ) { //Blank data
+ WBUFW(buf,0) = 0;
+ WBUFW(buf,2) = 0;
+ WBUFW(buf,4) = 0;
+ WBUFW(buf,6) = 0;
+ return;
+ }
+ if( item->card[0] == CARD0_PET ) { //pet eggs
+ WBUFW(buf,0) = 0;
+ WBUFW(buf,2) = 0;
+ WBUFW(buf,4) = 0;
+ WBUFW(buf,6) = item->card[3]; //Pet renamed flag.
+ return;
+ }
+ if( item->card[0] == CARD0_FORGE || item->card[0] == CARD0_CREATE ) { //Forged/created items
+ WBUFW(buf,0) = item->card[0];
+ WBUFW(buf,2) = item->card[1];
+ WBUFW(buf,4) = item->card[2];
+ WBUFW(buf,6) = item->card[3];
+ return;
+ }
+ //Client only receives four cards.. so randomly send them a set of cards. [Skotlex]
+ if( MAX_SLOTS > 4 && (j = itemdb_slot(item->nameid)) > 4 )
+ i = rnd()%(j-3); //eg: 6 slots, possible i values: 0->3, 1->4, 2->5 => i = rnd()%3;
+
+ //Normal items.
+ if( item->card[i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 )
+ WBUFW(buf,0) = j;
+ else
+ WBUFW(buf,0) = item->card[i];
+
+ if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 )
+ WBUFW(buf,2) = j;
+ else
+ WBUFW(buf,2) = item->card[i];
+
+ if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 )
+ WBUFW(buf,4) = j;
+ else
+ WBUFW(buf,4) = item->card[i];
+
+ if( item->card[++i] > 0 && (j=itemdb_viewid(item->card[i])) > 0 )
+ WBUFW(buf,6) = j;
+ else
+ WBUFW(buf,6) = item->card[i];
+}
+
+
+/// Notifies the client, about a received inventory item or the result of a pick-up request.
+/// 00a0 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B (ZC_ITEM_PICKUP_ACK)
+/// 029a <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B <expire time>.L (ZC_ITEM_PICKUP_ACK2)
+/// 02d4 <index>.W <amount>.W <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B <result>.B <expire time>.L <bindOnEquipType>.W (ZC_ITEM_PICKUP_ACK3)
+void clif_additem(struct map_session_data *sd, int n, int amount, int fail)
+{
+ int fd;
+#if PACKETVER < 20061218
+ const int cmd = 0xa0;
+#elif PACKETVER < 20071002
+ const int cmd = 0x29a;
+#else
+ const int cmd = 0x2d4;
+#endif
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ if( !session_isActive(fd) ) //Sasuke-
+ return;
+
+ WFIFOHEAD(fd,packet_len(cmd));
+ if( fail )
+ {
+ WFIFOW(fd,0)=cmd;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOW(fd,6)=0;
+ WFIFOB(fd,8)=0;
+ WFIFOB(fd,9)=0;
+ WFIFOB(fd,10)=0;
+ WFIFOW(fd,11)=0;
+ WFIFOW(fd,13)=0;
+ WFIFOW(fd,15)=0;
+ WFIFOW(fd,17)=0;
+ WFIFOW(fd,19)=0;
+ WFIFOB(fd,21)=0;
+ WFIFOB(fd,22)=fail;
+#if PACKETVER >= 20061218
+ WFIFOL(fd,23)=0;
+#endif
+#if PACKETVER >= 20071002
+ WFIFOW(fd,27)=0; // unknown
+#endif
+ }
+ else
+ {
+ if( n < 0 || n >= MAX_INVENTORY || sd->status.inventory[n].nameid <=0 || sd->inventory_data[n] == NULL )
+ return;
+
+ WFIFOW(fd,0)=cmd;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=amount;
+ if (sd->inventory_data[n]->view_id > 0)
+ WFIFOW(fd,6)=sd->inventory_data[n]->view_id;
+ else
+ WFIFOW(fd,6)=sd->status.inventory[n].nameid;
+ WFIFOB(fd,8)=sd->status.inventory[n].identify;
+ WFIFOB(fd,9)=sd->status.inventory[n].attribute;
+ WFIFOB(fd,10)=sd->status.inventory[n].refine;
+ clif_addcards(WFIFOP(fd,11), &sd->status.inventory[n]);
+ WFIFOW(fd,19)=pc_equippoint(sd,n);
+ WFIFOB(fd,21)=itemtype(sd->inventory_data[n]->type);
+ WFIFOB(fd,22)=fail;
+#if PACKETVER >= 20061218
+ WFIFOL(fd,23)=sd->status.inventory[n].expire_time;
+#endif
+#if PACKETVER >= 20071002
+ WFIFOW(fd,27)=0; // unknown
+#endif
+ }
+
+ WFIFOSET(fd,packet_len(cmd));
+}
+
+
+/// Notifies the client, that an inventory item was deleted or dropped (ZC_ITEM_THROW_ACK).
+/// 00af <index>.W <amount>.W
+void clif_dropitem(struct map_session_data *sd,int n,int amount)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0xaf));
+ WFIFOW(fd,0)=0xaf;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOSET(fd,packet_len(0xaf));
+}
+
+
+/// Notifies the client, that an inventory item was deleted (ZC_DELETE_ITEM_FROM_BODY).
+/// 07fa <delete type>.W <index>.W <amount>.W
+/// delete type:
+/// 0 = Normal
+/// 1 = Item used for a skill
+/// 2 = Refine failed
+/// 3 = Material changed
+/// 4 = Moved to storage
+/// 5 = Moved to cart
+/// 6 = Item sold
+/// 7 = Consumed by Four Spirit Analysis (SO_EL_ANALYSIS) skill
+void clif_delitem(struct map_session_data *sd,int n,int amount, short reason)
+{
+#if PACKETVER < 20091117
+ clif_dropitem(sd,n,amount);
+#else
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x7fa));
+ WFIFOW(fd,0)=0x7fa;
+ WFIFOW(fd,2)=reason;
+ WFIFOW(fd,4)=n+2;
+ WFIFOW(fd,6)=amount;
+ WFIFOSET(fd,packet_len(0x7fa));
+#endif
+}
+
+
+// Simplifies inventory/cart/storage packets by handling the packet section relevant to items. [Skotlex]
+// Equip is >= 0 for equippable items (holds the equip-point, is 0 for pet
+// armor/egg) -1 for stackable items, -2 for stackable items where arrows must send in the equip-point.
+void clif_item_sub(unsigned char *buf, int n, struct item *i, struct item_data *id, int equip)
+{
+ if (id->view_id > 0)
+ WBUFW(buf,n)=id->view_id;
+ else
+ WBUFW(buf,n)=i->nameid;
+ WBUFB(buf,n+2)=itemtype(id->type);
+ WBUFB(buf,n+3)=i->identify;
+ if (equip >= 0) { //Equippable item
+ WBUFW(buf,n+4)=equip;
+ WBUFW(buf,n+6)=i->equip;
+ WBUFB(buf,n+8)=i->attribute;
+ WBUFB(buf,n+9)=i->refine;
+ } else { //Stackable item.
+ WBUFW(buf,n+4)=i->amount;
+ if (equip == -2 && id->equip == EQP_AMMO)
+ WBUFW(buf,n+6)=EQP_AMMO;
+ else
+ WBUFW(buf,n+6)=0;
+ }
+
+}
+void clif_favorite_item(struct map_session_data* sd, unsigned short index);
+//Unified inventory function which sends all of the inventory (requires two packets, one for equipable items and one for stackable ones. [Skotlex]
+void clif_inventorylist(struct map_session_data *sd)
+{
+ int i,n,ne,arrow=-1;
+ unsigned char *buf;
+ unsigned char *bufe;
+
+#if PACKETVER < 5
+ const int s = 10; //Entry size.
+#elif PACKETVER < 20080102
+ const int s = 18;
+#else
+ const int s = 22;
+#endif
+#if PACKETVER < 20071002
+ const int se = 20;
+#elif PACKETVER < 20100629
+ const int se = 26;
+#else
+ const int se = 28;
+#endif
+
+ buf = (unsigned char*)aMalloc(MAX_INVENTORY * s + 4);
+ bufe = (unsigned char*)aMalloc(MAX_INVENTORY * se + 4);
+
+ for( i = 0, n = 0, ne = 0; i < MAX_INVENTORY; i++ )
+ {
+ if( sd->status.inventory[i].nameid <=0 || sd->inventory_data[i] == NULL )
+ continue;
+
+ if( !itemdb_isstackable2(sd->inventory_data[i]) )
+ { //Non-stackable (Equippable)
+ WBUFW(bufe,ne*se+4)=i+2;
+ clif_item_sub(bufe, ne*se+6, &sd->status.inventory[i], sd->inventory_data[i], pc_equippoint(sd,i));
+ clif_addcards(WBUFP(bufe, ne*se+16), &sd->status.inventory[i]);
+#if PACKETVER >= 20071002
+ WBUFL(bufe,ne*se+24)=sd->status.inventory[i].expire_time;
+ WBUFW(bufe,ne*se+28)=0; //Unknown
+#endif
+#if PACKETVER >= 20100629
+ if (sd->inventory_data[i]->equip&EQP_VISIBLE)
+ WBUFW(bufe,ne*se+30)= sd->inventory_data[i]->look;
+ else
+ WBUFW(bufe,ne*se+30)=0;
+#endif
+ ne++;
+ }
+ else
+ { //Stackable.
+ WBUFW(buf,n*s+4)=i+2;
+ clif_item_sub(buf, n*s+6, &sd->status.inventory[i], sd->inventory_data[i], -2);
+ if( sd->inventory_data[i]->equip == EQP_AMMO && sd->status.inventory[i].equip )
+ arrow=i;
+#if PACKETVER >= 5
+ clif_addcards(WBUFP(buf, n*s+14), &sd->status.inventory[i]);
+#endif
+#if PACKETVER >= 20080102
+ WBUFL(buf,n*s+22)=sd->status.inventory[i].expire_time;
+#endif
+ n++;
+ }
+ }
+ if( n )
+ {
+#if PACKETVER < 5
+ WBUFW(buf,0)=0xa3;
+#elif PACKETVER < 20080102
+ WBUFW(buf,0)=0x1ee;
+#else
+ WBUFW(buf,0)=0x2e8;
+#endif
+ WBUFW(buf,2)=4+n*s;
+ clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
+ }
+ if( arrow >= 0 )
+ clif_arrowequip(sd,arrow);
+
+ if( ne )
+ {
+#if PACKETVER < 20071002
+ WBUFW(bufe,0)=0xa4;
+#else
+ WBUFW(bufe,0)=0x2d0;
+#endif
+ WBUFW(bufe,2)=4+ne*se;
+ clif_send(bufe, WBUFW(bufe,2), &sd->bl, SELF);
+ }
+#if PACKETVER >= 20111122
+ for( i = 0; i < MAX_INVENTORY; i++ ) {
+ if( sd->status.inventory[i].nameid <= 0 || sd->inventory_data[i] == NULL )
+ continue;
+
+ if ( sd->status.inventory[i].favorite )
+ clif_favorite_item(sd, i);
+ }
+#endif
+
+ if( buf ) aFree(buf);
+ if( bufe ) aFree(bufe);
+}
+
+//Required when items break/get-repaired. Only sends equippable item list.
+void clif_equiplist(struct map_session_data *sd)
+{
+ int i,n,fd = sd->fd;
+ unsigned char *buf;
+#if PACKETVER < 20071002
+ const int cmd = 20;
+#elif PACKETVER < 20100629
+ const int cmd = 26;
+#else
+ const int cmd = 28;
+#endif
+
+ WFIFOHEAD(fd, MAX_INVENTORY * cmd + 4);
+ buf = WFIFOP(fd,0);
+
+ for(i=0,n=0;i<MAX_INVENTORY;i++){
+ if (sd->status.inventory[i].nameid <=0 || sd->inventory_data[i] == NULL)
+ continue;
+
+ if(itemdb_isstackable2(sd->inventory_data[i]))
+ continue;
+ //Equippable
+ WBUFW(buf,n*cmd+4)=i+2;
+ clif_item_sub(buf, n*cmd+6, &sd->status.inventory[i], sd->inventory_data[i], pc_equippoint(sd,i));
+ clif_addcards(WBUFP(buf, n*cmd+16), &sd->status.inventory[i]);
+#if PACKETVER >= 20071002
+ WBUFL(buf,n*cmd+24)=sd->status.inventory[i].expire_time;
+ WBUFW(buf,n*cmd+28)=0; //Unknown
+#endif
+#if PACKETVER >= 20100629
+ if (sd->inventory_data[i]->equip&EQP_VISIBLE)
+ WBUFW(buf,n*cmd+30)= sd->inventory_data[i]->look;
+ else
+ WBUFW(buf,n*cmd+30)=0;
+#endif
+ n++;
+ }
+ if (n) {
+#if PACKETVER < 20071002
+ WBUFW(buf,0)=0xa4;
+#else
+ WBUFW(buf,0)=0x2d0;
+#endif
+ WBUFW(buf,2)=4+n*cmd;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+}
+
+void clif_storagelist(struct map_session_data* sd, struct item* items, int items_length)
+{
+ struct item_data *id;
+ int i,n,ne;
+ unsigned char *buf;
+ unsigned char *bufe;
+#if PACKETVER < 5
+ const int s = 10; //Entry size.
+#elif PACKETVER < 20080102
+ const int s = 18;
+#else
+ const int s = 22;
+#endif
+#if PACKETVER < 20071002
+ const int cmd = 20;
+#elif PACKETVER < 20100629
+ const int cmd = 26;
+#else
+ const int cmd = 28;
+#endif
+
+ buf = (unsigned char*)aMalloc(items_length * s + 4);
+ bufe = (unsigned char*)aMalloc(items_length * cmd + 4);
+
+ for( i = 0, n = 0, ne = 0; i < items_length; i++ )
+ {
+ if( items[i].nameid <= 0 )
+ continue;
+ id = itemdb_search(items[i].nameid);
+ if( !itemdb_isstackable2(id) )
+ { //Equippable
+ WBUFW(bufe,ne*cmd+4)=i+1;
+ clif_item_sub(bufe, ne*cmd+6, &items[i], id, id->equip);
+ clif_addcards(WBUFP(bufe, ne*cmd+16), &items[i]);
+#if PACKETVER >= 20071002
+ WBUFL(bufe,ne*cmd+24)=items[i].expire_time;
+ WBUFW(bufe,ne*cmd+28)=0; //Unknown
+#endif
+ ne++;
+ }
+ else
+ { //Stackable
+ WBUFW(buf,n*s+4)=i+1;
+ clif_item_sub(buf, n*s+6, &items[i], id,-1);
+#if PACKETVER >= 5
+ clif_addcards(WBUFP(buf,n*s+14), &items[i]);
+#endif
+#if PACKETVER >= 20080102
+ WBUFL(buf,n*s+22)=items[i].expire_time;
+#endif
+ n++;
+ }
+ }
+ if( n )
+ {
+#if PACKETVER < 5
+ WBUFW(buf,0)=0xa5;
+#elif PACKETVER < 20080102
+ WBUFW(buf,0)=0x1f0;
+#else
+ WBUFW(buf,0)=0x2ea;
+#endif
+ WBUFW(buf,2)=4+n*s;
+ clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
+ }
+ if( ne )
+ {
+#if PACKETVER < 20071002
+ WBUFW(bufe,0)=0xa6;
+#else
+ WBUFW(bufe,0)=0x2d1;
+#endif
+ WBUFW(bufe,2)=4+ne*cmd;
+ clif_send(bufe, WBUFW(bufe,2), &sd->bl, SELF);
+ }
+
+ if( buf ) aFree(buf);
+ if( bufe ) aFree(bufe);
+}
+
+void clif_cartlist(struct map_session_data *sd)
+{
+ struct item_data *id;
+ int i,n,ne;
+ unsigned char *buf;
+ unsigned char *bufe;
+#if PACKETVER < 5
+ const int s = 10; //Entry size.
+#elif PACKETVER < 20080102
+ const int s = 18;
+#else
+ const int s = 22;
+#endif
+#if PACKETVER < 20071002
+ const int cmd = 20;
+#elif PACKETVER < 20100629
+ const int cmd = 26;
+#else
+ const int cmd = 28;
+#endif
+
+ buf = (unsigned char*)aMalloc(MAX_CART * s + 4);
+ bufe = (unsigned char*)aMalloc(MAX_CART * cmd + 4);
+
+ for( i = 0, n = 0, ne = 0; i < MAX_CART; i++ )
+ {
+ if( sd->status.cart[i].nameid <= 0 )
+ continue;
+ id = itemdb_search(sd->status.cart[i].nameid);
+ if( !itemdb_isstackable2(id) )
+ { //Equippable
+ WBUFW(bufe,ne*cmd+4)=i+2;
+ clif_item_sub(bufe, ne*cmd+6, &sd->status.cart[i], id, id->equip);
+ clif_addcards(WBUFP(bufe, ne*cmd+16), &sd->status.cart[i]);
+#if PACKETVER >= 20071002
+ WBUFL(bufe,ne*cmd+24)=sd->status.cart[i].expire_time;
+ WBUFW(bufe,ne*cmd+28)=0; //Unknown
+#endif
+ ne++;
+ }
+ else
+ { //Stackable
+ WBUFW(buf,n*s+4)=i+2;
+ clif_item_sub(buf, n*s+6, &sd->status.cart[i], id,-1);
+#if PACKETVER >= 5
+ clif_addcards(WBUFP(buf,n*s+14), &sd->status.cart[i]);
+#endif
+#if PACKETVER >= 20080102
+ WBUFL(buf,n*s+22)=sd->status.cart[i].expire_time;
+#endif
+ n++;
+ }
+ }
+ if( n )
+ {
+#if PACKETVER < 5
+ WBUFW(buf,0)=0x123;
+#elif PACKETVER < 20080102
+ WBUFW(buf,0)=0x1ef;
+#else
+ WBUFW(buf,0)=0x2e9;
+#endif
+ WBUFW(buf,2)=4+n*s;
+ clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
+ }
+ if( ne )
+ {
+#if PACKETVER < 20071002
+ WBUFW(bufe,0)=0x122;
+#else
+ WBUFW(bufe,0)=0x2d2;
+#endif
+ WBUFW(bufe,2)=4+ne*cmd;
+ clif_send(bufe, WBUFW(bufe,2), &sd->bl, SELF);
+ }
+
+ if( buf ) aFree(buf);
+ if( bufe ) aFree(bufe);
+}
+
+
+/// Removes cart (ZC_CARTOFF).
+/// 012b
+/// Client behaviour:
+/// Closes the cart storage and removes all it's items from memory.
+/// The Num & Weight values of the cart are left untouched and the cart is NOT removed.
+void clif_clearcart(int fd)
+{
+ WFIFOHEAD(fd, packet_len(0x12b));
+ WFIFOW(fd,0) = 0x12b;
+ WFIFOSET(fd, packet_len(0x12b));
+
+}
+
+
+/// Guild XY locators (ZC_NOTIFY_POSITION_TO_GUILDM) [Valaris]
+/// 01eb <account id>.L <x>.W <y>.W
+void clif_guild_xy(struct map_session_data *sd)
+{
+ unsigned char buf[10];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x1eb;
+ WBUFL(buf,2)=sd->status.account_id;
+ WBUFW(buf,6)=sd->bl.x;
+ WBUFW(buf,8)=sd->bl.y;
+ clif_send(buf,packet_len(0x1eb),&sd->bl,GUILD_SAMEMAP_WOS);
+}
+
+/*==========================================
+ * Sends x/y dot to a single fd. [Skotlex]
+ *------------------------------------------*/
+void clif_guild_xy_single(int fd, struct map_session_data *sd)
+{
+ if( sd->bg_id )
+ return;
+
+ WFIFOHEAD(fd,packet_len(0x1eb));
+ WFIFOW(fd,0)=0x1eb;
+ WFIFOL(fd,2)=sd->status.account_id;
+ WFIFOW(fd,6)=sd->bl.x;
+ WFIFOW(fd,8)=sd->bl.y;
+ WFIFOSET(fd,packet_len(0x1eb));
+}
+
+// Guild XY locators [Valaris]
+void clif_guild_xy_remove(struct map_session_data *sd)
+{
+ unsigned char buf[10];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x1eb;
+ WBUFL(buf,2)=sd->status.account_id;
+ WBUFW(buf,6)=-1;
+ WBUFW(buf,8)=-1;
+ clif_send(buf,packet_len(0x1eb),&sd->bl,GUILD_SAMEMAP_WOS);
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static int clif_hpmeter_sub(struct block_list *bl, va_list ap)
+{
+ struct map_session_data *sd, *tsd;
+#if PACKETVER < 20100126
+ const int cmd = 0x106;
+#else
+ const int cmd = 0x80e;
+#endif
+
+ sd = va_arg(ap, struct map_session_data *);
+ tsd = (TBL_PC *)bl;
+
+ nullpo_ret(sd);
+ nullpo_ret(tsd);
+
+ if( !tsd->fd || tsd == sd )
+ return 0;
+
+ if( !pc_has_permission(tsd, PC_PERM_VIEW_HPMETER) )
+ return 0;
+ WFIFOHEAD(tsd->fd,packet_len(cmd));
+ WFIFOW(tsd->fd,0) = cmd;
+ WFIFOL(tsd->fd,2) = sd->status.account_id;
+#if PACKETVER < 20100126
+ if( sd->battle_status.max_hp > INT16_MAX )
+ { //To correctly display the %hp bar. [Skotlex]
+ WFIFOW(tsd->fd,6) = sd->battle_status.hp/(sd->battle_status.max_hp/100);
+ WFIFOW(tsd->fd,8) = 100;
+ } else {
+ WFIFOW(tsd->fd,6) = sd->battle_status.hp;
+ WFIFOW(tsd->fd,8) = sd->battle_status.max_hp;
+ }
+#else
+ WFIFOL(tsd->fd,6) = sd->battle_status.hp;
+ WFIFOL(tsd->fd,10) = sd->battle_status.max_hp;
+#endif
+ WFIFOSET(tsd->fd,packet_len(cmd));
+ return 0;
+}
+
+/*==========================================
+ * Server tells all players that are allowed to view HP bars
+ * and are nearby 'sd' that 'sd' hp bar was updated.
+ *------------------------------------------*/
+static int clif_hpmeter(struct map_session_data *sd)
+{
+ nullpo_ret(sd);
+ map_foreachinarea(clif_hpmeter_sub, sd->bl.m, sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE, sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_PC, sd);
+ return 0;
+}
+
+/// Notifies client of a character parameter change.
+/// 00b0 <var id>.W <value>.L (ZC_PAR_CHANGE)
+/// 00b1 <var id>.W <value>.L (ZC_LONGPAR_CHANGE)
+/// 00be <status id>.W <value>.B (ZC_STATUS_CHANGE)
+/// 0121 <current count>.W <max count>.W <current weight>.L <max weight>.L (ZC_NOTIFY_CARTITEM_COUNTINFO)
+/// 013a <atk range>.W (ZC_ATTACK_RANGE)
+/// 0141 <status id>.L <base status>.L <plus status>.L (ZC_COUPLESTATUS)
+/// TODO: Extract individual packets.
+/// FIXME: Packet lengths from packet_len(cmd)
+void clif_updatestatus(struct map_session_data *sd,int type)
+{
+ int fd,len=8;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+
+ if ( !session_isActive(fd) ) // Invalid pointer fix, by sasuke [Kevin]
+ return;
+
+ WFIFOHEAD(fd, 14);
+ WFIFOW(fd,0)=0xb0;
+ WFIFOW(fd,2)=type;
+ switch(type){
+ // 00b0
+ case SP_WEIGHT:
+ pc_updateweightstatus(sd);
+ WFIFOHEAD(fd,14);
+ WFIFOW(fd,0)=0xb0; //Need to re-set as pc_updateweightstatus can alter the buffer. [Skotlex]
+ WFIFOW(fd,2)=type;
+ WFIFOL(fd,4)=sd->weight;
+ break;
+ case SP_MAXWEIGHT:
+ WFIFOL(fd,4)=sd->max_weight;
+ break;
+ case SP_SPEED:
+ WFIFOL(fd,4)=sd->battle_status.speed;
+ break;
+ case SP_BASELEVEL:
+ WFIFOL(fd,4)=sd->status.base_level;
+ break;
+ case SP_JOBLEVEL:
+ WFIFOL(fd,4)=sd->status.job_level;
+ break;
+ case SP_KARMA: // Adding this back, I wonder if the client intercepts this - [Lance]
+ WFIFOL(fd,4)=sd->status.karma;
+ break;
+ case SP_MANNER:
+ WFIFOL(fd,4)=sd->status.manner;
+ break;
+ case SP_STATUSPOINT:
+ WFIFOL(fd,4)=sd->status.status_point;
+ break;
+ case SP_SKILLPOINT:
+ WFIFOL(fd,4)=sd->status.skill_point;
+ break;
+ case SP_HIT:
+ WFIFOL(fd,4)=sd->battle_status.hit;
+ break;
+ case SP_FLEE1:
+ WFIFOL(fd,4)=sd->battle_status.flee;
+ break;
+ case SP_FLEE2:
+ WFIFOL(fd,4)=sd->battle_status.flee2/10;
+ break;
+ case SP_MAXHP:
+ WFIFOL(fd,4)=sd->battle_status.max_hp;
+ break;
+ case SP_MAXSP:
+ WFIFOL(fd,4)=sd->battle_status.max_sp;
+ break;
+ case SP_HP:
+ WFIFOL(fd,4)=sd->battle_status.hp;
+ // TODO: Won't these overwrite the current packet?
+ clif_hpmeter(sd);
+ if( !battle_config.party_hp_mode && sd->status.party_id )
+ clif_party_hp(sd);
+ if( sd->bg_id )
+ clif_bg_hp(sd);
+ break;
+ case SP_SP:
+ WFIFOL(fd,4)=sd->battle_status.sp;
+ break;
+ case SP_ASPD:
+ WFIFOL(fd,4)=sd->battle_status.amotion;
+ break;
+ case SP_ATK1:
+ WFIFOL(fd,4)=pc_leftside_atk(sd);
+ break;
+ case SP_DEF1:
+ WFIFOL(fd,4)=pc_leftside_def(sd);
+ break;
+ case SP_MDEF1:
+ WFIFOL(fd,4)=pc_leftside_mdef(sd);
+ break;
+ case SP_ATK2:
+ WFIFOL(fd,4)=pc_rightside_atk(sd);
+ break;
+ case SP_DEF2:
+ WFIFOL(fd,4)=pc_rightside_def(sd);
+ break;
+ case SP_MDEF2: {
+ //negative check (in case you have something like Berserk active)
+ int mdef2 = pc_rightside_mdef(sd);
+
+ WFIFOL(fd,4)=
+#ifndef RENEWAL
+ ( mdef2 < 0 ) ? 0 :
+#endif
+ mdef2;
+
+ }
+ break;
+ case SP_CRITICAL:
+ WFIFOL(fd,4)=sd->battle_status.cri/10;
+ break;
+ case SP_MATK1:
+ WFIFOL(fd,4)=pc_rightside_matk(sd);
+ break;
+ case SP_MATK2:
+ WFIFOL(fd,4)=pc_leftside_matk(sd);
+ break;
+
+
+ case SP_ZENY:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=sd->status.zeny;
+ break;
+ case SP_BASEEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=sd->status.base_exp;
+ break;
+ case SP_JOBEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=sd->status.job_exp;
+ break;
+ case SP_NEXTBASEEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=pc_nextbaseexp(sd);
+ break;
+ case SP_NEXTJOBEXP:
+ WFIFOW(fd,0)=0xb1;
+ WFIFOL(fd,4)=pc_nextjobexp(sd);
+ break;
+
+ /**
+ * SP_U<STAT> are used to update the amount of points necessary to increase that stat
+ **/
+ case SP_USTR:
+ case SP_UAGI:
+ case SP_UVIT:
+ case SP_UINT:
+ case SP_UDEX:
+ case SP_ULUK:
+ WFIFOW(fd,0)=0xbe;
+ WFIFOB(fd,4)=pc_need_status_point(sd,type-SP_USTR+SP_STR,1);
+ len=5;
+ break;
+
+ /**
+ * Tells the client how far it is allowed to attack (weapon range)
+ **/
+ case SP_ATTACKRANGE:
+ WFIFOW(fd,0)=0x13a;
+ WFIFOW(fd,2)=sd->battle_status.rhw.range;
+ len=4;
+ break;
+
+ case SP_STR:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.str;
+ WFIFOL(fd,10)=sd->battle_status.str - sd->status.str;
+ len=14;
+ break;
+ case SP_AGI:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.agi;
+ WFIFOL(fd,10)=sd->battle_status.agi - sd->status.agi;
+ len=14;
+ break;
+ case SP_VIT:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.vit;
+ WFIFOL(fd,10)=sd->battle_status.vit - sd->status.vit;
+ len=14;
+ break;
+ case SP_INT:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.int_;
+ WFIFOL(fd,10)=sd->battle_status.int_ - sd->status.int_;
+ len=14;
+ break;
+ case SP_DEX:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.dex;
+ WFIFOL(fd,10)=sd->battle_status.dex - sd->status.dex;
+ len=14;
+ break;
+ case SP_LUK:
+ WFIFOW(fd,0)=0x141;
+ WFIFOL(fd,2)=type;
+ WFIFOL(fd,6)=sd->status.luk;
+ WFIFOL(fd,10)=sd->battle_status.luk - sd->status.luk;
+ len=14;
+ break;
+
+ case SP_CARTINFO:
+ WFIFOW(fd,0)=0x121;
+ WFIFOW(fd,2)=sd->cart_num;
+ WFIFOW(fd,4)=MAX_CART;
+ WFIFOL(fd,6)=sd->cart_weight;
+ WFIFOL(fd,10)=sd->cart_weight_max;
+ len=14;
+ break;
+
+ default:
+ ShowError("clif_updatestatus : unrecognized type %d\n",type);
+ return;
+ }
+ WFIFOSET(fd,len);
+}
+
+
+/// Notifies client of a parameter change of an another player (ZC_PAR_CHANGE_USER).
+/// 01ab <account id>.L <var id>.W <value>.L
+void clif_changestatus(struct map_session_data* sd,int type,int val)
+{
+ unsigned char buf[12];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x1ab;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=type;
+
+ switch(type)
+ {
+ case SP_MANNER:
+ WBUFL(buf,8)=val;
+ break;
+ default:
+ ShowError("clif_changestatus : unrecognized type %d.\n",type);
+ return;
+ }
+
+ clif_send(buf,packet_len(0x1ab),&sd->bl,AREA_WOS);
+}
+
+
+/// Updates sprite/style properties of an object.
+/// 00c3 <id>.L <type>.B <value>.B (ZC_SPRITE_CHANGE)
+/// 01d7 <id>.L <type>.B <value>.L (ZC_SPRITE_CHANGE2)
+void clif_changelook(struct block_list *bl,int type,int val)
+{
+ unsigned char buf[16];
+ struct map_session_data* sd = NULL;
+ struct status_change* sc;
+ struct view_data* vd;
+ enum send_target target = AREA;
+ nullpo_retv(bl);
+
+ sd = BL_CAST(BL_PC, bl);
+ sc = status_get_sc(bl);
+ vd = status_get_viewdata(bl);
+ //nullpo_ret(vd);
+ if( vd ) //temp hack to let Warp Portal change appearance
+ switch(type)
+ {
+ case LOOK_WEAPON:
+ if (sd)
+ {
+ clif_get_weapon_view(sd, &vd->weapon, &vd->shield);
+ val = vd->weapon;
+ }
+ else vd->weapon = val;
+ break;
+ case LOOK_SHIELD:
+ if (sd)
+ {
+ clif_get_weapon_view(sd, &vd->weapon, &vd->shield);
+ val = vd->shield;
+ }
+ else vd->shield = val;
+ break;
+ case LOOK_BASE:
+ vd->class_ = val;
+ if (vd->class_ == JOB_WEDDING || vd->class_ == JOB_XMAS || vd->class_ == JOB_SUMMER)
+ vd->weapon = vd->shield = 0;
+ if (vd->cloth_color && (
+ (vd->class_ == JOB_WEDDING && battle_config.wedding_ignorepalette) ||
+ (vd->class_ == JOB_XMAS && battle_config.xmas_ignorepalette) ||
+ (vd->class_ == JOB_SUMMER && battle_config.summer_ignorepalette)
+ ))
+ clif_changelook(bl,LOOK_CLOTHES_COLOR,0);
+ break;
+ case LOOK_HAIR:
+ vd->hair_style = val;
+ break;
+ case LOOK_HEAD_BOTTOM:
+ vd->head_bottom = val;
+ break;
+ case LOOK_HEAD_TOP:
+ vd->head_top = val;
+ break;
+ case LOOK_HEAD_MID:
+ vd->head_mid = val;
+ break;
+ case LOOK_HAIR_COLOR:
+ vd->hair_color = val;
+ break;
+ case LOOK_CLOTHES_COLOR:
+ if (val && (
+ (vd->class_ == JOB_WEDDING && battle_config.wedding_ignorepalette) ||
+ (vd->class_ == JOB_XMAS && battle_config.xmas_ignorepalette) ||
+ (vd->class_ == JOB_SUMMER && battle_config.summer_ignorepalette)
+ ))
+ val = 0;
+ vd->cloth_color = val;
+ break;
+ case LOOK_SHOES:
+#if PACKETVER > 3
+ if (sd) {
+ int n;
+ if((n = sd->equip_index[2]) >= 0 && sd->inventory_data[n]) {
+ if(sd->inventory_data[n]->view_id > 0)
+ val = sd->inventory_data[n]->view_id;
+ else
+ val = sd->status.inventory[n].nameid;
+ } else
+ val = 0;
+ }
+#endif
+ //Shoes? No packet uses this....
+ break;
+ case LOOK_BODY:
+ case LOOK_FLOOR:
+ // unknown purpose
+ break;
+ case LOOK_ROBE:
+#if PACKETVER < 20110111
+ return;
+#else
+ vd->robe = val;
+#endif
+ break;
+ }
+
+ // prevent leaking the presence of GM-hidden objects
+ if( sc && sc->option&OPTION_INVISIBLE )
+ target = SELF;
+
+#if PACKETVER < 4
+ WBUFW(buf,0)=0xc3;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ WBUFB(buf,7)=val;
+ clif_send(buf,packet_len(0xc3),bl,target);
+#else
+ WBUFW(buf,0)=0x1d7;
+ WBUFL(buf,2)=bl->id;
+ if(type == LOOK_WEAPON || type == LOOK_SHIELD) {
+ WBUFB(buf,6)=LOOK_WEAPON;
+ WBUFW(buf,7)=vd->weapon;
+ WBUFW(buf,9)=vd->shield;
+ } else {
+ WBUFB(buf,6)=type;
+ WBUFL(buf,7)=val;
+ }
+ clif_send(buf,packet_len(0x1d7),bl,target);
+#endif
+}
+
+//Sends a change-base-look packet required for traps as they are triggered.
+void clif_changetraplook(struct block_list *bl,int val)
+{
+ unsigned char buf[32];
+#if PACKETVER < 4
+ WBUFW(buf,0)=0xc3;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=LOOK_BASE;
+ WBUFB(buf,7)=val;
+ clif_send(buf,packet_len(0xc3),bl,AREA);
+#else
+ WBUFW(buf,0)=0x1d7;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=LOOK_BASE;
+ WBUFW(buf,7)=val;
+ WBUFW(buf,9)=0;
+ clif_send(buf,packet_len(0x1d7),bl,AREA);
+#endif
+}
+
+//For the stupid cloth-dye bug. Resends the given view data to the area specified by bl.
+void clif_refreshlook(struct block_list *bl,int id,int type,int val,enum send_target target)
+{
+ unsigned char buf[32];
+#if PACKETVER < 4
+ WBUFW(buf,0)=0xc3;
+ WBUFL(buf,2)=id;
+ WBUFB(buf,6)=type;
+ WBUFB(buf,7)=val;
+ clif_send(buf,packet_len(0xc3),bl,target);
+#else
+ WBUFW(buf,0)=0x1d7;
+ WBUFL(buf,2)=id;
+ WBUFB(buf,6)=type;
+ WBUFW(buf,7)=val;
+ WBUFW(buf,9)=0;
+ clif_send(buf,packet_len(0x1d7),bl,target);
+#endif
+}
+
+
+/// Character status (ZC_STATUS).
+/// 00bd <stpoint>.W <str>.B <need str>.B <agi>.B <need agi>.B <vit>.B <need vit>.B
+/// <int>.B <need int>.B <dex>.B <need dex>.B <luk>.B <need luk>.B <atk>.W <atk2>.W
+/// <matk min>.W <matk max>.W <def>.W <def2>.W <mdef>.W <mdef2>.W <hit>.W
+/// <flee>.W <flee2>.W <crit>.W <aspd>.W <aspd2>.W
+void clif_initialstatus(struct map_session_data *sd)
+{
+ int fd, mdef2;
+ unsigned char *buf;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xbd));
+ buf=WFIFOP(fd,0);
+
+ WBUFW(buf,0)=0xbd;
+ WBUFW(buf,2)=min(sd->status.status_point, INT16_MAX);
+ WBUFB(buf,4)=min(sd->status.str, UINT8_MAX);
+ WBUFB(buf,5)=pc_need_status_point(sd,SP_STR,1);
+ WBUFB(buf,6)=min(sd->status.agi, UINT8_MAX);
+ WBUFB(buf,7)=pc_need_status_point(sd,SP_AGI,1);
+ WBUFB(buf,8)=min(sd->status.vit, UINT8_MAX);
+ WBUFB(buf,9)=pc_need_status_point(sd,SP_VIT,1);
+ WBUFB(buf,10)=min(sd->status.int_, UINT8_MAX);
+ WBUFB(buf,11)=pc_need_status_point(sd,SP_INT,1);
+ WBUFB(buf,12)=min(sd->status.dex, UINT8_MAX);
+ WBUFB(buf,13)=pc_need_status_point(sd,SP_DEX,1);
+ WBUFB(buf,14)=min(sd->status.luk, UINT8_MAX);
+ WBUFB(buf,15)=pc_need_status_point(sd,SP_LUK,1);
+
+ WBUFW(buf,16) = pc_leftside_atk(sd);
+ WBUFW(buf,18) = pc_rightside_atk(sd);
+ WBUFW(buf,20) = pc_rightside_matk(sd);
+ WBUFW(buf,22) = pc_leftside_matk(sd);
+ WBUFW(buf,24) = pc_leftside_def(sd);
+ WBUFW(buf,26) = pc_rightside_def(sd);
+ WBUFW(buf,28) = pc_leftside_mdef(sd);
+ mdef2 = pc_rightside_mdef(sd);
+ WBUFW(buf,30) =
+#ifndef RENEWAL
+ ( mdef2 < 0 ) ? 0 : //Negative check for Frenzy'ed characters.
+#endif
+ mdef2;
+ WBUFW(buf,32) = sd->battle_status.hit;
+ WBUFW(buf,34) = sd->battle_status.flee;
+ WBUFW(buf,36) = sd->battle_status.flee2/10;
+ WBUFW(buf,38) = sd->battle_status.cri/10;
+ WBUFW(buf,40) = sd->battle_status.amotion; // aspd
+ WBUFW(buf,42) = 0; // always 0 (plusASPD)
+
+ WFIFOSET(fd,packet_len(0xbd));
+
+ clif_updatestatus(sd,SP_STR);
+ clif_updatestatus(sd,SP_AGI);
+ clif_updatestatus(sd,SP_VIT);
+ clif_updatestatus(sd,SP_INT);
+ clif_updatestatus(sd,SP_DEX);
+ clif_updatestatus(sd,SP_LUK);
+
+ clif_updatestatus(sd,SP_ATTACKRANGE);
+ clif_updatestatus(sd,SP_ASPD);
+}
+
+
+/// Marks an ammunition item in inventory as equipped (ZC_EQUIP_ARROW).
+/// 013c <index>.W
+void clif_arrowequip(struct map_session_data *sd,int val)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ pc_stop_attack(sd); // [Valaris]
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0x013c));
+ WFIFOW(fd,0)=0x013c;
+ WFIFOW(fd,2)=val+2; //Item ID of the arrow
+ WFIFOSET(fd,packet_len(0x013c));
+}
+
+
+/// Ammunition action message (ZC_ACTION_FAILURE).
+/// 013b <type>.W
+/// type:
+/// 0 = MsgStringTable[242]="Please equip the proper ammunition first."
+/// 1 = MsgStringTable[243]="You can't Attack or use Skills because your Weight Limit has been exceeded."
+/// 2 = MsgStringTable[244]="You can't use Skills because Weight Limit has been exceeded."
+/// 3 = assassin, baby_assassin, assassin_cross => MsgStringTable[1040]="You have equipped throwing daggers."
+/// gunslinger => MsgStringTable[1175]="Bullets have been equipped."
+/// NOT ninja => MsgStringTable[245]="Ammunition has been equipped."
+void clif_arrow_fail(struct map_session_data *sd,int type)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, packet_len(0x013b));
+ WFIFOW(fd,0)=0x013b;
+ WFIFOW(fd,2)=type;
+ WFIFOSET(fd,packet_len(0x013b));
+}
+
+
+/// Presents a list of items, that can be processed by Arrow Crafting (ZC_MAKINGARROW_LIST).
+/// 01ad <packet len>.W { <name id>.W }*
+void clif_arrow_create_list(struct map_session_data *sd)
+{
+ int i, c, j;
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, MAX_SKILL_ARROW_DB*2+4);
+ WFIFOW(fd,0) = 0x1ad;
+
+ for (i = 0, c = 0; i < MAX_SKILL_ARROW_DB; i++) {
+ if (skill_arrow_db[i].nameid > 0 &&
+ (j = pc_search_inventory(sd, skill_arrow_db[i].nameid)) >= 0 &&
+ !sd->status.inventory[j].equip && sd->status.inventory[j].identify)
+ {
+ if ((j = itemdb_viewid(skill_arrow_db[i].nameid)) > 0)
+ WFIFOW(fd,c*2+4) = j;
+ else
+ WFIFOW(fd,c*2+4) = skill_arrow_db[i].nameid;
+ c++;
+ }
+ }
+ WFIFOW(fd,2) = c*2+4;
+ WFIFOSET(fd, WFIFOW(fd,2));
+ if (c > 0) {
+ sd->menuskill_id = AC_MAKINGARROW;
+ sd->menuskill_val = c;
+ }
+}
+
+
+/// Notifies the client, about the result of an status change request (ZC_STATUS_CHANGE_ACK).
+/// 00bc <status id>.W <result>.B <value>.B
+/// status id:
+/// SP_STR ~ SP_LUK
+/// result:
+/// 0 = failure
+/// 1 = success
+void clif_statusupack(struct map_session_data *sd,int type,int ok,int val)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xbc));
+ WFIFOW(fd,0)=0xbc;
+ WFIFOW(fd,2)=type;
+ WFIFOB(fd,4)=ok;
+ WFIFOB(fd,5)=cap_value(val,0,UINT8_MAX);
+ WFIFOSET(fd,packet_len(0xbc));
+}
+
+
+/// Notifies the client about the result of a request to equip an item (ZC_REQ_WEAR_EQUIP_ACK).
+/// 00aa <index>.W <equip location>.W <result>.B
+/// 00aa <index>.W <equip location>.W <view id>.W <result>.B (PACKETVER >= 20100629)
+/// result:
+/// 0 = failure
+/// 1 = success
+/// 2 = failure due to low level
+void clif_equipitemack(struct map_session_data *sd,int n,int pos,int ok)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xaa));
+ WFIFOW(fd,0)=0xaa;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=pos;
+#if PACKETVER < 20100629
+ WFIFOB(fd,6)=ok;
+#else
+ if (ok && sd->inventory_data[n]->equip&EQP_VISIBLE)
+ WFIFOW(fd,6)=sd->inventory_data[n]->look;
+ else
+ WFIFOW(fd,6)=0;
+ WFIFOB(fd,8)=ok;
+#endif
+ WFIFOSET(fd,packet_len(0xaa));
+}
+
+
+/// Notifies the client about the result of a request to take off an item (ZC_REQ_TAKEOFF_EQUIP_ACK).
+/// 00ac <index>.W <equip location>.W <result>.B
+/// result:
+/// 0 = failure
+/// 1 = success
+void clif_unequipitemack(struct map_session_data *sd,int n,int pos,int ok)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xac));
+ WFIFOW(fd,0)=0xac;
+ WFIFOW(fd,2)=n+2;
+ WFIFOW(fd,4)=pos;
+ WFIFOB(fd,6)=ok;
+ WFIFOSET(fd,packet_len(0xac));
+}
+
+
+/// Notifies clients in the area about an special/visual effect (ZC_NOTIFY_EFFECT).
+/// 019b <id>.L <effect id>.L
+/// effect id:
+/// 0 = base level up
+/// 1 = job level up
+/// 2 = refine failure
+/// 3 = refine success
+/// 4 = game over
+/// 5 = pharmacy success
+/// 6 = pharmacy failure
+/// 7 = base level up (super novice)
+/// 8 = job level up (super novice)
+/// 9 = base level up (taekwon)
+void clif_misceffect(struct block_list* bl,int type)
+{
+ unsigned char buf[32];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x19b;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = type;
+
+ clif_send(buf,packet_len(0x19b),bl,AREA);
+}
+
+
+/// Notifies clients in the area of a state change.
+/// 0119 <id>.L <body state>.W <health state>.W <effect state>.W <pk mode>.B (ZC_STATE_CHANGE)
+/// 0229 <id>.L <body state>.W <health state>.W <effect state>.L <pk mode>.B (ZC_STATE_CHANGE3)
+void clif_changeoption(struct block_list* bl)
+{
+ unsigned char buf[32];
+ struct status_change *sc;
+ struct map_session_data* sd;
+
+ nullpo_retv(bl);
+ sc = status_get_sc(bl);
+ if (!sc) return; //How can an option change if there's no sc?
+ sd = BL_CAST(BL_PC, bl);
+
+#if PACKETVER >= 7
+ WBUFW(buf,0) = 0x229;
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,6) = sc->opt1;
+ WBUFW(buf,8) = sc->opt2;
+ WBUFL(buf,10) = sc->option;
+ WBUFB(buf,14) = (sd)? sd->status.karma : 0;
+ if(disguised(bl)) {
+ clif_send(buf,packet_len(0x229),bl,AREA_WOS);
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf,packet_len(0x229),bl,SELF);
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,10) = OPTION_INVISIBLE;
+ clif_send(buf,packet_len(0x229),bl,SELF);
+ } else
+ clif_send(buf,packet_len(0x229),bl,AREA);
+#else
+ WBUFW(buf,0) = 0x119;
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,6) = sc->opt1;
+ WBUFW(buf,8) = sc->opt2;
+ WBUFW(buf,10) = sc->option;
+ WBUFB(buf,12) = (sd)? sd->status.karma : 0;
+ if(disguised(bl)) {
+ clif_send(buf,packet_len(0x119),bl,AREA_WOS);
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf,packet_len(0x119),bl,SELF);
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,10) = OPTION_INVISIBLE;
+ clif_send(buf,packet_len(0x119),bl,SELF);
+ } else
+ clif_send(buf,packet_len(0x119),bl,AREA);
+#endif
+}
+
+
+/// Displays status change effects on NPCs/monsters (ZC_NPC_SHOWEFST_UPDATE).
+/// 028a <id>.L <effect state>.L <level>.L <showEFST>.L
+void clif_changeoption2(struct block_list* bl)
+{
+ unsigned char buf[20];
+ struct status_change *sc;
+
+ sc = status_get_sc(bl);
+ if (!sc) return; //How can an option change if there's no sc?
+
+ WBUFW(buf,0) = 0x28a;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = sc->option;
+ WBUFL(buf,10) = clif_setlevel(bl);
+ WBUFL(buf,14) = sc->opt3;
+ if(disguised(bl)) {
+ clif_send(buf,packet_len(0x28a),bl,AREA_WOS);
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf,packet_len(0x28a),bl,SELF);
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = OPTION_INVISIBLE;
+ clif_send(buf,packet_len(0x28a),bl,SELF);
+ } else
+ clif_send(buf,packet_len(0x28a),bl,AREA);
+}
+
+
+/// Notifies the client about the result of an item use request.
+/// 00a8 <index>.W <amount>.W <result>.B (ZC_USE_ITEM_ACK)
+/// 01c8 <index>.W <name id>.W <id>.L <amount>.W <result>.B (ZC_USE_ITEM_ACK2)
+void clif_useitemack(struct map_session_data *sd,int index,int amount,bool ok)
+{
+ nullpo_retv(sd);
+
+ if(!ok) {
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xa8));
+ WFIFOW(fd,0)=0xa8;
+ WFIFOW(fd,2)=index+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOB(fd,6)=ok;
+ WFIFOSET(fd,packet_len(0xa8));
+ }
+ else {
+#if PACKETVER < 3
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xa8));
+ WFIFOW(fd,0)=0xa8;
+ WFIFOW(fd,2)=index+2;
+ WFIFOW(fd,4)=amount;
+ WFIFOB(fd,6)=ok;
+ WFIFOSET(fd,packet_len(0xa8));
+#else
+ unsigned char buf[32];
+
+ WBUFW(buf,0)=0x1c8;
+ WBUFW(buf,2)=index+2;
+ if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0)
+ WBUFW(buf,4)=sd->inventory_data[index]->view_id;
+ else
+ WBUFW(buf,4)=sd->status.inventory[index].nameid;
+ WBUFL(buf,6)=sd->bl.id;
+ WBUFW(buf,10)=amount;
+ WBUFB(buf,12)=ok;
+ clif_send(buf,packet_len(0x1c8),&sd->bl,AREA);
+#endif
+ }
+}
+
+
+/// Inform client whether chatroom creation was successful or not (ZC_ACK_CREATE_CHATROOM).
+/// 00d6 <flag>.B
+/// flag:
+/// 0 = Room has been successfully created (opens chat room)
+/// 1 = Room limit exceeded
+/// 2 = Same room already exists
+void clif_createchat(struct map_session_data* sd, int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0xd6));
+ WFIFOW(fd,0) = 0xd6;
+ WFIFOB(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0xd6));
+}
+
+
+/// Display a chat above the owner (ZC_ROOM_NEWENTRY).
+/// 00d7 <packet len>.W <owner id>.L <char id>.L <limit>.W <users>.W <type>.B <title>.?B
+/// type:
+/// 0 = private (password protected)
+/// 1 = public
+/// 2 = arena (npc waiting room)
+/// 3 = PK zone (non-clickable)
+void clif_dispchat(struct chat_data* cd, int fd)
+{
+ unsigned char buf[128];
+ uint8 type;
+
+ if( cd == NULL || cd->owner == NULL )
+ return;
+
+ type = (cd->owner->type == BL_PC ) ? (cd->pub) ? 1 : 0
+ : (cd->owner->type == BL_NPC) ? (cd->limit) ? 2 : 3
+ : 1;
+
+ WBUFW(buf, 0) = 0xd7;
+ WBUFW(buf, 2) = 17 + strlen(cd->title);
+ WBUFL(buf, 4) = cd->owner->id;
+ WBUFL(buf, 8) = cd->bl.id;
+ WBUFW(buf,12) = cd->limit;
+ WBUFW(buf,14) = (cd->owner->type == BL_NPC) ? cd->users+1 : cd->users;
+ WBUFB(buf,16) = type;
+ memcpy((char*)WBUFP(buf,17), cd->title, strlen(cd->title)); // not zero-terminated
+
+ if( fd ) {
+ WFIFOHEAD(fd,WBUFW(buf,2));
+ memcpy(WFIFOP(fd,0),buf,WBUFW(buf,2));
+ WFIFOSET(fd,WBUFW(buf,2));
+ } else {
+ clif_send(buf,WBUFW(buf,2),cd->owner,AREA_WOSC);
+ }
+}
+
+
+/// Chatroom properties adjustment (ZC_CHANGE_CHATROOM).
+/// 00df <packet len>.W <owner id>.L <chat id>.L <limit>.W <users>.W <type>.B <title>.?B
+/// type:
+/// 0 = private (password protected)
+/// 1 = public
+/// 2 = arena (npc waiting room)
+/// 3 = PK zone (non-clickable)
+void clif_changechatstatus(struct chat_data* cd)
+{
+ unsigned char buf[128];
+ uint8 type;
+
+ if( cd == NULL || cd->usersd[0] == NULL )
+ return;
+
+ type = (cd->owner->type == BL_PC ) ? (cd->pub) ? 1 : 0
+ : (cd->owner->type == BL_NPC) ? (cd->limit) ? 2 : 3
+ : 1;
+
+ WBUFW(buf, 0) = 0xdf;
+ WBUFW(buf, 2) = 17 + strlen(cd->title);
+ WBUFL(buf, 4) = cd->owner->id;
+ WBUFL(buf, 8) = cd->bl.id;
+ WBUFW(buf,12) = cd->limit;
+ WBUFW(buf,14) = (cd->owner->type == BL_NPC) ? cd->users+1 : cd->users;
+ WBUFB(buf,16) = type;
+ memcpy((char*)WBUFP(buf,17), cd->title, strlen(cd->title)); // not zero-terminated
+
+ clif_send(buf,WBUFW(buf,2),cd->owner,CHAT);
+}
+
+
+/// Removes the chatroom (ZC_DESTROY_ROOM).
+/// 00d8 <chat id>.L
+void clif_clearchat(struct chat_data *cd,int fd)
+{
+ unsigned char buf[32];
+
+ nullpo_retv(cd);
+
+ WBUFW(buf,0) = 0xd8;
+ WBUFL(buf,2) = cd->bl.id;
+ if( fd ) {
+ WFIFOHEAD(fd,packet_len(0xd8));
+ memcpy(WFIFOP(fd,0),buf,packet_len(0xd8));
+ WFIFOSET(fd,packet_len(0xd8));
+ } else {
+ clif_send(buf,packet_len(0xd8),cd->owner,AREA_WOSC);
+ }
+}
+
+
+/// Displays messages regarding join chat failures (ZC_REFUSE_ENTER_ROOM).
+/// 00da <result>.B
+/// result:
+/// 0 = room full
+/// 1 = wrong password
+/// 2 = kicked
+/// 3 = success (no message)
+/// 4 = no enough zeny
+/// 5 = too low level
+/// 6 = too high level
+/// 7 = unsuitable job class
+void clif_joinchatfail(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0xda));
+ WFIFOW(fd,0) = 0xda;
+ WFIFOB(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0xda));
+}
+
+
+/// Notifies the client about entering a chatroom (ZC_ENTER_ROOM).
+/// 00db <packet len>.W <chat id>.L { <role>.L <name>.24B }*
+/// role:
+/// 0 = owner (menu)
+/// 1 = normal
+void clif_joinchatok(struct map_session_data *sd,struct chat_data* cd)
+{
+ int fd;
+ int i,t;
+
+ nullpo_retv(sd);
+ nullpo_retv(cd);
+
+ fd = sd->fd;
+ if (!session_isActive(fd))
+ return;
+ t = (int)(cd->owner->type == BL_NPC);
+ WFIFOHEAD(fd, 8 + (28*(cd->users+t)));
+ WFIFOW(fd, 0) = 0xdb;
+ WFIFOW(fd, 2) = 8 + (28*(cd->users+t));
+ WFIFOL(fd, 4) = cd->bl.id;
+
+ if(cd->owner->type == BL_NPC){
+ WFIFOL(fd, 30) = 1;
+ WFIFOL(fd, 8) = 0;
+ memcpy(WFIFOP(fd, 12), ((struct npc_data *)cd->owner)->name, NAME_LENGTH);
+ for (i = 0; i < cd->users; i++) {
+ WFIFOL(fd, 8+(i+1)*28) = 1;
+ memcpy(WFIFOP(fd, 8+(i+t)*28+4), cd->usersd[i]->status.name, NAME_LENGTH);
+ }
+ } else
+ for (i = 0; i < cd->users; i++) {
+ WFIFOL(fd, 8+i*28) = (i != 0 || cd->owner->type == BL_NPC);
+ memcpy(WFIFOP(fd, 8+(i+t)*28+4), cd->usersd[i]->status.name, NAME_LENGTH);
+ }
+ WFIFOSET(fd, WFIFOW(fd, 2));
+}
+
+
+/// Notifies clients in a chat about a new member (ZC_MEMBER_NEWENTRY).
+/// 00dc <users>.W <name>.24B
+void clif_addchat(struct chat_data* cd,struct map_session_data *sd)
+{
+ unsigned char buf[32];
+
+ nullpo_retv(sd);
+ nullpo_retv(cd);
+
+ WBUFW(buf, 0) = 0xdc;
+ WBUFW(buf, 2) = cd->users;
+ memcpy(WBUFP(buf, 4),sd->status.name,NAME_LENGTH);
+ clif_send(buf,packet_len(0xdc),&sd->bl,CHAT_WOS);
+}
+
+
+/// Announce the new owner (ZC_ROLE_CHANGE).
+/// 00e1 <role>.L <nick>.24B
+/// role:
+/// 0 = owner (menu)
+/// 1 = normal
+void clif_changechatowner(struct chat_data* cd, struct map_session_data* sd)
+{
+ unsigned char buf[64];
+
+ nullpo_retv(sd);
+ nullpo_retv(cd);
+
+ WBUFW(buf, 0) = 0xe1;
+ WBUFL(buf, 2) = 1;
+ memcpy(WBUFP(buf,6),cd->usersd[0]->status.name,NAME_LENGTH);
+
+ WBUFW(buf,30) = 0xe1;
+ WBUFL(buf,32) = 0;
+ memcpy(WBUFP(buf,36),sd->status.name,NAME_LENGTH);
+
+ clif_send(buf,packet_len(0xe1)*2,&sd->bl,CHAT);
+}
+
+
+/// Notify about user leaving the chatroom (ZC_MEMBER_EXIT).
+/// 00dd <users>.W <nick>.24B <flag>.B
+/// flag:
+/// 0 = left
+/// 1 = kicked
+void clif_leavechat(struct chat_data* cd, struct map_session_data* sd, bool flag)
+{
+ unsigned char buf[32];
+
+ nullpo_retv(sd);
+ nullpo_retv(cd);
+
+ WBUFW(buf, 0) = 0xdd;
+ WBUFW(buf, 2) = cd->users-1;
+ memcpy(WBUFP(buf,4),sd->status.name,NAME_LENGTH);
+ WBUFB(buf,28) = flag;
+
+ clif_send(buf,packet_len(0xdd),&sd->bl,CHAT);
+}
+
+
+/// Opens a trade request window from char 'name'.
+/// 00e5 <nick>.24B (ZC_REQ_EXCHANGE_ITEM)
+/// 01f4 <nick>.24B <charid>.L <baselvl>.W (ZC_REQ_EXCHANGE_ITEM2)
+void clif_traderequest(struct map_session_data* sd, const char* name)
+{
+ int fd = sd->fd;
+
+#if PACKETVER < 6
+ WFIFOHEAD(fd,packet_len(0xe5));
+ WFIFOW(fd,0) = 0xe5;
+ safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0xe5));
+#else
+ struct map_session_data* tsd = map_id2sd(sd->trade_partner);
+ if( !tsd ) return;
+
+ WFIFOHEAD(fd,packet_len(0x1f4));
+ WFIFOW(fd,0) = 0x1f4;
+ safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH);
+ WFIFOL(fd,26) = tsd->status.char_id;
+ WFIFOW(fd,30) = tsd->status.base_level;
+ WFIFOSET(fd,packet_len(0x1f4));
+#endif
+}
+
+
+/// Reply to a trade-request.
+/// 00e7 <result>.B (ZC_ACK_EXCHANGE_ITEM)
+/// 01f5 <result>.B <charid>.L <baselvl>.W (ZC_ACK_EXCHANGE_ITEM2)
+/// result:
+/// 0 = Char is too far
+/// 1 = Character does not exist
+/// 2 = Trade failed
+/// 3 = Accept
+/// 4 = Cancel
+/// 5 = Busy
+void clif_tradestart(struct map_session_data* sd, uint8 type)
+{
+ int fd = sd->fd;
+ struct map_session_data* tsd = map_id2sd(sd->trade_partner);
+ if( PACKETVER < 6 || !tsd ) {
+ WFIFOHEAD(fd,packet_len(0xe7));
+ WFIFOW(fd,0) = 0xe7;
+ WFIFOB(fd,2) = type;
+ WFIFOSET(fd,packet_len(0xe7));
+ } else {
+ WFIFOHEAD(fd,packet_len(0x1f5));
+ WFIFOW(fd,0) = 0x1f5;
+ WFIFOB(fd,2) = type;
+ WFIFOL(fd,3) = tsd->status.char_id;
+ WFIFOW(fd,7) = tsd->status.base_level;
+ WFIFOSET(fd,packet_len(0x1f5));
+ }
+}
+
+
+/// Notifies the client about an item from other player in current trade.
+/// 00e9 <amount>.L <nameid>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_EXCHANGE_ITEM)
+/// 080f <nameid>.W <item type>.B <amount>.L <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_EXCHANGE_ITEM2)
+void clif_tradeadditem(struct map_session_data* sd, struct map_session_data* tsd, int index, int amount)
+{
+ int fd;
+ unsigned char *buf;
+#if PACKETVER < 20100223
+ const int cmd = 0xe9;
+#else
+ const int cmd = 0x80f;
+#endif
+ nullpo_retv(sd);
+ nullpo_retv(tsd);
+
+ fd = tsd->fd;
+ buf = WFIFOP(fd,0);
+ WFIFOHEAD(fd,packet_len(cmd));
+ WBUFW(buf,0) = cmd;
+ if( index == 0 )
+ {
+#if PACKETVER < 20100223
+ WBUFL(buf,2) = amount; //amount
+ WBUFW(buf,6) = 0; // type id
+#else
+ WBUFW(buf,2) = 0; // type id
+ WBUFB(buf,4) = 0; // item type
+ WBUFL(buf,5) = amount; // amount
+ buf = WBUFP(buf,1); //Advance 1B
+#endif
+ WBUFB(buf,8) = 0; //identify flag
+ WBUFB(buf,9) = 0; // attribute
+ WBUFB(buf,10)= 0; //refine
+ WBUFW(buf,11)= 0; //card (4w)
+ WBUFW(buf,13)= 0; //card (4w)
+ WBUFW(buf,15)= 0; //card (4w)
+ WBUFW(buf,17)= 0; //card (4w)
+ }
+ else
+ {
+ index -= 2; //index fix
+#if PACKETVER < 20100223
+ WBUFL(buf,2) = amount; //amount
+ if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0)
+ WBUFW(buf,6) = sd->inventory_data[index]->view_id;
+ else
+ WBUFW(buf,6) = sd->status.inventory[index].nameid; // type id
+#else
+ if(sd->inventory_data[index] && sd->inventory_data[index]->view_id > 0)
+ WBUFW(buf,2) = sd->inventory_data[index]->view_id;
+ else
+ WBUFW(buf,2) = sd->status.inventory[index].nameid; // type id
+ WBUFB(buf,4) = sd->inventory_data[index]->type; // item type
+ WBUFL(buf,5) = amount; // amount
+ buf = WBUFP(buf,1); //Advance 1B
+#endif
+ WBUFB(buf,8) = sd->status.inventory[index].identify; //identify flag
+ WBUFB(buf,9) = sd->status.inventory[index].attribute; // attribute
+ WBUFB(buf,10)= sd->status.inventory[index].refine; //refine
+ clif_addcards(WBUFP(buf, 11), &sd->status.inventory[index]);
+ }
+ WFIFOSET(fd,packet_len(cmd));
+}
+
+
+/// Notifies the client about the result of request to add an item to the current trade (ZC_ACK_ADD_EXCHANGE_ITEM).
+/// 00ea <index>.W <result>.B
+/// result:
+/// 0 = success
+/// 1 = overweight
+/// 2 = trade canceled
+void clif_tradeitemok(struct map_session_data* sd, int index, int fail)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0xea));
+ WFIFOW(fd,0) = 0xea;
+ WFIFOW(fd,2) = index;
+ WFIFOB(fd,4) = fail;
+ WFIFOSET(fd,packet_len(0xea));
+}
+
+
+/// Notifies the client about finishing one side of the current trade (ZC_CONCLUDE_EXCHANGE_ITEM).
+/// 00ec <who>.B
+/// who:
+/// 0 = self
+/// 1 = other player
+void clif_tradedeal_lock(struct map_session_data* sd, int fail)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0xec));
+ WFIFOW(fd,0) = 0xec;
+ WFIFOB(fd,2) = fail;
+ WFIFOSET(fd,packet_len(0xec));
+}
+
+
+/// Notifies the client about the trade being canceled (ZC_CANCEL_EXCHANGE_ITEM).
+/// 00ee
+void clif_tradecancelled(struct map_session_data* sd)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0xee));
+ WFIFOW(fd,0) = 0xee;
+ WFIFOSET(fd,packet_len(0xee));
+}
+
+
+/// Result of a trade (ZC_EXEC_EXCHANGE_ITEM).
+/// 00f0 <result>.B
+/// result:
+/// 0 = success
+/// 1 = failure
+void clif_tradecompleted(struct map_session_data* sd, int fail)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0xf0));
+ WFIFOW(fd,0) = 0xf0;
+ WFIFOB(fd,2) = fail;
+ WFIFOSET(fd,packet_len(0xf0));
+}
+
+
+/// Resets the trade window on the send side (ZC_EXCHANGEITEM_UNDO).
+/// 00f1
+/// NOTE: Unknown purpose. Items are not removed until the window is
+/// refreshed (ex. by putting another item in there).
+void clif_tradeundo(struct map_session_data* sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0xf1));
+ WFIFOW(fd,0) = 0xf1;
+ WFIFOSET(fd,packet_len(0xf1));
+}
+
+
+/// Updates storage total amount (ZC_NOTIFY_STOREITEM_COUNTINFO).
+/// 00f2 <current count>.W <max count>.W
+void clif_updatestorageamount(struct map_session_data* sd, int amount, int max_amount)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xf2));
+ WFIFOW(fd,0) = 0xf2;
+ WFIFOW(fd,2) = amount;
+ WFIFOW(fd,4) = max_amount;
+ WFIFOSET(fd,packet_len(0xf2));
+}
+
+
+/// Notifies the client of an item being added to the storage.
+/// 00f4 <index>.W <amount>.L <nameid>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_STORE)
+/// 01c4 <index>.W <amount>.L <nameid>.W <type>.B <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_STORE2)
+void clif_storageitemadded(struct map_session_data* sd, struct item* i, int index, int amount)
+{
+ int view,fd;
+
+ nullpo_retv(sd);
+ nullpo_retv(i);
+ fd=sd->fd;
+ view = itemdb_viewid(i->nameid);
+
+#if PACKETVER < 5
+ WFIFOHEAD(fd,packet_len(0xf4));
+ WFIFOW(fd, 0) = 0xf4; // Storage item added
+ WFIFOW(fd, 2) = index+1; // index
+ WFIFOL(fd, 4) = amount; // amount
+ WFIFOW(fd, 8) = ( view > 0 ) ? view : i->nameid; // id
+ WFIFOB(fd,10) = i->identify; //identify flag
+ WFIFOB(fd,11) = i->attribute; // attribute
+ WFIFOB(fd,12) = i->refine; //refine
+ clif_addcards(WFIFOP(fd,13), i);
+ WFIFOSET(fd,packet_len(0xf4));
+#else
+ WFIFOHEAD(fd,packet_len(0x1c4));
+ WFIFOW(fd, 0) = 0x1c4; // Storage item added
+ WFIFOW(fd, 2) = index+1; // index
+ WFIFOL(fd, 4) = amount; // amount
+ WFIFOW(fd, 8) = ( view > 0 ) ? view : i->nameid; // id
+ WFIFOB(fd,10) = itemdb_type(i->nameid); //type
+ WFIFOB(fd,11) = i->identify; //identify flag
+ WFIFOB(fd,12) = i->attribute; // attribute
+ WFIFOB(fd,13) = i->refine; //refine
+ clif_addcards(WFIFOP(fd,14), i);
+ WFIFOSET(fd,packet_len(0x1c4));
+#endif
+}
+
+
+/// Notifies the client of an item being deleted from the storage (ZC_DELETE_ITEM_FROM_STORE).
+/// 00f6 <index>.W <amount>.L
+void clif_storageitemremoved(struct map_session_data* sd, int index, int amount)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xf6));
+ WFIFOW(fd,0)=0xf6; // Storage item removed
+ WFIFOW(fd,2)=index+1;
+ WFIFOL(fd,4)=amount;
+ WFIFOSET(fd,packet_len(0xf6));
+}
+
+
+/// Closes storage (ZC_CLOSE_STORE).
+/// 00f8
+void clif_storageclose(struct map_session_data* sd)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xf8));
+ WFIFOW(fd,0) = 0xf8; // Storage Closed
+ WFIFOSET(fd,packet_len(0xf8));
+}
+
+/*==========================================
+ * Server tells 'sd' player client the abouts of 'dstsd' player
+ *------------------------------------------*/
+static void clif_getareachar_pc(struct map_session_data* sd,struct map_session_data* dstsd)
+{
+ struct block_list *d_bl;
+ int i;
+
+ if( dstsd->chatID ) {
+ struct chat_data *cd = NULL;
+ if( (cd = (struct chat_data*)map_id2bl(dstsd->chatID)) && cd->usersd[0]==dstsd)
+ clif_dispchat(cd,sd->fd);
+ } else if( dstsd->state.vending )
+ clif_showvendingboard(&dstsd->bl,dstsd->message,sd->fd);
+ else if( dstsd->state.buyingstore )
+ clif_buyingstore_entry_single(sd, dstsd);
+
+ if(dstsd->spiritball > 0)
+ clif_spiritball_single(sd->fd, dstsd);
+ for(i = 1; i < 5; i++){
+ if( dstsd->talisman[i] > 0 )
+ clif_talisman_single(sd->fd, dstsd, i);
+ }
+ if( dstsd->sc.option&OPTION_MOUNTING ) {
+ //New Mounts are not complaint to the original method, so we gotta tell this guy that I'm mounting.
+ clif_status_load_single(sd->fd,dstsd->bl.id,SI_ALL_RIDING,2,1,0,0);
+ }
+#ifdef NEW_CARTS
+ if( dstsd->sc.data[SC_PUSH_CART] )
+ clif_status_load_single(sd->fd, dstsd->bl.id, SI_ON_PUSH_CART, 2, dstsd->sc.data[SC_PUSH_CART]->val1, 0, 0);
+#endif
+ if( (sd->status.party_id && dstsd->status.party_id == sd->status.party_id) || //Party-mate, or hpdisp setting.
+ (sd->bg_id && sd->bg_id == dstsd->bg_id) || //BattleGround
+ pc_has_permission(sd, PC_PERM_VIEW_HPMETER)
+ )
+ clif_hpmeter_single(sd->fd, dstsd->bl.id, dstsd->battle_status.hp, dstsd->battle_status.max_hp);
+
+ // display link (sd - dstsd) to sd
+ ARR_FIND( 0, 5, i, sd->devotion[i] == dstsd->bl.id );
+ if( i < 5 ) clif_devotion(&sd->bl, sd);
+ // display links (dstsd - devotees) to sd
+ ARR_FIND( 0, 5, i, dstsd->devotion[i] > 0 );
+ if( i < 5 ) clif_devotion(&dstsd->bl, sd);
+ // display link (dstsd - crusader) to sd
+ if( dstsd->sc.data[SC_DEVOTION] && (d_bl = map_id2bl(dstsd->sc.data[SC_DEVOTION]->val1)) != NULL )
+ clif_devotion(d_bl, sd);
+}
+
+void clif_getareachar_unit(struct map_session_data* sd,struct block_list *bl)
+{
+ uint8 buf[128];
+ struct unit_data *ud;
+ struct view_data *vd;
+ int len;
+
+ vd = status_get_viewdata(bl);
+ if (!vd || vd->class_ == INVISIBLE_CLASS)
+ return;
+
+ /**
+ * Hide NPC from maya purple card.
+ **/
+ if(bl->type == BL_NPC && !((TBL_NPC*)bl)->chat_id && (((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE))
+ return;
+
+ ud = unit_bl2ud(bl);
+ len = ( ud && ud->walktimer != INVALID_TIMER ) ? clif_set_unit_walking(bl,ud,buf) : clif_set_unit_idle(bl,buf,false);
+ clif_send(buf,len,&sd->bl,SELF);
+
+ if (vd->cloth_color)
+ clif_refreshlook(&sd->bl,bl->id,LOOK_CLOTHES_COLOR,vd->cloth_color,SELF);
+
+ switch (bl->type)
+ {
+ case BL_PC:
+ {
+ TBL_PC* tsd = (TBL_PC*)bl;
+ clif_getareachar_pc(sd, tsd);
+ if(tsd->state.size==SZ_BIG) // tiny/big players [Valaris]
+ clif_specialeffect_single(bl,423,sd->fd);
+ else if(tsd->state.size==SZ_MEDIUM)
+ clif_specialeffect_single(bl,421,sd->fd);
+ if( tsd->bg_id && map[tsd->bl.m].flag.battleground )
+ clif_sendbgemblem_single(sd->fd,tsd);
+ if( tsd->sc.data[SC_CAMOUFLAGE] )
+ clif_status_load(bl,SI_CAMOUFLAGE,1);
+ }
+ break;
+ case BL_MER: // Devotion Effects
+ if( ((TBL_MER*)bl)->devotion_flag )
+ clif_devotion(bl, sd);
+ break;
+ case BL_NPC:
+ {
+ TBL_NPC* nd = (TBL_NPC*)bl;
+ if( nd->chat_id )
+ clif_dispchat((struct chat_data*)map_id2bl(nd->chat_id),sd->fd);
+ if( nd->size == SZ_BIG )
+ clif_specialeffect_single(bl,423,sd->fd);
+ else if( nd->size == SZ_MEDIUM )
+ clif_specialeffect_single(bl,421,sd->fd);
+ }
+ break;
+ case BL_MOB:
+ {
+ TBL_MOB* md = (TBL_MOB*)bl;
+ if(md->special_state.size==SZ_BIG) // tiny/big mobs [Valaris]
+ clif_specialeffect_single(bl,423,sd->fd);
+ else if(md->special_state.size==SZ_MEDIUM)
+ clif_specialeffect_single(bl,421,sd->fd);
+#if PACKETVER >= 20120404
+ if( !(md->status.mode&MD_BOSS) ){
+ int i;
+ for(i = 0; i < DAMAGELOG_SIZE; i++)// must show hp bar to all char who already hit the mob.
+ if( md->dmglog[i].id == sd->status.char_id )
+ clif_monster_hp_bar(md, sd->fd);
+ }
+#endif
+ }
+ break;
+ case BL_PET:
+ if (vd->head_bottom)
+ clif_pet_equip(sd, (TBL_PET*)bl); // needed to display pet equip properly
+ break;
+ }
+}
+
+//Modifies the type of damage according to status changes [Skotlex]
+//Aegis data specifies that: 4 endure against single hit sources, 9 against multi-hit.
+static inline int clif_calc_delay(int type, int div, int damage, int delay)
+{
+ return ( delay == 0 && damage > 0 ) ? ( div > 1 ? 9 : 4 ) : type;
+}
+
+/*==========================================
+ * Estimates walk delay based on the damage criteria. [Skotlex]
+ *------------------------------------------*/
+static int clif_calc_walkdelay(struct block_list *bl,int delay, int type, int damage, int div_)
+{
+ if (type == 4 || type == 9 || damage <=0)
+ return 0;
+
+ if (bl->type == BL_PC) {
+ if (battle_config.pc_walk_delay_rate != 100)
+ delay = delay*battle_config.pc_walk_delay_rate/100;
+ } else
+ if (battle_config.walk_delay_rate != 100)
+ delay = delay*battle_config.walk_delay_rate/100;
+
+ if (div_ > 1) //Multi-hit skills mean higher delays.
+ delay += battle_config.multihit_delay*(div_-1);
+
+ return delay>0?delay:1; //Return 1 to specify there should be no noticeable delay, but you should stop walking.
+}
+
+
+/// Sends a 'damage' packet (src performs action on dst)
+/// 008a <src ID>.L <dst ID>.L <server tick>.L <src speed>.L <dst speed>.L <damage>.W <div>.W <type>.B <damage2>.W (ZC_NOTIFY_ACT)
+/// 02e1 <src ID>.L <dst ID>.L <server tick>.L <src speed>.L <dst speed>.L <damage>.L <div>.W <type>.B <damage2>.L (ZC_NOTIFY_ACT2)
+/// type:
+/// 0 = damage [ damage: total damage, div: amount of hits, damage2: assassin dual-wield damage ]
+/// 1 = pick up item
+/// 2 = sit down
+/// 3 = stand up
+/// 4 = damage (endure)
+/// 5 = (splash?)
+/// 6 = (skill?)
+/// 7 = (repeat damage?)
+/// 8 = multi-hit damage
+/// 9 = multi-hit damage (endure)
+/// 10 = critical hit
+/// 11 = lucky dodge
+/// 12 = (touch skill?)
+int clif_damage(struct block_list* src, struct block_list* dst, unsigned int tick, int sdelay, int ddelay, int damage, int div, int type, int damage2)
+{
+ unsigned char buf[33];
+ struct status_change *sc;
+#if PACKETVER < 20071113
+ const int cmd = 0x8a;
+#else
+ const int cmd = 0x2e1;
+#endif
+
+ nullpo_ret(src);
+ nullpo_ret(dst);
+
+ type = clif_calc_delay(type,div,damage+damage2,ddelay);
+ sc = status_get_sc(dst);
+ if(sc && sc->count) {
+ if(sc->data[SC_HALLUCINATION]) {
+ if(damage) damage = damage*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100;
+ if(damage2) damage2 = damage2*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100;
+ }
+ }
+
+ WBUFW(buf,0)=cmd;
+ WBUFL(buf,2)=src->id;
+ WBUFL(buf,6)=dst->id;
+ WBUFL(buf,10)=tick;
+ WBUFL(buf,14)=sdelay;
+ WBUFL(buf,18)=ddelay;
+#if PACKETVER < 20071113
+ if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
+ WBUFW(buf,22)=damage?div:0;
+ WBUFW(buf,27)=damage2?div:0;
+ } else {
+ WBUFW(buf,22)=min(damage, INT16_MAX);
+ WBUFW(buf,27)=damage2;
+ }
+ WBUFW(buf,24)=div;
+ WBUFB(buf,26)=type;
+#else
+ if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
+ WBUFL(buf,22)=damage?div:0;
+ WBUFL(buf,29)=damage2?div:0;
+ } else {
+ WBUFL(buf,22)=damage;
+ WBUFL(buf,29)=damage2;
+ }
+ WBUFW(buf,26)=div;
+ WBUFB(buf,28)=type;
+#endif
+ if(disguised(dst)) {
+ clif_send(buf,packet_len(cmd),dst,AREA_WOS);
+ WBUFL(buf,6) = -dst->id;
+ clif_send(buf,packet_len(cmd),dst,SELF);
+ } else
+ clif_send(buf,packet_len(cmd),dst,AREA);
+
+ if(disguised(src)) {
+ WBUFL(buf,2) = -src->id;
+ if (disguised(dst))
+ WBUFL(buf,6) = dst->id;
+#if PACKETVER < 20071113
+ if(damage > 0) WBUFW(buf,22) = -1;
+ if(damage2 > 0) WBUFW(buf,27) = -1;
+#else
+ if(damage > 0) WBUFL(buf,22) = -1;
+ if(damage2 > 0) WBUFL(buf,29) = -1;
+#endif
+ clif_send(buf,packet_len(cmd),src,SELF);
+ }
+
+ if(src == dst) {
+ unit_setdir(src,unit_getdir(src));
+ }
+ //Return adjusted can't walk delay for further processing.
+ return clif_calc_walkdelay(dst,ddelay,type,damage+damage2,div);
+}
+
+/*==========================================
+ * src picks up dst
+ *------------------------------------------*/
+void clif_takeitem(struct block_list* src, struct block_list* dst)
+{
+ //clif_damage(src,dst,0,0,0,0,0,1,0);
+ unsigned char buf[32];
+
+ nullpo_retv(src);
+ nullpo_retv(dst);
+
+ WBUFW(buf, 0) = 0x8a;
+ WBUFL(buf, 2) = src->id;
+ WBUFL(buf, 6) = dst->id;
+ WBUFB(buf,26) = 1;
+ clif_send(buf, packet_len(0x8a), src, AREA);
+
+}
+
+/*==========================================
+ * inform clients in area that `bl` is sitting
+ *------------------------------------------*/
+void clif_sitting(struct block_list* bl)
+{
+ unsigned char buf[32];
+ nullpo_retv(bl);
+
+ WBUFW(buf, 0) = 0x8a;
+ WBUFL(buf, 2) = bl->id;
+ WBUFB(buf,26) = 2;
+ clif_send(buf, packet_len(0x8a), bl, AREA);
+
+ if(disguised(bl)) {
+ WBUFL(buf, 2) = - bl->id;
+ clif_send(buf, packet_len(0x8a), bl, SELF);
+ }
+}
+
+/*==========================================
+ * inform clients in area that `bl` is standing
+ *------------------------------------------*/
+void clif_standing(struct block_list* bl)
+{
+ unsigned char buf[32];
+ nullpo_retv(bl);
+
+ WBUFW(buf, 0) = 0x8a;
+ WBUFL(buf, 2) = bl->id;
+ WBUFB(buf,26) = 3;
+ clif_send(buf, packet_len(0x8a), bl, AREA);
+
+ if(disguised(bl)) {
+ WBUFL(buf, 2) = - bl->id;
+ clif_send(buf, packet_len(0x8a), bl, SELF);
+ }
+}
+
+
+/// Inform client(s) about a map-cell change (ZC_UPDATE_MAPINFO).
+/// 0192 <x>.W <y>.W <type>.W <map name>.16B
+void clif_changemapcell(int fd, int16 m, int x, int y, int type, enum send_target target)
+{
+ unsigned char buf[32];
+
+ WBUFW(buf,0) = 0x192;
+ WBUFW(buf,2) = x;
+ WBUFW(buf,4) = y;
+ WBUFW(buf,6) = type;
+ mapindex_getmapname_ext(map[m].name,(char*)WBUFP(buf,8));
+
+ if( fd )
+ {
+ WFIFOHEAD(fd,packet_len(0x192));
+ memcpy(WFIFOP(fd,0), buf, packet_len(0x192));
+ WFIFOSET(fd,packet_len(0x192));
+ }
+ else
+ {
+ struct block_list dummy_bl;
+ dummy_bl.type = BL_NUL;
+ dummy_bl.x = x;
+ dummy_bl.y = y;
+ dummy_bl.m = m;
+ clif_send(buf,packet_len(0x192),&dummy_bl,target);
+ }
+}
+
+
+/// Notifies the client about an item on floor (ZC_ITEM_ENTRY).
+/// 009d <id>.L <name id>.W <identified>.B <x>.W <y>.W <amount>.W <subX>.B <subY>.B
+void clif_getareachar_item(struct map_session_data* sd,struct flooritem_data* fitem)
+{
+ int view,fd;
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x9d));
+ WFIFOW(fd,0)=0x9d;
+ WFIFOL(fd,2)=fitem->bl.id;
+ if((view = itemdb_viewid(fitem->item_data.nameid)) > 0)
+ WFIFOW(fd,6)=view;
+ else
+ WFIFOW(fd,6)=fitem->item_data.nameid;
+ WFIFOB(fd,8)=fitem->item_data.identify;
+ WFIFOW(fd,9)=fitem->bl.x;
+ WFIFOW(fd,11)=fitem->bl.y;
+ WFIFOW(fd,13)=fitem->item_data.amount;
+ WFIFOB(fd,15)=fitem->subx;
+ WFIFOB(fd,16)=fitem->suby;
+ WFIFOSET(fd,packet_len(0x9d));
+}
+
+
+/// Notifies the client of a skill unit.
+/// 011f <id>.L <creator id>.L <x>.W <y>.W <unit id>.B <visible>.B (ZC_SKILL_ENTRY)
+/// 01c9 <id>.L <creator id>.L <x>.W <y>.W <unit id>.B <visible>.B <has msg>.B <msg>.80B (ZC_SKILL_ENTRY2)
+static void clif_getareachar_skillunit(struct map_session_data *sd, struct skill_unit *unit)
+{
+ int fd = sd->fd;
+
+ if( unit->group->state.guildaura )
+ return;
+
+#if PACKETVER >= 3
+ if(unit->group->unit_id==UNT_GRAFFITI) { // Graffiti [Valaris]
+ WFIFOHEAD(fd,packet_len(0x1c9));
+ WFIFOW(fd, 0)=0x1c9;
+ WFIFOL(fd, 2)=unit->bl.id;
+ WFIFOL(fd, 6)=unit->group->src_id;
+ WFIFOW(fd,10)=unit->bl.x;
+ WFIFOW(fd,12)=unit->bl.y;
+ WFIFOB(fd,14)=unit->group->unit_id;
+ WFIFOB(fd,15)=1;
+ WFIFOB(fd,16)=1;
+ safestrncpy((char*)WFIFOP(fd,17),unit->group->valstr,MESSAGE_SIZE);
+ WFIFOSET(fd,packet_len(0x1c9));
+ return;
+ }
+#endif
+ WFIFOHEAD(fd,packet_len(0x11f));
+ WFIFOW(fd, 0)=0x11f;
+ WFIFOL(fd, 2)=unit->bl.id;
+ WFIFOL(fd, 6)=unit->group->src_id;
+ WFIFOW(fd,10)=unit->bl.x;
+ WFIFOW(fd,12)=unit->bl.y;
+ if (battle_config.traps_setting&1 && skill_get_inf2(unit->group->skill_id)&INF2_TRAP)
+ WFIFOB(fd,14)=UNT_DUMMYSKILL; //Use invisible unit id for traps.
+ else if (skill_get_unit_flag(unit->group->skill_id) & UF_RANGEDSINGLEUNIT && !(unit->val2 & UF_RANGEDSINGLEUNIT))
+ WFIFOB(fd,14)=UNT_DUMMYSKILL; //Use invisible unit id for traps.
+ else
+ WFIFOB(fd,14)=unit->group->unit_id;
+ WFIFOB(fd,15)=1; // ignored by client (always gets set to 1)
+ WFIFOSET(fd,packet_len(0x11f));
+
+ if(unit->group->skill_id == WZ_ICEWALL)
+ clif_changemapcell(fd,unit->bl.m,unit->bl.x,unit->bl.y,5,SELF);
+}
+
+
+/*==========================================
+ * Server tells client to remove unit of id 'unit->bl.id'
+ *------------------------------------------*/
+static void clif_clearchar_skillunit(struct skill_unit *unit, int fd)
+{
+ nullpo_retv(unit);
+
+ WFIFOHEAD(fd,packet_len(0x120));
+ WFIFOW(fd, 0)=0x120;
+ WFIFOL(fd, 2)=unit->bl.id;
+ WFIFOSET(fd,packet_len(0x120));
+
+ if(unit->group && unit->group->skill_id == WZ_ICEWALL)
+ clif_changemapcell(fd,unit->bl.m,unit->bl.x,unit->bl.y,unit->val2,SELF);
+}
+
+
+/// Removes a skill unit (ZC_SKILL_DISAPPEAR).
+/// 0120 <id>.L
+void clif_skill_delunit(struct skill_unit *unit)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(unit);
+
+ WBUFW(buf, 0)=0x120;
+ WBUFL(buf, 2)=unit->bl.id;
+ clif_send(buf,packet_len(0x120),&unit->bl,AREA);
+}
+
+
+/// Sent when an object gets ankle-snared (ZC_SKILL_UPDATE).
+/// 01ac <id>.L
+/// Only affects units with class [139,153] client-side.
+void clif_skillunit_update(struct block_list* bl)
+{
+ unsigned char buf[6];
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x1ac;
+ WBUFL(buf,2) = bl->id;
+
+ clif_send(buf,packet_len(0x1ac),bl,AREA);
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static int clif_getareachar(struct block_list* bl,va_list ap)
+{
+ struct map_session_data *sd;
+
+ nullpo_ret(bl);
+
+ sd=va_arg(ap,struct map_session_data*);
+
+ if (sd == NULL || !sd->fd)
+ return 0;
+
+ switch(bl->type){
+ case BL_ITEM:
+ clif_getareachar_item(sd,(struct flooritem_data*) bl);
+ break;
+ case BL_SKILL:
+ clif_getareachar_skillunit(sd,(TBL_SKILL*)bl);
+ break;
+ default:
+ if(&sd->bl == bl)
+ break;
+ clif_getareachar_unit(sd,bl);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * tbl has gone out of view-size of bl
+ *------------------------------------------*/
+int clif_outsight(struct block_list *bl,va_list ap)
+{
+ struct block_list *tbl;
+ struct view_data *vd;
+ TBL_PC *sd, *tsd;
+ tbl=va_arg(ap,struct block_list*);
+ if(bl == tbl) return 0;
+ sd = BL_CAST(BL_PC, bl);
+ tsd = BL_CAST(BL_PC, tbl);
+
+ if (tsd && tsd->fd)
+ { //tsd has lost sight of the bl object.
+ switch(bl->type){
+ case BL_PC:
+ if (sd->vd.class_ != INVISIBLE_CLASS)
+ clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
+ if(sd->chatID){
+ struct chat_data *cd;
+ cd=(struct chat_data*)map_id2bl(sd->chatID);
+ if(cd->usersd[0]==sd)
+ clif_dispchat(cd,tsd->fd);
+ }
+ if( sd->state.vending )
+ clif_closevendingboard(bl,tsd->fd);
+ if( sd->state.buyingstore )
+ clif_buyingstore_disappear_entry_single(tsd, sd);
+ break;
+ case BL_ITEM:
+ clif_clearflooritem((struct flooritem_data*)bl,tsd->fd);
+ break;
+ case BL_SKILL:
+ clif_clearchar_skillunit((struct skill_unit *)bl,tsd->fd);
+ break;
+ case BL_NPC:
+ if( !(((TBL_NPC*)bl)->sc.option&OPTION_INVISIBLE) )
+ clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
+ break;
+ default:
+ if ((vd=status_get_viewdata(bl)) && vd->class_ != INVISIBLE_CLASS)
+ clif_clearunit_single(bl->id,CLR_OUTSIGHT,tsd->fd);
+ break;
+ }
+ }
+ if (sd && sd->fd)
+ { //sd is watching tbl go out of view.
+ if (((vd=status_get_viewdata(tbl)) && vd->class_ != INVISIBLE_CLASS) &&
+ !(tbl->type == BL_NPC && (((TBL_NPC*)tbl)->sc.option&OPTION_INVISIBLE)))
+ clif_clearunit_single(tbl->id,CLR_OUTSIGHT,sd->fd);
+ }
+ return 0;
+}
+
+/*==========================================
+ * tbl has come into view of bl
+ *------------------------------------------*/
+int clif_insight(struct block_list *bl,va_list ap)
+{
+ struct block_list *tbl;
+ TBL_PC *sd, *tsd;
+ tbl=va_arg(ap,struct block_list*);
+
+ if (bl == tbl) return 0;
+
+ sd = BL_CAST(BL_PC, bl);
+ tsd = BL_CAST(BL_PC, tbl);
+
+ if (tsd && tsd->fd)
+ { //Tell tsd that bl entered into his view
+ switch(bl->type){
+ case BL_ITEM:
+ clif_getareachar_item(tsd,(struct flooritem_data*)bl);
+ break;
+ case BL_SKILL:
+ clif_getareachar_skillunit(tsd,(TBL_SKILL*)bl);
+ break;
+ default:
+ clif_getareachar_unit(tsd,bl);
+ break;
+ }
+ }
+ if (sd && sd->fd)
+ { //Tell sd that tbl walked into his view
+ clif_getareachar_unit(sd,tbl);
+ }
+ return 0;
+}
+
+
+/// Updates whole skill tree (ZC_SKILLINFO_LIST).
+/// 010f <packet len>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <skill name>.24B <upgradable>.B }*
+void clif_skillinfoblock(struct map_session_data *sd)
+{
+ int fd;
+ int i,len,id;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ if (!fd) return;
+
+ WFIFOHEAD(fd, MAX_SKILL * 37 + 4);
+ WFIFOW(fd,0) = 0x10f;
+ for ( i = 0, len = 4; i < MAX_SKILL; i++)
+ {
+ if( (id = sd->status.skill[i].id) != 0 )
+ {
+ // workaround for bugreport:5348
+ if (len + 37 > 8192)
+ break;
+
+ WFIFOW(fd,len) = id;
+ WFIFOL(fd,len+2) = skill_get_inf(id);
+ WFIFOW(fd,len+6) = sd->status.skill[i].lv;
+ WFIFOW(fd,len+8) = skill_get_sp(id,sd->status.skill[i].lv);
+ WFIFOW(fd,len+10)= skill_get_range2(&sd->bl, id,sd->status.skill[i].lv);
+ safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
+ if(sd->status.skill[i].flag == SKILL_FLAG_PERMANENT)
+ WFIFOB(fd,len+36) = (sd->status.skill[i].lv < skill_tree_get_max(id, sd->status.class_))? 1:0;
+ else
+ WFIFOB(fd,len+36) = 0;
+ len += 37;
+ }
+ }
+ WFIFOW(fd,2)=len;
+ WFIFOSET(fd,len);
+
+ // workaround for bugreport:5348; send the remaining skills one by one to bypass packet size limit
+ for ( ; i < MAX_SKILL; i++)
+ {
+ if( (id = sd->status.skill[i].id) != 0 )
+ {
+ clif_addskill(sd, id);
+ clif_skillinfo(sd, id, 0);
+ }
+ }
+}
+/**
+ * Server tells client 'sd' to add skill of id 'id' to it's skill tree (e.g. with Ice Falcion item)
+ **/
+
+/// Adds new skill to the skill tree (ZC_ADD_SKILL).
+/// 0111 <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <skill name>.24B <upgradable>.B
+void clif_addskill(struct map_session_data *sd, int id)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ if (!fd) return;
+
+ if( sd->status.skill[id].id <= 0 )
+ return;
+
+ WFIFOHEAD(fd, packet_len(0x111));
+ WFIFOW(fd,0) = 0x111;
+ WFIFOW(fd,2) = id;
+ WFIFOL(fd,4) = skill_get_inf(id);
+ WFIFOW(fd,8) = sd->status.skill[id].lv;
+ WFIFOW(fd,10) = skill_get_sp(id,sd->status.skill[id].lv);
+ WFIFOW(fd,12)= skill_get_range2(&sd->bl, id,sd->status.skill[id].lv);
+ safestrncpy((char*)WFIFOP(fd,14), skill_get_name(id), NAME_LENGTH);
+ if( sd->status.skill[id].flag == SKILL_FLAG_PERMANENT )
+ WFIFOB(fd,38) = (sd->status.skill[id].lv < skill_tree_get_max(id, sd->status.class_))? 1:0;
+ else
+ WFIFOB(fd,38) = 0;
+ WFIFOSET(fd,packet_len(0x111));
+}
+
+
+/// Deletes a skill from the skill tree (ZC_SKILLINFO_DELETE).
+/// 0441 <skill id>.W
+void clif_deleteskill(struct map_session_data *sd, int id)
+{
+#if PACKETVER >= 20081217
+ int fd;
+
+ nullpo_retv(sd);
+ fd = sd->fd;
+ if( !fd ) return;
+
+ WFIFOHEAD(fd,packet_len(0x441));
+ WFIFOW(fd,0) = 0x441;
+ WFIFOW(fd,2) = id;
+ WFIFOSET(fd,packet_len(0x441));
+#endif
+ clif_skillinfoblock(sd);
+}
+
+
+/// Updates a skill in the skill tree (ZC_SKILLINFO_UPDATE).
+/// 010e <skill id>.W <level>.W <sp cost>.W <attack range>.W <upgradable>.B
+void clif_skillup(struct map_session_data *sd,uint16 skill_id)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x10e));
+ WFIFOW(fd,0) = 0x10e;
+ WFIFOW(fd,2) = skill_id;
+ WFIFOW(fd,4) = sd->status.skill[skill_id].lv;
+ WFIFOW(fd,6) = skill_get_sp(skill_id,sd->status.skill[skill_id].lv);
+ WFIFOW(fd,8) = skill_get_range2(&sd->bl,skill_id,sd->status.skill[skill_id].lv);
+ WFIFOB(fd,10) = (sd->status.skill[skill_id].lv < skill_tree_get_max(sd->status.skill[skill_id].id, sd->status.class_)) ? 1 : 0;
+ WFIFOSET(fd,packet_len(0x10e));
+}
+
+
+/// Updates a skill in the skill tree (ZC_SKILLINFO_UPDATE2).
+/// 07e1 <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <upgradable>.B
+void clif_skillinfo(struct map_session_data *sd,int skill, int inf)
+{
+ const int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x7e1));
+ WFIFOW(fd,0) = 0x7e1;
+ WFIFOW(fd,2) = skill;
+ WFIFOL(fd,4) = inf?inf:skill_get_inf(skill);
+ WFIFOW(fd,8) = sd->status.skill[skill].lv;
+ WFIFOW(fd,10) = skill_get_sp(skill,sd->status.skill[skill].lv);
+ WFIFOW(fd,12) = skill_get_range2(&sd->bl,skill,sd->status.skill[skill].lv);
+ if( sd->status.skill[skill].flag == SKILL_FLAG_PERMANENT )
+ WFIFOB(fd,14) = (sd->status.skill[skill].lv < skill_tree_get_max(skill, sd->status.class_))? 1:0;
+ else
+ WFIFOB(fd,14) = 0;
+ WFIFOSET(fd,packet_len(0x7e1));
+}
+
+
+/// Notifies clients in area, that an object is about to use a skill.
+/// 013e <src id>.L <dst id>.L <x>.W <y>.W <skill id>.W <property>.L <delaytime>.L (ZC_USESKILL_ACK)
+/// 07fb <src id>.L <dst id>.L <x>.W <y>.W <skill id>.W <property>.L <delaytime>.L <is disposable>.B (ZC_USESKILL_ACK2)
+/// property:
+/// 0 = Yellow cast aura
+/// 1 = Water elemental cast aura
+/// 2 = Earth elemental cast aura
+/// 3 = Fire elemental cast aura
+/// 4 = Wind elemental cast aura
+/// 5 = Poison elemental cast aura
+/// 6 = Holy elemental cast aura
+/// ? = like 0
+/// is disposable:
+/// 0 = yellow chat text "[src name] will use skill [skill name]."
+/// 1 = no text
+void clif_skillcasting(struct block_list* bl, int src_id, int dst_id, int dst_x, int dst_y, uint16 skill_id, int property, int casttime)
+{
+#if PACKETVER < 20091124
+ const int cmd = 0x13e;
+#else
+ const int cmd = 0x7fb;
+#endif
+ unsigned char buf[32];
+
+ WBUFW(buf,0) = cmd;
+ WBUFL(buf,2) = src_id;
+ WBUFL(buf,6) = dst_id;
+ WBUFW(buf,10) = dst_x;
+ WBUFW(buf,12) = dst_y;
+ WBUFW(buf,14) = skill_id;
+ WBUFL(buf,16) = property<0?0:property; //Avoid sending negatives as element [Skotlex]
+ WBUFL(buf,20) = casttime;
+#if PACKETVER >= 20091124
+ WBUFB(buf,24) = 0; // isDisposable
+#endif
+
+ if (disguised(bl)) {
+ clif_send(buf,packet_len(cmd), bl, AREA_WOS);
+ WBUFL(buf,2) = -src_id;
+ clif_send(buf,packet_len(cmd), bl, SELF);
+ } else
+ clif_send(buf,packet_len(cmd), bl, AREA);
+}
+
+
+/// Notifies clients in area, that an object canceled casting (ZC_DISPEL).
+/// 01b9 <id>.L
+void clif_skillcastcancel(struct block_list* bl)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x1b9;
+ WBUFL(buf,2) = bl->id;
+ clif_send(buf,packet_len(0x1b9), bl, AREA);
+}
+
+
+/// Notifies the client about the result of a skill use request (ZC_ACK_TOUSESKILL).
+/// 0110 <skill id>.W <num>.L <result>.B <cause>.B
+/// num (only used when skill id = NV_BASIC and cause = 0):
+/// 0 = "skill failed" MsgStringTable[159]
+/// 1 = "no emotions" MsgStringTable[160]
+/// 2 = "no sit" MsgStringTable[161]
+/// 3 = "no chat" MsgStringTable[162]
+/// 4 = "no party" MsgStringTable[163]
+/// 5 = "no shout" MsgStringTable[164]
+/// 6 = "no PKing" MsgStringTable[165]
+/// 7 = "no alligning" MsgStringTable[383]
+/// ? = ignored
+/// cause:
+/// 0 = "not enough skill level" MsgStringTable[214] (AL_WARP)
+/// "steal failed" MsgStringTable[205] (TF_STEAL)
+/// "envenom failed" MsgStringTable[207] (TF_POISON)
+/// "skill failed" MsgStringTable[204] (otherwise)
+/// ... = @see enum useskill_fail_cause
+/// ? = ignored
+///
+/// if(result!=0) doesn't display any of the previous messages
+/// Note: when this packet is received an unknown flag is always set to 0,
+/// suggesting this is an ACK packet for the UseSkill packets and should be sent on success too [FlavioJS]
+void clif_skill_fail(struct map_session_data *sd,uint16 skill_id,enum useskill_fail_cause cause,int btype)
+{
+ int fd;
+
+ if (!sd) { //Since this is the most common nullpo....
+ ShowDebug("clif_skill_fail: Error, received NULL sd for skill %d\n", skill_id);
+ return;
+ }
+
+ fd=sd->fd;
+ if (!fd) return;
+
+ if(battle_config.display_skill_fail&1)
+ return; //Disable all skill failed messages
+
+ if(cause==USESKILL_FAIL_SKILLINTERVAL && !sd->state.showdelay)
+ return; //Disable delay failed messages
+
+ if(skill_id == RG_SNATCHER && battle_config.display_skill_fail&4)
+ return;
+
+ if(skill_id == TF_POISON && battle_config.display_skill_fail&8)
+ return;
+
+ WFIFOHEAD(fd,packet_len(0x110));
+ WFIFOW(fd,0) = 0x110;
+ WFIFOW(fd,2) = skill_id;
+ WFIFOL(fd,4) = btype;
+ WFIFOB(fd,8) = 0;// success
+ WFIFOB(fd,9) = cause;
+ WFIFOSET(fd,packet_len(0x110));
+}
+
+
+/// Skill cooldown display icon (ZC_SKILL_POSTDELAY).
+/// 043d <skill ID>.W <tick>.L
+void clif_skill_cooldown(struct map_session_data *sd, uint16 skill_id, unsigned int tick)
+{
+#if PACKETVER>=20081112
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x43d));
+ WFIFOW(fd,0) = 0x43d;
+ WFIFOW(fd,2) = skill_id;
+ WFIFOL(fd,4) = tick;
+ WFIFOSET(fd,packet_len(0x43d));
+#endif
+}
+
+
+/// Skill attack effect and damage.
+/// 0114 <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <damage>.W <level>.W <div>.W <type>.B (ZC_NOTIFY_SKILL)
+/// 01de <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <damage>.L <level>.W <div>.W <type>.B (ZC_NOTIFY_SKILL2)
+int clif_skill_damage(struct block_list *src,struct block_list *dst,unsigned int tick,int sdelay,int ddelay,int damage,int div,uint16 skill_id,uint16 skill_lv,int type)
+{
+ unsigned char buf[64];
+ struct status_change *sc;
+
+ nullpo_ret(src);
+ nullpo_ret(dst);
+
+ type = clif_calc_delay(type,div,damage,ddelay);
+ sc = status_get_sc(dst);
+ if(sc && sc->count) {
+ if(sc->data[SC_HALLUCINATION] && damage)
+ damage = damage*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100;
+ }
+
+#if PACKETVER < 3
+ WBUFW(buf,0)=0x114;
+ WBUFW(buf,2)=skill_id;
+ WBUFL(buf,4)=src->id;
+ WBUFL(buf,8)=dst->id;
+ WBUFL(buf,12)=tick;
+ WBUFL(buf,16)=sdelay;
+ WBUFL(buf,20)=ddelay;
+ if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
+ WBUFW(buf,24)=damage?div:0;
+ } else {
+ WBUFW(buf,24)=damage;
+ }
+ WBUFW(buf,26)=skill_lv;
+ WBUFW(buf,28)=div;
+ WBUFB(buf,30)=type;
+ if (disguised(dst)) {
+ clif_send(buf,packet_len(0x114),dst,AREA_WOS);
+ WBUFL(buf,8)=-dst->id;
+ clif_send(buf,packet_len(0x114),dst,SELF);
+ } else
+ clif_send(buf,packet_len(0x114),dst,AREA);
+
+ if(disguised(src)) {
+ WBUFL(buf,4)=-src->id;
+ if (disguised(dst))
+ WBUFL(buf,8)=dst->id;
+ if(damage > 0)
+ WBUFW(buf,24)=-1;
+ clif_send(buf,packet_len(0x114),src,SELF);
+ }
+#else
+ WBUFW(buf,0)=0x1de;
+ WBUFW(buf,2)=skill_id;
+ WBUFL(buf,4)=src->id;
+ WBUFL(buf,8)=dst->id;
+ WBUFL(buf,12)=tick;
+ WBUFL(buf,16)=sdelay;
+ WBUFL(buf,20)=ddelay;
+ if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
+ WBUFL(buf,24)=damage?div:0;
+ } else {
+ WBUFL(buf,24)=damage;
+ }
+ WBUFW(buf,28)=skill_lv;
+ WBUFW(buf,30)=div;
+ WBUFB(buf,32)=type;
+ if (disguised(dst)) {
+ clif_send(buf,packet_len(0x1de),dst,AREA_WOS);
+ WBUFL(buf,8)=-dst->id;
+ clif_send(buf,packet_len(0x1de),dst,SELF);
+ } else
+ clif_send(buf,packet_len(0x1de),dst,AREA);
+
+ if(disguised(src)) {
+ WBUFL(buf,4)=-src->id;
+ if (disguised(dst))
+ WBUFL(buf,8)=dst->id;
+ if(damage > 0)
+ WBUFL(buf,24)=-1;
+ clif_send(buf,packet_len(0x1de),src,SELF);
+ }
+#endif
+
+ //Because the damage delay must be synced with the client, here is where the can-walk tick must be updated. [Skotlex]
+ return clif_calc_walkdelay(dst,ddelay,type,damage,div);
+}
+
+
+/// Ground skill attack effect and damage (ZC_NOTIFY_SKILL_POSITION).
+/// 0115 <skill id>.W <src id>.L <dst id>.L <tick>.L <src delay>.L <dst delay>.L <x>.W <y>.W <damage>.W <level>.W <div>.W <type>.B
+/*
+int clif_skill_damage2(struct block_list *src,struct block_list *dst,unsigned int tick,int sdelay,int ddelay,int damage,int div,uint16 skill_id,uint16 skill_lv,int type)
+{
+ unsigned char buf[64];
+ struct status_change *sc;
+
+ nullpo_ret(src);
+ nullpo_ret(dst);
+
+ type = (type>0)?type:skill_get_hit(skill_id);
+ type = clif_calc_delay(type,div,damage,ddelay);
+ sc = status_get_sc(dst);
+
+ if(sc && sc->count) {
+ if(sc->data[SC_HALLUCINATION] && damage)
+ damage = damage*(sc->data[SC_HALLUCINATION]->val2) + rnd()%100;
+ }
+
+ WBUFW(buf,0)=0x115;
+ WBUFW(buf,2)=skill_id;
+ WBUFL(buf,4)=src->id;
+ WBUFL(buf,8)=dst->id;
+ WBUFL(buf,12)=tick;
+ WBUFL(buf,16)=sdelay;
+ WBUFL(buf,20)=ddelay;
+ WBUFW(buf,24)=dst->x;
+ WBUFW(buf,26)=dst->y;
+ if (battle_config.hide_woe_damage && map_flag_gvg(src->m)) {
+ WBUFW(buf,28)=damage?div:0;
+ } else {
+ WBUFW(buf,28)=damage;
+ }
+ WBUFW(buf,30)=skill_lv;
+ WBUFW(buf,32)=div;
+ WBUFB(buf,34)=type;
+ clif_send(buf,packet_len(0x115),src,AREA);
+ if(disguised(src)) {
+ WBUFL(buf,4)=-src->id;
+ if(damage > 0)
+ WBUFW(buf,28)=-1;
+ clif_send(buf,packet_len(0x115),src,SELF);
+ }
+ if (disguised(dst)) {
+ WBUFL(buf,8)=-dst->id;
+ if (disguised(src))
+ WBUFL(buf,4)=src->id;
+ else if(damage > 0)
+ WBUFW(buf,28)=-1;
+ clif_send(buf,packet_len(0x115),dst,SELF);
+ }
+
+ //Because the damage delay must be synced with the client, here is where the can-walk tick must be updated. [Skotlex]
+ return clif_calc_walkdelay(dst,ddelay,type,damage,div);
+}
+*/
+
+
+/// Non-damaging skill effect (ZC_USE_SKILL).
+/// 011a <skill id>.W <skill lv>.W <dst id>.L <src id>.L <result>.B
+int clif_skill_nodamage(struct block_list *src,struct block_list *dst,uint16 skill_id,int heal,int fail)
+{
+ unsigned char buf[32];
+
+ nullpo_ret(dst);
+
+ WBUFW(buf,0)=0x11a;
+ WBUFW(buf,2)=skill_id;
+ WBUFW(buf,4)=min(heal, INT16_MAX);
+ WBUFL(buf,6)=dst->id;
+ WBUFL(buf,10)=src?src->id:0;
+ WBUFB(buf,14)=fail;
+
+ if (disguised(dst)) {
+ clif_send(buf,packet_len(0x11a),dst,AREA_WOS);
+ WBUFL(buf,6)=-dst->id;
+ clif_send(buf,packet_len(0x11a),dst,SELF);
+ } else
+ clif_send(buf,packet_len(0x11a),dst,AREA);
+
+ if(src && disguised(src)) {
+ WBUFL(buf,10)=-src->id;
+ if (disguised(dst))
+ WBUFL(buf,6)=dst->id;
+ clif_send(buf,packet_len(0x11a),src,SELF);
+ }
+
+ return fail;
+}
+
+
+/// Non-damaging ground skill effect (ZC_NOTIFY_GROUNDSKILL).
+/// 0117 <skill id>.W <src id>.L <level>.W <x>.W <y>.W <tick>.L
+void clif_skill_poseffect(struct block_list *src,uint16 skill_id,int val,int x,int y,int tick)
+{
+ unsigned char buf[32];
+
+ nullpo_retv(src);
+
+ WBUFW(buf,0)=0x117;
+ WBUFW(buf,2)=skill_id;
+ WBUFL(buf,4)=src->id;
+ WBUFW(buf,8)=val;
+ WBUFW(buf,10)=x;
+ WBUFW(buf,12)=y;
+ WBUFL(buf,14)=tick;
+ if(disguised(src)) {
+ clif_send(buf,packet_len(0x117),src,AREA_WOS);
+ WBUFL(buf,4)=-src->id;
+ clif_send(buf,packet_len(0x117),src,SELF);
+ } else
+ clif_send(buf,packet_len(0x117),src,AREA);
+}
+
+
+/*==========================================
+ * Tells all client's nearby 'unit' sight range that it spawned
+ *------------------------------------------*/
+//FIXME: this is just an AREA version of clif_getareachar_skillunit()
+void clif_skill_setunit(struct skill_unit *unit)
+{
+ unsigned char buf[128];
+
+ nullpo_retv(unit);
+
+ if( unit->group->state.guildaura )
+ return;
+
+#if PACKETVER >= 3
+ if(unit->group->unit_id==UNT_GRAFFITI) { // Graffiti [Valaris]
+ WBUFW(buf, 0)=0x1c9;
+ WBUFL(buf, 2)=unit->bl.id;
+ WBUFL(buf, 6)=unit->group->src_id;
+ WBUFW(buf,10)=unit->bl.x;
+ WBUFW(buf,12)=unit->bl.y;
+ WBUFB(buf,14)=unit->group->unit_id;
+ WBUFB(buf,15)=1;
+ WBUFB(buf,16)=1;
+ safestrncpy((char*)WBUFP(buf,17),unit->group->valstr,MESSAGE_SIZE);
+ clif_send(buf,packet_len(0x1c9),&unit->bl,AREA);
+ return;
+ }
+#endif
+ WBUFW(buf, 0)=0x11f;
+ WBUFL(buf, 2)=unit->bl.id;
+ WBUFL(buf, 6)=unit->group->src_id;
+ WBUFW(buf,10)=unit->bl.x;
+ WBUFW(buf,12)=unit->bl.y;
+ if (unit->group->state.song_dance&0x1 && unit->val2&UF_ENSEMBLE)
+ WBUFB(buf,14)=unit->val2&UF_SONG?UNT_DISSONANCE:UNT_UGLYDANCE;
+ else if (skill_get_unit_flag(unit->group->skill_id) & UF_RANGEDSINGLEUNIT && !(unit->val2 & UF_RANGEDSINGLEUNIT))
+ WBUFB(buf, 14) = UNT_DUMMYSKILL; // Only display the unit at center.
+ else
+ WBUFB(buf,14)=unit->group->unit_id;
+ WBUFB(buf,15)=1; // ignored by client (always gets set to 1)
+ clif_send(buf,packet_len(0x11f),&unit->bl,AREA);
+}
+
+
+/// Presents a list of available warp destinations (ZC_WARPLIST).
+/// 011c <skill id>.W { <map name>.16B }*4
+void clif_skill_warppoint(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv, unsigned short map1, unsigned short map2, unsigned short map3, unsigned short map4)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x11c));
+ WFIFOW(fd,0) = 0x11c;
+ WFIFOW(fd,2) = skill_id;
+ memset(WFIFOP(fd,4), 0x00, 4*MAP_NAME_LENGTH_EXT);
+ if (map1 == (unsigned short)-1) strcpy((char*)WFIFOP(fd,4), "Random");
+ else // normal map name
+ if (map1 > 0) mapindex_getmapname_ext(mapindex_id2name(map1), (char*)WFIFOP(fd,4));
+ if (map2 > 0) mapindex_getmapname_ext(mapindex_id2name(map2), (char*)WFIFOP(fd,20));
+ if (map3 > 0) mapindex_getmapname_ext(mapindex_id2name(map3), (char*)WFIFOP(fd,36));
+ if (map4 > 0) mapindex_getmapname_ext(mapindex_id2name(map4), (char*)WFIFOP(fd,52));
+ WFIFOSET(fd,packet_len(0x11c));
+
+ sd->menuskill_id = skill_id;
+ if (skill_id == AL_WARP)
+ sd->menuskill_val = (sd->ud.skillx<<16)|sd->ud.skilly; //Store warp position here.
+ else
+ sd->menuskill_val = skill_lv;
+}
+
+
+/// Memo message (ZC_ACK_REMEMBER_WARPPOINT).
+/// 011e <type>.B
+/// type:
+/// 0 = "Saved location as a Memo Point for Warp skill." in color 0xFFFF00 (cyan)
+/// 1 = "Skill Level is not high enough." in color 0x0000FF (red)
+/// 2 = "You haven't learned Warp." in color 0x0000FF (red)
+///
+/// @param sd Who receives the message
+/// @param type What message
+void clif_skill_memomessage(struct map_session_data* sd, int type)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x11e));
+ WFIFOW(fd,0)=0x11e;
+ WFIFOB(fd,2)=type;
+ WFIFOSET(fd,packet_len(0x11e));
+}
+
+
+/// Teleport message (ZC_NOTIFY_MAPINFO).
+/// 0189 <type>.W
+/// type:
+/// 0 = "Unable to Teleport in this area" in color 0xFFFF00 (cyan)
+/// 1 = "Saved point cannot be memorized." in color 0x0000FF (red)
+///
+/// @param sd Who receives the message
+/// @param type What message
+void clif_skill_teleportmessage(struct map_session_data *sd, int type)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x189));
+ WFIFOW(fd,0)=0x189;
+ WFIFOW(fd,2)=type;
+ WFIFOSET(fd,packet_len(0x189));
+}
+
+
+/// Displays Sense (WZ_ESTIMATION) information window (ZC_MONSTER_INFO).
+/// 018c <class>.W <level>.W <size>.W <hp>.L <def>.W <race>.W <mdef>.W <element>.W
+/// <water%>.B <earth%>.B <fire%>.B <wind%>.B <poison%>.B <holy%>.B <shadow%>.B <ghost%>.B <undead%>.B
+void clif_skill_estimation(struct map_session_data *sd,struct block_list *dst)
+{
+ struct status_data *status;
+ unsigned char buf[64];
+ int i;//, fix;
+
+ nullpo_retv(sd);
+ nullpo_retv(dst);
+
+ if( dst->type != BL_MOB )
+ return;
+
+ status = status_get_status_data(dst);
+
+ WBUFW(buf, 0)=0x18c;
+ WBUFW(buf, 2)=status_get_class(dst);
+ WBUFW(buf, 4)=status_get_lv(dst);
+ WBUFW(buf, 6)=status->size;
+ WBUFL(buf, 8)=status->hp;
+ WBUFW(buf,12)= (battle_config.estimation_type&1?status->def:0)
+ +(battle_config.estimation_type&2?status->def2:0);
+ WBUFW(buf,14)=status->race;
+ WBUFW(buf,16)= (battle_config.estimation_type&1?status->mdef:0)
+ +(battle_config.estimation_type&2?status->mdef2:0);
+ WBUFW(buf,18)= status->def_ele;
+ for(i=0;i<9;i++)
+ WBUFB(buf,20+i)= (unsigned char)battle_attr_ratio(i+1,status->def_ele, status->ele_lv);
+// The following caps negative attributes to 0 since the client displays them as 255-fix. [Skotlex]
+// WBUFB(buf,20+i)= (unsigned char)((fix=battle_attr_ratio(i+1,status->def_ele, status->ele_lv))<0?0:fix);
+
+ clif_send(buf,packet_len(0x18c),&sd->bl,sd->status.party_id>0?PARTY_SAMEMAP:SELF);
+}
+
+
+/// Presents a textual list of producable items (ZC_MAKABLEITEMLIST).
+/// 018d <packet len>.W { <name id>.W { <material id>.W }*3 }*
+/// material id:
+/// unused by the client
+void clif_skill_produce_mix_list(struct map_session_data *sd, int skill_id , int trigger)
+{
+ int i,c,view,fd;
+ nullpo_retv(sd);
+
+ if(sd->menuskill_id == skill_id)
+ return; //Avoid resending the menu twice or more times...
+ if( skill_id == GC_CREATENEWPOISON )
+ skill_id = GC_RESEARCHNEWPOISON;
+
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_SKILL_PRODUCE_DB * 8 + 8);
+ WFIFOW(fd, 0)=0x18d;
+
+ for(i=0,c=0;i<MAX_SKILL_PRODUCE_DB;i++){
+ if( skill_can_produce_mix(sd,skill_produce_db[i].nameid, trigger, 1) &&
+ ( skill_id > 0 && skill_produce_db[i].req_skill == skill_id || skill_id < 0 )
+ ){
+ if((view = itemdb_viewid(skill_produce_db[i].nameid)) > 0)
+ WFIFOW(fd,c*8+ 4)= view;
+ else
+ WFIFOW(fd,c*8+ 4)= skill_produce_db[i].nameid;
+ WFIFOW(fd,c*8+ 6)= 0;
+ WFIFOW(fd,c*8+ 8)= 0;
+ WFIFOW(fd,c*8+10)= 0;
+ c++;
+ }
+ }
+ WFIFOW(fd, 2)=c*8+8;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ if(c > 0) {
+ sd->menuskill_id = skill_id;
+ sd->menuskill_val = trigger;
+ return;
+ }
+}
+
+
+/// Present a list of producable items (ZC_MAKINGITEM_LIST).
+/// 025a <packet len>.W <mk type>.W { <name id>.W }*
+/// mk type:
+/// 1 = cooking
+/// 2 = arrow
+/// 3 = elemental
+/// 4 = GN_MIX_COOKING
+/// 5 = GN_MAKEBOMB
+/// 6 = GN_S_PHARMACY
+void clif_cooking_list(struct map_session_data *sd, int trigger, uint16 skill_id, int qty, int list_type)
+{
+ int fd;
+ int i, c;
+ int view;
+
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, 6 + 2 * MAX_SKILL_PRODUCE_DB);
+ WFIFOW(fd,0) = 0x25a;
+ WFIFOW(fd,4) = list_type; // list type
+
+ c = 0;
+ for( i = 0; i < MAX_SKILL_PRODUCE_DB; i++ ) {
+ if( !skill_can_produce_mix(sd,skill_produce_db[i].nameid,trigger, qty) )
+ continue;
+
+ if( (view = itemdb_viewid(skill_produce_db[i].nameid)) > 0 )
+ WFIFOW(fd, 6 + 2 * c) = view;
+ else
+ WFIFOW(fd, 6 + 2 * c) = skill_produce_db[i].nameid;
+
+ c++;
+ }
+
+ if( skill_id == AM_PHARMACY ) { // Only send it while Cooking else check for c.
+ WFIFOW(fd,2) = 6 + 2 * c;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+
+ if( c > 0 ) {
+ sd->menuskill_id = skill_id;
+ sd->menuskill_val = trigger;
+ if( skill_id != AM_PHARMACY ) {
+ sd->menuskill_val2 = qty; // amount.
+ WFIFOW(fd,2) = 6 + 2 * c;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ } else {
+ clif_menuskill_clear(sd);
+ if( skill_id != AM_PHARMACY ) { // AM_PHARMACY is used to Cooking.
+ // It fails.
+#if PACKETVER >= 20090922
+ clif_msg_skill(sd,skill_id,0x625);
+#else
+ WFIFOW(fd,2) = 6 + 2 * c;
+ WFIFOSET(fd,WFIFOW(fd,2));
+#endif
+ }
+ }
+}
+
+
+/// Notifies clients of a status change.
+/// 0196 <index>.W <id>.L <state>.B (ZC_MSG_STATE_CHANGE) [used for ending status changes and starting them on non-pc units (when needed)]
+/// 043f <index>.W <id>.L <state>.B <remain msec>.L { <val>.L }*3 (ZC_MSG_STATE_CHANGE2) [used exclusively for starting statuses on pcs]
+void clif_status_change(struct block_list *bl,int type,int flag,int tick,int val1, int val2, int val3)
+{
+ unsigned char buf[32];
+ struct map_session_data *sd;
+
+ if (type == SI_BLANK) //It shows nothing on the client...
+ return;
+
+ nullpo_retv(bl);
+
+ sd = BL_CAST(BL_PC, bl);
+
+ if (!(status_type2relevant_bl_types(type)&bl->type)) // only send status changes that actually matter to the client
+ return;
+
+#if PACKETVER >= 20090121
+ if(flag && battle_config.display_status_timers && sd)
+ WBUFW(buf,0)=0x43f;
+ else
+#endif
+ WBUFW(buf,0)=0x196;
+ WBUFW(buf,2)=type;
+ WBUFL(buf,4)=bl->id;
+ WBUFB(buf,8)=flag;
+#if PACKETVER >= 20090121
+ if(flag && battle_config.display_status_timers && sd)
+ {
+ if (tick <= 0)
+ tick = 9999; // this is indeed what official servers do
+
+ WBUFL(buf,9) = tick;
+ WBUFL(buf,13) = val1;
+ WBUFL(buf,17) = val2;
+ WBUFL(buf,21) = val3;
+ }
+#endif
+ clif_send(buf,packet_len(WBUFW(buf,0)),bl, (sd && sd->status.option&OPTION_INVISIBLE) ? SELF : AREA);
+}
+
+/// Send message (modified by [Yor]) (ZC_NOTIFY_PLAYERCHAT).
+/// 008e <packet len>.W <message>.?B
+void clif_displaymessage(const int fd, const char* mes)
+{
+ nullpo_retv(mes);
+
+ //Scrapped, as these are shared by disconnected players =X [Skotlex]
+ if (fd == 0)
+ ;
+ else {
+ char *message, *line;
+
+ message = aStrdup(mes);
+ line = strtok(message, "\n");
+ while(line != NULL) {
+ // Limit message to 255+1 characters (otherwise it causes a buffer overflow in the client)
+ int len = strnlen(line, 255);
+
+ if (len > 0) { // don't send a void message (it's not displaying on the client chat). @help can send void line.
+ WFIFOHEAD(fd, 5 + len);
+ WFIFOW(fd,0) = 0x8e;
+ WFIFOW(fd,2) = 5 + len; // 4 + len + NULL teminate
+ safestrncpy((char *)WFIFOP(fd,4), line, len + 1);
+ WFIFOSET(fd, 5 + len);
+ }
+ line = strtok(NULL, "\n");
+ }
+ aFree(message);
+ }
+}
+
+/// Send broadcast message in yellow or blue without font formatting (ZC_BROADCAST).
+/// 009a <packet len>.W <message>.?B
+void clif_broadcast(struct block_list* bl, const char* mes, int len, int type, enum send_target target)
+{
+ int lp = type ? 4 : 0;
+ unsigned char *buf = (unsigned char*)aMalloc((4 + lp + len)*sizeof(unsigned char));
+
+ WBUFW(buf,0) = 0x9a;
+ WBUFW(buf,2) = 4 + lp + len;
+ if (type == 0x10) // bc_blue
+ WBUFL(buf,4) = 0x65756c62; //If there's "blue" at the beginning of the message, game client will display it in blue instead of yellow.
+ else if (type == 0x20) // bc_woe
+ WBUFL(buf,4) = 0x73737373; //If there's "ssss", game client will recognize message as 'WoE broadcast'.
+ memcpy(WBUFP(buf, 4 + lp), mes, len);
+ clif_send(buf, WBUFW(buf,2), bl, target);
+
+ if (buf)
+ aFree(buf);
+}
+
+/*==========================================
+ * Displays a message on a 'bl' to all it's nearby clients
+ * Used by npc_globalmessage
+ *------------------------------------------*/
+void clif_GlobalMessage(struct block_list* bl, const char* message) {
+ char buf[100];
+ int len;
+ nullpo_retv(bl);
+
+ if(!message)
+ return;
+
+ len = strlen(message)+1;
+
+ if( len > sizeof(buf)-8 ) {
+ ShowWarning("clif_GlobalMessage: Truncating too long message '%s' (len=%d).\n", message, len);
+ len = sizeof(buf)-8;
+ }
+
+ WBUFW(buf,0)=0x8d;
+ WBUFW(buf,2)=len+8;
+ WBUFL(buf,4)=bl->id;
+ safestrncpy((char *) WBUFP(buf,8),message,len);
+ clif_send((unsigned char *) buf,WBUFW(buf,2),bl,ALL_CLIENT);
+
+}
+
+/*==========================================
+ * Send main chat message [LuzZza]
+ *------------------------------------------*/
+void clif_MainChatMessage(const char* message) {
+ uint8 buf[200];
+ int len;
+
+ if(!message)
+ return;
+
+ len = strlen(message)+1;
+ if (len+8 > sizeof(buf)) {
+ ShowDebug("clif_MainChatMessage: Received message too long (len %d): %s\n", len, message);
+ len = sizeof(buf)-8;
+ }
+ WBUFW(buf,0)=0x8d;
+ WBUFW(buf,2)=len+8;
+ WBUFL(buf,4)=0;
+ safestrncpy((char *) WBUFP(buf,8),message,len);
+ clif_send(buf,WBUFW(buf,2),NULL,CHAT_MAINCHAT);
+}
+
+/// Send broadcast message with font formatting (ZC_BROADCAST2).
+/// 01c3 <packet len>.W <fontColor>.L <fontType>.W <fontSize>.W <fontAlign>.W <fontY>.W <message>.?B
+void clif_broadcast2(struct block_list* bl, const char* mes, int len, unsigned long fontColor, short fontType, short fontSize, short fontAlign, short fontY, enum send_target target)
+{
+ unsigned char *buf = (unsigned char*)aMalloc((16 + len)*sizeof(unsigned char));
+
+ WBUFW(buf,0) = 0x1c3;
+ WBUFW(buf,2) = len + 16;
+ WBUFL(buf,4) = fontColor;
+ WBUFW(buf,8) = fontType;
+ WBUFW(buf,10) = fontSize;
+ WBUFW(buf,12) = fontAlign;
+ WBUFW(buf,14) = fontY;
+ memcpy(WBUFP(buf,16), mes, len);
+ clif_send(buf, WBUFW(buf,2), bl, target);
+
+ if (buf)
+ aFree(buf);
+}
+
+
+/// Displays heal effect (ZC_RECOVERY).
+/// 013d <var id>.W <amount>.W
+/// var id:
+/// 5 = HP (SP_HP)
+/// 7 = SP (SP_SP)
+/// ? = ignored
+void clif_heal(int fd,int type,int val)
+{
+ WFIFOHEAD(fd,packet_len(0x13d));
+ WFIFOW(fd,0)=0x13d;
+ WFIFOW(fd,2)=type;
+ WFIFOW(fd,4)=cap_value(val,0,INT16_MAX);
+ WFIFOSET(fd,packet_len(0x13d));
+}
+
+
+/// Displays resurrection effect (ZC_RESURRECTION).
+/// 0148 <id>.L <type>.W
+/// type:
+/// ignored
+void clif_resurrection(struct block_list *bl,int type)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0)=0x148;
+ WBUFL(buf,2)=bl->id;
+ WBUFW(buf,6)=0;
+
+ clif_send(buf,packet_len(0x148),bl,type==1 ? AREA : AREA_WOS);
+ if (disguised(bl))
+ clif_spawn(bl);
+}
+
+
+/// Sets the map property (ZC_NOTIFY_MAPPROPERTY).
+/// 0199 <type>.W
+void clif_map_property(struct map_session_data* sd, enum map_property property)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x199));
+ WFIFOW(fd,0)=0x199;
+ WFIFOW(fd,2)=property;
+ WFIFOSET(fd,packet_len(0x199));
+}
+
+
+/// Set the map type (ZC_NOTIFY_MAPPROPERTY2).
+/// 01d6 <type>.W
+void clif_map_type(struct map_session_data* sd, enum map_type type)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x1D6));
+ WFIFOW(fd,0)=0x1D6;
+ WFIFOW(fd,2)=type;
+ WFIFOSET(fd,packet_len(0x1D6));
+}
+
+
+/// Updates PvP ranking (ZC_NOTIFY_RANKING).
+/// 019a <id>.L <ranking>.L <total>.L
+void clif_pvpset(struct map_session_data *sd,int pvprank,int pvpnum,int type)
+{
+ if(type == 2) {
+ int fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x19a));
+ WFIFOW(fd,0) = 0x19a;
+ WFIFOL(fd,2) = sd->bl.id;
+ WFIFOL(fd,6) = pvprank;
+ WFIFOL(fd,10) = pvpnum;
+ WFIFOSET(fd,packet_len(0x19a));
+ } else {
+ unsigned char buf[32];
+ WBUFW(buf,0) = 0x19a;
+ WBUFL(buf,2) = sd->bl.id;
+ if(sd->sc.option&(OPTION_HIDE|OPTION_CLOAK))
+ WBUFL(buf,6) = UINT32_MAX; //On client displays as --
+ else
+ WBUFL(buf,6) = pvprank;
+ WBUFL(buf,10) = pvpnum;
+ if(sd->sc.option&OPTION_INVISIBLE || sd->disguise) //Causes crashes when a 'mob' with pvp info dies.
+ clif_send(buf,packet_len(0x19a),&sd->bl,SELF);
+ else if(!type)
+ clif_send(buf,packet_len(0x19a),&sd->bl,AREA);
+ else
+ clif_send(buf,packet_len(0x19a),&sd->bl,ALL_SAMEMAP);
+ }
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------*/
+void clif_map_property_mapall(int map, enum map_property property)
+{
+ struct block_list bl;
+ unsigned char buf[16];
+
+ bl.id = 0;
+ bl.type = BL_NUL;
+ bl.m = map;
+ WBUFW(buf,0)=0x199;
+ WBUFW(buf,2)=property;
+ clif_send(buf,packet_len(0x199),&bl,ALL_SAMEMAP);
+}
+
+
+/// Notifies the client about the result of a refine attempt (ZC_ACK_ITEMREFINING).
+/// 0188 <result>.W <index>.W <refine>.W
+/// result:
+/// 0 = success
+/// 1 = failure
+/// 2 = downgrade
+void clif_refine(int fd, int fail, int index, int val)
+{
+ WFIFOHEAD(fd,packet_len(0x188));
+ WFIFOW(fd,0)=0x188;
+ WFIFOW(fd,2)=fail;
+ WFIFOW(fd,4)=index+2;
+ WFIFOW(fd,6)=val;
+ WFIFOSET(fd,packet_len(0x188));
+}
+
+
+/// Notifies the client about the result of a weapon refine attempt (ZC_ACK_WEAPONREFINE).
+/// 0223 <result>.L <nameid>.W
+/// result:
+/// 0 = "weapon upgraded: %s" MsgStringTable[911] in rgb(0,255,255)
+/// 1 = "weapon upgraded: %s" MsgStringTable[912] in rgb(0,205,205)
+/// 2 = "cannot upgrade %s until you level up the upgrade weapon skill" MsgStringTable[913] in rgb(255,200,200)
+/// 3 = "you lack the item %s to upgrade the weapon" MsgStringTable[914] in rgb(255,200,200)
+void clif_upgrademessage(int fd, int result, int item_id)
+{
+ WFIFOHEAD(fd,packet_len(0x223));
+ WFIFOW(fd,0)=0x223;
+ WFIFOL(fd,2)=result;
+ WFIFOW(fd,6)=item_id;
+ WFIFOSET(fd,packet_len(0x223));
+}
+
+
+/// Whisper is transmitted to the destination player (ZC_WHISPER).
+/// 0097 <packet len>.W <nick>.24B <message>.?B
+/// 0097 <packet len>.W <nick>.24B <isAdmin>.L <message>.?B (PACKETVER >= 20091104)
+void clif_wis_message(int fd, const char* nick, const char* mes, int mes_len)
+{
+#if PACKETVER < 20091104
+ WFIFOHEAD(fd, mes_len + NAME_LENGTH + 4);
+ WFIFOW(fd,0) = 0x97;
+ WFIFOW(fd,2) = mes_len + NAME_LENGTH + 4;
+ safestrncpy((char*)WFIFOP(fd,4), nick, NAME_LENGTH);
+ safestrncpy((char*)WFIFOP(fd,28), mes, mes_len);
+ WFIFOSET(fd,WFIFOW(fd,2));
+#else
+ WFIFOHEAD(fd, mes_len + NAME_LENGTH + 8);
+ WFIFOW(fd,0) = 0x97;
+ WFIFOW(fd,2) = mes_len + NAME_LENGTH + 8;
+ safestrncpy((char*)WFIFOP(fd,4), nick, NAME_LENGTH);
+ WFIFOL(fd,28) = 0; // isAdmin; if nonzero, also displays text above char
+ // TODO: WFIFOL(fd,28) = pc_get_group_level(ssd);
+ safestrncpy((char*)WFIFOP(fd,32), mes, mes_len);
+ WFIFOSET(fd,WFIFOW(fd,2));
+#endif
+}
+
+
+/// Inform the player about the result of his whisper action (ZC_ACK_WHISPER).
+/// 0098 <result>.B
+/// result:
+/// 0 = success to send wisper
+/// 1 = target character is not loged in
+/// 2 = ignored by target
+/// 3 = everyone ignored by target
+void clif_wis_end(int fd, int flag)
+{
+ WFIFOHEAD(fd,packet_len(0x98));
+ WFIFOW(fd,0) = 0x98;
+ WFIFOW(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0x98));
+}
+
+
+/// Returns character name requested by char_id (ZC_ACK_REQNAME_BYGID).
+/// 0194 <char id>.L <name>.24B
+void clif_solved_charname(int fd, int charid, const char* name)
+{
+ WFIFOHEAD(fd,packet_len(0x194));
+ WFIFOW(fd,0)=0x194;
+ WFIFOL(fd,2)=charid;
+ safestrncpy((char*)WFIFOP(fd,6), name, NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x194));
+}
+
+
+/// Presents a list of items that can be carded/composed (ZC_ITEMCOMPOSITION_LIST).
+/// 017b <packet len>.W { <name id>.W }*
+void clif_use_card(struct map_session_data *sd,int idx)
+{
+ int i,c,ep;
+ int fd=sd->fd;
+
+ nullpo_retv(sd);
+ if (idx < 0 || idx >= MAX_INVENTORY) //Crash-fix from bad packets.
+ return;
+
+ if (!sd->inventory_data[idx] || sd->inventory_data[idx]->type != IT_CARD)
+ return; //Avoid parsing invalid item indexes (no card/no item)
+
+ ep=sd->inventory_data[idx]->equip;
+ WFIFOHEAD(fd,MAX_INVENTORY * 2 + 4);
+ WFIFOW(fd,0)=0x17b;
+
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ int j;
+
+ if(sd->inventory_data[i] == NULL)
+ continue;
+ if(sd->inventory_data[i]->type!=IT_WEAPON && sd->inventory_data[i]->type!=IT_ARMOR)
+ continue;
+ if(itemdb_isspecial(sd->status.inventory[i].card[0])) //Can't slot it
+ continue;
+
+ if(sd->status.inventory[i].identify==0 ) //Not identified
+ continue;
+
+ if((sd->inventory_data[i]->equip&ep)==0) //Not equippable on this part.
+ continue;
+
+ if(sd->inventory_data[i]->type==IT_WEAPON && ep==EQP_SHIELD) //Shield card won't go on left weapon.
+ continue;
+
+ ARR_FIND( 0, sd->inventory_data[i]->slot, j, sd->status.inventory[i].card[j] == 0 );
+ if( j == sd->inventory_data[i]->slot ) // No room
+ continue;
+
+ WFIFOW(fd,4+c*2)=i+2;
+ c++;
+ }
+ WFIFOW(fd,2)=4+c*2;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Notifies the client about the result of item carding/composition (ZC_ACK_ITEMCOMPOSITION).
+/// 017d <equip index>.W <card index>.W <result>.B
+/// result:
+/// 0 = success
+/// 1 = failure
+void clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x17d));
+ WFIFOW(fd,0)=0x17d;
+ WFIFOW(fd,2)=idx_equip+2;
+ WFIFOW(fd,4)=idx_card+2;
+ WFIFOB(fd,6)=flag;
+ WFIFOSET(fd,packet_len(0x17d));
+}
+
+
+/// Presents a list of items that can be identified (ZC_ITEMIDENTIFY_LIST).
+/// 0177 <packet len>.W { <name id>.W }*
+void clif_item_identify_list(struct map_session_data *sd)
+{
+ int i,c;
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,MAX_INVENTORY * 2 + 4);
+ WFIFOW(fd,0)=0x177;
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && !sd->status.inventory[i].identify){
+ WFIFOW(fd,c*2+4)=i+2;
+ c++;
+ }
+ }
+ if(c > 0) {
+ WFIFOW(fd,2)=c*2+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ sd->menuskill_id = MC_IDENTIFY;
+ sd->menuskill_val = c;
+ }
+}
+
+
+/// Notifies the client about the result of a item identify request (ZC_ACK_ITEMIDENTIFY).
+/// 0179 <index>.W <result>.B
+void clif_item_identified(struct map_session_data *sd,int idx,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x179));
+ WFIFOW(fd, 0)=0x179;
+ WFIFOW(fd, 2)=idx+2;
+ WFIFOB(fd, 4)=flag;
+ WFIFOSET(fd,packet_len(0x179));
+}
+
+
+/// Presents a list of items that can be repaired (ZC_REPAIRITEMLIST).
+/// 01fc <packet len>.W { <index>.W <name id>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }*
+void clif_item_repair_list(struct map_session_data *sd,struct map_session_data *dstsd, int lv)
+{
+ int i,c;
+ int fd;
+ int nameid;
+
+ nullpo_retv(sd);
+ nullpo_retv(dstsd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd, MAX_INVENTORY * 13 + 4);
+ WFIFOW(fd,0)=0x1fc;
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ if((nameid=dstsd->status.inventory[i].nameid) > 0 && dstsd->status.inventory[i].attribute!=0){// && skill_can_repair(sd,nameid)){
+ WFIFOW(fd,c*13+4) = i;
+ WFIFOW(fd,c*13+6) = nameid;
+ WFIFOB(fd,c*13+8) = dstsd->status.inventory[i].refine;
+ clif_addcards(WFIFOP(fd,c*13+9), &dstsd->status.inventory[i]);
+ c++;
+ }
+ }
+ if(c > 0) {
+ WFIFOW(fd,2)=c*13+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ sd->menuskill_id = BS_REPAIRWEAPON;
+ sd->menuskill_val = dstsd->bl.id;
+ sd->menuskill_val2 = lv;
+ }else
+ clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0);
+}
+
+
+/// Notifies the client about the result of a item repair request (ZC_ACK_ITEMREPAIR).
+/// 01fe <index>.W <result>.B
+/// index:
+/// ignored (inventory index)
+/// result:
+/// 0 = Item repair success.
+/// 1 = Item repair failure.
+void clif_item_repaireffect(struct map_session_data *sd,int idx,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x1fe));
+ WFIFOW(fd, 0)=0x1fe;
+ WFIFOW(fd, 2)=idx+2;
+ WFIFOB(fd, 4)=flag;
+ WFIFOSET(fd,packet_len(0x1fe));
+
+}
+
+
+/// Displays a message, that an equipment got damaged (ZC_EQUIPITEM_DAMAGED).
+/// 02bb <equip location>.W <account id>.L
+void clif_item_damaged(struct map_session_data* sd, unsigned short position)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x2bb));
+ WFIFOW(fd,0) = 0x2bb;
+ WFIFOW(fd,2) = position;
+ WFIFOL(fd,4) = sd->bl.id; // TODO: the packet seems to be sent to other people as well, probably party and/or guild.
+ WFIFOSET(fd,packet_len(0x2bb));
+}
+
+
+/// Presents a list of weapon items that can be refined [Taken from jAthena] (ZC_NOTIFY_WEAPONITEMLIST).
+/// 0221 <packet len>.W { <index>.W <name id>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }*
+void clif_item_refine_list(struct map_session_data *sd)
+{
+ int i,c;
+ int fd;
+ uint16 skill_lv;
+ int wlv;
+ int refine_item[5];
+
+ nullpo_retv(sd);
+
+ skill_lv = pc_checkskill(sd,WS_WEAPONREFINE);
+
+ fd=sd->fd;
+
+ refine_item[0] = -1;
+ refine_item[1] = pc_search_inventory(sd,1010);
+ refine_item[2] = pc_search_inventory(sd,1011);
+ refine_item[3] = refine_item[4] = pc_search_inventory(sd,984);
+
+ WFIFOHEAD(fd, MAX_INVENTORY * 13 + 4);
+ WFIFOW(fd,0)=0x221;
+ for(i=c=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].refine < skill_lv &&
+ sd->status.inventory[i].identify && (wlv=itemdb_wlv(sd->status.inventory[i].nameid)) >=1 &&
+ refine_item[wlv]!=-1 && !(sd->status.inventory[i].equip&EQP_ARMS)){
+ WFIFOW(fd,c*13+ 4)=i+2;
+ WFIFOW(fd,c*13+ 6)=sd->status.inventory[i].nameid;
+ WFIFOB(fd,c*13+ 8)=sd->status.inventory[i].refine;
+ clif_addcards(WFIFOP(fd,c*13+9), &sd->status.inventory[i]);
+ c++;
+ }
+ }
+ WFIFOW(fd,2)=c*13+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ if (c > 0) {
+ sd->menuskill_id = WS_WEAPONREFINE;
+ sd->menuskill_val = skill_lv;
+ }
+}
+
+
+/// Notification of an auto-casted skill (ZC_AUTORUN_SKILL).
+/// 0147 <skill id>.W <type>.L <level>.W <sp cost>.W <atk range>.W <skill name>.24B <upgradable>.B
+void clif_item_skill(struct map_session_data *sd,uint16 skill_id,uint16 skill_lv)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x147));
+ WFIFOW(fd, 0)=0x147;
+ WFIFOW(fd, 2)=skill_id;
+ WFIFOW(fd, 4)=skill_get_inf(skill_id);
+ WFIFOW(fd, 6)=0;
+ WFIFOW(fd, 8)=skill_lv;
+ WFIFOW(fd,10)=skill_get_sp(skill_id,skill_lv);
+ WFIFOW(fd,12)=skill_get_range2(&sd->bl, skill_id,skill_lv);
+ safestrncpy((char*)WFIFOP(fd,14),skill_get_name(skill_id),NAME_LENGTH);
+ WFIFOB(fd,38)=0;
+ WFIFOSET(fd,packet_len(0x147));
+}
+
+
+/// Adds an item to character's cart.
+/// 0124 <index>.W <amount>.L <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_CART)
+/// 01c5 <index>.W <amount>.L <name id>.W <type>.B <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W (ZC_ADD_ITEM_TO_CART2)
+void clif_cart_additem(struct map_session_data *sd,int n,int amount,int fail)
+{
+ int view,fd;
+ unsigned char *buf;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ if(n<0 || n>=MAX_CART || sd->status.cart[n].nameid<=0)
+ return;
+
+#if PACKETVER < 5
+ WFIFOHEAD(fd,packet_len(0x124));
+ buf=WFIFOP(fd,0);
+ WBUFW(buf,0)=0x124;
+ WBUFW(buf,2)=n+2;
+ WBUFL(buf,4)=amount;
+ if((view = itemdb_viewid(sd->status.cart[n].nameid)) > 0)
+ WBUFW(buf,8)=view;
+ else
+ WBUFW(buf,8)=sd->status.cart[n].nameid;
+ WBUFB(buf,10)=sd->status.cart[n].identify;
+ WBUFB(buf,11)=sd->status.cart[n].attribute;
+ WBUFB(buf,12)=sd->status.cart[n].refine;
+ clif_addcards(WBUFP(buf,13), &sd->status.cart[n]);
+ WFIFOSET(fd,packet_len(0x124));
+#else
+ WFIFOHEAD(fd,packet_len(0x1c5));
+ buf=WFIFOP(fd,0);
+ WBUFW(buf,0)=0x1c5;
+ WBUFW(buf,2)=n+2;
+ WBUFL(buf,4)=amount;
+ if((view = itemdb_viewid(sd->status.cart[n].nameid)) > 0)
+ WBUFW(buf,8)=view;
+ else
+ WBUFW(buf,8)=sd->status.cart[n].nameid;
+ WBUFB(buf,10)=itemdb_type(sd->status.cart[n].nameid);
+ WBUFB(buf,11)=sd->status.cart[n].identify;
+ WBUFB(buf,12)=sd->status.cart[n].attribute;
+ WBUFB(buf,13)=sd->status.cart[n].refine;
+ clif_addcards(WBUFP(buf,14), &sd->status.cart[n]);
+ WFIFOSET(fd,packet_len(0x1c5));
+#endif
+}
+
+
+/// Deletes an item from character's cart (ZC_DELETE_ITEM_FROM_CART).
+/// 0125 <index>.W <amount>.L
+void clif_cart_delitem(struct map_session_data *sd,int n,int amount)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x125));
+ WFIFOW(fd,0)=0x125;
+ WFIFOW(fd,2)=n+2;
+ WFIFOL(fd,4)=amount;
+ WFIFOSET(fd,packet_len(0x125));
+}
+
+
+/// Opens the shop creation menu (ZC_OPENSTORE).
+/// 012d <num>.W
+/// num:
+/// number of allowed item slots
+void clif_openvendingreq(struct map_session_data* sd, int num)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x12d));
+ WFIFOW(fd,0) = 0x12d;
+ WFIFOW(fd,2) = num;
+ WFIFOSET(fd,packet_len(0x12d));
+}
+
+
+/// Displays a vending board to target/area (ZC_STORE_ENTRY).
+/// 0131 <owner id>.L <message>.80B
+void clif_showvendingboard(struct block_list* bl, const char* message, int fd)
+{
+ unsigned char buf[128];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x131;
+ WBUFL(buf,2) = bl->id;
+ safestrncpy((char*)WBUFP(buf,6), message, 80);
+
+ if( fd ) {
+ WFIFOHEAD(fd,packet_len(0x131));
+ memcpy(WFIFOP(fd,0),buf,packet_len(0x131));
+ WFIFOSET(fd,packet_len(0x131));
+ } else {
+ clif_send(buf,packet_len(0x131),bl,AREA_WOS);
+ }
+}
+
+
+/// Removes a vending board from screen (ZC_DISAPPEAR_ENTRY).
+/// 0132 <owner id>.L
+void clif_closevendingboard(struct block_list* bl, int fd)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x132;
+ WBUFL(buf,2) = bl->id;
+ if( fd ) {
+ WFIFOHEAD(fd,packet_len(0x132));
+ memcpy(WFIFOP(fd,0),buf,packet_len(0x132));
+ WFIFOSET(fd,packet_len(0x132));
+ } else {
+ clif_send(buf,packet_len(0x132),bl,AREA_WOS);
+ }
+}
+
+
+/// Sends a list of items in a shop.
+/// R 0133 <packet len>.W <owner id>.L { <price>.L <amount>.W <index>.W <type>.B <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }* (ZC_PC_PURCHASE_ITEMLIST_FROMMC)
+/// R 0800 <packet len>.W <owner id>.L <unique id>.L { <price>.L <amount>.W <index>.W <type>.B <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }* (ZC_PC_PURCHASE_ITEMLIST_FROMMC2)
+void clif_vendinglist(struct map_session_data* sd, int id, struct s_vending* vending)
+{
+ int i,fd;
+ int count;
+ struct map_session_data* vsd;
+#if PACKETVER < 20100105
+ const int cmd = 0x133;
+ const int offset = 8;
+#else
+ const int cmd = 0x800;
+ const int offset = 12;
+#endif
+
+ nullpo_retv(sd);
+ nullpo_retv(vending);
+ nullpo_retv(vsd=map_id2sd(id));
+
+ fd = sd->fd;
+ count = vsd->vend_num;
+
+ WFIFOHEAD(fd, offset+count*22);
+ WFIFOW(fd,0) = cmd;
+ WFIFOW(fd,2) = offset+count*22;
+ WFIFOL(fd,4) = id;
+#if PACKETVER >= 20100105
+ WFIFOL(fd,8) = vsd->vender_id;
+#endif
+
+ for( i = 0; i < count; i++ )
+ {
+ int index = vending[i].index;
+ struct item_data* data = itemdb_search(vsd->status.cart[index].nameid);
+ WFIFOL(fd,offset+ 0+i*22) = vending[i].value;
+ WFIFOW(fd,offset+ 4+i*22) = vending[i].amount;
+ WFIFOW(fd,offset+ 6+i*22) = vending[i].index + 2;
+ WFIFOB(fd,offset+ 8+i*22) = itemtype(data->type);
+ WFIFOW(fd,offset+ 9+i*22) = ( data->view_id > 0 ) ? data->view_id : vsd->status.cart[index].nameid;
+ WFIFOB(fd,offset+11+i*22) = vsd->status.cart[index].identify;
+ WFIFOB(fd,offset+12+i*22) = vsd->status.cart[index].attribute;
+ WFIFOB(fd,offset+13+i*22) = vsd->status.cart[index].refine;
+ clif_addcards(WFIFOP(fd,offset+14+i*22), &vsd->status.cart[index]);
+ }
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Shop purchase failure (ZC_PC_PURCHASE_RESULT_FROMMC).
+/// 0135 <index>.W <amount>.W <result>.B
+/// result:
+/// 0 = success
+/// 1 = not enough zeny
+/// 2 = overweight
+/// 4 = out of stock
+/// 5 = "cannot use an npc shop while in a trade"
+/// 6 = Because the store information was incorrect the item was not purchased.
+/// 7 = No sales information.
+void clif_buyvending(struct map_session_data* sd, int index, int amount, int fail)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x135));
+ WFIFOW(fd,0) = 0x135;
+ WFIFOW(fd,2) = index+2;
+ WFIFOW(fd,4) = amount;
+ WFIFOB(fd,6) = fail;
+ WFIFOSET(fd,packet_len(0x135));
+}
+
+
+/// Shop creation success (ZC_PC_PURCHASE_MYITEMLIST).
+/// 0136 <packet len>.W <owner id>.L { <price>.L <index>.W <amount>.W <type>.B <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }*
+void clif_openvending(struct map_session_data* sd, int id, struct s_vending* vending)
+{
+ int i,fd;
+ int count;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ count = sd->vend_num;
+
+ WFIFOHEAD(fd, 8+count*22);
+ WFIFOW(fd,0) = 0x136;
+ WFIFOW(fd,2) = 8+count*22;
+ WFIFOL(fd,4) = id;
+ for( i = 0; i < count; i++ )
+ {
+ int index = vending[i].index;
+ struct item_data* data = itemdb_search(sd->status.cart[index].nameid);
+ WFIFOL(fd, 8+i*22) = vending[i].value;
+ WFIFOW(fd,12+i*22) = vending[i].index + 2;
+ WFIFOW(fd,14+i*22) = vending[i].amount;
+ WFIFOB(fd,16+i*22) = itemtype(data->type);
+ WFIFOW(fd,17+i*22) = ( data->view_id > 0 ) ? data->view_id : sd->status.cart[index].nameid;
+ WFIFOB(fd,19+i*22) = sd->status.cart[index].identify;
+ WFIFOB(fd,20+i*22) = sd->status.cart[index].attribute;
+ WFIFOB(fd,21+i*22) = sd->status.cart[index].refine;
+ clif_addcards(WFIFOP(fd,22+i*22), &sd->status.cart[index]);
+ }
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Inform merchant that someone has bought an item (ZC_DELETEITEM_FROM_MCSTORE).
+/// 0137 <index>.W <amount>.W
+void clif_vendingreport(struct map_session_data* sd, int index, int amount)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x137));
+ WFIFOW(fd,0) = 0x137;
+ WFIFOW(fd,2) = index+2;
+ WFIFOW(fd,4) = amount;
+ WFIFOSET(fd,packet_len(0x137));
+}
+
+
+/// Result of organizing a party (ZC_ACK_MAKE_GROUP).
+/// 00fa <result>.B
+/// result:
+/// 0 = opens party window and shows MsgStringTable[77]="party successfully organized"
+/// 1 = MsgStringTable[78]="party name already exists"
+/// 2 = MsgStringTable[79]="already in a party"
+/// 3 = cannot organize parties on this map
+/// ? = nothing
+void clif_party_created(struct map_session_data *sd,int result)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xfa));
+ WFIFOW(fd,0)=0xfa;
+ WFIFOB(fd,2)=result;
+ WFIFOSET(fd,packet_len(0xfa));
+}
+
+
+/// Adds new member to a party.
+/// 0104 <account id>.L <role>.L <x>.W <y>.W <state>.B <party name>.24B <char name>.24B <map name>.16B (ZC_ADD_MEMBER_TO_GROUP)
+/// 01e9 <account id>.L <role>.L <x>.W <y>.W <state>.B <party name>.24B <char name>.24B <map name>.16B <item pickup rule>.B <item share rule>.B (ZC_ADD_MEMBER_TO_GROUP2)
+/// role:
+/// 0 = leader
+/// 1 = normal
+/// state:
+/// 0 = connected
+/// 1 = disconnected
+void clif_party_member_info(struct party_data *p, struct map_session_data *sd)
+{
+ unsigned char buf[81];
+ int i;
+
+ if (!sd) { //Pick any party member (this call is used when changing item share rules)
+ ARR_FIND( 0, MAX_PARTY, i, p->data[i].sd != 0 );
+ } else {
+ ARR_FIND( 0, MAX_PARTY, i, p->data[i].sd == sd );
+ }
+ if (i >= MAX_PARTY) return; //Should never happen...
+ sd = p->data[i].sd;
+
+ WBUFW(buf, 0) = 0x1e9;
+ WBUFL(buf, 2) = sd->status.account_id;
+ WBUFL(buf, 6) = (p->party.member[i].leader)?0:1;
+ WBUFW(buf,10) = sd->bl.x;
+ WBUFW(buf,12) = sd->bl.y;
+ WBUFB(buf,14) = (p->party.member[i].online)?0:1;
+ memcpy(WBUFP(buf,15), p->party.name, NAME_LENGTH);
+ memcpy(WBUFP(buf,39), sd->status.name, NAME_LENGTH);
+ mapindex_getmapname_ext(map[sd->bl.m].name, (char*)WBUFP(buf,63));
+ WBUFB(buf,79) = (p->party.item&1)?1:0;
+ WBUFB(buf,80) = (p->party.item&2)?1:0;
+ clif_send(buf,packet_len(0x1e9),&sd->bl,PARTY);
+}
+
+
+/// Sends party information (ZC_GROUP_LIST).
+/// 00fb <packet len>.W <party name>.24B { <account id>.L <nick>.24B <map name>.16B <role>.B <state>.B }*
+/// role:
+/// 0 = leader
+/// 1 = normal
+/// state:
+/// 0 = connected
+/// 1 = disconnected
+void clif_party_info(struct party_data* p, struct map_session_data *sd)
+{
+ unsigned char buf[2+2+NAME_LENGTH+(4+NAME_LENGTH+MAP_NAME_LENGTH_EXT+1+1)*MAX_PARTY];
+ struct map_session_data* party_sd = NULL;
+ int i, c;
+
+ nullpo_retv(p);
+
+ WBUFW(buf,0) = 0xfb;
+ memcpy(WBUFP(buf,4), p->party.name, NAME_LENGTH);
+ for(i = 0, c = 0; i < MAX_PARTY; i++)
+ {
+ struct party_member* m = &p->party.member[i];
+ if(!m->account_id) continue;
+
+ if(party_sd == NULL) party_sd = p->data[i].sd;
+
+ WBUFL(buf,28+c*46) = m->account_id;
+ memcpy(WBUFP(buf,28+c*46+4), m->name, NAME_LENGTH);
+ mapindex_getmapname_ext(mapindex_id2name(m->map), (char*)WBUFP(buf,28+c*46+28));
+ WBUFB(buf,28+c*46+44) = (m->leader) ? 0 : 1;
+ WBUFB(buf,28+c*46+45) = (m->online) ? 0 : 1;
+ c++;
+ }
+ WBUFW(buf,2) = 28+c*46;
+
+ if(sd) { // send only to self
+ clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
+ } else if (party_sd) { // send to whole party
+ clif_send(buf, WBUFW(buf,2), &party_sd->bl, PARTY);
+ }
+}
+
+
+/// The player's 'party invite' state, sent during login (ZC_PARTY_CONFIG).
+/// 02c9 <flag>.B
+/// flag:
+/// 0 = allow party invites
+/// 1 = auto-deny party invites
+void clif_partyinvitationstate(struct map_session_data* sd)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x2c9));
+ WFIFOW(fd, 0) = 0x2c9;
+ WFIFOB(fd, 2) = 0; // not implemented
+ WFIFOSET(fd, packet_len(0x2c9));
+}
+
+
+/// Party invitation request.
+/// 00fe <party id>.L <party name>.24B (ZC_REQ_JOIN_GROUP)
+/// 02c6 <party id>.L <party name>.24B (ZC_PARTY_JOIN_REQ)
+void clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd)
+{
+#if PACKETVER < 20070821
+ const int cmd = 0xfe;
+#else
+ const int cmd = 0x2c6;
+#endif
+ int fd;
+ struct party_data *p;
+
+ nullpo_retv(sd);
+ nullpo_retv(tsd);
+
+ fd=tsd->fd;
+
+ if( (p=party_search(sd->status.party_id))==NULL )
+ return;
+
+ WFIFOHEAD(fd,packet_len(cmd));
+ WFIFOW(fd,0)=cmd;
+ WFIFOL(fd,2)=sd->status.party_id;
+ memcpy(WFIFOP(fd,6),p->party.name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len(cmd));
+}
+
+
+/// Party invite result.
+/// 00fd <nick>.24S <result>.B (ZC_ACK_REQ_JOIN_GROUP)
+/// 02c5 <nick>.24S <result>.L (ZC_PARTY_JOIN_REQ_ACK)
+/// result=0 : char is already in a party -> MsgStringTable[80]
+/// result=1 : party invite was rejected -> MsgStringTable[81]
+/// result=2 : party invite was accepted -> MsgStringTable[82]
+/// result=3 : party is full -> MsgStringTable[83]
+/// result=4 : char of the same account already joined the party -> MsgStringTable[608]
+/// result=5 : char blocked party invite -> MsgStringTable[1324] (since 20070904)
+/// result=7 : char is not online or doesn't exist -> MsgStringTable[71] (since 20070904)
+/// result=8 : (%s) TODO instance related? -> MsgStringTable[1388] (since 20080527)
+/// return=9 : TODO map prohibits party joining? -> MsgStringTable[1871] (since 20110205)
+void clif_party_inviteack(struct map_session_data* sd, const char* nick, int result)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd=sd->fd;
+
+#if PACKETVER < 20070904
+ if( result == 7 ) {
+ clif_displaymessage(fd, msg_txt(3));
+ return;
+ }
+#endif
+
+#if PACKETVER < 20070821
+ WFIFOHEAD(fd,packet_len(0xfd));
+ WFIFOW(fd,0) = 0xfd;
+ safestrncpy((char*)WFIFOP(fd,2),nick,NAME_LENGTH);
+ WFIFOB(fd,26) = result;
+ WFIFOSET(fd,packet_len(0xfd));
+#else
+ WFIFOHEAD(fd,packet_len(0x2c5));
+ WFIFOW(fd,0) = 0x2c5;
+ safestrncpy((char*)WFIFOP(fd,2),nick,NAME_LENGTH);
+ WFIFOL(fd,26) = result;
+ WFIFOSET(fd,packet_len(0x2c5));
+#endif
+}
+
+
+/// Updates party settings.
+/// 0101 <exp option>.L (ZC_GROUPINFO_CHANGE)
+/// 07d8 <exp option>.L <item pick rule>.B <item share rule>.B (ZC_REQ_GROUPINFO_CHANGE_V2)
+/// exp option:
+/// 0 = exp sharing disabled
+/// 1 = exp sharing enabled
+/// 2 = cannot change exp sharing
+///
+/// flag:
+/// 0 = send to party
+/// 1 = send to sd
+void clif_party_option(struct party_data *p,struct map_session_data *sd,int flag)
+{
+ unsigned char buf[16];
+#if PACKETVER < 20090603
+ const int cmd = 0x101;
+#else
+ const int cmd = 0x7d8;
+#endif
+
+ nullpo_retv(p);
+
+ if(!sd && flag==0){
+ int i;
+ for(i=0;i<MAX_PARTY && !p->data[i].sd;i++);
+ if (i < MAX_PARTY)
+ sd = p->data[i].sd;
+ }
+ if(!sd) return;
+ WBUFW(buf,0)=cmd;
+ WBUFL(buf,2)=((flag&0x01)?2:p->party.exp);
+#if PACKETVER >= 20090603
+ WBUFB(buf,6)=(p->party.item&1)?1:0;
+ WBUFB(buf,7)=(p->party.item&2)?1:0;
+#endif
+ if(flag==0)
+ clif_send(buf,packet_len(cmd),&sd->bl,PARTY);
+ else
+ clif_send(buf,packet_len(cmd),&sd->bl,SELF);
+}
+
+
+/// 0105 <account id>.L <char name>.24B <result>.B (ZC_DELETE_MEMBER_FROM_GROUP).
+/// result:
+/// 0 = leave
+/// 1 = expel
+/// 2 = cannot leave party on this map
+/// 3 = cannot expel from party on this map
+void clif_party_withdraw(struct party_data* p, struct map_session_data* sd, int account_id, const char* name, int flag)
+{
+ unsigned char buf[64];
+ int i;
+
+ nullpo_retv(p);
+
+ if(!sd && (flag&0xf0)==0)
+ {
+ for(i=0;i<MAX_PARTY && !p->data[i].sd;i++);
+ if (i < MAX_PARTY)
+ sd = p->data[i].sd;
+ }
+
+ if(!sd) return;
+
+ WBUFW(buf,0)=0x105;
+ WBUFL(buf,2)=account_id;
+ memcpy(WBUFP(buf,6),name,NAME_LENGTH);
+ WBUFB(buf,30)=flag&0x0f;
+ if((flag&0xf0)==0)
+ clif_send(buf,packet_len(0x105),&sd->bl,PARTY);
+ else
+ clif_send(buf,packet_len(0x105),&sd->bl,SELF);
+}
+
+
+/// Party chat message (ZC_NOTIFY_CHAT_PARTY).
+/// 0109 <packet len>.W <account id>.L <message>.?B
+void clif_party_message(struct party_data* p, int account_id, const char* mes, int len)
+{
+ struct map_session_data *sd;
+ int i;
+
+ nullpo_retv(p);
+
+ for(i=0; i < MAX_PARTY && !p->data[i].sd;i++);
+ if(i < MAX_PARTY){
+ unsigned char buf[1024];
+
+ if( len > sizeof(buf)-8 )
+ {
+ ShowWarning("clif_party_message: Truncated message '%s' (len=%d, max=%d, party_id=%d).\n", mes, len, sizeof(buf)-8, p->party.party_id);
+ len = sizeof(buf)-8;
+ }
+
+ sd = p->data[i].sd;
+ WBUFW(buf,0)=0x109;
+ WBUFW(buf,2)=len+8;
+ WBUFL(buf,4)=account_id;
+ safestrncpy((char *)WBUFP(buf,8), mes, len);
+ clif_send(buf,len+8,&sd->bl,PARTY);
+ }
+}
+
+
+/// Updates the position of a party member on the minimap (ZC_NOTIFY_POSITION_TO_GROUPM).
+/// 0107 <account id>.L <x>.W <y>.W
+void clif_party_xy(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x107;
+ WBUFL(buf,2)=sd->status.account_id;
+ WBUFW(buf,6)=sd->bl.x;
+ WBUFW(buf,8)=sd->bl.y;
+ clif_send(buf,packet_len(0x107),&sd->bl,PARTY_SAMEMAP_WOS);
+}
+
+
+/*==========================================
+ * Sends x/y dot to a single fd. [Skotlex]
+ *------------------------------------------*/
+void clif_party_xy_single(int fd, struct map_session_data *sd)
+{
+ WFIFOHEAD(fd,packet_len(0x107));
+ WFIFOW(fd,0)=0x107;
+ WFIFOL(fd,2)=sd->status.account_id;
+ WFIFOW(fd,6)=sd->bl.x;
+ WFIFOW(fd,8)=sd->bl.y;
+ WFIFOSET(fd,packet_len(0x107));
+}
+
+
+/// Updates HP bar of a party member.
+/// 0106 <account id>.L <hp>.W <max hp>.W (ZC_NOTIFY_HP_TO_GROUPM)
+/// 080e <account id>.L <hp>.L <max hp>.L (ZC_NOTIFY_HP_TO_GROUPM_R2)
+void clif_party_hp(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+#if PACKETVER < 20100126
+ const int cmd = 0x106;
+#else
+ const int cmd = 0x80e;
+#endif
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=cmd;
+ WBUFL(buf,2)=sd->status.account_id;
+#if PACKETVER < 20100126
+ if (sd->battle_status.max_hp > INT16_MAX) { //To correctly display the %hp bar. [Skotlex]
+ WBUFW(buf,6) = sd->battle_status.hp/(sd->battle_status.max_hp/100);
+ WBUFW(buf,8) = 100;
+ } else {
+ WBUFW(buf,6) = sd->battle_status.hp;
+ WBUFW(buf,8) = sd->battle_status.max_hp;
+ }
+#else
+ WBUFL(buf,6) = sd->battle_status.hp;
+ WBUFL(buf,10) = sd->battle_status.max_hp;
+#endif
+ clif_send(buf,packet_len(cmd),&sd->bl,PARTY_AREA_WOS);
+}
+
+
+/*==========================================
+ * Sends HP bar to a single fd. [Skotlex]
+ *------------------------------------------*/
+void clif_hpmeter_single(int fd, int id, unsigned int hp, unsigned int maxhp)
+{
+#if PACKETVER < 20100126
+ const int cmd = 0x106;
+#else
+ const int cmd = 0x80e;
+#endif
+ WFIFOHEAD(fd,packet_len(cmd));
+ WFIFOW(fd,0) = cmd;
+ WFIFOL(fd,2) = id;
+#if PACKETVER < 20100126
+ if( maxhp > INT16_MAX )
+ {// To correctly display the %hp bar. [Skotlex]
+ WFIFOW(fd,6) = hp/(maxhp/100);
+ WFIFOW(fd,8) = 100;
+ } else {
+ WFIFOW(fd,6) = hp;
+ WFIFOW(fd,8) = maxhp;
+ }
+#else
+ WFIFOL(fd,6) = hp;
+ WFIFOL(fd,10) = maxhp;
+#endif
+ WFIFOSET(fd, packet_len(cmd));
+}
+
+/// Notifies the client, that it's attack target is too far (ZC_ATTACK_FAILURE_FOR_DISTANCE).
+/// 0139 <target id>.L <target x>.W <target y>.W <x>.W <y>.W <atk range>.W
+void clif_movetoattack(struct map_session_data *sd,struct block_list *bl)
+{
+ int fd;
+
+ nullpo_retv(sd);
+ nullpo_retv(bl);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x139));
+ WFIFOW(fd, 0)=0x139;
+ WFIFOL(fd, 2)=bl->id;
+ WFIFOW(fd, 6)=bl->x;
+ WFIFOW(fd, 8)=bl->y;
+ WFIFOW(fd,10)=sd->bl.x;
+ WFIFOW(fd,12)=sd->bl.y;
+ WFIFOW(fd,14)=sd->battle_status.rhw.range;
+ WFIFOSET(fd,packet_len(0x139));
+}
+
+
+/// Notifies the client about the result of an item produce request (ZC_ACK_REQMAKINGITEM).
+/// 018f <result>.W <name id>.W
+/// result:
+/// 0 = success
+/// 1 = failure
+/// 2 = success (alchemist)
+/// 3 = failure (alchemist)
+void clif_produceeffect(struct map_session_data* sd,int flag,int nameid)
+{
+ int view,fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ clif_solved_charname(fd, sd->status.char_id, sd->status.name);
+ WFIFOHEAD(fd,packet_len(0x18f));
+ WFIFOW(fd, 0)=0x18f;
+ WFIFOW(fd, 2)=flag;
+ if((view = itemdb_viewid(nameid)) > 0)
+ WFIFOW(fd, 4)=view;
+ else
+ WFIFOW(fd, 4)=nameid;
+ WFIFOSET(fd,packet_len(0x18f));
+}
+
+
+/// Initiates the pet taming process (ZC_START_CAPTURE).
+/// 019e
+void clif_catch_process(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x19e));
+ WFIFOW(fd,0)=0x19e;
+ WFIFOSET(fd,packet_len(0x19e));
+}
+
+
+/// Displays the result of a pet taming attempt (ZC_TRYCAPTURE_MONSTER).
+/// 01a0 <result>.B
+/// 0 = failure
+/// 1 = success
+void clif_pet_roulette(struct map_session_data *sd,int data)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x1a0));
+ WFIFOW(fd,0)=0x1a0;
+ WFIFOB(fd,2)=data;
+ WFIFOSET(fd,packet_len(0x1a0));
+}
+
+
+/// Presents a list of pet eggs that can be hatched (ZC_PETEGG_LIST).
+/// 01a6 <packet len>.W { <index>.W }*
+void clif_sendegg(struct map_session_data *sd)
+{
+ int i,n=0,fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ if (battle_config.pet_no_gvg && map_flag_gvg(sd->bl.m))
+ { //Disable pet hatching in GvG grounds during Guild Wars [Skotlex]
+ clif_displaymessage(fd, msg_txt(666));
+ return;
+ }
+ WFIFOHEAD(fd, MAX_INVENTORY * 2 + 4);
+ WFIFOW(fd,0)=0x1a6;
+ for(i=0,n=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL ||
+ sd->inventory_data[i]->type!=IT_PETEGG ||
+ sd->status.inventory[i].amount<=0)
+ continue;
+ WFIFOW(fd,n*2+4)=i+2;
+ n++;
+ }
+ WFIFOW(fd,2)=4+n*2;
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ sd->menuskill_id = SA_TAMINGMONSTER;
+ sd->menuskill_val = -1;
+}
+
+
+/// Sends a specific pet data update (ZC_CHANGESTATE_PET).
+/// 01a4 <type>.B <id>.L <data>.L
+/// type:
+/// 0 = pre-init (data = 0)
+/// 1 = intimacy (data = 0~4)
+/// 2 = hunger (data = 0~4)
+/// 3 = accessory
+/// 4 = performance (data = 1~3: normal, 4: special)
+/// 5 = hairstyle
+///
+/// If sd is null, the update is sent to nearby objects, otherwise it is sent only to that player.
+void clif_send_petdata(struct map_session_data* sd, struct pet_data* pd, int type, int param)
+{
+ uint8 buf[16];
+ nullpo_retv(pd);
+
+ WBUFW(buf,0) = 0x1a4;
+ WBUFB(buf,2) = type;
+ WBUFL(buf,3) = pd->bl.id;
+ WBUFL(buf,7) = param;
+ if (sd)
+ clif_send(buf, packet_len(0x1a4), &sd->bl, SELF);
+ else
+ clif_send(buf, packet_len(0x1a4), &pd->bl, AREA);
+}
+
+
+/// Pet's base data (ZC_PROPERTY_PET).
+/// 01a2 <name>.24B <renamed>.B <level>.W <hunger>.W <intimacy>.W <accessory id>.W <class>.W
+void clif_send_petstatus(struct map_session_data *sd)
+{
+ int fd;
+ struct s_pet *pet;
+
+ nullpo_retv(sd);
+ nullpo_retv(sd->pd);
+
+ fd=sd->fd;
+ pet = &sd->pd->pet;
+ WFIFOHEAD(fd,packet_len(0x1a2));
+ WFIFOW(fd,0)=0x1a2;
+ memcpy(WFIFOP(fd,2),pet->name,NAME_LENGTH);
+ WFIFOB(fd,26)=battle_config.pet_rename?0:pet->rename_flag;
+ WFIFOW(fd,27)=pet->level;
+ WFIFOW(fd,29)=pet->hungry;
+ WFIFOW(fd,31)=pet->intimate;
+ WFIFOW(fd,33)=pet->equip;
+#if PACKETVER >= 20081126
+ WFIFOW(fd,35)=pet->class_;
+#endif
+ WFIFOSET(fd,packet_len(0x1a2));
+}
+
+
+/// Notification about a pet's emotion/talk (ZC_PET_ACT).
+/// 01aa <id>.L <data>.L
+/// data:
+/// @see CZ_PET_ACT.
+void clif_pet_emotion(struct pet_data *pd,int param)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(pd);
+
+ memset(buf,0,packet_len(0x1aa));
+
+ WBUFW(buf,0)=0x1aa;
+ WBUFL(buf,2)=pd->bl.id;
+ if(param >= 100 && pd->petDB->talk_convert_class) {
+ if(pd->petDB->talk_convert_class < 0)
+ return;
+ else if(pd->petDB->talk_convert_class > 0) {
+ // replace mob_id component of talk/act data
+ param -= (pd->pet.class_ - 100)*100;
+ param += (pd->petDB->talk_convert_class - 100)*100;
+ }
+ }
+ WBUFL(buf,6)=param;
+
+ clif_send(buf,packet_len(0x1aa),&pd->bl,AREA);
+}
+
+
+/// Result of request to feed a pet (ZC_FEED_PET).
+/// 01a3 <result>.B <name id>.W
+/// result:
+/// 0 = failure
+/// 1 = success
+void clif_pet_food(struct map_session_data *sd,int foodid,int fail)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x1a3));
+ WFIFOW(fd,0)=0x1a3;
+ WFIFOB(fd,2)=fail;
+ WFIFOW(fd,3)=foodid;
+ WFIFOSET(fd,packet_len(0x1a3));
+}
+
+
+/// Presents a list of skills that can be auto-spelled (ZC_AUTOSPELLLIST).
+/// 01cd { <skill id>.L }*7
+void clif_autospell(struct map_session_data *sd,uint16 skill_lv)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x1cd));
+ WFIFOW(fd, 0)=0x1cd;
+
+ if(skill_lv>0 && pc_checkskill(sd,MG_NAPALMBEAT)>0)
+ WFIFOL(fd,2)= MG_NAPALMBEAT;
+ else
+ WFIFOL(fd,2)= 0x00000000;
+ if(skill_lv>1 && pc_checkskill(sd,MG_COLDBOLT)>0)
+ WFIFOL(fd,6)= MG_COLDBOLT;
+ else
+ WFIFOL(fd,6)= 0x00000000;
+ if(skill_lv>1 && pc_checkskill(sd,MG_FIREBOLT)>0)
+ WFIFOL(fd,10)= MG_FIREBOLT;
+ else
+ WFIFOL(fd,10)= 0x00000000;
+ if(skill_lv>1 && pc_checkskill(sd,MG_LIGHTNINGBOLT)>0)
+ WFIFOL(fd,14)= MG_LIGHTNINGBOLT;
+ else
+ WFIFOL(fd,14)= 0x00000000;
+ if(skill_lv>4 && pc_checkskill(sd,MG_SOULSTRIKE)>0)
+ WFIFOL(fd,18)= MG_SOULSTRIKE;
+ else
+ WFIFOL(fd,18)= 0x00000000;
+ if(skill_lv>7 && pc_checkskill(sd,MG_FIREBALL)>0)
+ WFIFOL(fd,22)= MG_FIREBALL;
+ else
+ WFIFOL(fd,22)= 0x00000000;
+ if(skill_lv>9 && pc_checkskill(sd,MG_FROSTDIVER)>0)
+ WFIFOL(fd,26)= MG_FROSTDIVER;
+ else
+ WFIFOL(fd,26)= 0x00000000;
+
+ WFIFOSET(fd,packet_len(0x1cd));
+ sd->menuskill_id = SA_AUTOSPELL;
+ sd->menuskill_val = skill_lv;
+}
+
+
+/// Devotion's visual effect (ZC_DEVOTIONLIST).
+/// 01cf <devoter id>.L { <devotee id>.L }*5 <max distance>.W
+void clif_devotion(struct block_list *src, struct map_session_data *tsd)
+{
+ unsigned char buf[56];
+ int i;
+
+ nullpo_retv(src);
+ memset(buf,0,packet_len(0x1cf));
+
+ WBUFW(buf,0) = 0x1cf;
+ WBUFL(buf,2) = src->id;
+ if( src->type == BL_MER )
+ {
+ struct mercenary_data *md = BL_CAST(BL_MER,src);
+ if( md && md->master && md->devotion_flag )
+ WBUFL(buf,6) = md->master->bl.id;
+
+ WBUFW(buf,26) = skill_get_range2(src, ML_DEVOTION, mercenary_checkskill(md, ML_DEVOTION));
+ }
+ else
+ {
+ struct map_session_data *sd = BL_CAST(BL_PC,src);
+ if( sd == NULL )
+ return;
+
+ for( i = 0; i < 5; i++ )
+ WBUFL(buf,6+4*i) = sd->devotion[i];
+ WBUFW(buf,26) = skill_get_range2(src, CR_DEVOTION, pc_checkskill(sd, CR_DEVOTION));
+ }
+
+ if( tsd )
+ clif_send(buf, packet_len(0x1cf), &tsd->bl, SELF);
+ else
+ clif_send(buf, packet_len(0x1cf), src, AREA);
+}
+
+/*==========================================
+ * Server tells clients nearby 'sd' (and himself) to display 'sd->spiritball' number of spiritballs on 'sd'
+ * Notifies clients in an area of an object's spirits.
+ * 01d0 <id>.L <amount>.W (ZC_SPIRITS)
+ * 01e1 <id>.L <amount>.W (ZC_SPIRITS2)
+ *------------------------------------------*/
+void clif_spiritball(struct block_list *bl) {
+ unsigned char buf[16];
+ TBL_PC *sd = BL_CAST(BL_PC,bl);
+ TBL_HOM *hd = BL_CAST(BL_HOM,bl);
+
+ nullpo_retv(bl);
+
+ WBUFW(buf, 0) = 0x1d0;
+ WBUFL(buf, 2) = bl->id;
+ WBUFW(buf, 6) = 0; //init to 0
+ switch(bl->type){
+ case BL_PC: WBUFW(buf, 6) = sd->spiritball; break;
+ case BL_HOM: WBUFW(buf, 6) = hd->homunculus.spiritball; break;
+ }
+ clif_send(buf, packet_len(0x1d0), bl, AREA);
+}
+
+
+/// Notifies clients in area of a character's combo delay (ZC_COMBODELAY).
+/// 01d2 <account id>.L <delay>.L
+void clif_combo_delay(struct block_list *bl,int wait)
+{
+ unsigned char buf[32];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0)=0x1d2;
+ WBUFL(buf,2)=bl->id;
+ WBUFL(buf,6)=wait;
+ clif_send(buf,packet_len(0x1d2),bl,AREA);
+}
+
+
+/// Notifies clients in area that a character has blade-stopped another (ZC_BLADESTOP).
+/// 01d1 <src id>.L <dst id>.L <flag>.L
+/// flag:
+/// 0 = inactive
+/// 1 = active
+void clif_bladestop(struct block_list *src, int dst_id, int active)
+{
+ unsigned char buf[32];
+
+ nullpo_retv(src);
+
+ WBUFW(buf,0)=0x1d1;
+ WBUFL(buf,2)=src->id;
+ WBUFL(buf,6)=dst_id;
+ WBUFL(buf,10)=active;
+
+ clif_send(buf,packet_len(0x1d1),src,AREA);
+}
+
+
+/// MVP effect (ZC_MVP).
+/// 010c <account id>.L
+void clif_mvp_effect(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x10c;
+ WBUFL(buf,2)=sd->bl.id;
+ clif_send(buf,packet_len(0x10c),&sd->bl,AREA);
+}
+
+
+/// MVP item reward message (ZC_MVP_GETTING_ITEM).
+/// 010a <name id>.W
+void clif_mvp_item(struct map_session_data *sd,int nameid)
+{
+ int view,fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x10a));
+ WFIFOW(fd,0)=0x10a;
+ if((view = itemdb_viewid(nameid)) > 0)
+ WFIFOW(fd,2)=view;
+ else
+ WFIFOW(fd,2)=nameid;
+ WFIFOSET(fd,packet_len(0x10a));
+}
+
+
+/// MVP EXP reward message (ZC_MVP_GETTING_SPECIAL_EXP).
+/// 010b <exp>.L
+void clif_mvp_exp(struct map_session_data *sd, unsigned int exp)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x10b));
+ WFIFOW(fd,0)=0x10b;
+ WFIFOL(fd,2)=cap_value(exp,0,INT32_MAX);
+ WFIFOSET(fd,packet_len(0x10b));
+}
+
+
+/// Dropped MVP item reward message (ZC_THROW_MVPITEM).
+/// 010d
+///
+/// "You are the MVP, but cannot obtain the reward because
+/// you are overweight."
+void clif_mvp_noitem(struct map_session_data* sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x10d));
+ WFIFOW(fd,0) = 0x10d;
+ WFIFOSET(fd,packet_len(0x10d));
+}
+
+
+/// Guild creation result (ZC_RESULT_MAKE_GUILD).
+/// 0167 <result>.B
+/// result:
+/// 0 = "Guild has been created."
+/// 1 = "You are already in a Guild."
+/// 2 = "That Guild Name already exists."
+/// 3 = "You need the neccessary item to create a Guild."
+void clif_guild_created(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x167));
+ WFIFOW(fd,0)=0x167;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len(0x167));
+}
+
+
+/// Notifies the client that it is belonging to a guild (ZC_UPDATE_GDID).
+/// 016c <guild id>.L <emblem id>.L <mode>.L <ismaster>.B <inter sid>.L <guild name>.24B
+/// mode:
+/// &0x01 = allow invite
+/// &0x10 = allow expel
+void clif_guild_belonginfo(struct map_session_data *sd, struct guild *g)
+{
+ int ps,fd;
+ nullpo_retv(sd);
+ nullpo_retv(g);
+
+ fd=sd->fd;
+ ps=guild_getposition(g,sd);
+ WFIFOHEAD(fd,packet_len(0x16c));
+ WFIFOW(fd,0)=0x16c;
+ WFIFOL(fd,2)=g->guild_id;
+ WFIFOL(fd,6)=g->emblem_id;
+ WFIFOL(fd,10)=g->position[ps].mode;
+ WFIFOB(fd,14)=(bool)(sd->state.gmaster_flag==g);
+ WFIFOL(fd,15)=0; // InterSID (unknown purpose)
+ memcpy(WFIFOP(fd,19),g->name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x16c));
+}
+
+
+/// Guild member login notice.
+/// 016d <account id>.L <char id>.L <status>.L (ZC_UPDATE_CHARSTAT)
+/// 01f2 <account id>.L <char id>.L <status>.L <gender>.W <hair style>.W <hair color>.W (ZC_UPDATE_CHARSTAT2)
+/// status:
+/// 0 = offline
+/// 1 = online
+void clif_guild_memberlogin_notice(struct guild *g,int idx,int flag)
+{
+ unsigned char buf[64];
+ struct map_session_data* sd;
+
+ nullpo_retv(g);
+
+ WBUFW(buf, 0)=0x1f2;
+ WBUFL(buf, 2)=g->member[idx].account_id;
+ WBUFL(buf, 6)=g->member[idx].char_id;
+ WBUFL(buf,10)=flag;
+
+ if( ( sd = g->member[idx].sd ) != NULL )
+ {
+ WBUFW(buf,14) = sd->status.sex;
+ WBUFW(buf,16) = sd->status.hair;
+ WBUFW(buf,18) = sd->status.hair_color;
+ clif_send(buf,packet_len(0x1f2),&sd->bl,GUILD_WOS);
+ }
+ else if( ( sd = guild_getavailablesd(g) ) != NULL )
+ {
+ WBUFW(buf,14) = 0;
+ WBUFW(buf,16) = 0;
+ WBUFW(buf,18) = 0;
+ clif_send(buf,packet_len(0x1f2),&sd->bl,GUILD);
+ }
+}
+
+// Function `clif_guild_memberlogin_notice` sends info about
+// logins and logouts of a guild member to the rest members.
+// But at the 1st time (after a player login or map changing)
+// the client won't show the message.
+// So I suggest use this function for sending "first-time-info"
+// to some player on entering the game or changing location.
+// At next time the client would always show the message.
+// The function sends all the statuses in the single packet
+// to economize traffic. [LuzZza]
+void clif_guild_send_onlineinfo(struct map_session_data *sd)
+{
+ struct guild *g;
+ unsigned char buf[14*128];
+ int i, count=0, p_len;
+
+ nullpo_retv(sd);
+
+ p_len = packet_len(0x16d);
+
+ if(!(g = guild_search(sd->status.guild_id)))
+ return;
+
+ for(i=0; i<g->max_member; i++) {
+
+ if(g->member[i].account_id > 0 &&
+ g->member[i].account_id != sd->status.account_id) {
+
+ WBUFW(buf,count*p_len) = 0x16d;
+ WBUFL(buf,count*p_len+2) = g->member[i].account_id;
+ WBUFL(buf,count*p_len+6) = g->member[i].char_id;
+ WBUFL(buf,count*p_len+10) = g->member[i].online;
+ count++;
+ }
+ }
+
+ clif_send(buf, p_len*count, &sd->bl, SELF);
+}
+
+
+/// Bitmask of enabled guild window tabs (ZC_ACK_GUILD_MENUINTERFACE).
+/// 014e <menu flag>.L
+/// menu flag:
+/// 0x00 = Basic Info (always on)
+/// &0x01 = Member manager
+/// &0x02 = Positions
+/// &0x04 = Skills
+/// &0x10 = Expulsion list
+/// &0x40 = Unknown (GMENUFLAG_ALLGUILDLIST)
+/// &0x80 = Notice
+void clif_guild_masterormember(struct map_session_data *sd)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x14e));
+ WFIFOW(fd,0) = 0x14e;
+ WFIFOL(fd,2) = (sd->state.gmaster_flag) ? 0xd7 : 0x57;
+ WFIFOSET(fd,packet_len(0x14e));
+}
+
+
+/// Guild basic information (Territories [Valaris])
+/// 0150 <guild id>.L <level>.L <member num>.L <member max>.L <exp>.L <max exp>.L <points>.L <honor>.L <virtue>.L <emblem id>.L <name>.24B <master name>.24B <manage land>.16B (ZC_GUILD_INFO)
+/// 01b6 <guild id>.L <level>.L <member num>.L <member max>.L <exp>.L <max exp>.L <points>.L <honor>.L <virtue>.L <emblem id>.L <name>.24B <master name>.24B <manage land>.16B <zeny>.L (ZC_GUILD_INFO2)
+void clif_guild_basicinfo(struct map_session_data *sd) {
+ int fd;
+ struct guild *g;
+
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ if( (g = guild_search(sd->status.guild_id)) == NULL )
+ return;
+
+ WFIFOHEAD(fd,packet_len(0x1b6));
+ WFIFOW(fd, 0)=0x1b6;//0x150;
+ WFIFOL(fd, 2)=g->guild_id;
+ WFIFOL(fd, 6)=g->guild_lv;
+ WFIFOL(fd,10)=g->connect_member;
+ WFIFOL(fd,14)=g->max_member;
+ WFIFOL(fd,18)=g->average_lv;
+ WFIFOL(fd,22)=(uint32)cap_value(g->exp,0,INT32_MAX);
+ WFIFOL(fd,26)=g->next_exp;
+ WFIFOL(fd,30)=0; // Tax Points
+ WFIFOL(fd,34)=0; // Honor: (left) Vulgar [-100,100] Famed (right)
+ WFIFOL(fd,38)=0; // Virtue: (down) Wicked [-100,100] Righteous (up)
+ WFIFOL(fd,42)=g->emblem_id;
+ memcpy(WFIFOP(fd,46),g->name, NAME_LENGTH);
+ memcpy(WFIFOP(fd,70),g->master, NAME_LENGTH);
+
+ safestrncpy((char*)WFIFOP(fd,94),msg_txt(300+guild_checkcastles(g)),16); // "'N' castles"
+ WFIFOL(fd,110) = 0; // zeny
+
+ WFIFOSET(fd,packet_len(0x1b6));
+}
+
+
+/// Guild alliance and opposition list (ZC_MYGUILD_BASIC_INFO).
+/// 014c <packet len>.W { <relation>.L <guild id>.L <guild name>.24B }*
+void clif_guild_allianceinfo(struct map_session_data *sd)
+{
+ int fd,i,c;
+ struct guild *g;
+
+ nullpo_retv(sd);
+ if( (g = guild_search(sd->status.guild_id)) == NULL )
+ return;
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, MAX_GUILDALLIANCE * 32 + 4);
+ WFIFOW(fd, 0)=0x14c;
+ for(i=c=0;i<MAX_GUILDALLIANCE;i++){
+ struct guild_alliance *a=&g->alliance[i];
+ if(a->guild_id>0){
+ WFIFOL(fd,c*32+4)=a->opposition;
+ WFIFOL(fd,c*32+8)=a->guild_id;
+ memcpy(WFIFOP(fd,c*32+12),a->name,NAME_LENGTH);
+ c++;
+ }
+ }
+ WFIFOW(fd, 2)=c*32+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Guild member manager information (ZC_MEMBERMGR_INFO).
+/// 0154 <packet len>.W { <account>.L <char id>.L <hair style>.W <hair color>.W <gender>.W <class>.W <level>.W <contrib exp>.L <state>.L <position>.L <memo>.50B <name>.24B }*
+/// state:
+/// 0 = offline
+/// 1 = online
+/// memo:
+/// probably member's self-introduction (unused, no client UI/packets for editing it)
+void clif_guild_memberlist(struct map_session_data *sd)
+{
+ int fd;
+ int i,c;
+ struct guild *g;
+ nullpo_retv(sd);
+
+ if( (fd = sd->fd) == 0 )
+ return;
+ if( (g = guild_search(sd->status.guild_id)) == NULL )
+ return;
+
+ WFIFOHEAD(fd, g->max_member * 104 + 4);
+ WFIFOW(fd, 0)=0x154;
+ for(i=0,c=0;i<g->max_member;i++){
+ struct guild_member *m=&g->member[i];
+ if(m->account_id==0)
+ continue;
+ WFIFOL(fd,c*104+ 4)=m->account_id;
+ WFIFOL(fd,c*104+ 8)=m->char_id;
+ WFIFOW(fd,c*104+12)=m->hair;
+ WFIFOW(fd,c*104+14)=m->hair_color;
+ WFIFOW(fd,c*104+16)=m->gender;
+ WFIFOW(fd,c*104+18)=m->class_;
+ WFIFOW(fd,c*104+20)=m->lv;
+ WFIFOL(fd,c*104+22)=(int)cap_value(m->exp,0,INT32_MAX);
+ WFIFOL(fd,c*104+26)=m->online;
+ WFIFOL(fd,c*104+30)=m->position;
+ memset(WFIFOP(fd,c*104+34),0,50); //[Ind] - This is displayed in the 'note' column but being you can't edit it it's sent empty.
+ memcpy(WFIFOP(fd,c*104+84),m->name,NAME_LENGTH);
+ c++;
+ }
+ WFIFOW(fd, 2)=c*104+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Guild position name information (ZC_POSITION_ID_NAME_INFO).
+/// 0166 <packet len>.W { <position id>.L <position name>.24B }*
+void clif_guild_positionnamelist(struct map_session_data *sd)
+{
+ int i,fd;
+ struct guild *g;
+
+ nullpo_retv(sd);
+ if( (g = guild_search(sd->status.guild_id)) == NULL )
+ return;
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, MAX_GUILDPOSITION * 28 + 4);
+ WFIFOW(fd, 0)=0x166;
+ for(i=0;i<MAX_GUILDPOSITION;i++){
+ WFIFOL(fd,i*28+4)=i;
+ memcpy(WFIFOP(fd,i*28+8),g->position[i].name,NAME_LENGTH);
+ }
+ WFIFOW(fd,2)=i*28+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Guild position information (ZC_POSITION_INFO).
+/// 0160 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L }*
+/// mode:
+/// &0x01 = allow invite
+/// &0x10 = allow expel
+/// ranking:
+/// TODO
+void clif_guild_positioninfolist(struct map_session_data *sd)
+{
+ int i,fd;
+ struct guild *g;
+
+ nullpo_retv(sd);
+ if( (g = guild_search(sd->status.guild_id)) == NULL )
+ return;
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, MAX_GUILDPOSITION * 16 + 4);
+ WFIFOW(fd, 0)=0x160;
+ for(i=0;i<MAX_GUILDPOSITION;i++){
+ struct guild_position *p=&g->position[i];
+ WFIFOL(fd,i*16+ 4)=i;
+ WFIFOL(fd,i*16+ 8)=p->mode;
+ WFIFOL(fd,i*16+12)=i;
+ WFIFOL(fd,i*16+16)=p->exp_mode;
+ }
+ WFIFOW(fd, 2)=i*16+4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Notifies clients in a guild about updated position information (ZC_ACK_CHANGE_GUILD_POSITIONINFO).
+/// 0174 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L <position name>.24B }*
+/// mode:
+/// &0x01 = allow invite
+/// &0x10 = allow expel
+/// ranking:
+/// TODO
+void clif_guild_positionchanged(struct guild *g,int idx)
+{
+ // FIXME: This packet is intended to update the clients after a
+ // commit of position info changes, not sending one packet per
+ // position.
+ struct map_session_data *sd;
+ unsigned char buf[128];
+
+ nullpo_retv(g);
+
+ WBUFW(buf, 0)=0x174;
+ WBUFW(buf, 2)=44; // packet len
+ // GUILD_REG_POSITION_INFO{
+ WBUFL(buf, 4)=idx;
+ WBUFL(buf, 8)=g->position[idx].mode;
+ WBUFL(buf,12)=idx;
+ WBUFL(buf,16)=g->position[idx].exp_mode;
+ memcpy(WBUFP(buf,20),g->position[idx].name,NAME_LENGTH);
+ // }*
+ if( (sd=guild_getavailablesd(g))!=NULL )
+ clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD);
+}
+
+
+/// Notifies clients in a guild about updated member position assignments (ZC_ACK_REQ_CHANGE_MEMBERS).
+/// 0156 <packet len>.W { <account id>.L <char id>.L <position id>.L }*
+void clif_guild_memberpositionchanged(struct guild *g,int idx)
+{
+ // FIXME: This packet is intended to update the clients after a
+ // commit of member position assignment changes, not sending one
+ // packet per position.
+ struct map_session_data *sd;
+ unsigned char buf[64];
+
+ nullpo_retv(g);
+
+ WBUFW(buf, 0)=0x156;
+ WBUFW(buf, 2)=16; // packet len
+ // MEMBER_POSITION_INFO{
+ WBUFL(buf, 4)=g->member[idx].account_id;
+ WBUFL(buf, 8)=g->member[idx].char_id;
+ WBUFL(buf,12)=g->member[idx].position;
+ // }*
+ if( (sd=guild_getavailablesd(g))!=NULL )
+ clif_send(buf,WBUFW(buf,2),&sd->bl,GUILD);
+}
+
+
+/// Sends emblems bitmap data to the client that requested it (ZC_GUILD_EMBLEM_IMG).
+/// 0152 <packet len>.W <guild id>.L <emblem id>.L <emblem data>.?B
+void clif_guild_emblem(struct map_session_data *sd,struct guild *g)
+{
+ int fd;
+ nullpo_retv(sd);
+ nullpo_retv(g);
+
+ fd = sd->fd;
+ if( g->emblem_len <= 0 )
+ return;
+
+ WFIFOHEAD(fd,g->emblem_len+12);
+ WFIFOW(fd,0)=0x152;
+ WFIFOW(fd,2)=g->emblem_len+12;
+ WFIFOL(fd,4)=g->guild_id;
+ WFIFOL(fd,8)=g->emblem_id;
+ memcpy(WFIFOP(fd,12),g->emblem_data,g->emblem_len);
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Sends update of the guild id/emblem id to everyone in the area (ZC_CHANGE_GUILD).
+/// 01b4 <id>.L <guild id>.L <emblem id>.W
+void clif_guild_emblem_area(struct block_list* bl)
+{
+ uint8 buf[12];
+
+ nullpo_retv(bl);
+
+ // TODO this packet doesn't force the update of ui components that have the emblem visible
+ // (emblem in the flag npcs and emblem over the head in agit maps) [FlavioJS]
+ WBUFW(buf,0) = 0x1b4;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = status_get_guild_id(bl);
+ WBUFW(buf,10) = status_get_emblem_id(bl);
+ clif_send(buf, 12, bl, AREA_WOS);
+}
+
+
+/// Sends guild skills (ZC_GUILD_SKILLINFO).
+/// 0162 <packet len>.W <skill points>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <atk range>.W <skill name>.24B <upgradable>.B }*
+void clif_guild_skillinfo(struct map_session_data* sd)
+{
+ int fd;
+ struct guild* g;
+ int i,c;
+
+ nullpo_retv(sd);
+ if( (g = guild_search(sd->status.guild_id)) == NULL )
+ return;
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, 6 + MAX_GUILDSKILL*37);
+ WFIFOW(fd,0) = 0x0162;
+ WFIFOW(fd,4) = g->skill_point;
+ for(i = 0, c = 0; i < MAX_GUILDSKILL; i++)
+ {
+ if(g->skill[i].id > 0 && guild_check_skill_require(g, g->skill[i].id))
+ {
+ int id = g->skill[i].id;
+ int p = 6 + c*37;
+ WFIFOW(fd,p+0) = id;
+ WFIFOL(fd,p+2) = skill_get_inf(id);
+ WFIFOW(fd,p+6) = g->skill[i].lv;
+ WFIFOW(fd,p+8) = skill_get_sp(id, g->skill[i].lv);
+ WFIFOW(fd,p+10) = skill_get_range(id, g->skill[i].lv);
+ safestrncpy((char*)WFIFOP(fd,p+12), skill_get_name(id), NAME_LENGTH);
+ WFIFOB(fd,p+36)= (g->skill[i].lv < guild_skill_get_max(id) && sd == g->member[0].sd) ? 1 : 0;
+ c++;
+ }
+ }
+ WFIFOW(fd,2) = 6 + c*37;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Sends guild notice to client (ZC_GUILD_NOTICE).
+/// 016f <subject>.60B <notice>.120B
+void clif_guild_notice(struct map_session_data* sd, struct guild* g)
+{
+ int fd;
+
+ nullpo_retv(sd);
+ nullpo_retv(g);
+
+ fd = sd->fd;
+
+ if ( !session_isActive(fd) )
+ return;
+
+ if(g->mes1[0] == '\0' && g->mes2[0] == '\0')
+ return;
+
+ WFIFOHEAD(fd,packet_len(0x16f));
+ WFIFOW(fd,0) = 0x16f;
+ memcpy(WFIFOP(fd,2), g->mes1, MAX_GUILDMES1);
+ memcpy(WFIFOP(fd,62), g->mes2, MAX_GUILDMES2);
+ WFIFOSET(fd,packet_len(0x16f));
+}
+
+
+/// Guild invite (ZC_REQ_JOIN_GUILD).
+/// 016a <guild id>.L <guild name>.24B
+void clif_guild_invite(struct map_session_data *sd,struct guild *g)
+{
+ int fd;
+
+ nullpo_retv(sd);
+ nullpo_retv(g);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x16a));
+ WFIFOW(fd,0)=0x16a;
+ WFIFOL(fd,2)=g->guild_id;
+ memcpy(WFIFOP(fd,6),g->name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x16a));
+}
+
+
+/// Reply to invite request (ZC_ACK_REQ_JOIN_GUILD).
+/// 0169 <answer>.B
+/// answer:
+/// 0 = Already in guild.
+/// 1 = Offer rejected.
+/// 2 = Offer accepted.
+/// 3 = Guild full.
+void clif_guild_inviteack(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x169));
+ WFIFOW(fd,0)=0x169;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len(0x169));
+}
+
+
+/// Notifies clients of a guild of a leaving member (ZC_ACK_LEAVE_GUILD).
+/// 015a <char name>.24B <reason>.40B
+void clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes)
+{
+ unsigned char buf[128];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf, 0)=0x15a;
+ memcpy(WBUFP(buf, 2),name,NAME_LENGTH);
+ memcpy(WBUFP(buf,26),mes,40);
+ clif_send(buf,packet_len(0x15a),&sd->bl,GUILD_NOBG);
+}
+
+
+/// Notifies clients of a guild of an expelled member.
+/// 015c <char name>.24B <reason>.40B <account name>.24B (ZC_ACK_BAN_GUILD)
+/// 0839 <char name>.24B <reason>.40B (ZC_ACK_BAN_GUILD_SSO)
+void clif_guild_expulsion(struct map_session_data* sd, const char* name, const char* mes, int account_id)
+{
+ unsigned char buf[128];
+#if PACKETVER < 20100803
+ const unsigned short cmd = 0x15c;
+#else
+ const unsigned short cmd = 0x839;
+#endif
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0) = cmd;
+ safestrncpy((char*)WBUFP(buf,2), name, NAME_LENGTH);
+ safestrncpy((char*)WBUFP(buf,26), mes, 40);
+#if PACKETVER < 20100803
+ memset(WBUFP(buf,66), 0, NAME_LENGTH); // account name (not used for security reasons)
+#endif
+ clif_send(buf, packet_len(cmd), &sd->bl, GUILD_NOBG);
+}
+
+
+/// Guild expulsion list (ZC_BAN_LIST).
+/// 0163 <packet len>.W { <char name>.24B <account name>.24B <reason>.40B }*
+/// 0163 <packet len>.W { <char name>.24B <reason>.40B }* (PACKETVER >= 20100803)
+void clif_guild_expulsionlist(struct map_session_data* sd)
+{
+#if PACKETVER < 20100803
+ const int offset = NAME_LENGTH*2+40;
+#else
+ const int offset = NAME_LENGTH+40;
+#endif
+ int fd, i, c = 0;
+ struct guild* g;
+
+ nullpo_retv(sd);
+
+ if( (g = guild_search(sd->status.guild_id)) == NULL )
+ return;
+
+ fd = sd->fd;
+
+ WFIFOHEAD(fd,4 + MAX_GUILDEXPULSION * offset);
+ WFIFOW(fd,0) = 0x163;
+
+ for( i = 0; i < MAX_GUILDEXPULSION; i++ )
+ {
+ struct guild_expulsion* e = &g->expulsion[i];
+
+ if( e->account_id > 0 )
+ {
+ memcpy(WFIFOP(fd,4 + c*offset), e->name, NAME_LENGTH);
+#if PACKETVER < 20100803
+ memset(WFIFOP(fd,4 + c*offset+24), 0, NAME_LENGTH); // account name (not used for security reasons)
+ memcpy(WFIFOP(fd,4 + c*offset+48), e->mes, 40);
+#else
+ memcpy(WFIFOP(fd,4 + c*offset+24), e->mes, 40);
+#endif
+ c++;
+ }
+ }
+ WFIFOW(fd,2) = 4 + c*offset;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Guild chat message (ZC_GUILD_CHAT).
+/// 017f <packet len>.W <message>.?B
+void clif_guild_message(struct guild *g,int account_id,const char *mes,int len)
+{// TODO: account_id is not used, candidate for deletion? [Ai4rei]
+ struct map_session_data *sd;
+ uint8 buf[256];
+
+ if( len == 0 )
+ {
+ return;
+ }
+ else if( len > sizeof(buf)-5 )
+ {
+ ShowWarning("clif_guild_message: Truncated message '%s' (len=%d, max=%d, guild_id=%d).\n", mes, len, sizeof(buf)-5, g->guild_id);
+ len = sizeof(buf)-5;
+ }
+
+ WBUFW(buf, 0) = 0x17f;
+ WBUFW(buf, 2) = len + 5;
+ safestrncpy((char*)WBUFP(buf,4), mes, len+1);
+
+ if ((sd = guild_getavailablesd(g)) != NULL)
+ clif_send(buf, WBUFW(buf,2), &sd->bl, GUILD_NOBG);
+}
+
+
+/*==========================================
+ * Server tells client 'sd' that his guild skill 'skill_id' gone to level 'lv'
+ *------------------------------------------*/
+int clif_guild_skillup(struct map_session_data *sd,uint16 skill_id,int lv)
+{// TODO: Merge with clif_skillup (same packet).
+ int fd;
+
+ nullpo_ret(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,11);
+ WFIFOW(fd,0) = 0x10e;
+ WFIFOW(fd,2) = skill_id;
+ WFIFOW(fd,4) = lv;
+ WFIFOW(fd,6) = skill_get_sp(skill_id,lv);
+ WFIFOW(fd,8) = skill_get_range(skill_id,lv);
+ WFIFOB(fd,10) = 1;
+ WFIFOSET(fd,11);
+ return 0;
+}
+
+
+/// Request for guild alliance (ZC_REQ_ALLY_GUILD).
+/// 0171 <inviter account id>.L <guild name>.24B
+void clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x171));
+ WFIFOW(fd,0)=0x171;
+ WFIFOL(fd,2)=account_id;
+ memcpy(WFIFOP(fd,6),name,NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x171));
+}
+
+
+/// Notifies the client about the result of a alliance request (ZC_ACK_REQ_ALLY_GUILD).
+/// 0173 <answer>.B
+/// answer:
+/// 0 = Already allied.
+/// 1 = You rejected the offer.
+/// 2 = You accepted the offer.
+/// 3 = They have too any alliances.
+/// 4 = You have too many alliances.
+/// 5 = Alliances are disabled.
+void clif_guild_allianceack(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x173));
+ WFIFOW(fd,0)=0x173;
+ WFIFOL(fd,2)=flag;
+ WFIFOSET(fd,packet_len(0x173));
+}
+
+
+/// Notifies the client that a alliance or opposition has been removed (ZC_DELETE_RELATED_GUILD).
+/// 0184 <other guild id>.L <relation>.L
+/// relation:
+/// 0 = Ally
+/// 1 = Enemy
+void clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ if (fd <= 0)
+ return;
+ WFIFOHEAD(fd,packet_len(0x184));
+ WFIFOW(fd,0)=0x184;
+ WFIFOL(fd,2)=guild_id;
+ WFIFOL(fd,6)=flag;
+ WFIFOSET(fd,packet_len(0x184));
+}
+
+
+/// Notifies the client about the result of a opposition request (ZC_ACK_REQ_HOSTILE_GUILD).
+/// 0181 <result>.B
+/// result:
+/// 0 = Antagonist has been set.
+/// 1 = Guild has too many Antagonists.
+/// 2 = Already set as an Antagonist.
+/// 3 = Antagonists are disabled.
+void clif_guild_oppositionack(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x181));
+ WFIFOW(fd,0)=0x181;
+ WFIFOB(fd,2)=flag;
+ WFIFOSET(fd,packet_len(0x181));
+}
+
+
+/// Adds alliance or opposition (ZC_ADD_RELATED_GUILD).
+/// 0185 <relation>.L <guild id>.L <guild name>.24B
+/*
+void clif_guild_allianceadded(struct guild *g,int idx)
+{
+ unsigned char buf[64];
+ WBUFW(buf,0)=0x185;
+ WBUFL(buf,2)=g->alliance[idx].opposition;
+ WBUFL(buf,6)=g->alliance[idx].guild_id;
+ memcpy(WBUFP(buf,10),g->alliance[idx].name,NAME_LENGTH);
+ clif_send(buf,packet_len(0x185),guild_getavailablesd(g),GUILD);
+}
+*/
+
+
+/// Notifies the client about the result of a guild break (ZC_ACK_DISORGANIZE_GUILD_RESULT).
+/// 015e <reason>.L
+/// 0 = success
+/// 1 = invalid key (guild name, @see clif_parse_GuildBreak)
+/// 2 = there are still members in the guild
+void clif_guild_broken(struct map_session_data *sd,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x15e));
+ WFIFOW(fd,0)=0x15e;
+ WFIFOL(fd,2)=flag;
+ WFIFOSET(fd,packet_len(0x15e));
+}
+
+
+/// Displays emotion on an object (ZC_EMOTION).
+/// 00c0 <id>.L <type>.B
+/// type:
+/// enum emotion_type
+void clif_emotion(struct block_list *bl,int type)
+{
+ unsigned char buf[8];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0)=0xc0;
+ WBUFL(buf,2)=bl->id;
+ WBUFB(buf,6)=type;
+ clif_send(buf,packet_len(0xc0),bl,AREA);
+}
+
+
+/// Displays the contents of a talkiebox trap (ZC_TALKBOX_CHATCONTENTS).
+/// 0191 <id>.L <contents>.80B
+void clif_talkiebox(struct block_list* bl, const char* talkie)
+{
+ unsigned char buf[MESSAGE_SIZE+6];
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x191;
+ WBUFL(buf,2) = bl->id;
+ safestrncpy((char*)WBUFP(buf,6),talkie,MESSAGE_SIZE);
+ clif_send(buf,packet_len(0x191),bl,AREA);
+}
+
+
+/// Displays wedding effect centered on an object (ZC_CONGRATULATION).
+/// 01ea <id>.L
+void clif_wedding_effect(struct block_list *bl)
+{
+ unsigned char buf[6];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x1ea;
+ WBUFL(buf,2) = bl->id;
+ clif_send(buf, packet_len(0x1ea), bl, AREA);
+}
+
+
+/// Notifies the client of the name of the partner character (ZC_COUPLENAME).
+/// 01e6 <partner name>.24B
+void clif_callpartner(struct map_session_data *sd)
+{
+ unsigned char buf[26];
+ const char *p;
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0) = 0x1e6;
+
+ if( sd->status.partner_id )
+ {
+ if( ( p = map_charid2nick(sd->status.partner_id) ) != NULL )
+ {
+ memcpy(WBUFP(buf,2), p, NAME_LENGTH);
+ }
+ else
+ {
+ WBUFB(buf,2) = 0;
+ }
+ }
+ else
+ {// Send zero-length name if no partner, to initialize the client buffer.
+ WBUFB(buf,2) = 0;
+ }
+
+ clif_send(buf, packet_len(0x1e6), &sd->bl, AREA);
+}
+
+
+/// Initiates the partner "taming" process [DracoRPG] (ZC_START_COUPLE).
+/// 01e4
+/// This packet while still implemented by the client is no longer being officially used.
+/*
+void clif_marriage_process(struct map_session_data *sd)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x1e4));
+ WFIFOW(fd,0)=0x1e4;
+ WFIFOSET(fd,packet_len(0x1e4));
+}
+*/
+
+
+/// Notice of divorce (ZC_DIVORCE).
+/// 0205 <partner name>.24B
+void clif_divorced(struct map_session_data* sd, const char* name)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x205));
+ WFIFOW(fd,0)=0x205;
+ memcpy(WFIFOP(fd,2), name, NAME_LENGTH);
+ WFIFOSET(fd, packet_len(0x205));
+}
+
+
+/// Marriage proposal (ZC_REQ_COUPLE).
+/// 01e2 <account id>.L <char id>.L <char name>.24B
+/// This packet while still implemented by the client is no longer being officially used.
+/*
+void clif_marriage_proposal(int fd, struct map_session_data *sd, struct map_session_data* ssd)
+{
+ nullpo_retv(sd);
+
+ WFIFOHEAD(fd,packet_len(0x1e2));
+ WFIFOW(fd,0) = 0x1e2;
+ WFIFOL(fd,2) = ssd->status.account_id;
+ WFIFOL(fd,6) = ssd->status.char_id;
+ safestrncpy((char*)WFIFOP(fd,10), ssd->status.name, NAME_LENGTH);
+ WFIFOSET(fd, packet_len(0x1e2));
+}
+*/
+
+
+/*==========================================
+ *
+ *------------------------------------------*/
+void clif_disp_onlyself(struct map_session_data *sd, const char *mes, int len)
+{
+ clif_disp_message(&sd->bl, mes, len, SELF);
+}
+
+/*==========================================
+ * Displays a message using the guild-chat colors to the specified targets. [Skotlex]
+ *------------------------------------------*/
+void clif_disp_message(struct block_list* src, const char* mes, int len, enum send_target target)
+{
+ unsigned char buf[256];
+
+ if( len == 0 )
+ {
+ return;
+ }
+ else if( len > sizeof(buf)-5 )
+ {
+ ShowWarning("clif_disp_message: Truncated message '%s' (len=%d, max=%d, aid=%d).\n", mes, len, sizeof(buf)-5, src->id);
+ len = sizeof(buf)-5;
+ }
+
+ WBUFW(buf, 0) = 0x17f;
+ WBUFW(buf, 2) = len + 5;
+ safestrncpy((char*)WBUFP(buf,4), mes, len+1);
+ clif_send(buf, WBUFW(buf,2), src, target);
+}
+
+
+/// Notifies the client about the result of a request to disconnect another player (ZC_ACK_DISCONNECT_CHARACTER).
+/// 00cd <result>.L (unknown packet version or invalid information at packet_len_table)
+/// 00cd <result>.B
+/// result:
+/// 0 = failure
+/// 1 = success
+void clif_GM_kickack(struct map_session_data *sd, int id)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0xcd));
+ WFIFOW(fd,0) = 0xcd;
+ WFIFOB(fd,2) = id; // FIXME: this is not account id
+ WFIFOSET(fd, packet_len(0xcd));
+}
+
+
+void clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd)
+{
+ int fd = tsd->fd;
+
+ if( fd > 0 )
+ clif_authfail_fd(fd, 15);
+ else
+ map_quit(tsd);
+
+ if( sd )
+ clif_GM_kickack(sd,tsd->status.account_id);
+}
+
+
+/// Displays various manner-related status messages (ZC_ACK_GIVE_MANNER_POINT).
+/// 014a <result>.L
+/// result:
+/// 0 = "A manner point has been successfully aligned."
+/// 1 = MP_FAILURE_EXHAUST
+/// 2 = MP_FAILURE_ALREADY_GIVING
+/// 3 = "Chat Block has been applied by GM due to your ill-mannerous action."
+/// 4 = "Automated Chat Block has been applied due to Anti-Spam System."
+/// 5 = "You got a good point from %s."
+void clif_manner_message(struct map_session_data* sd, uint32 type)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x14a));
+ WFIFOW(fd,0) = 0x14a;
+ WFIFOL(fd,2) = type;
+ WFIFOSET(fd, packet_len(0x14a));
+}
+
+
+/// Followup to 0x14a type 3/5, informs who did the manner adjustment action (ZC_NOTIFY_MANNER_POINT_GIVEN).
+/// 014b <type>.B <GM name>.24B
+/// type:
+/// 0 = positive (unmute)
+/// 1 = negative (mute)
+void clif_GM_silence(struct map_session_data* sd, struct map_session_data* tsd, uint8 type)
+{
+ int fd;
+ nullpo_retv(sd);
+ nullpo_retv(tsd);
+
+ fd = tsd->fd;
+ WFIFOHEAD(fd,packet_len(0x14b));
+ WFIFOW(fd,0) = 0x14b;
+ WFIFOB(fd,2) = type;
+ safestrncpy((char*)WFIFOP(fd,3), sd->status.name, NAME_LENGTH);
+ WFIFOSET(fd, packet_len(0x14b));
+}
+
+
+/// Notifies the client about the result of a request to allow/deny whispers from a player (ZC_SETTING_WHISPER_PC).
+/// 00d1 <type>.B <result>.B
+/// type:
+/// 0 = /ex (deny)
+/// 1 = /in (allow)
+/// result:
+/// 0 = success
+/// 1 = failure
+/// 2 = too many blocks
+void clif_wisexin(struct map_session_data *sd,int type,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xd1));
+ WFIFOW(fd,0)=0xd1;
+ WFIFOB(fd,2)=type;
+ WFIFOB(fd,3)=flag;
+ WFIFOSET(fd,packet_len(0xd1));
+}
+
+/// Notifies the client about the result of a request to allow/deny whispers from anyone (ZC_SETTING_WHISPER_STATE).
+/// 00d2 <type>.B <result>.B
+/// type:
+/// 0 = /exall (deny)
+/// 1 = /inall (allow)
+/// result:
+/// 0 = success
+/// 1 = failure
+void clif_wisall(struct map_session_data *sd,int type,int flag)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0xd2));
+ WFIFOW(fd,0)=0xd2;
+ WFIFOB(fd,2)=type;
+ WFIFOB(fd,3)=flag;
+ WFIFOSET(fd,packet_len(0xd2));
+}
+
+
+/// Play a BGM! [Rikter/Yommy] (ZC_PLAY_NPC_BGM).
+/// 07fe <bgm>.24B
+void clif_playBGM(struct map_session_data* sd, const char* name)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x7fe));
+ WFIFOW(fd,0) = 0x7fe;
+ safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x7fe));
+}
+
+
+/// Plays/stops a wave sound (ZC_SOUND).
+/// 01d3 <file name>.24B <act>.B <term>.L <npc id>.L
+/// file name:
+/// relative to data\wav
+/// act:
+/// 0 = play (once)
+/// 1 = play (repeat, does not work)
+/// 2 = stops all sound instances of file name (does not work)
+/// term:
+/// unknown purpose, only relevant to act = 1
+/// npc id:
+/// The accustic direction of the sound is determined by the
+/// relative position of the NPC to the player (3D sound).
+void clif_soundeffect(struct map_session_data* sd, struct block_list* bl, const char* name, int type)
+{
+ int fd;
+
+ nullpo_retv(sd);
+ nullpo_retv(bl);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x1d3));
+ WFIFOW(fd,0) = 0x1d3;
+ safestrncpy((char*)WFIFOP(fd,2), name, NAME_LENGTH);
+ WFIFOB(fd,26) = type;
+ WFIFOL(fd,27) = 0;
+ WFIFOL(fd,31) = bl->id;
+ WFIFOSET(fd,packet_len(0x1d3));
+}
+
+void clif_soundeffectall(struct block_list* bl, const char* name, int type, enum send_target coverage)
+{
+ unsigned char buf[40];
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = 0x1d3;
+ safestrncpy((char*)WBUFP(buf,2), name, NAME_LENGTH);
+ WBUFB(buf,26) = type;
+ WBUFL(buf,27) = 0;
+ WBUFL(buf,31) = bl->id;
+ clif_send(buf, packet_len(0x1d3), bl, coverage);
+}
+
+
+/// Displays special effects (npcs, weather, etc) [Valaris] (ZC_NOTIFY_EFFECT2).
+/// 01f3 <id>.L <effect id>.L
+/// effect id:
+/// @see doc/effect_list.txt
+void clif_specialeffect(struct block_list* bl, int type, enum send_target target)
+{
+ unsigned char buf[24];
+
+ nullpo_retv(bl);
+
+ memset(buf, 0, packet_len(0x1f3));
+
+ WBUFW(buf,0) = 0x1f3;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = type;
+
+ clif_send(buf, packet_len(0x1f3), bl, target);
+
+ if (disguised(bl)) {
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf, packet_len(0x1f3), bl, SELF);
+ }
+}
+
+void clif_specialeffect_single(struct block_list* bl, int type, int fd)
+{
+ WFIFOHEAD(fd,10);
+ WFIFOW(fd,0) = 0x1f3;
+ WFIFOL(fd,2) = bl->id;
+ WFIFOL(fd,6) = type;
+ WFIFOSET(fd,10);
+}
+
+
+/// Notifies clients of an special/visual effect that accepts an value (ZC_NOTIFY_EFFECT3).
+/// 0284 <id>.L <effect id>.L <num data>.L
+/// effect id:
+/// @see doc/effect_list.txt
+/// num data:
+/// effect-dependent value
+void clif_specialeffect_value(struct block_list* bl, int effect_id, int num, send_target target)
+{
+ uint8 buf[14];
+
+ WBUFW(buf,0) = 0x284;
+ WBUFL(buf,2) = bl->id;
+ WBUFL(buf,6) = effect_id;
+ WBUFL(buf,10) = num;
+
+ clif_send(buf, packet_len(0x284), bl, target);
+
+ if( disguised(bl) )
+ {
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf, packet_len(0x284), bl, SELF);
+ }
+}
+// Modification of clif_messagecolor to send colored messages to players to chat log only (doesn't display overhead)
+/// 02c1 <packet len>.W <id>.L <color>.L <message>.?B
+int clif_colormes(struct map_session_data * sd, enum clif_colors color, const char* msg) {
+ unsigned short msg_len = strlen(msg) + 1;
+
+ WFIFOHEAD(sd->fd,msg_len + 12);
+ WFIFOW(sd->fd,0) = 0x2C1;
+ WFIFOW(sd->fd,2) = msg_len + 12;
+ WFIFOL(sd->fd,4) = 0;
+ WFIFOL(sd->fd,8) = color_table[color];
+ safestrncpy((char*)WFIFOP(sd->fd,12), msg, msg_len);
+ clif_send(WFIFOP(sd->fd,0), WFIFOW(sd->fd,2), &sd->bl, SELF);
+
+ return 0;
+}
+
+/// Monster/NPC color chat [SnakeDrak] (ZC_NPC_CHAT).
+/// 02c1 <packet len>.W <id>.L <color>.L <message>.?B
+void clif_messagecolor(struct block_list* bl, unsigned long color, const char* msg) {
+ unsigned short msg_len = strlen(msg) + 1;
+ uint8 buf[256];
+ color = (color & 0x0000FF) << 16 | (color & 0x00FF00) | (color & 0xFF0000) >> 16; // RGB to BGR
+
+ nullpo_retv(bl);
+
+ if( msg_len > sizeof(buf)-12 )
+ {
+ ShowWarning("clif_messagecolor: Truncating too long message '%s' (len=%u).\n", msg, msg_len);
+ msg_len = sizeof(buf)-12;
+ }
+
+ WBUFW(buf,0) = 0x2C1;
+ WBUFW(buf,2) = msg_len + 12;
+ WBUFL(buf,4) = bl->id;
+ WBUFL(buf,8) = color;
+ memcpy(WBUFP(buf,12), msg, msg_len);
+
+ clif_send(buf, WBUFW(buf,2), bl, AREA_CHAT_WOC);
+}
+
+/// Public chat message [Valaris] (ZC_NOTIFY_CHAT).
+/// 008d <packet len>.W <id>.L <message>.?B
+void clif_message(struct block_list* bl, const char* msg) {
+ unsigned short msg_len = strlen(msg) + 1;
+ uint8 buf[256];
+ nullpo_retv(bl);
+
+ if( msg_len > sizeof(buf)-8 ) {
+ ShowWarning("clif_message: Truncating too long message '%s' (len=%u).\n", msg, msg_len);
+ msg_len = sizeof(buf)-8;
+ }
+
+ WBUFW(buf,0) = 0x8d;
+ WBUFW(buf,2) = msg_len + 8;
+ WBUFL(buf,4) = bl->id;
+ safestrncpy((char*)WBUFP(buf,8), msg, msg_len);
+
+ clif_send(buf, WBUFW(buf,2), bl, AREA_CHAT_WOC);
+}
+
+// refresh the client's screen, getting rid of any effects
+void clif_refresh(struct map_session_data *sd)
+{
+ int i;
+ nullpo_retv(sd);
+
+ clif_changemap(sd,sd->mapindex,sd->bl.x,sd->bl.y);
+ clif_inventorylist(sd);
+ if(pc_iscarton(sd)) {
+ clif_cartlist(sd);
+ clif_updatestatus(sd,SP_CARTINFO);
+ }
+ clif_updatestatus(sd,SP_WEIGHT);
+ clif_updatestatus(sd,SP_MAXWEIGHT);
+ clif_updatestatus(sd,SP_STR);
+ clif_updatestatus(sd,SP_AGI);
+ clif_updatestatus(sd,SP_VIT);
+ clif_updatestatus(sd,SP_INT);
+ clif_updatestatus(sd,SP_DEX);
+ clif_updatestatus(sd,SP_LUK);
+ if (sd->spiritball)
+ clif_spiritball_single(sd->fd, sd);
+ for(i = 1; i < 5; i++){
+ if( sd->talisman[i] > 0 )
+ clif_talisman_single(sd->fd, sd, i);
+ }
+ if (sd->vd.cloth_color)
+ clif_refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF);
+ if(merc_is_hom_active(sd->hd))
+ clif_send_homdata(sd,SP_ACK,0);
+ if( sd->md ) {
+ clif_mercenary_info(sd);
+ clif_mercenary_skillblock(sd);
+ }
+ if( sd->ed )
+ clif_elemental_info(sd);
+ map_foreachinrange(clif_getareachar,&sd->bl,AREA_SIZE,BL_ALL,sd);
+ clif_weather_check(sd);
+ if( sd->chatID )
+ chat_leavechat(sd,0);
+ if( sd->state.vending )
+ clif_openvending(sd, sd->bl.id, sd->vending);
+ if( pc_issit(sd) )
+ clif_sitting(&sd->bl); // FIXME: just send to self, not area
+ if( pc_isdead(sd) ) // When you refresh, resend the death packet.
+ clif_clearunit_single(sd->bl.id,CLR_DEAD,sd->fd);
+ else
+ clif_changed_dir(&sd->bl, SELF);
+
+ // unlike vending, resuming buyingstore crashes the client.
+ buyingstore_close(sd);
+
+ mail_clear(sd);
+}
+
+
+/// Updates the object's (bl) name on client.
+/// 0095 <id>.L <char name>.24B (ZC_ACK_REQNAME)
+/// 0195 <id>.L <char name>.24B <party name>.24B <guild name>.24B <position name>.24B (ZC_ACK_REQNAMEALL)
+void clif_charnameack (int fd, struct block_list *bl)
+{
+ unsigned char buf[103];
+ int cmd = 0x95, i, ps = -1;
+
+ nullpo_retv(bl);
+
+ WBUFW(buf,0) = cmd;
+ WBUFL(buf,2) = bl->id;
+
+ switch( bl->type )
+ {
+ case BL_PC:
+ {
+ struct map_session_data *ssd = (struct map_session_data *)bl;
+ struct party_data *p = NULL;
+ struct guild *g = NULL;
+
+ //Requesting your own "shadow" name. [Skotlex]
+ if (ssd->fd == fd && ssd->disguise)
+ WBUFL(buf,2) = -bl->id;
+
+ if( ssd->fakename[0] )
+ {
+ WBUFW(buf, 0) = cmd = 0x195;
+ memcpy(WBUFP(buf,6), ssd->fakename, NAME_LENGTH);
+ WBUFB(buf,30) = WBUFB(buf,54) = WBUFB(buf,78) = 0;
+ break;
+ }
+ memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH);
+
+ if( ssd->status.party_id )
+ {
+ p = party_search(ssd->status.party_id);
+ }
+ if( ssd->status.guild_id )
+ {
+ if( ( g = guild_search(ssd->status.guild_id) ) != NULL )
+ {
+ ARR_FIND(0, g->max_member, i, g->member[i].account_id == ssd->status.account_id && g->member[i].char_id == ssd->status.char_id);
+ if( i < g->max_member ) ps = g->member[i].position;
+ }
+ }
+
+ if( !battle_config.display_party_name && g == NULL )
+ {// do not display party unless the player is also in a guild
+ p = NULL;
+ }
+
+ if (p == NULL && g == NULL)
+ break;
+
+ WBUFW(buf, 0) = cmd = 0x195;
+ if (p)
+ memcpy(WBUFP(buf,30), p->party.name, NAME_LENGTH);
+ else
+ WBUFB(buf,30) = 0;
+
+ if (g && ps >= 0 && ps < MAX_GUILDPOSITION)
+ {
+ memcpy(WBUFP(buf,54), g->name,NAME_LENGTH);
+ memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH);
+ } else { //Assume no guild.
+ WBUFB(buf,54) = 0;
+ WBUFB(buf,78) = 0;
+ }
+ }
+ break;
+ //[blackhole89]
+ case BL_HOM:
+ memcpy(WBUFP(buf,6), ((TBL_HOM*)bl)->homunculus.name, NAME_LENGTH);
+ break;
+ case BL_MER:
+ memcpy(WBUFP(buf,6), ((TBL_MER*)bl)->db->name, NAME_LENGTH);
+ break;
+ case BL_PET:
+ memcpy(WBUFP(buf,6), ((TBL_PET*)bl)->pet.name, NAME_LENGTH);
+ break;
+ case BL_NPC:
+ memcpy(WBUFP(buf,6), ((TBL_NPC*)bl)->name, NAME_LENGTH);
+ break;
+ case BL_MOB:
+ {
+ struct mob_data *md = (struct mob_data *)bl;
+ nullpo_retv(md);
+
+ memcpy(WBUFP(buf,6), md->name, NAME_LENGTH);
+ if( md->guardian_data && md->guardian_data->guild_id )
+ {
+ WBUFW(buf, 0) = cmd = 0x195;
+ WBUFB(buf,30) = 0;
+ memcpy(WBUFP(buf,54), md->guardian_data->guild_name, NAME_LENGTH);
+ memcpy(WBUFP(buf,78), md->guardian_data->castle->castle_name, NAME_LENGTH);
+ }
+ else if( battle_config.show_mob_info )
+ {
+ char mobhp[50], *str_p = mobhp;
+ WBUFW(buf, 0) = cmd = 0x195;
+ if( battle_config.show_mob_info&4 )
+ str_p += sprintf(str_p, "Lv. %d | ", md->level);
+ if( battle_config.show_mob_info&1 )
+ str_p += sprintf(str_p, "HP: %u/%u | ", md->status.hp, md->status.max_hp);
+ if( battle_config.show_mob_info&2 )
+ str_p += sprintf(str_p, "HP: %d%% | ", get_percentage(md->status.hp, md->status.max_hp));
+ //Even thought mobhp ain't a name, we send it as one so the client
+ //can parse it. [Skotlex]
+ if( str_p != mobhp )
+ {
+ *(str_p-3) = '\0'; //Remove trailing space + pipe.
+ memcpy(WBUFP(buf,30), mobhp, NAME_LENGTH);
+ WBUFB(buf,54) = 0;
+ WBUFB(buf,78) = 0;
+ }
+ }
+ }
+ break;
+ case BL_CHAT: //FIXME: Clients DO request this... what should be done about it? The chat's title may not fit... [Skotlex]
+// memcpy(WBUFP(buf,6), (struct chat*)->title, NAME_LENGTH);
+// break;
+ return;
+ case BL_ELEM:
+ memcpy(WBUFP(buf,6), ((TBL_ELEM*)bl)->db->name, NAME_LENGTH);
+ break;
+ default:
+ ShowError("clif_charnameack: bad type %d(%d)\n", bl->type, bl->id);
+ return;
+ }
+
+ // if no receipient specified just update nearby clients
+ if (fd == 0)
+ clif_send(buf, packet_len(cmd), bl, AREA);
+ else {
+ WFIFOHEAD(fd, packet_len(cmd));
+ memcpy(WFIFOP(fd, 0), buf, packet_len(cmd));
+ WFIFOSET(fd, packet_len(cmd));
+ }
+}
+
+
+//Used to update when a char leaves a party/guild. [Skotlex]
+//Needed because when you send a 0x95 packet, the client will not remove the cached party/guild info that is not sent.
+void clif_charnameupdate (struct map_session_data *ssd)
+{
+ unsigned char buf[103];
+ int cmd = 0x195, ps = -1, i;
+ struct party_data *p = NULL;
+ struct guild *g = NULL;
+
+ nullpo_retv(ssd);
+
+ if( ssd->fakename[0] )
+ return; //No need to update as the party/guild was not displayed anyway.
+
+ WBUFW(buf,0) = cmd;
+ WBUFL(buf,2) = ssd->bl.id;
+
+ memcpy(WBUFP(buf,6), ssd->status.name, NAME_LENGTH);
+
+ if (!battle_config.display_party_name) {
+ if (ssd->status.party_id > 0 && ssd->status.guild_id > 0 && (g = guild_search(ssd->status.guild_id)) != NULL)
+ p = party_search(ssd->status.party_id);
+ }else{
+ if (ssd->status.party_id > 0)
+ p = party_search(ssd->status.party_id);
+ }
+
+ if( ssd->status.guild_id > 0 && (g = guild_search(ssd->status.guild_id)) != NULL )
+ {
+ ARR_FIND(0, g->max_member, i, g->member[i].account_id == ssd->status.account_id && g->member[i].char_id == ssd->status.char_id);
+ if( i < g->max_member ) ps = g->member[i].position;
+ }
+
+ if( p )
+ memcpy(WBUFP(buf,30), p->party.name, NAME_LENGTH);
+ else
+ WBUFB(buf,30) = 0;
+
+ if( g && ps >= 0 && ps < MAX_GUILDPOSITION )
+ {
+ memcpy(WBUFP(buf,54), g->name,NAME_LENGTH);
+ memcpy(WBUFP(buf,78), g->position[ps].name, NAME_LENGTH);
+ }
+ else
+ {
+ WBUFB(buf,54) = 0;
+ WBUFB(buf,78) = 0;
+ }
+
+ // Update nearby clients
+ clif_send(buf, packet_len(cmd), &ssd->bl, AREA);
+}
+
+
+/// Taekwon Jump (TK_HIGHJUMP) effect (ZC_HIGHJUMP).
+/// 01ff <id>.L <x>.W <y>.W
+///
+/// Visually moves(instant) a character to x,y. The char moves even
+/// when the target cell isn't walkable. If the char is sitting it
+/// stays that way.
+void clif_slide(struct block_list *bl, int x, int y)
+{
+ unsigned char buf[10];
+ nullpo_retv(bl);
+
+ WBUFW(buf, 0) = 0x01ff;
+ WBUFL(buf, 2) = bl->id;
+ WBUFW(buf, 6) = x;
+ WBUFW(buf, 8) = y;
+ clif_send(buf, packet_len(0x1ff), bl, AREA);
+
+ if( disguised(bl) )
+ {
+ WBUFL(buf,2) = -bl->id;
+ clif_send(buf, packet_len(0x1ff), bl, SELF);
+ }
+}
+
+
+/*------------------------------------------
+ * @me command by lordalfa, rewritten implementation by Skotlex
+ *------------------------------------------*/
+void clif_disp_overhead(struct map_session_data *sd, const char* mes)
+{
+ unsigned char buf[256]; //This should be more than sufficient, the theorical max is CHAT_SIZE + 8 (pads and extra inserted crap)
+ int len_mes = strlen(mes)+1; //Account for \0
+
+ if (len_mes > sizeof(buf)-8) {
+ ShowError("clif_disp_overhead: Message too long (length %d)\n", len_mes);
+ len_mes = sizeof(buf)-8; //Trunk it to avoid problems.
+ }
+ // send message to others
+ WBUFW(buf,0) = 0x8d;
+ WBUFW(buf,2) = len_mes + 8; // len of message + 8 (command+len+id)
+ WBUFL(buf,4) = sd->bl.id;
+ safestrncpy((char*)WBUFP(buf,8), mes, len_mes);
+ clif_send(buf, WBUFW(buf,2), &sd->bl, AREA_CHAT_WOC);
+
+ // send back message to the speaker
+ WBUFW(buf,0) = 0x8e;
+ WBUFW(buf, 2) = len_mes + 4;
+ safestrncpy((char*)WBUFP(buf,4), mes, len_mes);
+ clif_send(buf, WBUFW(buf,2), &sd->bl, SELF);
+}
+
+/*==========================
+ * Minimap fix [Kevin]
+ * Remove dot from minimap
+ *--------------------------*/
+void clif_party_xy_remove(struct map_session_data *sd)
+{
+ unsigned char buf[16];
+ nullpo_retv(sd);
+ WBUFW(buf,0)=0x107;
+ WBUFL(buf,2)=sd->status.account_id;
+ WBUFW(buf,6)=-1;
+ WBUFW(buf,8)=-1;
+ clif_send(buf,packet_len(0x107),&sd->bl,PARTY_SAMEMAP_WOS);
+}
+
+
+/// Displays a skill message (thanks to Rayce) (ZC_SKILLMSG).
+/// 0215 <msg id>.L
+/// msg id:
+/// 0x15 = End all negative status (PA_GOSPEL)
+/// 0x16 = Immunity to all status (PA_GOSPEL)
+/// 0x17 = MaxHP +100% (PA_GOSPEL)
+/// 0x18 = MaxSP +100% (PA_GOSPEL)
+/// 0x19 = All stats +20 (PA_GOSPEL)
+/// 0x1c = Enchant weapon with Holy element (PA_GOSPEL)
+/// 0x1d = Enchant armor with Holy element (PA_GOSPEL)
+/// 0x1e = DEF +25% (PA_GOSPEL)
+/// 0x1f = ATK +100% (PA_GOSPEL)
+/// 0x20 = HIT/Flee +50 (PA_GOSPEL)
+/// 0x28 = Full strip failed because of coating (ST_FULLSTRIP)
+/// ? = nothing
+void clif_gospel_info(struct map_session_data *sd, int type)
+{
+ int fd=sd->fd;
+ WFIFOHEAD(fd,packet_len(0x215));
+ WFIFOW(fd,0)=0x215;
+ WFIFOL(fd,2)=type;
+ WFIFOSET(fd, packet_len(0x215));
+
+}
+
+
+/// Multi-purpose mission information packet (ZC_STARSKILL).
+/// 020e <mapname>.24B <monster_id>.L <star>.B <result>.B
+/// result:
+/// 0 = Star Gladiator %s has designed <mapname>'s as the %s.
+/// star:
+/// 0 = Place of the Sun
+/// 1 = Place of the Moon
+/// 2 = Place of the Stars
+/// 1 = Star Gladiator %s's %s: <mapname>
+/// star:
+/// 0 = Place of the Sun
+/// 1 = Place of the Moon
+/// 2 = Place of the Stars
+/// 10 = Star Gladiator %s has designed <mapname>'s as the %s.
+/// star:
+/// 0 = Target of the Sun
+/// 1 = Target of the Moon
+/// 2 = Target of the Stars
+/// 11 = Star Gladiator %s's %s: <mapname used as monster name>
+/// star:
+/// 0 = Monster of the Sun
+/// 1 = Monster of the Moon
+/// 2 = Monster of the Stars
+/// 20 = [TaeKwon Mission] Target Monster : <mapname used as monster name> (<star>%)
+/// 21 = [Taming Mission] Target Monster : <mapname used as monster name>
+/// 22 = [Collector Rank] Target Item : <monster_id used as item id>
+/// 30 = [Sun, Moon and Stars Angel] Designed places and monsters have been reset.
+/// 40 = Target HP : <monster_id used as HP>
+void clif_starskill(struct map_session_data* sd, const char* mapname, int monster_id, unsigned char star, unsigned char result)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x20e));
+ WFIFOW(fd,0) = 0x20e;
+ safestrncpy((char*)WFIFOP(fd,2), mapname, NAME_LENGTH);
+ WFIFOL(fd,26) = monster_id;
+ WFIFOB(fd,30) = star;
+ WFIFOB(fd,31) = result;
+ WFIFOSET(fd,packet_len(0x20e));
+}
+
+/*==========================================
+ * Info about Star Glaldiator save map [Komurka]
+ * type: 1: Information, 0: Map registered
+ *------------------------------------------*/
+void clif_feel_info(struct map_session_data* sd, unsigned char feel_level, unsigned char type)
+{
+ char mapname[MAP_NAME_LENGTH_EXT];
+
+ mapindex_getmapname_ext(mapindex_id2name(sd->feel_map[feel_level].index), mapname);
+ clif_starskill(sd, mapname, 0, feel_level, type ? 1 : 0);
+}
+
+/*==========================================
+ * Info about Star Glaldiator hate mob [Komurka]
+ * type: 1: Register mob, 0: Information.
+ *------------------------------------------*/
+void clif_hate_info(struct map_session_data *sd, unsigned char hate_level,int class_, unsigned char type)
+{
+ if( pcdb_checkid(class_) )
+ {
+ clif_starskill(sd, job_name(class_), class_, hate_level, type ? 10 : 11);
+ }
+ else if( mobdb_checkid(class_) )
+ {
+ clif_starskill(sd, mob_db(class_)->jname, class_, hate_level, type ? 10 : 11);
+ }
+ else
+ {
+ ShowWarning("clif_hate_info: Received invalid class %d for this packet (char_id=%d, hate_level=%u, type=%u).\n", class_, sd->status.char_id, (unsigned int)hate_level, (unsigned int)type);
+ }
+}
+
+/*==========================================
+ * Info about TaeKwon Do TK_MISSION mob [Skotlex]
+ *------------------------------------------*/
+void clif_mission_info(struct map_session_data *sd, int mob_id, unsigned char progress)
+{
+ clif_starskill(sd, mob_db(mob_id)->jname, mob_id, progress, 20);
+}
+
+/*==========================================
+ * Feel/Hate reset (thanks to Rayce) [Skotlex]
+ *------------------------------------------*/
+void clif_feel_hate_reset(struct map_session_data *sd)
+{
+ clif_starskill(sd, "", 0, 0, 30);
+}
+
+
+/// Equip window (un)tick ack (ZC_CONFIG).
+/// 02d9 <type>.L <value>.L
+/// type:
+/// 0 = open equip window
+/// value:
+/// 0 = disabled
+/// 1 = enabled
+void clif_equiptickack(struct map_session_data* sd, int flag)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x2d9));
+ WFIFOW(fd, 0) = 0x2d9;
+ WFIFOL(fd, 2) = 0;
+ WFIFOL(fd, 6) = flag;
+ WFIFOSET(fd, packet_len(0x2d9));
+}
+
+
+/// The player's 'view equip' state, sent during login (ZC_CONFIG_NOTIFY).
+/// 02da <open equip window>.B
+/// open equip window:
+/// 0 = disabled
+/// 1 = enabled
+void clif_equipcheckbox(struct map_session_data* sd)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x2da));
+ WFIFOW(fd, 0) = 0x2da;
+ WFIFOB(fd, 2) = (sd->status.show_equip ? 1 : 0);
+ WFIFOSET(fd, packet_len(0x2da));
+}
+
+
+/// Sends info about a player's equipped items.
+/// 02d7 <packet len>.W <name>.24B <class>.W <hairstyle>.W <up-viewid>.W <mid-viewid>.W <low-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.26B* (ZC_EQUIPWIN_MICROSCOPE)
+/// 02d7 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE, PACKETVER >= 20100629)
+/// 0859 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE2, PACKETVER >= 20101124)
+/// 0859 <packet len>.W <name>.24B <class>.W <hairstyle>.W <bottom-viewid>.W <mid-viewid>.W <up-viewid>.W <robe>.W <haircolor>.W <cloth-dye>.W <gender>.B {equip item}.28B* (ZC_EQUIPWIN_MICROSCOPE2, PACKETVER >= 20110111)
+void clif_viewequip_ack(struct map_session_data* sd, struct map_session_data* tsd)
+{
+ uint8* buf;
+ int i, n, fd, offset = 0;
+#if PACKETVER < 20100629
+ const int s = 26;
+#else
+ const int s = 28;
+#endif
+ nullpo_retv(sd);
+ nullpo_retv(tsd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, MAX_INVENTORY * s + 43);
+ buf = WFIFOP(fd,0);
+
+#if PACKETVER < 20101124
+ WBUFW(buf, 0) = 0x2d7;
+#else
+ WBUFW(buf, 0) = 0x859;
+#endif
+ safestrncpy((char*)WBUFP(buf, 4), tsd->status.name, NAME_LENGTH);
+ WBUFW(buf,28) = tsd->status.class_;
+ WBUFW(buf,30) = tsd->vd.hair_style;
+ WBUFW(buf,32) = tsd->vd.head_bottom;
+ WBUFW(buf,34) = tsd->vd.head_mid;
+ WBUFW(buf,36) = tsd->vd.head_top;
+#if PACKETVER >= 20110111
+ WBUFW(buf,38) = tsd->vd.robe;
+ offset+= 2;
+ buf = WBUFP(buf,2);
+#endif
+ WBUFW(buf,38) = tsd->vd.hair_color;
+ WBUFW(buf,40) = tsd->vd.cloth_color;
+ WBUFB(buf,42) = tsd->vd.sex;
+
+ for(i=0,n=0; i < MAX_INVENTORY; i++)
+ {
+ if (tsd->status.inventory[i].nameid <= 0 || tsd->inventory_data[i] == NULL) // Item doesn't exist
+ continue;
+ if (!itemdb_isequip2(tsd->inventory_data[i])) // Is not equippable
+ continue;
+
+ // Inventory position
+ WBUFW(buf, n*s+43) = i + 2;
+ // Add refine, identify flag, element, etc.
+ clif_item_sub(WBUFP(buf,0), n*s+45, &tsd->status.inventory[i], tsd->inventory_data[i], pc_equippoint(tsd, i));
+ // Add cards
+ clif_addcards(WBUFP(buf, n*s+55), &tsd->status.inventory[i]);
+ // Expiration date stuff, if all of those are set to 0 then the client doesn't show anything related (6 bytes)
+ WBUFL(buf, n*s+63) = tsd->status.inventory[i].expire_time;
+ WBUFW(buf, n*s+67) = 0;
+#if PACKETVER >= 20100629
+ if (tsd->inventory_data[i]->equip&EQP_VISIBLE)
+ WBUFW(buf, n*s+69) = tsd->inventory_data[i]->look;
+ else
+ WBUFW(buf, n*s+69) = 0;
+#endif
+ n++;
+ }
+
+ WFIFOW(fd, 2) = 43+offset+n*s; // Set length
+ WFIFOSET(fd, WFIFOW(fd, 2));
+}
+
+
+/// Display msgstringtable.txt string (ZC_MSG).
+/// 0291 <message>.W
+void clif_msg(struct map_session_data* sd, unsigned short id)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x291));
+ WFIFOW(fd, 0) = 0x291;
+ WFIFOW(fd, 2) = id; // zero-based msgstringtable.txt index
+ WFIFOSET(fd, packet_len(0x291));
+}
+
+
+/// Display msgstringtable.txt string and fill in a valid for %d format (ZC_MSG_VALUE).
+/// 0x7e2 <message>.W <value>.L
+void clif_msg_value(struct map_session_data* sd, unsigned short id, int value)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x7e2));
+ WFIFOW(fd,0) = 0x7e2;
+ WFIFOW(fd,2) = id;
+ WFIFOL(fd,4) = value;
+ WFIFOSET(fd, packet_len(0x7e2));
+}
+
+
+/// Displays msgstringtable.txt string, prefixed with a skill name. (ZC_MSG_SKILL).
+/// 07e6 <skill id>.W <msg id>.L
+///
+/// NOTE: Message has following format and is printed in color 0xCDCDFF (purple):
+/// "[SkillName] Message"
+void clif_msg_skill(struct map_session_data* sd, uint16 skill_id, int msg_id)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x7e6));
+ WFIFOW(fd,0) = 0x7e6;
+ WFIFOW(fd,2) = skill_id;
+ WFIFOL(fd,4) = msg_id;
+ WFIFOSET(fd, packet_len(0x7e6));
+}
+
+
+/// View player equip request denied
+void clif_viewequip_fail(struct map_session_data* sd)
+{
+ clif_msg(sd, 0x54d);
+}
+
+
+/// Validates one global/guild/party/whisper message packet and tries to recognize its components.
+/// Returns true if the packet was parsed successfully.
+/// Formats: 0 - <packet id>.w <packet len>.w (<name> : <message>).?B 00
+/// 1 - <packet id>.w <packet len>.w <name>.24B <message>.?B 00
+static bool clif_process_message(struct map_session_data* sd, int format, char** name_, int* namelen_, char** message_, int* messagelen_)
+{
+ char *text, *name, *message;
+ unsigned int packetlen, textlen, namelen, messagelen;
+ int fd = sd->fd;
+
+ *name_ = NULL;
+ *namelen_ = 0;
+ *message_ = NULL;
+ *messagelen_ = 0;
+
+ packetlen = RFIFOW(fd,2);
+ // basic structure checks
+ if( packetlen < 4 + 1 )
+ { // 4-byte header and at least an empty string is expected
+ ShowWarning("clif_process_message: Received malformed packet from player '%s' (no message data)!\n", sd->status.name);
+ return false;
+ }
+
+ text = (char*)RFIFOP(fd,4);
+ textlen = packetlen - 4;
+
+ // process <name> part of the packet
+ if( format == 0 )
+ {// name and message are separated by ' : '
+ // validate name
+ name = text;
+ namelen = strnlen(sd->status.name, NAME_LENGTH-1); // name length (w/o zero byte)
+
+ if( strncmp(name, sd->status.name, namelen) || // the text must start with the speaker's name
+ name[namelen] != ' ' || name[namelen+1] != ':' || name[namelen+2] != ' ' ) // followed by ' : '
+ {
+ //Hacked message, or infamous "client desynch" issue where they pick one char while loading another.
+ ShowWarning("clif_process_message: Player '%s' sent a message using an incorrect name! Forcing a relog...\n", sd->status.name);
+ set_eof(fd); // Just kick them out to correct it.
+ return false;
+ }
+
+ message = name + namelen + 3;
+ messagelen = textlen - namelen - 3; // this should be the message length (w/ zero byte included)
+ }
+ else
+ {// name has fixed width
+ if( textlen < NAME_LENGTH + 1 )
+ {
+ ShowWarning("clif_process_message: Received malformed packet from player '%s' (packet length is incorrect)!\n", sd->status.name);
+ return false;
+ }
+
+ // validate name
+ name = text;
+ namelen = strnlen(name, NAME_LENGTH-1); // name length (w/o zero byte)
+
+ if( name[namelen] != '\0' )
+ { // only restriction is that the name must be zero-terminated
+ ShowWarning("clif_process_message: Player '%s' sent an unterminated name!\n", sd->status.name);
+ return false;
+ }
+
+ message = name + NAME_LENGTH;
+ messagelen = textlen - NAME_LENGTH; // this should be the message length (w/ zero byte included)
+ }
+
+ if( messagelen != strnlen(message, messagelen)+1 )
+ { // the declared length must match real length
+ ShowWarning("clif_process_message: Received malformed packet from player '%s' (length is incorrect)!\n", sd->status.name);
+ return false;
+ }
+ // verify <message> part of the packet
+ if( message[messagelen-1] != '\0' )
+ { // message must be zero-terminated
+ ShowWarning("clif_process_message: Player '%s' sent an unterminated message string!\n", sd->status.name);
+ return false;
+ }
+ if( messagelen > CHAT_SIZE_MAX-1 )
+ { // messages mustn't be too long
+ // Normally you can only enter CHATBOX_SIZE-1 letters into the chat box, but Frost Joke / Dazzler's text can be longer.
+ // Also, the physical size of strings that use multibyte encoding can go multiple times over the chatbox capacity.
+ // Neither the official client nor server place any restriction on the length of the data in the packet,
+ // but we'll only allow reasonably long strings here. This also makes sure that they fit into the `chatlog` table.
+ ShowWarning("clif_process_message: Player '%s' sent a message too long ('%.*s')!\n", sd->status.name, CHAT_SIZE_MAX-1, message);
+ return false;
+ }
+
+ *name_ = name;
+ *namelen_ = namelen;
+ *message_ = message;
+ *messagelen_ = messagelen;
+ return true;
+}
+
+// ---------------------
+// clif_guess_PacketVer
+// ---------------------
+// Parses a WantToConnection packet to try to identify which is the packet version used. [Skotlex]
+// error codes:
+// 0 - Success
+// 1 - Unknown packet_ver
+// 2 - Invalid account_id
+// 3 - Invalid char_id
+// 4 - Invalid login_id1 (reserved)
+// 5 - Invalid client_tick (reserved)
+// 6 - Invalid sex
+// Only the first 'invalid' error that appears is used.
+static int clif_guess_PacketVer(int fd, int get_previous, int *error)
+{
+ static int err = 1;
+ static int packet_ver = -1;
+ int cmd, packet_len, value; //Value is used to temporarily store account/char_id/sex
+
+ if (get_previous)
+ {//For quick reruns, since the normal code flow is to fetch this once to identify the packet version, then again in the wanttoconnect function. [Skotlex]
+ if( error )
+ *error = err;
+ return packet_ver;
+ }
+
+ //By default, start searching on the default one.
+ err = 1;
+ packet_ver = clif_config.packet_db_ver;
+ cmd = RFIFOW(fd,0);
+ packet_len = RFIFOREST(fd);
+
+#define SET_ERROR(n) \
+ if( err == 1 )\
+ err = n;\
+//define SET_ERROR
+
+ // FIXME: If the packet is not received at once, this will FAIL.
+ // Figure out, when it happens, that only part of the packet is
+ // received, or fix the function to be able to deal with that
+ // case.
+#define CHECK_PACKET_VER() \
+ if( cmd != clif_config.connect_cmd[packet_ver] || packet_len != packet_db[packet_ver][cmd].len )\
+ ;/* not wanttoconnection or wrong length */\
+ else if( (value=(int)RFIFOL(fd, packet_db[packet_ver][cmd].pos[0])) < START_ACCOUNT_NUM || value > END_ACCOUNT_NUM )\
+ { SET_ERROR(2); }/* invalid account_id */\
+ else if( (value=(int)RFIFOL(fd, packet_db[packet_ver][cmd].pos[1])) <= 0 )\
+ { SET_ERROR(3); }/* invalid char_id */\
+ /* RFIFOL(fd, packet_db[packet_ver][cmd].pos[2]) - don't care about login_id1 */\
+ /* RFIFOL(fd, packet_db[packet_ver][cmd].pos[3]) - don't care about client_tick */\
+ else if( (value=(int)RFIFOB(fd, packet_db[packet_ver][cmd].pos[4])) != 0 && value != 1 )\
+ { SET_ERROR(6); }/* invalid sex */\
+ else\
+ {\
+ err = 0;\
+ if( error )\
+ *error = 0;\
+ return packet_ver;\
+ }\
+//define CHECK_PACKET_VER
+
+ CHECK_PACKET_VER();//Default packet version found.
+
+ for (packet_ver = MAX_PACKET_VER; packet_ver > 0; packet_ver--)
+ { //Start guessing the version, giving priority to the newer ones. [Skotlex]
+ CHECK_PACKET_VER();
+ }
+ if( error )
+ *error = err;
+ packet_ver = -1;
+ return -1;
+#undef SET_ERROR
+#undef CHECK_PACKET_VER
+}
+
+// ------------
+// clif_parse_*
+// ------------
+// Parses incoming (player) connection
+
+
+/// Request to connect to map-server.
+/// 0072 <account id>.L <char id>.L <auth code>.L <client time>.L <gender>.B (CZ_ENTER)
+/// 0436 <account id>.L <char id>.L <auth code>.L <client time>.L <gender>.B (CZ_ENTER2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_WantToConnection(int fd, TBL_PC* sd)
+{
+ struct block_list* bl;
+ struct auth_node* node;
+ int cmd, account_id, char_id, login_id1, sex;
+ unsigned int client_tick; //The client tick is a tick, therefore it needs be unsigned. [Skotlex]
+ int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 (by [Yor])
+
+ if (sd) {
+ ShowError("clif_parse_WantToConnection : invalid request (character already logged in)\n");
+ return;
+ }
+
+ // Only valid packet version get here
+ packet_ver = clif_guess_PacketVer(fd, 1, NULL);
+
+ cmd = RFIFOW(fd,0);
+ account_id = RFIFOL(fd, packet_db[packet_ver][cmd].pos[0]);
+ char_id = RFIFOL(fd, packet_db[packet_ver][cmd].pos[1]);
+ login_id1 = RFIFOL(fd, packet_db[packet_ver][cmd].pos[2]);
+ client_tick = RFIFOL(fd, packet_db[packet_ver][cmd].pos[3]);
+ sex = RFIFOB(fd, packet_db[packet_ver][cmd].pos[4]);
+
+ if( packet_ver < 5 || // reject really old client versions
+ (packet_ver <= 9 && (battle_config.packet_ver_flag & 1) == 0) || // older than 6sept04
+ (packet_ver > 9 && (battle_config.packet_ver_flag & 1<<(packet_ver-9)) == 0)) // version not allowed
+ {// packet version rejected
+ ShowInfo("Rejected connection attempt, forbidden packet version (AID/CID: '"CL_WHITE"%d/%d"CL_RESET"', Packet Ver: '"CL_WHITE"%d"CL_RESET"', IP: '"CL_WHITE"%s"CL_RESET"').\n", account_id, char_id, packet_ver, ip2str(session[fd]->client_addr, NULL));
+ WFIFOHEAD(fd,packet_len(0x6a));
+ WFIFOW(fd,0) = 0x6a;
+ WFIFOB(fd,2) = 5; // Your Game's EXE file is not the latest version
+ WFIFOSET(fd,packet_len(0x6a));
+ set_eof(fd);
+ return;
+ }
+
+ if( runflag != MAPSERVER_ST_RUNNING )
+ {// not allowed
+ clif_authfail_fd(fd,1);// server closed
+ return;
+ }
+
+ //Check for double login.
+ bl = map_id2bl(account_id);
+ if(bl && bl->type != BL_PC) {
+ ShowError("clif_parse_WantToConnection: a non-player object already has id %d, please increase the starting account number\n", account_id);
+ WFIFOHEAD(fd,packet_len(0x6a));
+ WFIFOW(fd,0) = 0x6a;
+ WFIFOB(fd,2) = 3; // Rejected by server
+ WFIFOSET(fd,packet_len(0x6a));
+ set_eof(fd);
+ return;
+ }
+
+ if (bl ||
+ ((node=chrif_search(account_id)) && //An already existing node is valid only if it is for this login.
+ !(node->account_id == account_id && node->char_id == char_id && node->state == ST_LOGIN)))
+ {
+ clif_authfail_fd(fd, 8); //Still recognizes last connection
+ return;
+ }
+
+ CREATE(sd, TBL_PC, 1);
+ sd->fd = fd;
+ sd->packet_ver = packet_ver;
+ session[fd]->session_data = sd;
+
+ pc_setnewpc(sd, account_id, char_id, login_id1, client_tick, sex, fd);
+
+#if PACKETVER < 20070521
+ WFIFOHEAD(fd,4);
+ WFIFOL(fd,0) = sd->bl.id;
+ WFIFOSET(fd,4);
+#else
+ WFIFOHEAD(fd,packet_len(0x283));
+ WFIFOW(fd,0) = 0x283;
+ WFIFOL(fd,2) = sd->bl.id;
+ WFIFOSET(fd,packet_len(0x283));
+#endif
+
+ chrif_authreq(sd);
+}
+
+
+/// Notification from the client, that it has finished map loading and is about to display player's character (CZ_NOTIFY_ACTORINIT).
+/// 007d
+void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
+{
+ if(sd->bl.prev != NULL)
+ return;
+
+ if (!sd->state.active)
+ { //Character loading is not complete yet!
+ //Let pc_reg_received reinvoke this when ready.
+ sd->state.connect_new = 0;
+ return;
+ }
+
+ if (sd->state.rewarp)
+ { //Rewarp player.
+ sd->state.rewarp = 0;
+ clif_changemap(sd, sd->mapindex, sd->bl.x, sd->bl.y);
+ return;
+ }
+
+ sd->state.warping = 0;
+
+ // look
+#if PACKETVER < 4
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ clif_changelook(&sd->bl,LOOK_SHIELD,sd->status.shield);
+#else
+ clif_changelook(&sd->bl,LOOK_WEAPON,0);
+#endif
+
+ if(sd->vd.cloth_color)
+ clif_refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF);
+
+ // item
+ clif_inventorylist(sd); // inventory list first, otherwise deleted items in pc_checkitem show up as 'unknown item'
+ pc_checkitem(sd);
+
+ // cart
+ if(pc_iscarton(sd)) {
+ clif_cartlist(sd);
+ clif_updatestatus(sd,SP_CARTINFO);
+ }
+
+ // weight
+ clif_updatestatus(sd,SP_WEIGHT);
+ clif_updatestatus(sd,SP_MAXWEIGHT);
+
+ // guild
+ // (needs to go before clif_spawn() to show guild emblems correctly)
+ if(sd->status.guild_id)
+ guild_send_memberinfoshort(sd,1);
+
+ if(battle_config.pc_invincible_time > 0) {
+ if(map_flag_gvg(sd->bl.m))
+ pc_setinvincibletimer(sd,battle_config.pc_invincible_time<<1);
+ else
+ pc_setinvincibletimer(sd,battle_config.pc_invincible_time);
+ }
+
+ if( map[sd->bl.m].users++ == 0 && battle_config.dynamic_mobs )
+ map_spawnmobs(sd->bl.m);
+ if( !(sd->sc.option&OPTION_INVISIBLE) )
+ {// increment the number of pvp players on the map
+ map[sd->bl.m].users_pvp++;
+ }
+ if( map[sd->bl.m].instance_id )
+ {
+ instance[map[sd->bl.m].instance_id].users++;
+ instance_check_idle(map[sd->bl.m].instance_id);
+ }
+ sd->state.debug_remove_map = 0; // temporary state to track double remove_map's [FlavioJS]
+
+ // reset the callshop flag if the player changes map
+ sd->state.callshop = 0;
+
+ map_addblock(&sd->bl);
+ clif_spawn(&sd->bl);
+
+ // Party
+ // (needs to go after clif_spawn() to show hp bars correctly)
+ if(sd->status.party_id) {
+ party_send_movemap(sd);
+ clif_party_hp(sd); // Show hp after displacement [LuzZza]
+ }
+
+ if( sd->bg_id ) clif_bg_hp(sd); // BattleGround System
+
+ if(map[sd->bl.m].flag.pvp && !(sd->sc.option&OPTION_INVISIBLE)) {
+ if(!battle_config.pk_mode) { // remove pvp stuff for pk_mode [Valaris]
+ if (!map[sd->bl.m].flag.pvp_nocalcrank)
+ sd->pvp_timer = add_timer(gettick()+200, pc_calc_pvprank_timer, sd->bl.id, 0);
+ sd->pvp_rank = 0;
+ sd->pvp_lastusers = 0;
+ sd->pvp_point = 5;
+ sd->pvp_won = 0;
+ sd->pvp_lost = 0;
+ }
+ clif_map_property(sd, MAPPROPERTY_FREEPVPZONE);
+ } else
+ // set flag, if it's a duel [LuzZza]
+ if(sd->duel_group)
+ clif_map_property(sd, MAPPROPERTY_FREEPVPZONE);
+
+ if (map[sd->bl.m].flag.gvg_dungeon)
+ clif_map_property(sd, MAPPROPERTY_FREEPVPZONE); //TODO: Figure out the real packet to send here.
+
+ if( map_flag_gvg(sd->bl.m) )
+ clif_map_property(sd, MAPPROPERTY_AGITZONE);
+
+ // info about nearby objects
+ // must use foreachinarea (CIRCULAR_AREA interferes with foreachinrange)
+ map_foreachinarea(clif_getareachar, sd->bl.m, sd->bl.x-AREA_SIZE, sd->bl.y-AREA_SIZE, sd->bl.x+AREA_SIZE, sd->bl.y+AREA_SIZE, BL_ALL, sd);
+
+ // pet
+ if( sd->pd )
+ {
+ if( battle_config.pet_no_gvg && map_flag_gvg(sd->bl.m) )
+ { //Return the pet to egg. [Skotlex]
+ clif_displaymessage(sd->fd, msg_txt(666));
+ pet_menu(sd, 3); //Option 3 is return to egg.
+ }
+ else
+ {
+ map_addblock(&sd->pd->bl);
+ clif_spawn(&sd->pd->bl);
+ clif_send_petdata(sd,sd->pd,0,0);
+ clif_send_petstatus(sd);
+// skill_unit_move(&sd->pd->bl,gettick(),1);
+ }
+ }
+
+ //homunculus [blackhole89]
+ if( merc_is_hom_active(sd->hd) )
+ {
+ map_addblock(&sd->hd->bl);
+ clif_spawn(&sd->hd->bl);
+ clif_send_homdata(sd,SP_ACK,0);
+ clif_hominfo(sd,sd->hd,1);
+ clif_hominfo(sd,sd->hd,0); //for some reason, at least older clients want this sent twice
+ clif_homskillinfoblock(sd);
+ if( battle_config.hom_setting&0x8 )
+ status_calc_bl(&sd->hd->bl, SCB_SPEED); //Homunc mimic their master's speed on each map change
+ if( !(battle_config.hom_setting&0x2) )
+ skill_unit_move(&sd->hd->bl,gettick(),1); // apply land skills immediately
+ }
+
+ if( sd->md ) {
+ map_addblock(&sd->md->bl);
+ clif_spawn(&sd->md->bl);
+ clif_mercenary_info(sd);
+ clif_mercenary_skillblock(sd);
+ status_calc_bl(&sd->md->bl, SCB_SPEED); // Mercenary mimic their master's speed on each map change
+ }
+
+ if( sd->ed ) {
+ map_addblock(&sd->ed->bl);
+ clif_spawn(&sd->ed->bl);
+ clif_elemental_info(sd);
+ clif_elemental_updatestatus(sd,SP_HP);
+ clif_hpmeter_single(sd->fd,sd->ed->bl.id,sd->ed->battle_status.hp,sd->ed->battle_status.max_hp);
+ clif_elemental_updatestatus(sd,SP_SP);
+ status_calc_bl(&sd->ed->bl, SCB_SPEED); //Elemental mimic their master's speed on each map change
+ }
+
+ if(sd->state.connect_new) {
+ int lv;
+ sd->state.connect_new = 0;
+ clif_skillinfoblock(sd);
+ clif_hotkeys_send(sd);
+ clif_updatestatus(sd,SP_BASEEXP);
+ clif_updatestatus(sd,SP_NEXTBASEEXP);
+ clif_updatestatus(sd,SP_JOBEXP);
+ clif_updatestatus(sd,SP_NEXTJOBEXP);
+ clif_updatestatus(sd,SP_SKILLPOINT);
+ clif_initialstatus(sd);
+
+ if (sd->sc.option&OPTION_FALCON)
+ clif_status_load(&sd->bl, SI_FALCON, 1);
+
+ if (sd->sc.option&OPTION_RIDING)
+ clif_status_load(&sd->bl, SI_RIDING, 1);
+ else if (sd->sc.option&OPTION_WUGRIDER)
+ clif_status_load(&sd->bl, SI_WUGRIDER, 1);
+
+ if(sd->status.manner < 0)
+ sc_start(&sd->bl,SC_NOCHAT,100,0,0);
+
+ //Auron reported that This skill only triggers when you logon on the map o.O [Skotlex]
+ if ((lv = pc_checkskill(sd,SG_KNOWLEDGE)) > 0) {
+ if(sd->bl.m == sd->feel_map[0].m
+ || sd->bl.m == sd->feel_map[1].m
+ || sd->bl.m == sd->feel_map[2].m)
+ sc_start(&sd->bl, SC_KNOWLEDGE, 100, lv, skill_get_time(SG_KNOWLEDGE, lv));
+ }
+
+ if(sd->pd && sd->pd->pet.intimate > 900)
+ clif_pet_emotion(sd->pd,(sd->pd->pet.class_ - 100)*100 + 50 + pet_hungry_val(sd->pd));
+
+ if(merc_is_hom_active(sd->hd))
+ merc_hom_init_timers(sd->hd);
+
+ if (night_flag && map[sd->bl.m].flag.nightenabled) {
+ sd->state.night = 1;
+ clif_status_load(&sd->bl, SI_NIGHT, 1);
+ }
+
+ // Notify everyone that this char logged in [Skotlex].
+ map_foreachpc(clif_friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 1);
+
+ //Login Event
+ npc_script_event(sd, NPCE_LOGIN);
+ } else {
+ //For some reason the client "loses" these on warp/map-change.
+ clif_updatestatus(sd,SP_STR);
+ clif_updatestatus(sd,SP_AGI);
+ clif_updatestatus(sd,SP_VIT);
+ clif_updatestatus(sd,SP_INT);
+ clif_updatestatus(sd,SP_DEX);
+ clif_updatestatus(sd,SP_LUK);
+
+ // abort currently running script
+ sd->state.using_fake_npc = 0;
+ sd->state.menu_or_input = 0;
+ sd->npc_menu = 0;
+
+ if(sd->npc_id)
+ npc_event_dequeue(sd);
+ }
+
+ if( sd->state.changemap )
+ {// restore information that gets lost on map-change
+#if PACKETVER >= 20070918
+ clif_partyinvitationstate(sd);
+ clif_equipcheckbox(sd);
+#endif
+ if( (battle_config.bg_flee_penalty != 100 || battle_config.gvg_flee_penalty != 100) &&
+ (map_flag_gvg(sd->state.pmap) || map_flag_gvg(sd->bl.m) || map[sd->state.pmap].flag.battleground || map[sd->bl.m].flag.battleground) )
+ status_calc_bl(&sd->bl, SCB_FLEE); //Refresh flee penalty
+
+ if( night_flag && map[sd->bl.m].flag.nightenabled )
+ { //Display night.
+ if( !sd->state.night )
+ {
+ sd->state.night = 1;
+ clif_status_load(&sd->bl, SI_NIGHT, 1);
+ }
+ }
+ else if( sd->state.night )
+ { //Clear night display.
+ sd->state.night = 0;
+ clif_status_load(&sd->bl, SI_NIGHT, 0);
+ }
+
+ if( map[sd->bl.m].flag.battleground )
+ {
+ clif_map_type(sd, MAPTYPE_BATTLEFIELD); // Battleground Mode
+ if( map[sd->bl.m].flag.battleground == 2 )
+ clif_bg_updatescore_single(sd);
+ }
+
+ if( map[sd->bl.m].flag.allowks && !map_flag_ks(sd->bl.m) )
+ {
+ char output[128];
+ sprintf(output, "[ Kill Steal Protection Disable. KS is allowed in this map ]");
+ clif_broadcast(&sd->bl, output, strlen(output) + 1, 0x10, SELF);
+ }
+
+ map_iwall_get(sd); // Updates Walls Info on this Map to Client
+ sd->state.changemap = false;
+ }
+
+ mail_clear(sd);
+
+ /* Guild Aura Init */
+ if( sd->state.gmaster_flag ) {
+ guild_guildaura_refresh(sd,GD_LEADERSHIP,guild_checkskill(sd->state.gmaster_flag,GD_LEADERSHIP));
+ guild_guildaura_refresh(sd,GD_GLORYWOUNDS,guild_checkskill(sd->state.gmaster_flag,GD_GLORYWOUNDS));
+ guild_guildaura_refresh(sd,GD_SOULCOLD,guild_checkskill(sd->state.gmaster_flag,GD_SOULCOLD));
+ guild_guildaura_refresh(sd,GD_HAWKEYES,guild_checkskill(sd->state.gmaster_flag,GD_HAWKEYES));
+ }
+
+ if( sd->state.vending ) { /* show we have a vending */
+ clif_openvending(sd,sd->bl.id,sd->vending);
+ clif_showvendingboard(&sd->bl,sd->message,0);
+ }
+
+ if(map[sd->bl.m].flag.loadevent) // Lance
+ npc_script_event(sd, NPCE_LOADMAP);
+
+ if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobexp(sd))
+ clif_status_load(&sd->bl, SI_DEVIL, 1); //blindness [Komurka]
+
+ if (sd->sc.opt2) //Client loses these on warp.
+ clif_changeoption(&sd->bl);
+
+ clif_weather_check(sd);
+
+ // For automatic triggering of NPCs after map loading (so you don't need to walk 1 step first)
+ if (map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNPC))
+ npc_touch_areanpc(sd,sd->bl.m,sd->bl.x,sd->bl.y);
+ else
+ sd->areanpc_id = 0;
+
+ /* it broke at some point (e.g. during a crash), so we make it visibly dead again. */
+ if( !sd->status.hp && !pc_isdead(sd) && status_isdead(&sd->bl) )
+ pc_setdead(sd);
+
+ // If player is dead, and is spawned (such as @refresh) send death packet. [Valaris]
+ if(pc_isdead(sd))
+ clif_clearunit_area(&sd->bl, CLR_DEAD);
+ else {
+ skill_usave_trigger(sd);
+ clif_changed_dir(&sd->bl, SELF);
+ }
+
+// Trigger skill effects if you appear standing on them
+ if(!battle_config.pc_invincible_time)
+ skill_unit_move(&sd->bl,gettick(),1);
+}
+
+
+/// Server's tick (ZC_NOTIFY_TIME).
+/// 007f <time>.L
+void clif_notify_time(struct map_session_data* sd, unsigned long time)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x7f));
+ WFIFOW(fd,0) = 0x7f;
+ WFIFOL(fd,2) = time;
+ WFIFOSET(fd,packet_len(0x7f));
+}
+
+
+/// Request for server's tick.
+/// 007e <client tick>.L (CZ_REQUEST_TIME)
+/// 0360 <client tick>.L (CZ_REQUEST_TIME2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_TickSend(int fd, struct map_session_data *sd)
+{
+ sd->client_tick = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+
+ clif_notify_time(sd, gettick());
+}
+
+
+/// Sends hotkey bar.
+/// 02b9 { <is skill>.B <id>.L <count>.W }*27 (ZC_SHORTCUT_KEY_LIST)
+/// 07d9 { <is skill>.B <id>.L <count>.W }*36 (ZC_SHORTCUT_KEY_LIST_V2, PACKETVER >= 20090603)
+/// 07d9 { <is skill>.B <id>.L <count>.W }*38 (ZC_SHORTCUT_KEY_LIST_V2, PACKETVER >= 20090617)
+void clif_hotkeys_send(struct map_session_data *sd) {
+#ifdef HOTKEY_SAVING
+ const int fd = sd->fd;
+ int i;
+#if PACKETVER < 20090603
+ const int cmd = 0x2b9;
+#else
+ const int cmd = 0x7d9;
+#endif
+ if (!fd) return;
+ WFIFOHEAD(fd, 2+MAX_HOTKEYS*7);
+ WFIFOW(fd, 0) = cmd;
+ for(i = 0; i < MAX_HOTKEYS; i++) {
+ WFIFOB(fd, 2 + 0 + i * 7) = sd->status.hotkeys[i].type; // type: 0: item, 1: skill
+ WFIFOL(fd, 2 + 1 + i * 7) = sd->status.hotkeys[i].id; // item or skill ID
+ WFIFOW(fd, 2 + 5 + i * 7) = sd->status.hotkeys[i].lv; // skill level
+ }
+ WFIFOSET(fd, packet_len(cmd));
+#endif
+}
+
+
+/// Request to update a position on the hotkey bar (CZ_SHORTCUT_KEY_CHANGE).
+/// 02ba <index>.W <is skill>.B <id>.L <count>.W
+void clif_parse_Hotkey(int fd, struct map_session_data *sd) {
+#ifdef HOTKEY_SAVING
+ unsigned short idx;
+ int cmd;
+
+ cmd = RFIFOW(fd, 0);
+ idx = RFIFOW(fd, packet_db[sd->packet_ver][cmd].pos[0]);
+ if (idx >= MAX_HOTKEYS) return;
+
+ sd->status.hotkeys[idx].type = RFIFOB(fd, packet_db[sd->packet_ver][cmd].pos[1]);
+ sd->status.hotkeys[idx].id = RFIFOL(fd, packet_db[sd->packet_ver][cmd].pos[2]);
+ sd->status.hotkeys[idx].lv = RFIFOW(fd, packet_db[sd->packet_ver][cmd].pos[3]);
+#endif
+}
+
+
+/// Displays cast-like progress bar (ZC_PROGRESS).
+/// 02f0 <color>.L <time>.L
+void clif_progressbar(struct map_session_data * sd, unsigned long color, unsigned int second)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x2f0));
+ WFIFOW(fd,0) = 0x2f0;
+ WFIFOL(fd,2) = color;
+ WFIFOL(fd,6) = second;
+ WFIFOSET(fd,packet_len(0x2f0));
+}
+
+
+/// Removes an ongoing progress bar (ZC_PROGRESS_CANCEL).
+/// 02f2
+void clif_progressbar_abort(struct map_session_data * sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x2f2));
+ WFIFOW(fd,0) = 0x2f2;
+ WFIFOSET(fd,packet_len(0x2f2));
+}
+
+
+/// Notification from the client, that the progress bar has reached 100% (CZ_PROGRESS).
+/// 02f1
+void clif_parse_progressbar(int fd, struct map_session_data * sd)
+{
+ int npc_id = sd->progressbar.npc_id;
+
+ if( gettick() < sd->progressbar.timeout && sd->st )
+ sd->st->state = END;
+
+ sd->progressbar.npc_id = sd->progressbar.timeout = 0;
+ npc_scriptcont(sd, npc_id);
+}
+
+
+/// Request to walk to a certain position on the current map.
+/// 0085 <dest>.3B (CZ_REQUEST_MOVE)
+/// 035f <dest>.3B (CZ_REQUEST_MOVE2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_WalkToXY(int fd, struct map_session_data *sd)
+{
+ short x, y;
+
+ if (pc_isdead(sd)) {
+ clif_clearunit_area(&sd->bl, CLR_DEAD);
+ return;
+ }
+
+ if (sd->sc.opt1 && ( sd->sc.opt1 == OPT1_STONEWAIT || sd->sc.opt1 == OPT1_BURNING ))
+ ; //You CAN walk on this OPT1 value.
+ else if( sd->progressbar.npc_id )
+ clif_progressbar_abort(sd);
+ else if (pc_cant_act(sd))
+ return;
+
+ if(sd->sc.data[SC_RUN] || sd->sc.data[SC_WUGDASH])
+ return;
+
+ pc_delinvincibletimer(sd);
+
+ RFIFOPOS(fd, packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0], &x, &y, NULL);
+
+ //Set last idle time... [Skotlex]
+ sd->idletime = last_tick;
+
+ unit_walktoxy(&sd->bl, x, y, 4);
+}
+
+
+/// Notification about the result of a disconnect request (ZC_ACK_REQ_DISCONNECT).
+/// 018b <result>.W
+/// result:
+/// 0 = disconnect (quit)
+/// 1 = cannot disconnect (wait 10 seconds)
+/// ? = ignored
+void clif_disconnect_ack(struct map_session_data* sd, short result)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x18b));
+ WFIFOW(fd,0) = 0x18b;
+ WFIFOW(fd,2) = result;
+ WFIFOSET(fd,packet_len(0x18b));
+}
+
+
+/// Request to disconnect from server (CZ_REQ_DISCONNECT).
+/// 018a <type>.W
+/// type:
+/// 0 = quit
+void clif_parse_QuitGame(int fd, struct map_session_data *sd)
+{
+ /* Rovert's prevent logout option fixed [Valaris] */
+ if( !sd->sc.data[SC_CLOAKING] && !sd->sc.data[SC_HIDING] && !sd->sc.data[SC_CHASEWALK] && !sd->sc.data[SC_CLOAKINGEXCEED] &&
+ (!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout) )
+ {
+ set_eof(fd);
+ clif_disconnect_ack(sd, 0);
+ } else {
+ clif_disconnect_ack(sd, 1);
+ }
+}
+
+
+/// Requesting unit's name.
+/// 0094 <id>.L (CZ_REQNAME)
+/// 0368 <id>.L (CZ_REQNAME2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_GetCharNameRequest(int fd, struct map_session_data *sd)
+{
+ int id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ struct block_list* bl;
+ //struct status_change *sc;
+
+ if( id < 0 && -id == sd->bl.id ) // for disguises [Valaris]
+ id = sd->bl.id;
+
+ bl = map_id2bl(id);
+ if( bl == NULL )
+ return; // Lagged clients could request names of already gone mobs/players. [Skotlex]
+
+ if( sd->bl.m != bl->m || !check_distance_bl(&sd->bl, bl, AREA_SIZE) )
+ return; // Block namerequests past view range
+
+ // 'see people in GM hide' cheat detection
+ /* disabled due to false positives (network lag + request name of char that's about to hide = race condition)
+ sc = status_get_sc(bl);
+ if (sc && sc->option&OPTION_INVISIBLE && !disguised(bl) &&
+ bl->type != BL_NPC && //Skip hidden NPCs which can be seen using Maya Purple
+ pc_get_group_level(sd) < battle_config.hack_info_GM_level
+ ) {
+ char gm_msg[256];
+ sprintf(gm_msg, "Hack on NameRequest: character '%s' (account: %d) requested the name of an invisible target (id: %d).\n", sd->status.name, sd->status.account_id, id);
+ ShowWarning(gm_msg);
+ // information is sent to all online GMs
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, gm_msg);
+ return;
+ }
+ */
+
+ clif_charnameack(fd, bl);
+}
+
+
+/// Validates and processes global messages
+/// 008c <packet len>.W <text>.?B (<name> : <message>) 00 (CZ_REQUEST_CHAT)
+/// There are various variants of this packet.
+void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
+{
+ const char* text = (char*)RFIFOP(fd,4);
+ int textlen = RFIFOW(fd,2) - 4;
+
+ char *name, *message, *fakename = NULL;
+ int namelen, messagelen;
+
+ bool is_fake;
+
+ // validate packet and retrieve name and message
+ if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
+ return;
+
+ if( is_atcommand(fd, sd, message, 1) )
+ return;
+
+ if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) )
+ return;
+
+ if( battle_config.min_chat_delay )
+ { //[Skotlex]
+ if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0)
+ return;
+ sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
+ }
+ /**
+ * Fake Name Design by FatalEror (bug report #9)
+ **/
+ if( ( is_fake = ( sd->fakename[0] ) ) ) {
+ fakename = (char*) aMalloc(strlen(sd->fakename)+messagelen+3);
+ strcpy(fakename, sd->fakename);
+ strcat(fakename, " : ");
+ strcat(fakename, message);
+ textlen = strlen(fakename) + 1;
+ }
+ // send message to others (using the send buffer for temp. storage)
+ WFIFOHEAD(fd, 8 + textlen);
+ WFIFOW(fd,0) = 0x8d;
+ WFIFOW(fd,2) = 8 + textlen;
+ WFIFOL(fd,4) = sd->bl.id;
+ safestrncpy((char*)WFIFOP(fd,8), is_fake ? fakename : text, textlen);
+ //FIXME: chat has range of 9 only
+ clif_send(WFIFOP(fd,0), WFIFOW(fd,2), &sd->bl, sd->chatID ? CHAT_WOS : AREA_CHAT_WOC);
+
+ // send back message to the speaker
+ if( is_fake ) {
+ WFIFOW(fd,0) = 0x8e;
+ WFIFOW(fd,2) = textlen + 4;
+ safestrncpy((char*)WFIFOP(fd,4), fakename, textlen);
+ aFree(fakename);
+ } else {
+ memcpy(WFIFOP(fd,0), RFIFOP(fd,0), RFIFOW(fd,2));
+ WFIFOW(fd,0) = 0x8e;
+ }
+ WFIFOSET(fd, WFIFOW(fd,2));
+#ifdef PCRE_SUPPORT
+ // trigger listening npcs
+ map_foreachinrange(npc_chat_sub, &sd->bl, AREA_SIZE, BL_NPC, text, textlen, &sd->bl);
+#endif
+
+ // Chat logging type 'O' / Global Chat
+ log_chat(LOG_CHAT_GLOBAL, 0, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, message);
+}
+
+
+/// /mm /mapmove (as @rura GM command) (CZ_MOVETO_MAP).
+/// Request to warp to a map on given coordinates.
+/// 0140 <map name>.16B <x>.W <y>.W
+void clif_parse_MapMove(int fd, struct map_session_data *sd)
+{
+ char command[MAP_NAME_LENGTH_EXT+25];
+ char* map_name;
+
+ map_name = (char*)RFIFOP(fd,2);
+ map_name[MAP_NAME_LENGTH_EXT-1]='\0';
+ sprintf(command, "%cmapmove %s %d %d", atcommand_symbol, map_name, RFIFOW(fd,18), RFIFOW(fd,20));
+ is_atcommand(fd, sd, command, 1);
+}
+
+
+/// Updates body and head direction of an object (ZC_CHANGE_DIRECTION).
+/// 009c <id>.L <head dir>.W <dir>.B
+/// head dir:
+/// 0 = straight
+/// 1 = turned CW
+/// 2 = turned CCW
+/// dir:
+/// 0 = north
+/// 1 = northwest
+/// 2 = west
+/// 3 = southwest
+/// 4 = south
+/// 5 = southeast
+/// 6 = east
+/// 7 = northeast
+void clif_changed_dir(struct block_list *bl, enum send_target target)
+{
+ unsigned char buf[64];
+
+ WBUFW(buf,0) = 0x9c;
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,6) = bl->type==BL_PC?((TBL_PC*)bl)->head_dir:0;
+ WBUFB(buf,8) = unit_getdir(bl);
+
+ clif_send(buf, packet_len(0x9c), bl, target);
+
+ if (disguised(bl)) {
+ WBUFL(buf,2) = -bl->id;
+ WBUFW(buf,6) = 0;
+ clif_send(buf, packet_len(0x9c), bl, SELF);
+ }
+}
+
+
+/// Request to change own body and head direction.
+/// 009b <head dir>.W <dir>.B (CZ_CHANGE_DIRECTION)
+/// 0361 <head dir>.W <dir>.B (CZ_CHANGE_DIRECTION2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_ChangeDir(int fd, struct map_session_data *sd)
+{
+ unsigned char headdir, dir;
+
+ headdir = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ dir = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ pc_setdir(sd, dir, headdir);
+
+ clif_changed_dir(&sd->bl, AREA_WOS);
+}
+
+
+/// Request to show an emotion (CZ_REQ_EMOTION).
+/// 00bf <type>.B
+/// type:
+/// @see enum emotion_type
+void clif_parse_Emotion(int fd, struct map_session_data *sd)
+{
+ int emoticon = RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+
+ if (battle_config.basic_skill_check == 0 || pc_checkskill(sd, NV_BASIC) >= 2) {
+ if (emoticon == E_MUTE) {// prevent use of the mute emote [Valaris]
+ clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 1);
+ return;
+ }
+ // fix flood of emotion icon (ro-proxy): flood only the hacker player
+ if (sd->emotionlasttime + 1 >= time(NULL)) { // not more than 1 per second
+ sd->emotionlasttime = time(NULL);
+ clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 1);
+ return;
+ }
+ sd->emotionlasttime = time(NULL);
+
+ if(battle_config.client_reshuffle_dice && emoticon>=E_DICE1 && emoticon<=E_DICE6)
+ {// re-roll dice
+ emoticon = rnd()%6+E_DICE1;
+ }
+
+ clif_emotion(&sd->bl, emoticon);
+ } else
+ clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 1);
+}
+
+
+/// Amount of currently online players, reply to /w /who (ZC_USER_COUNT).
+/// 00c2 <count>.L
+void clif_user_count(struct map_session_data* sd, int count)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0xc2));
+ WFIFOW(fd,0) = 0xc2;
+ WFIFOL(fd,2) = count;
+ WFIFOSET(fd,packet_len(0xc2));
+}
+
+
+/// /w /who (CZ_REQ_USER_COUNT).
+/// Request to display amount of currently connected players.
+/// 00c1
+void clif_parse_HowManyConnections(int fd, struct map_session_data *sd)
+{
+ clif_user_count(sd, map_getusers());
+}
+
+
+void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type, int target_id, unsigned int tick)
+{
+ if (pc_isdead(sd)) {
+ clif_clearunit_area(&sd->bl, CLR_DEAD);
+ return;
+ }
+
+ if (sd->sc.count &&
+ (sd->sc.data[SC_TRICKDEAD] ||
+ sd->sc.data[SC_AUTOCOUNTER] ||
+ sd->sc.data[SC_BLADESTOP] ||
+ sd->sc.data[SC__MANHOLE] ||
+ sd->sc.data[SC_CURSEDCIRCLE_ATKER] ||
+ sd->sc.data[SC_CURSEDCIRCLE_TARGET] ))
+ return;
+
+ pc_stop_walking(sd, 1);
+ pc_stop_attack(sd);
+
+ if(target_id<0 && -target_id == sd->bl.id) // for disguises [Valaris]
+ target_id = sd->bl.id;
+
+ switch(action_type)
+ {
+ case 0x00: // once attack
+ case 0x07: // continuous attack
+
+ if( pc_cant_act(sd) || sd->sc.option&OPTION_HIDE )
+ return;
+
+ if( sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER) )
+ return;
+
+ if( sd->sc.data[SC_BASILICA] || sd->sc.data[SC__SHADOWFORM] )
+ return;
+
+ if (!battle_config.sdelay_attack_enable && pc_checkskill(sd, SA_FREECAST) <= 0) {
+ if (DIFF_TICK(tick, sd->ud.canact_tick) < 0) {
+ clif_skill_fail(sd, 1, USESKILL_FAIL_SKILLINTERVAL, 0);
+ return;
+ }
+ }
+
+ pc_delinvincibletimer(sd);
+ sd->idletime = last_tick;
+ unit_attack(&sd->bl, target_id, action_type != 0);
+ break;
+ case 0x02: // sitdown
+ if (battle_config.basic_skill_check && pc_checkskill(sd, NV_BASIC) < 3) {
+ clif_skill_fail(sd, 1, USESKILL_FAIL_LEVEL, 2);
+ break;
+ }
+
+ if(pc_issit(sd)) {
+ //Bugged client? Just refresh them.
+ clif_sitting(&sd->bl);
+ return;
+ }
+
+ if (sd->ud.skilltimer != INVALID_TIMER || (sd->sc.opt1 && sd->sc.opt1 != OPT1_BURNING ))
+ break;
+
+ if (sd->sc.count && (
+ sd->sc.data[SC_DANCING] ||
+ (sd->sc.data[SC_GRAVITATION] && sd->sc.data[SC_GRAVITATION]->val3 == BCT_SELF)
+ )) //No sitting during these states either.
+ break;
+
+ pc_setsit(sd);
+ skill_sit(sd,1);
+ clif_sitting(&sd->bl);
+ break;
+ case 0x03: // standup
+ if (!pc_issit(sd)) {
+ //Bugged client? Just refresh them.
+ clif_standing(&sd->bl);
+ return;
+ }
+ pc_setstand(sd);
+ skill_sit(sd,0);
+ clif_standing(&sd->bl);
+ break;
+ }
+}
+
+
+/// Request for an action.
+/// 0089 <target id>.L <action>.B (CZ_REQUEST_ACT)
+/// 0437 <target id>.L <action>.B (CZ_REQUEST_ACT2)
+/// action:
+/// 0 = attack
+/// 1 = pick up item
+/// 2 = sit down
+/// 3 = stand up
+/// 7 = continous attack
+/// 12 = (touch skill?)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_ActionRequest(int fd, struct map_session_data *sd)
+{
+ clif_parse_ActionRequest_sub(sd,
+ RFIFOB(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]),
+ RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]),
+ gettick()
+ );
+}
+
+
+/// Response to the death/system menu (CZ_RESTART).
+/// 00b2 <type>.B
+/// type:
+/// 0 = restart (respawn)
+/// 1 = char-select (disconnect)
+void clif_parse_Restart(int fd, struct map_session_data *sd)
+{
+ switch(RFIFOB(fd,2)) {
+ case 0x00:
+ pc_respawn(sd,CLR_RESPAWN);
+ break;
+ case 0x01:
+ /* Rovert's Prevent logout option - Fixed [Valaris] */
+ if( !sd->sc.data[SC_CLOAKING] && !sd->sc.data[SC_HIDING] && !sd->sc.data[SC_CHASEWALK] && !sd->sc.data[SC_CLOAKINGEXCEED] &&
+ (!battle_config.prevent_logout || DIFF_TICK(gettick(), sd->canlog_tick) > battle_config.prevent_logout) )
+ { //Send to char-server for character selection.
+ chrif_charselectreq(sd, session[fd]->client_addr);
+ } else {
+ clif_disconnect_ack(sd, 1);
+ }
+ break;
+ }
+}
+
+
+/// Validates and processes whispered messages (CZ_WHISPER).
+/// 0096 <packet len>.W <nick>.24B <message>.?B
+void clif_parse_WisMessage(int fd, struct map_session_data* sd)
+{
+ struct map_session_data* dstsd;
+ int i;
+
+ char *target, *message;
+ int namelen, messagelen;
+
+ // validate packet and retrieve name and message
+ if( !clif_process_message(sd, 1, &target, &namelen, &message, &messagelen) )
+ return;
+
+ if ( is_atcommand(fd, sd, message, 1) )
+ return;
+
+ if (sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT))
+ return;
+
+ if (battle_config.min_chat_delay) { //[Skotlex]
+ if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0) {
+ return;
+ }
+ sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
+ }
+
+ // Chat logging type 'W' / Whisper
+ log_chat(LOG_CHAT_WHISPER, 0, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, target, message);
+
+ //-------------------------------------------------------//
+ // Lordalfa - Paperboy - To whisper NPC commands //
+ //-------------------------------------------------------//
+ if (target[0] && (strncasecmp(target,"NPC:",4) == 0) && (strlen(target) > 4))
+ {
+ char* str = target+4; //Skip the NPC: string part.
+ struct npc_data* npc;
+ if ((npc = npc_name2id(str))) {
+ char split_data[NUM_WHISPER_VAR][CHAT_SIZE_MAX];
+ char *split;
+ char output[256];
+
+ str = message;
+ // skip codepage indicator, if detected
+ if( str[0] == '|' && strlen(str) >= 4 )
+ str += 3;
+ for( i = 0; i < NUM_WHISPER_VAR; ++i ) {// Splits the message using '#' as separators
+ split = strchr(str,'#');
+ if( split == NULL ) { // use the remaining string
+ safestrncpy(split_data[i], str, ARRAYLENGTH(split_data[i]));
+ for( ++i; i < NUM_WHISPER_VAR; ++i )
+ split_data[i][0] = '\0';
+ break;
+ }
+ *split = '\0';
+ safestrncpy(split_data[i], str, ARRAYLENGTH(split_data[i]));
+ str = split+1;
+ }
+
+ for( i = 0; i < NUM_WHISPER_VAR; ++i ) {
+ sprintf(output, "@whispervar%d$", i);
+ set_var(sd,output,(char *) split_data[i]);
+ }
+
+ sprintf(output, "%s::OnWhisperGlobal", npc->exname);
+ npc_event(sd,output,0); // Calls the NPC label
+
+ return;
+ }
+ } else if(strcmpi(target, main_chat_nick) == 0) { // Main chat [LuzZza]
+ if(!sd->state.mainchat)
+ clif_displaymessage(fd, msg_txt(388)); // You should enable main chat with "@main on" command.
+ else {
+ // send the main message using inter-server system
+ intif_main_message( sd, message );
+ }
+
+ return;
+ }
+
+ // searching destination character
+ dstsd = map_nick2sd(target);
+
+ if (dstsd == NULL || strcmp(dstsd->status.name, target) != 0) {
+ // player is not on this map-server
+ // At this point, don't send wisp/page if it's not exactly the same name, because (example)
+ // if there are 'Test' player on an other map-server and 'test' player on this map-server,
+ // and if we ask for 'Test', we must not contact 'test' player
+ // so, we send information to inter-server, which is the only one which decide (and copy correct name).
+ intif_wis_message(sd, target, message, messagelen);
+ return;
+ }
+
+ // if player ignores everyone
+ if (dstsd->state.ignoreAll) {
+ if (dstsd->sc.option & OPTION_INVISIBLE && pc_get_group_level(sd) < pc_get_group_level(dstsd))
+ clif_wis_end(fd, 1); // 1: target character is not loged in
+ else
+ clif_wis_end(fd, 3); // 3: everyone ignored by target
+ return;
+ }
+
+ // if player is autotrading
+ if( dstsd->state.autotrade == 1 ) {
+ char output[256];
+ sprintf(output, "%s is in autotrade mode and cannot receive whispered messages.", dstsd->status.name);
+ clif_wis_message(fd, wisp_server_name, output, strlen(output) + 1);
+ return;
+ }
+
+ // if player ignores the source character
+ ARR_FIND(0, MAX_IGNORE_LIST, i, dstsd->ignore[i].name[0] == '\0' || strcmp(dstsd->ignore[i].name, sd->status.name) == 0);
+ if(i < MAX_IGNORE_LIST && dstsd->ignore[i].name[0] != '\0') { // source char present in ignore list
+ clif_wis_end(fd, 2); // 2: ignored by target
+ return;
+ }
+
+ // notify sender of success
+ clif_wis_end(fd, 0); // 0: success to send wisper
+
+ // Normal message
+ clif_wis_message(dstsd->fd, sd->status.name, message, messagelen);
+}
+
+
+/// /b /nb (CZ_BROADCAST).
+/// Request to broadcast a message on whole server.
+/// 0099 <packet len>.W <text>.?B 00
+void clif_parse_Broadcast(int fd, struct map_session_data* sd) {
+ char command[CHAT_SIZE_MAX+11];
+ char* msg = (char*)RFIFOP(fd,4);
+ unsigned int len = RFIFOW(fd,2)-4;
+
+ // as the length varies depending on the command used, just block unreasonably long strings
+ mes_len_check(msg, len, CHAT_SIZE_MAX);
+
+ sprintf(command, "%ckami %s", atcommand_symbol, msg);
+ is_atcommand(fd, sd, command, 1);
+}
+
+
+/// Request to pick up an item.
+/// 009f <id>.L (CZ_ITEM_PICKUP)
+/// 0362 <id>.L (CZ_ITEM_PICKUP2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_TakeItem(int fd, struct map_session_data *sd)
+{
+ struct flooritem_data *fitem;
+ int map_object_id;
+
+ map_object_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+
+ fitem = (struct flooritem_data*)map_id2bl(map_object_id);
+
+ do {
+ if (pc_isdead(sd)) {
+ clif_clearunit_area(&sd->bl, CLR_DEAD);
+ break;
+ }
+
+ if (fitem == NULL || fitem->bl.type != BL_ITEM || fitem->bl.m != sd->bl.m)
+ break;
+
+ if( sd->sc.cant.pickup )
+ break;
+
+ if (pc_cant_act(sd))
+ break;
+
+ if (!pc_takeitem(sd, fitem))
+ break;
+
+ return;
+ } while (0);
+ // Client REQUIRES a fail packet or you can no longer pick items.
+ clif_additem(sd,0,0,6);
+}
+
+
+/// Request to drop an item.
+/// 00a2 <index>.W <amount>.W (CZ_ITEM_THROW)
+/// 0363 <index>.W <amount>.W (CZ_ITEM_THROW2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_DropItem(int fd, struct map_session_data *sd)
+{
+ int item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
+ int item_amount = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+
+ for(;;) {
+ if (pc_isdead(sd))
+ break;
+
+ if (pc_cant_act(sd))
+ break;
+
+ if (sd->sc.count && (
+ sd->sc.data[SC_AUTOCOUNTER] ||
+ sd->sc.data[SC_BLADESTOP] ||
+ (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOITEM)
+ ))
+ break;
+
+ if (!pc_dropitem(sd, item_index, item_amount))
+ break;
+
+ return;
+ }
+
+ //Because the client does not like being ignored.
+ clif_dropitem(sd, item_index,0);
+}
+
+
+/// Request to use an item.
+/// 00a7 <index>.W <account id>.L (CZ_USE_ITEM)
+/// 0439 <index>.W <account id>.L (CZ_USE_ITEM2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_UseItem(int fd, struct map_session_data *sd)
+{
+ int n;
+
+ if (pc_isdead(sd)) {
+ clif_clearunit_area(&sd->bl, CLR_DEAD);
+ return;
+ }
+
+ //This flag enables you to use items while in an NPC. [Skotlex]
+ if (sd->npc_id) {
+ if (sd->npc_id != sd->npc_item_flag)
+ return;
+ }
+ else if (pc_istrading(sd))
+ return;
+
+ //Whether the item is used or not is irrelevant, the char ain't idle. [Skotlex]
+ sd->idletime = last_tick;
+ n = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
+
+ if(n <0 || n >= MAX_INVENTORY)
+ return;
+ if (!pc_useitem(sd,n))
+ clif_useitemack(sd,n,0,false); //Send an empty ack packet or the client gets stuck.
+}
+
+
+/// Request to equip an item (CZ_REQ_WEAR_EQUIP).
+/// 00a9 <index>.W <position>.W
+void clif_parse_EquipItem(int fd,struct map_session_data *sd)
+{
+ int index;
+
+ if(pc_isdead(sd)) {
+ clif_clearunit_area(&sd->bl,CLR_DEAD);
+ return;
+ }
+ index = RFIFOW(fd,2)-2;
+ if (index < 0 || index >= MAX_INVENTORY)
+ return; //Out of bounds check.
+
+ if(sd->npc_id) {
+ if (sd->npc_id != sd->npc_item_flag)
+ return;
+ } else if (sd->state.storage_flag || sd->sc.opt1)
+ ; //You can equip/unequip stuff while storage is open/under status changes
+ else if (pc_cant_act(sd))
+ return;
+
+ if(!sd->status.inventory[index].identify) {
+ clif_equipitemack(sd,index,0,0); // fail
+ return;
+ }
+
+ if(!sd->inventory_data[index])
+ return;
+
+ if(sd->inventory_data[index]->type == IT_PETARMOR){
+ pet_equipitem(sd,index);
+ return;
+ }
+
+ //Client doesn't send the position for ammo.
+ if(sd->inventory_data[index]->type == IT_AMMO)
+ pc_equipitem(sd,index,EQP_AMMO);
+ else
+ pc_equipitem(sd,index,RFIFOW(fd,4));
+}
+
+
+/// Request to take off an equip (CZ_REQ_TAKEOFF_EQUIP).
+/// 00ab <index>.W
+void clif_parse_UnequipItem(int fd,struct map_session_data *sd)
+{
+ int index;
+
+ if(pc_isdead(sd)) {
+ clif_clearunit_area(&sd->bl,CLR_DEAD);
+ return;
+ }
+
+ if (sd->state.storage_flag || sd->sc.opt1)
+ ; //You can equip/unequip stuff while storage is open/under status changes
+ else if (pc_cant_act(sd))
+ return;
+
+ index = RFIFOW(fd,2)-2;
+
+ pc_unequipitem(sd,index,1);
+}
+
+
+/// Request to start a conversation with an NPC (CZ_CONTACTNPC).
+/// 0090 <id>.L <type>.B
+/// type:
+/// 1 = click
+void clif_parse_NpcClicked(int fd,struct map_session_data *sd)
+{
+ struct block_list *bl;
+
+ if(pc_isdead(sd)) {
+ clif_clearunit_area(&sd->bl,CLR_DEAD);
+ return;
+ }
+
+ if (pc_cant_act(sd))
+ return;
+
+ bl = map_id2bl(RFIFOL(fd,2));
+ if (!bl) return;
+ switch (bl->type) {
+ case BL_MOB:
+ case BL_PC:
+ clif_parse_ActionRequest_sub(sd, 0x07, bl->id, gettick());
+ break;
+ case BL_NPC:
+ if( bl->m != -1 )// the user can't click floating npcs directly (hack attempt)
+ npc_click(sd,(TBL_NPC*)bl);
+ break;
+ }
+}
+
+
+/// Selection between buy/sell was made (CZ_ACK_SELECT_DEALTYPE).
+/// 00c5 <id>.L <type>.B
+/// type:
+/// 0 = buy
+/// 1 = sell
+void clif_parse_NpcBuySellSelected(int fd,struct map_session_data *sd)
+{
+ if (sd->state.trading)
+ return;
+ npc_buysellsel(sd,RFIFOL(fd,2),RFIFOB(fd,6));
+}
+
+
+/// Notification about the result of a purchase attempt from an NPC shop (ZC_PC_PURCHASE_RESULT).
+/// 00ca <result>.B
+/// result:
+/// 0 = "The deal has successfully completed."
+/// 1 = "You do not have enough zeny."
+/// 2 = "You are over your Weight Limit."
+/// 3 = "Out of the maximum capacity, you have too many items."
+void clif_npc_buy_result(struct map_session_data* sd, unsigned char result)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0xca));
+ WFIFOW(fd,0) = 0xca;
+ WFIFOB(fd,2) = result;
+ WFIFOSET(fd,packet_len(0xca));
+}
+
+
+/// Request to buy chosen items from npc shop (CZ_PC_PURCHASE_ITEMLIST).
+/// 00c8 <packet len>.W { <amount>.W <name id>.W }*
+void clif_parse_NpcBuyListSend(int fd, struct map_session_data* sd)
+{
+ int n = (RFIFOW(fd,2)-4) /4;
+ unsigned short* item_list = (unsigned short*)RFIFOP(fd,4);
+ int result;
+
+ if( sd->state.trading || !sd->npc_shopid )
+ result = 1;
+ else
+ result = npc_buylist(sd,n,item_list);
+
+ sd->npc_shopid = 0; //Clear shop data.
+
+ clif_npc_buy_result(sd, result);
+}
+
+
+/// Notification about the result of a sell attempt to an NPC shop (ZC_PC_SELL_RESULT).
+/// 00cb <result>.B
+/// result:
+/// 0 = "The deal has successfully completed."
+/// 1 = "The deal has failed."
+void clif_npc_sell_result(struct map_session_data* sd, unsigned char result)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0xcb));
+ WFIFOW(fd,0) = 0xcb;
+ WFIFOB(fd,2) = result;
+ WFIFOSET(fd,packet_len(0xcb));
+}
+
+
+/// Request to sell chosen items to npc shop (CZ_PC_SELL_ITEMLIST).
+/// 00c9 <packet len>.W { <index>.W <amount>.W }*
+void clif_parse_NpcSellListSend(int fd,struct map_session_data *sd)
+{
+ int fail=0,n;
+ unsigned short *item_list;
+
+ n = (RFIFOW(fd,2)-4) /4;
+ item_list = (unsigned short*)RFIFOP(fd,4);
+
+ if (sd->state.trading || !sd->npc_shopid)
+ fail = 1;
+ else
+ fail = npc_selllist(sd,n,item_list);
+
+ sd->npc_shopid = 0; //Clear shop data.
+
+ clif_npc_sell_result(sd, fail);
+}
+
+
+/// Chatroom creation request (CZ_CREATE_CHATROOM).
+/// 00d5 <packet len>.W <limit>.W <type>.B <passwd>.8B <title>.?B
+/// type:
+/// 0 = private
+/// 1 = public
+void clif_parse_CreateChatRoom(int fd, struct map_session_data* sd)
+{
+ int len = RFIFOW(fd,2)-15;
+ int limit = RFIFOW(fd,4);
+ bool pub = (RFIFOB(fd,6) != 0);
+ const char* password = (char*)RFIFOP(fd,7); //not zero-terminated
+ const char* title = (char*)RFIFOP(fd,15); // not zero-terminated
+ char s_password[CHATROOM_PASS_SIZE];
+ char s_title[CHATROOM_TITLE_SIZE];
+
+ if (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM)
+ return;
+ if(battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 4) {
+ clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,3);
+ return;
+ }
+
+ if( len <= 0 )
+ return; // invalid input
+
+ safestrncpy(s_password, password, CHATROOM_PASS_SIZE);
+ safestrncpy(s_title, title, min(len+1,CHATROOM_TITLE_SIZE)); //NOTE: assumes that safestrncpy will not access the len+1'th byte
+
+ chat_createpcchat(sd, s_title, s_password, limit, pub);
+}
+
+
+/// Chatroom join request (CZ_REQ_ENTER_ROOM).
+/// 00d9 <chat ID>.L <passwd>.8B
+void clif_parse_ChatAddMember(int fd, struct map_session_data* sd)
+{
+ int chatid = RFIFOL(fd,2);
+ const char* password = (char*)RFIFOP(fd,6); // not zero-terminated
+
+ chat_joinchat(sd,chatid,password);
+}
+
+
+/// Chatroom properties adjustment request (CZ_CHANGE_CHATROOM).
+/// 00de <packet len>.W <limit>.W <type>.B <passwd>.8B <title>.?B
+/// type:
+/// 0 = private
+/// 1 = public
+void clif_parse_ChatRoomStatusChange(int fd, struct map_session_data* sd)
+{
+ int len = RFIFOW(fd,2)-15;
+ int limit = RFIFOW(fd,4);
+ bool pub = (RFIFOB(fd,6) != 0);
+ const char* password = (char*)RFIFOP(fd,7); // not zero-terminated
+ const char* title = (char*)RFIFOP(fd,15); // not zero-terminated
+ char s_password[CHATROOM_PASS_SIZE];
+ char s_title[CHATROOM_TITLE_SIZE];
+
+ if( len <= 0 )
+ return; // invalid input
+
+ safestrncpy(s_password, password, CHATROOM_PASS_SIZE);
+ safestrncpy(s_title, title, min(len+1,CHATROOM_TITLE_SIZE)); //NOTE: assumes that safestrncpy will not access the len+1'th byte
+
+ chat_changechatstatus(sd, s_title, s_password, limit, pub);
+}
+
+
+/// Request to change the chat room ownership (CZ_REQ_ROLE_CHANGE).
+/// 00e0 <role>.L <nick>.24B
+/// role:
+/// 0 = owner
+/// 1 = normal
+void clif_parse_ChangeChatOwner(int fd, struct map_session_data* sd)
+{
+ chat_changechatowner(sd,(char*)RFIFOP(fd,6));
+}
+
+
+/// Request to expel a player from chat room (CZ_REQ_EXPEL_MEMBER).
+/// 00e2 <name>.24B
+void clif_parse_KickFromChat(int fd,struct map_session_data *sd)
+{
+ chat_kickchat(sd,(char*)RFIFOP(fd,2));
+}
+
+
+/// Request to leave the current chatroom (CZ_EXIT_ROOM).
+/// 00e3
+void clif_parse_ChatLeave(int fd, struct map_session_data* sd)
+{
+ chat_leavechat(sd,0);
+}
+
+
+//Handles notifying asker and rejecter of what has just ocurred.
+//Type is used to determine the correct msg_txt to use:
+//0:
+static void clif_noask_sub(struct map_session_data *src, struct map_session_data *target, int type)
+{
+ const char* msg;
+ char output[256];
+ // Your request has been rejected by autoreject option.
+ msg = msg_txt(392);
+ clif_disp_onlyself(src, msg, strlen(msg));
+ //Notice that a request was rejected.
+ snprintf(output, 256, msg_txt(393+type), src->status.name, 256);
+ clif_disp_onlyself(target, output, strlen(output));
+}
+
+
+/// Request to begin a trade (CZ_REQ_EXCHANGE_ITEM).
+/// 00e4 <account id>.L
+void clif_parse_TradeRequest(int fd,struct map_session_data *sd)
+{
+ struct map_session_data *t_sd;
+
+ t_sd = map_id2sd(RFIFOL(fd,2));
+
+ if(!sd->chatID && pc_cant_act(sd))
+ return; //You can trade while in a chatroom.
+
+ // @noask [LuzZza]
+ if(t_sd && t_sd->state.noask) {
+ clif_noask_sub(sd, t_sd, 0);
+ return;
+ }
+
+ if( battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 1)
+ {
+ clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,0);
+ return;
+ }
+
+ trade_traderequest(sd,t_sd);
+}
+
+
+/// Answer to a trade request (CZ_ACK_EXCHANGE_ITEM).
+/// 00e6 <result>.B
+/// result:
+/// 3 = accepted
+/// 4 = rejected
+void clif_parse_TradeAck(int fd,struct map_session_data *sd)
+{
+ trade_tradeack(sd,RFIFOB(fd,2));
+}
+
+
+/// Request to add an item to current trade (CZ_ADD_EXCHANGE_ITEM).
+/// 00e8 <index>.W <amount>.L
+void clif_parse_TradeAddItem(int fd,struct map_session_data *sd)
+{
+ short index = RFIFOW(fd,2);
+ int amount = RFIFOL(fd,4);
+
+ if( index == 0 )
+ trade_tradeaddzeny(sd, amount);
+ else
+ trade_tradeadditem(sd, index, (short)amount);
+}
+
+
+/// Request to lock items in current trade (CZ_CONCLUDE_EXCHANGE_ITEM).
+/// 00eb
+void clif_parse_TradeOk(int fd,struct map_session_data *sd)
+{
+ trade_tradeok(sd);
+}
+
+
+/// Request to cancel current trade (CZ_CANCEL_EXCHANGE_ITEM).
+/// 00ed
+void clif_parse_TradeCancel(int fd,struct map_session_data *sd)
+{
+ trade_tradecancel(sd);
+}
+
+
+/// Request to commit current trade (CZ_EXEC_EXCHANGE_ITEM).
+/// 00ef
+void clif_parse_TradeCommit(int fd,struct map_session_data *sd)
+{
+ trade_tradecommit(sd);
+}
+
+
+/// Request to stop chasing/attacking an unit (CZ_CANCEL_LOCKON).
+/// 0118
+void clif_parse_StopAttack(int fd,struct map_session_data *sd)
+{
+ pc_stop_attack(sd);
+}
+
+
+/// Request to move an item from inventory to cart (CZ_MOVE_ITEM_FROM_BODY_TO_CART).
+/// 0126 <index>.W <amount>.L
+void clif_parse_PutItemToCart(int fd,struct map_session_data *sd)
+{
+ if (pc_istrading(sd))
+ return;
+ if (!pc_iscarton(sd))
+ return;
+ pc_putitemtocart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4));
+}
+
+
+/// Request to move an item from cart to inventory (CZ_MOVE_ITEM_FROM_CART_TO_BODY).
+/// 0127 <index>.W <amount>.L
+void clif_parse_GetItemFromCart(int fd,struct map_session_data *sd)
+{
+ if (!pc_iscarton(sd))
+ return;
+ pc_getitemfromcart(sd,RFIFOW(fd,2)-2,RFIFOL(fd,4));
+}
+
+
+/// Request to remove cart/falcon/peco/dragon (CZ_REQ_CARTOFF).
+/// 012a
+void clif_parse_RemoveOption(int fd,struct map_session_data *sd)
+{
+ /**
+ * Attempts to remove these options when this function is called (will remove all available)
+ **/
+#ifdef NEW_CARTS
+ pc_setoption(sd,sd->sc.option&~(OPTION_RIDING|OPTION_FALCON|OPTION_DRAGON|OPTION_MADOGEAR));
+ if( sd->sc.data[SC_PUSH_CART] )
+ pc_setcart(sd,0);
+#else
+ pc_setoption(sd,sd->sc.option&~(OPTION_CART|OPTION_RIDING|OPTION_FALCON|OPTION_DRAGON|OPTION_MADOGEAR));
+#endif
+}
+
+
+/// Request to change cart's visual look (CZ_REQ_CHANGECART).
+/// 01af <num>.W
+void clif_parse_ChangeCart(int fd,struct map_session_data *sd)
+{// TODO: State tracking?
+ int type;
+
+ if( sd && pc_checkskill(sd, MC_CHANGECART) < 1 )
+ return;
+
+ type = (int)RFIFOW(fd,2);
+#ifdef NEW_CARTS
+ if( (type == 9 && sd->status.base_level > 131) ||
+ (type == 8 && sd->status.base_level > 121) ||
+ (type == 7 && sd->status.base_level > 111) ||
+ (type == 6 && sd->status.base_level > 101) ||
+ (type == 5 && sd->status.base_level > 90) ||
+ (type == 4 && sd->status.base_level > 80) ||
+ (type == 3 && sd->status.base_level > 65) ||
+ (type == 2 && sd->status.base_level > 40) ||
+ (type == 1))
+#else
+ if( (type == 5 && sd->status.base_level > 90) ||
+ (type == 4 && sd->status.base_level > 80) ||
+ (type == 3 && sd->status.base_level > 65) ||
+ (type == 2 && sd->status.base_level > 40) ||
+ (type == 1))
+#endif
+ pc_setcart(sd,type);
+}
+
+
+/// Request to increase status (CZ_STATUS_CHANGE).
+/// 00bb <status id>.W <amount>.B
+/// status id:
+/// SP_STR ~ SP_LUK
+/// amount:
+/// client sends always 1 for this, even when using /str+ and
+/// the like
+void clif_parse_StatusUp(int fd,struct map_session_data *sd)
+{
+ pc_statusup(sd,RFIFOW(fd,2));
+}
+
+
+/// Request to increase level of a skill (CZ_UPGRADE_SKILLLEVEL).
+/// 0112 <skill id>.W
+void clif_parse_SkillUp(int fd,struct map_session_data *sd)
+{
+ pc_skillup(sd,RFIFOW(fd,2));
+}
+
+static void clif_parse_UseSkillToId_homun(struct homun_data *hd, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, int target_id)
+{
+ int lv;
+
+ if( !hd )
+ return;
+ if( skillnotok_hom(skill_id, hd) )
+ return;
+ if( hd->bl.id != target_id && skill_get_inf(skill_id)&INF_SELF_SKILL )
+ target_id = hd->bl.id;
+ if( hd->ud.skilltimer != INVALID_TIMER )
+ {
+ if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) return;
+ }
+ else if( DIFF_TICK(tick, hd->ud.canact_tick) < 0 )
+ return;
+
+ lv = merc_hom_checkskill(hd, skill_id);
+ if( skill_lv > lv )
+ skill_lv = lv;
+ if( skill_lv )
+ unit_skilluse_id(&hd->bl, target_id, skill_id, skill_lv);
+}
+
+static void clif_parse_UseSkillToPos_homun(struct homun_data *hd, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, short x, short y, int skillmoreinfo)
+{
+ int lv;
+ if( !hd )
+ return;
+ if( skillnotok_hom(skill_id, hd) )
+ return;
+ if( hd->ud.skilltimer != INVALID_TIMER ) {
+ if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) return;
+ } else if( DIFF_TICK(tick, hd->ud.canact_tick) < 0 )
+ return;
+
+ if( hd->sc.data[SC_BASILICA] )
+ return;
+ lv = merc_hom_checkskill(hd, skill_id);
+ if( skill_lv > lv )
+ skill_lv = lv;
+ if( skill_lv )
+ unit_skilluse_pos(&hd->bl, x, y, skill_id, skill_lv);
+}
+
+static void clif_parse_UseSkillToId_mercenary(struct mercenary_data *md, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, int target_id)
+{
+ int lv;
+
+ if( !md )
+ return;
+ if( skillnotok_mercenary(skill_id, md) )
+ return;
+ if( md->bl.id != target_id && skill_get_inf(skill_id)&INF_SELF_SKILL )
+ target_id = md->bl.id;
+ if( md->ud.skilltimer != INVALID_TIMER )
+ {
+ if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST ) return;
+ }
+ else if( DIFF_TICK(tick, md->ud.canact_tick) < 0 )
+ return;
+
+ lv = mercenary_checkskill(md, skill_id);
+ if( skill_lv > lv )
+ skill_lv = lv;
+ if( skill_lv )
+ unit_skilluse_id(&md->bl, target_id, skill_id, skill_lv);
+}
+
+static void clif_parse_UseSkillToPos_mercenary(struct mercenary_data *md, struct map_session_data *sd, unsigned int tick, uint16 skill_id, uint16 skill_lv, short x, short y, int skillmoreinfo)
+{
+ int lv;
+ if( !md )
+ return;
+ if( skillnotok_mercenary(skill_id, md) )
+ return;
+ if( md->ud.skilltimer != INVALID_TIMER )
+ return;
+ if( DIFF_TICK(tick, md->ud.canact_tick) < 0 )
+ {
+ clif_skill_fail(md->master, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0);
+ return;
+ }
+
+ if( md->sc.data[SC_BASILICA] )
+ return;
+ lv = mercenary_checkskill(md, skill_id);
+ if( skill_lv > lv )
+ skill_lv = lv;
+ if( skill_lv )
+ unit_skilluse_pos(&md->bl, x, y, skill_id, skill_lv);
+}
+
+
+/// Request to use a targeted skill.
+/// 0113 <skill lv>.W <skill id>.W <target id>.L (CZ_USE_SKILL)
+/// 0438 <skill lv>.W <skill id>.W <target id>.L (CZ_USE_SKILL2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
+{
+ uint16 skill_id, skill_lv;
+ int tmp, target_id;
+ unsigned int tick = gettick();
+
+ skill_lv = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ skill_id = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ target_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]);
+
+ if( skill_lv < 1 ) skill_lv = 1; //No clue, I have seen the client do this with guild skills :/ [Skotlex]
+
+ tmp = skill_get_inf(skill_id);
+ if (tmp&INF_GROUND_SKILL || !tmp)
+ return; //Using a ground/passive skill on a target? WRONG.
+
+ if( skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE + MAX_HOMUNSKILL )
+ {
+ clif_parse_UseSkillToId_homun(sd->hd, sd, tick, skill_id, skill_lv, target_id);
+ return;
+ }
+
+ if( skill_id >= MC_SKILLBASE && skill_id < MC_SKILLBASE + MAX_MERCSKILL )
+ {
+ clif_parse_UseSkillToId_mercenary(sd->md, sd, tick, skill_id, skill_lv, target_id);
+ return;
+ }
+
+ // Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
+ sd->idletime = last_tick;
+
+ if( pc_cant_act(sd) && skill_id != RK_REFRESH && !(skill_id == SR_GENTLETOUCH_CURE && (sd->sc.opt1 == OPT1_STONE || sd->sc.opt1 == OPT1_FREEZE || sd->sc.opt1 == OPT1_STUN)) )
+ return;
+ if( pc_issit(sd) )
+ return;
+
+ if( skillnotok(skill_id, sd) )
+ return;
+
+ if( sd->bl.id != target_id && tmp&INF_SELF_SKILL )
+ target_id = sd->bl.id; // never trust the client
+
+ if( target_id < 0 && -target_id == sd->bl.id ) // for disguises [Valaris]
+ target_id = sd->bl.id;
+
+ if( sd->ud.skilltimer != INVALID_TIMER )
+ {
+ if( skill_id != SA_CASTCANCEL && skill_id != SO_SPELLFIST )
+ return;
+ }
+ else if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 )
+ {
+ if( sd->skillitem != skill_id )
+ {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0);
+ return;
+ }
+ }
+
+ if( sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER) )
+ return;
+
+ if( sd->sc.data[SC_BASILICA] && (skill_id != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) )
+ return; // On basilica only caster can use Basilica again to stop it.
+
+ if( sd->menuskill_id ) {
+ if( sd->menuskill_id == SA_TAMINGMONSTER ) {
+ clif_menuskill_clear(sd); //Cancel pet capture.
+ } else if( sd->menuskill_id != SA_AUTOSPELL )
+ return; //Can't use skills while a menu is open.
+ }
+ if( sd->skillitem == skill_id ) {
+ if( skill_lv != sd->skillitemlv )
+ skill_lv = sd->skillitemlv;
+ if( !(tmp&INF_SELF_SKILL) )
+ pc_delinvincibletimer(sd); // Target skills thru items cancel invincibility. [Inkfish]
+ unit_skilluse_id(&sd->bl, target_id, skill_id, skill_lv);
+ return;
+ }
+
+ sd->skillitem = sd->skillitemlv = 0;
+
+ if( skill_id >= GD_SKILLBASE ) {
+ if( sd->state.gmaster_flag )
+ skill_lv = guild_checkskill(sd->state.gmaster_flag, skill_id);
+ else
+ skill_lv = 0;
+ } else {
+ tmp = pc_checkskill(sd, skill_id);
+ if( skill_lv > tmp )
+ skill_lv = tmp;
+ }
+
+ pc_delinvincibletimer(sd);
+
+ if( skill_lv )
+ unit_skilluse_id(&sd->bl, target_id, skill_id, skill_lv);
+}
+
+/*==========================================
+ * Client tells server he'd like to use AoE skill id 'skill_id' of level 'skill_lv' on 'x','y' location
+ *------------------------------------------*/
+static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, uint16 skill_lv, uint16 skill_id, short x, short y, int skillmoreinfo)
+{
+ unsigned int tick = gettick();
+
+ if( !(skill_get_inf(skill_id)&INF_GROUND_SKILL) )
+ return; //Using a target skill on the ground? WRONG.
+
+ if( skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE + MAX_HOMUNSKILL ) {
+ clif_parse_UseSkillToPos_homun(sd->hd, sd, tick, skill_id, skill_lv, x, y, skillmoreinfo);
+ return;
+ }
+
+ if( skill_id >= MC_SKILLBASE && skill_id < MC_SKILLBASE + MAX_MERCSKILL )
+ {
+ clif_parse_UseSkillToPos_mercenary(sd->md, sd, tick, skill_id, skill_lv, x, y, skillmoreinfo);
+ return;
+ }
+
+ //Whether skill fails or not is irrelevant, the char ain't idle. [Skotlex]
+ sd->idletime = last_tick;
+
+ if( skillnotok(skill_id, sd) )
+ return;
+ if( skillmoreinfo != -1 )
+ {
+ if( pc_issit(sd) )
+ {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ return;
+ }
+ //You can't use Graffiti/TalkieBox AND have a vending open, so this is safe.
+ safestrncpy(sd->message, (char*)RFIFOP(fd,skillmoreinfo), MESSAGE_SIZE);
+ }
+
+ if( sd->ud.skilltimer != INVALID_TIMER )
+ return;
+
+ if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 ) {
+ if( sd->skillitem != skill_id ) {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0);
+ return;
+ }
+ }
+
+ if( sd->sc.option&(OPTION_WEDDING|OPTION_XMAS|OPTION_SUMMER) )
+ return;
+
+ if( sd->sc.data[SC_BASILICA] && (skill_id != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) )
+ return; // On basilica only caster can use Basilica again to stop it.
+
+ if( sd->menuskill_id ) {
+ if( sd->menuskill_id == SA_TAMINGMONSTER ) {
+ clif_menuskill_clear(sd); //Cancel pet capture.
+ } else if( sd->menuskill_id != SA_AUTOSPELL )
+ return; //Can't use skills while a menu is open.
+ }
+
+ pc_delinvincibletimer(sd);
+
+ if( sd->skillitem == skill_id ) {
+ if( skill_lv != sd->skillitemlv )
+ skill_lv = sd->skillitemlv;
+ unit_skilluse_pos(&sd->bl, x, y, skill_id, skill_lv);
+ } else {
+ int lv;
+ sd->skillitem = sd->skillitemlv = 0;
+ if( (lv = pc_checkskill(sd, skill_id)) > 0 ) {
+ if( skill_lv > lv )
+ skill_lv = lv;
+ unit_skilluse_pos(&sd->bl, x, y, skill_id,skill_lv);
+ }
+ }
+}
+
+
+/// Request to use a ground skill.
+/// 0116 <skill lv>.W <skill id>.W <x>.W <y>.W (CZ_USE_SKILL_TOGROUND)
+/// 0366 <skill lv>.W <skill id>.W <x>.W <y>.W (CZ_USE_SKILL_TOGROUND2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_UseSkillToPos(int fd, struct map_session_data *sd)
+{
+ if (pc_cant_act(sd))
+ return;
+ if (pc_issit(sd))
+ return;
+
+ clif_parse_UseSkillToPosSub(fd, sd,
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), //skill lv
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]), //skill num
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]), //pos x
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[3]), //pos y
+ -1 //Skill more info.
+ );
+}
+
+
+/// Request to use a ground skill with text.
+/// 0190 <skill lv>.W <skill id>.W <x>.W <y>.W <contents>.80B (CZ_USE_SKILL_TOGROUND_WITHTALKBOX)
+/// 0367 <skill lv>.W <skill id>.W <x>.W <y>.W <contents>.80B (CZ_USE_SKILL_TOGROUND_WITHTALKBOX2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_UseSkillToPosMoreInfo(int fd, struct map_session_data *sd)
+{
+ if (pc_cant_act(sd))
+ return;
+ if (pc_issit(sd))
+ return;
+
+ clif_parse_UseSkillToPosSub(fd, sd,
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), //Skill lv
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]), //Skill num
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[2]), //pos x
+ RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[3]), //pos y
+ packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[4] //skill more info
+ );
+}
+
+
+/// Answer to map selection dialog (CZ_SELECT_WARPPOINT).
+/// 011b <skill id>.W <map name>.16B
+void clif_parse_UseSkillMap(int fd, struct map_session_data* sd)
+{
+ uint16 skill_id = RFIFOW(fd,2);
+ char map_name[MAP_NAME_LENGTH];
+ mapindex_getmapname((char*)RFIFOP(fd,4), map_name);
+
+ if(skill_id != sd->menuskill_id)
+ return;
+
+ if( pc_cant_act(sd) ) {
+ clif_menuskill_clear(sd);
+ return;
+ }
+
+ pc_delinvincibletimer(sd);
+ skill_castend_map(sd,skill_id,map_name);
+}
+
+
+/// Request to set a memo on current map (CZ_REMEMBER_WARPPOINT).
+/// 011d
+void clif_parse_RequestMemo(int fd,struct map_session_data *sd)
+{
+ if (!pc_isdead(sd))
+ pc_memo(sd,-1);
+}
+
+
+/// Answer to pharmacy item selection dialog (CZ_REQMAKINGITEM).
+/// 018e <name id>.W { <material id>.W }*3
+void clif_parse_ProduceMix(int fd,struct map_session_data *sd)
+{
+ switch( sd->menuskill_id ) {
+ case -1:
+ case AM_PHARMACY:
+ case RK_RUNEMASTERY:
+ case GC_RESEARCHNEWPOISON:
+ break;
+ default:
+ return;
+ }
+ if (pc_istrading(sd)) {
+ //Make it fail to avoid shop exploits where you sell something different than you see.
+ clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0);
+ clif_menuskill_clear(sd);
+ return;
+ }
+ if( skill_can_produce_mix(sd,RFIFOW(fd,2),sd->menuskill_val, 1) )
+ skill_produce_mix(sd,0,RFIFOW(fd,2),RFIFOW(fd,4),RFIFOW(fd,6),RFIFOW(fd,8), 1);
+ clif_menuskill_clear(sd);
+}
+
+
+/// Answer to mixing item selection dialog (CZ_REQ_MAKINGITEM).
+/// 025b <mk type>.W <name id>.W
+/// mk type:
+/// 1 = cooking
+/// 2 = arrow
+/// 3 = elemental
+/// 4 = GN_MIX_COOKING
+/// 5 = GN_MAKEBOMB
+/// 6 = GN_S_PHARMACY
+void clif_parse_Cooking(int fd,struct map_session_data *sd) {
+ int type = RFIFOW(fd,2);
+ int nameid = RFIFOW(fd,4);
+ int amount = sd->menuskill_val2?sd->menuskill_val2:1;
+ if( type == 6 && sd->menuskill_id != GN_MIX_COOKING && sd->menuskill_id != GN_S_PHARMACY )
+ return;
+
+ if (pc_istrading(sd)) {
+ //Make it fail to avoid shop exploits where you sell something different than you see.
+ clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0);
+ clif_menuskill_clear(sd);
+ return;
+ }
+ if( skill_can_produce_mix(sd,nameid,sd->menuskill_val, amount) )
+ skill_produce_mix(sd,0,nameid,0,0,0,amount);
+ clif_menuskill_clear(sd);
+}
+
+
+/// Answer to repair weapon item selection dialog (CZ_REQ_ITEMREPAIR).
+/// 01fd <index>.W <name id>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W
+void clif_parse_RepairItem(int fd, struct map_session_data *sd)
+{
+ if (sd->menuskill_id != BS_REPAIRWEAPON)
+ return;
+ if (pc_istrading(sd)) {
+ //Make it fail to avoid shop exploits where you sell something different than you see.
+ clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0);
+ clif_menuskill_clear(sd);
+ return;
+ }
+ skill_repairweapon(sd,RFIFOW(fd,2));
+ clif_menuskill_clear(sd);
+}
+
+
+/// Answer to refine weapon item selection dialog (CZ_REQ_WEAPONREFINE).
+/// 0222 <index>.L
+void clif_parse_WeaponRefine(int fd, struct map_session_data *sd)
+{
+ int idx;
+
+ if (sd->menuskill_id != WS_WEAPONREFINE) //Packet exploit?
+ return;
+ if (pc_istrading(sd)) {
+ //Make it fail to avoid shop exploits where you sell something different than you see.
+ clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0);
+ clif_menuskill_clear(sd);
+ return;
+ }
+ idx = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ skill_weaponrefine(sd, idx-2);
+ clif_menuskill_clear(sd);
+}
+
+
+/// Answer to script menu dialog (CZ_CHOOSE_MENU).
+/// 00b8 <npc id>.L <choice>.B
+/// choice:
+/// 1~254 = menu item
+/// 255 = cancel
+/// NOTE: If there were more than 254 items in the list, choice
+/// overflows to choice%256.
+void clif_parse_NpcSelectMenu(int fd,struct map_session_data *sd)
+{
+ int npc_id = RFIFOL(fd,2);
+ uint8 select = RFIFOB(fd,6);
+
+ if( (select > sd->npc_menu && select != 0xff) || select == 0 )
+ {
+ TBL_NPC* nd = map_id2nd(npc_id);
+ ShowWarning("Invalid menu selection on npc %d:'%s' - got %d, valid range is [%d..%d] (player AID:%d, CID:%d, name:'%s')!\n", npc_id, (nd)?nd->name:"invalid npc id", select, 1, sd->npc_menu, sd->bl.id, sd->status.char_id, sd->status.name);
+ clif_GM_kick(NULL,sd);
+ return;
+ }
+
+ sd->npc_menu = select;
+ npc_scriptcont(sd,npc_id);
+}
+
+
+/// NPC dialog 'next' click (CZ_REQ_NEXT_SCRIPT).
+/// 00b9 <npc id>.L
+void clif_parse_NpcNextClicked(int fd,struct map_session_data *sd)
+{
+ npc_scriptcont(sd,RFIFOL(fd,2));
+}
+
+
+/// NPC numeric input dialog value (CZ_INPUT_EDITDLG).
+/// 0143 <npc id>.L <value>.L
+void clif_parse_NpcAmountInput(int fd,struct map_session_data *sd)
+{
+ int npcid = RFIFOL(fd,2);
+ int amount = (int)RFIFOL(fd,6);
+
+ sd->npc_amount = amount;
+ npc_scriptcont(sd, npcid);
+}
+
+
+/// NPC text input dialog value (CZ_INPUT_EDITDLGSTR).
+/// 01d5 <packet len>.W <npc id>.L <string>.?B
+void clif_parse_NpcStringInput(int fd, struct map_session_data* sd)
+{
+ int message_len = RFIFOW(fd,2)-8;
+ int npcid = RFIFOL(fd,4);
+ const char* message = (char*)RFIFOP(fd,8);
+
+ if( message_len <= 0 )
+ return; // invalid input
+
+ safestrncpy(sd->npc_str, message, min(message_len,CHATBOX_SIZE));
+ npc_scriptcont(sd, npcid);
+}
+
+
+/// NPC dialog 'close' click (CZ_CLOSE_DIALOG).
+/// 0146 <npc id>.L
+void clif_parse_NpcCloseClicked(int fd,struct map_session_data *sd)
+{
+ if (!sd->npc_id) //Avoid parsing anything when the script was done with. [Skotlex]
+ return;
+ npc_scriptcont(sd,RFIFOL(fd,2));
+}
+
+
+/// Answer to identify item selection dialog (CZ_REQ_ITEMIDENTIFY).
+/// 0178 <index>.W
+/// index:
+/// -1 = cancel
+void clif_parse_ItemIdentify(int fd,struct map_session_data *sd)
+{
+ short idx = RFIFOW(fd,2);
+
+ if (sd->menuskill_id != MC_IDENTIFY)
+ return;
+ if( idx == -1 ) {// cancel pressed
+ clif_menuskill_clear(sd);
+ return;
+ }
+ skill_identify(sd,idx-2);
+ clif_menuskill_clear(sd);
+}
+
+
+/// Answer to arrow crafting item selection dialog (CZ_REQ_MAKINGARROW).
+/// 01ae <name id>.W
+void clif_parse_SelectArrow(int fd,struct map_session_data *sd)
+{
+ if (pc_istrading(sd)) {
+ //Make it fail to avoid shop exploits where you sell something different than you see.
+ clif_skill_fail(sd,sd->ud.skill_id,USESKILL_FAIL_LEVEL,0);
+ clif_menuskill_clear(sd);
+ return;
+ }
+ switch( sd->menuskill_id ) {
+ case AC_MAKINGARROW:
+ skill_arrow_create(sd,RFIFOW(fd,2));
+ break;
+ case SA_CREATECON:
+ skill_produce_mix(sd,SA_CREATECON,RFIFOW(fd,2),0,0,0, 1);
+ break;
+ case WL_READING_SB:
+ skill_spellbook(sd,RFIFOW(fd,2));
+ break;
+ case GC_POISONINGWEAPON:
+ skill_poisoningweapon(sd,RFIFOW(fd,2));
+ break;
+ case NC_MAGICDECOY:
+ skill_magicdecoy(sd,RFIFOW(fd,2));
+ break;
+ }
+
+ clif_menuskill_clear(sd);
+}
+
+
+/// Answer to SA_AUTOSPELL skill selection dialog (CZ_SELECTAUTOSPELL).
+/// 01ce <skill id>.L
+void clif_parse_AutoSpell(int fd,struct map_session_data *sd)
+{
+ if (sd->menuskill_id != SA_AUTOSPELL)
+ return;
+ skill_autospell(sd,RFIFOL(fd,2));
+ clif_menuskill_clear(sd);
+}
+
+
+/// Request to display item carding/composition list (CZ_REQ_ITEMCOMPOSITION_LIST).
+/// 017a <card index>.W
+void clif_parse_UseCard(int fd,struct map_session_data *sd)
+{
+ if (sd->state.trading != 0)
+ return;
+ clif_use_card(sd,RFIFOW(fd,2)-2);
+}
+
+
+/// Answer to carding/composing item selection dialog (CZ_REQ_ITEMCOMPOSITION).
+/// 017c <card index>.W <equip index>.W
+void clif_parse_InsertCard(int fd,struct map_session_data *sd)
+{
+ if (sd->state.trading != 0)
+ return;
+ pc_insert_card(sd,RFIFOW(fd,2)-2,RFIFOW(fd,4)-2);
+}
+
+
+/// Request of character's name by char ID.
+/// 0193 <char id>.L (CZ_REQNAME_BYGID)
+/// 0369 <char id>.L (CZ_REQNAME_BYGID2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_SolveCharName(int fd, struct map_session_data *sd)
+{
+ int charid;
+
+ charid = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ map_reqnickdb(sd, charid);
+}
+
+
+/// /resetskill /resetstate (CZ_RESET).
+/// Request to reset stats or skills.
+/// 0197 <type>.W
+/// type:
+/// 0 = state
+/// 1 = skill
+void clif_parse_ResetChar(int fd, struct map_session_data *sd) {
+ char cmd[15];
+
+ if( RFIFOW(fd,2) )
+ sprintf(cmd,"%cresetskill",atcommand_symbol);
+ else
+ sprintf(cmd,"%cresetstat",atcommand_symbol);
+
+ is_atcommand(fd, sd, cmd, 1);
+}
+
+
+/// /lb /nlb (CZ_LOCALBROADCAST).
+/// Request to broadcast a message on current map.
+/// 019c <packet len>.W <text>.?B
+void clif_parse_LocalBroadcast(int fd, struct map_session_data* sd)
+{
+ char command[CHAT_SIZE_MAX+16];
+ char* msg = (char*)RFIFOP(fd,4);
+ unsigned int len = RFIFOW(fd,2)-4;
+
+ // as the length varies depending on the command used, just block unreasonably long strings
+ mes_len_check(msg, len, CHAT_SIZE_MAX);
+
+ sprintf(command, "%clkami %s", atcommand_symbol, msg);
+ is_atcommand(fd, sd, command, 1);
+}
+
+
+/// Request to move an item from inventory to storage.
+/// 00f3 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_BODY_TO_STORE)
+/// 0364 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_BODY_TO_STORE2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_MoveToKafra(int fd, struct map_session_data *sd)
+{
+ int item_index, item_amount;
+
+ if (pc_istrading(sd))
+ return;
+
+ item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-2;
+ item_amount = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+ if (item_index < 0 || item_index >= MAX_INVENTORY || item_amount < 1)
+ return;
+
+ if (sd->state.storage_flag == 1)
+ storage_storageadd(sd, item_index, item_amount);
+ else
+ if (sd->state.storage_flag == 2)
+ storage_guild_storageadd(sd, item_index, item_amount);
+}
+
+
+/// Request to move an item from storage to inventory.
+/// 00f5 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_STORE_TO_BODY)
+/// 0365 <index>.W <amount>.L (CZ_MOVE_ITEM_FROM_STORE_TO_BODY2)
+/// There are various variants of this packet, some of them have padding between fields.
+void clif_parse_MoveFromKafra(int fd,struct map_session_data *sd)
+{
+ int item_index, item_amount;
+
+ item_index = RFIFOW(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0])-1;
+ item_amount = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1]);
+
+ if (sd->state.storage_flag == 1)
+ storage_storageget(sd, item_index, item_amount);
+ else
+ if(sd->state.storage_flag == 2)
+ storage_guild_storageget(sd, item_index, item_amount);
+}
+
+
+/// Request to move an item from cart to storage (CZ_MOVE_ITEM_FROM_CART_TO_STORE).
+/// 0129 <index>.W <amount>.L
+void clif_parse_MoveToKafraFromCart(int fd, struct map_session_data *sd)
+{
+ if( sd->state.vending )
+ return;
+ if (!pc_iscarton(sd))
+ return;
+
+ if (sd->state.storage_flag == 1)
+ storage_storageaddfromcart(sd, RFIFOW(fd,2) - 2, RFIFOL(fd,4));
+ else
+ if (sd->state.storage_flag == 2)
+ storage_guild_storageaddfromcart(sd, RFIFOW(fd,2) - 2, RFIFOL(fd,4));
+}
+
+
+/// Request to move an item from storage to cart (CZ_MOVE_ITEM_FROM_STORE_TO_CART).
+/// 0128 <index>.W <amount>.L
+void clif_parse_MoveFromKafraToCart(int fd, struct map_session_data *sd)
+{
+ if( sd->state.vending )
+ return;
+ if (!pc_iscarton(sd))
+ return;
+
+ if (sd->state.storage_flag == 1)
+ storage_storagegettocart(sd, RFIFOW(fd,2)-1, RFIFOL(fd,4));
+ else
+ if (sd->state.storage_flag == 2)
+ storage_guild_storagegettocart(sd, RFIFOW(fd,2)-1, RFIFOL(fd,4));
+}
+
+
+/// Request to close storage (CZ_CLOSE_STORE).
+/// 00f7
+void clif_parse_CloseKafra(int fd, struct map_session_data *sd)
+{
+ if( sd->state.storage_flag == 1 )
+ storage_storageclose(sd);
+ else
+ if( sd->state.storage_flag == 2 )
+ storage_guild_storageclose(sd);
+}
+
+
+/// Displays kafra storage password dialog (ZC_REQ_STORE_PASSWORD).
+/// 023a <info>.W
+/// info:
+/// 0 = password has not been set yet
+/// 1 = storage is password-protected
+/// 8 = too many wrong passwords
+/// ? = ignored
+/// NOTE: This packet is only available on certain non-kRO clients.
+void clif_storagepassword(struct map_session_data* sd, short info)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x23a));
+ WFIFOW(fd,0) = 0x23a;
+ WFIFOW(fd,2) = info;
+ WFIFOSET(fd,packet_len(0x23a));
+}
+
+
+/// Answer to the kafra storage password dialog (CZ_ACK_STORE_PASSWORD).
+/// 023b <type>.W <password>.16B <new password>.16B
+/// type:
+/// 2 = change password
+/// 3 = check password
+/// NOTE: This packet is only available on certain non-kRO clients.
+void clif_parse_StoragePassword(int fd, struct map_session_data *sd)
+{
+ //TODO
+}
+
+
+/// Result of kafra storage password validation (ZC_RESULT_STORE_PASSWORD).
+/// 023c <result>.W <error count>.W
+/// result:
+/// 4 = password change success
+/// 5 = password change failure
+/// 6 = password check success
+/// 7 = password check failure
+/// 8 = too many wrong passwords
+/// ? = ignored
+/// NOTE: This packet is only available on certain non-kRO clients.
+void clif_storagepassword_result(struct map_session_data* sd, short result, short error_count)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x23c));
+ WFIFOW(fd,0) = 0x23c;
+ WFIFOW(fd,2) = result;
+ WFIFOW(fd,4) = error_count;
+ WFIFOSET(fd,packet_len(0x23c));
+}
+
+
+/// Party creation request
+/// 00f9 <party name>.24B (CZ_MAKE_GROUP)
+/// 01e8 <party name>.24B <item pickup rule>.B <item share rule>.B (CZ_MAKE_GROUP2)
+void clif_parse_CreateParty(int fd, struct map_session_data *sd)
+{
+ char* name = (char*)RFIFOP(fd,2);
+ name[NAME_LENGTH-1] = '\0';
+
+ if( map[sd->bl.m].flag.partylock )
+ {// Party locked.
+ clif_displaymessage(fd, msg_txt(227));
+ return;
+ }
+ if( battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 7 )
+ {
+ clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,4);
+ return;
+ }
+
+ party_create(sd,name,0,0);
+}
+
+void clif_parse_CreateParty2(int fd, struct map_session_data *sd)
+{
+ char* name = (char*)RFIFOP(fd,2);
+ int item1 = RFIFOB(fd,26);
+ int item2 = RFIFOB(fd,27);
+ name[NAME_LENGTH-1] = '\0';
+
+ if( map[sd->bl.m].flag.partylock )
+ {// Party locked.
+ clif_displaymessage(fd, msg_txt(227));
+ return;
+ }
+ if( battle_config.basic_skill_check && pc_checkskill(sd,NV_BASIC) < 7 )
+ {
+ clif_skill_fail(sd,1,USESKILL_FAIL_LEVEL,4);
+ return;
+ }
+
+ party_create(sd,name,item1,item2);
+}
+
+
+/// Party invitation request
+/// 00fc <account id>.L (CZ_REQ_JOIN_GROUP)
+/// 02c4 <char name>.24B (CZ_PARTY_JOIN_REQ)
+void clif_parse_PartyInvite(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *t_sd;
+
+ if(map[sd->bl.m].flag.partylock)
+ {// Party locked.
+ clif_displaymessage(fd, msg_txt(227));
+ return;
+ }
+
+ t_sd = map_id2sd(RFIFOL(fd,2));
+
+ if(t_sd && t_sd->state.noask)
+ {// @noask [LuzZza]
+ clif_noask_sub(sd, t_sd, 1);
+ return;
+ }
+
+ party_invite(sd, t_sd);
+}
+
+void clif_parse_PartyInvite2(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *t_sd;
+ char *name = (char*)RFIFOP(fd,2);
+ name[NAME_LENGTH-1] = '\0';
+
+ if(map[sd->bl.m].flag.partylock)
+ {// Party locked.
+ clif_displaymessage(fd, msg_txt(227));
+ return;
+ }
+
+ t_sd = map_nick2sd(name);
+
+ if(t_sd && t_sd->state.noask)
+ {// @noask [LuzZza]
+ clif_noask_sub(sd, t_sd, 1);
+ return;
+ }
+
+ party_invite(sd, t_sd);
+}
+
+
+/// Party invitation reply
+/// 00ff <party id>.L <flag>.L (CZ_JOIN_GROUP)
+/// 02c7 <party id>.L <flag>.B (CZ_PARTY_JOIN_REQ_ACK)
+/// flag:
+/// 0 = reject
+/// 1 = accept
+void clif_parse_ReplyPartyInvite(int fd,struct map_session_data *sd)
+{
+ party_reply_invite(sd,RFIFOL(fd,2),RFIFOL(fd,6));
+}
+
+void clif_parse_ReplyPartyInvite2(int fd,struct map_session_data *sd)
+{
+ party_reply_invite(sd,RFIFOL(fd,2),RFIFOB(fd,6));
+}
+
+
+/// Request to leave party (CZ_REQ_LEAVE_GROUP).
+/// 0100
+void clif_parse_LeaveParty(int fd, struct map_session_data *sd)
+{
+ if(map[sd->bl.m].flag.partylock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(227));
+ return;
+ }
+ party_leave(sd);
+}
+
+
+/// Request to expel a party member (CZ_REQ_EXPEL_GROUP_MEMBER).
+/// 0103 <account id>.L <char name>.24B
+void clif_parse_RemovePartyMember(int fd, struct map_session_data *sd)
+{
+ if(map[sd->bl.m].flag.partylock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(227));
+ return;
+ }
+ party_removemember(sd,RFIFOL(fd,2),(char*)RFIFOP(fd,6));
+}
+
+
+/// Request to change party options.
+/// 0102 <exp share rule>.L (CZ_CHANGE_GROUPEXPOPTION)
+/// 07d7 <exp share rule>.L <item pickup rule>.B <item share rule>.B (CZ_GROUPINFO_CHANGE_V2)
+void clif_parse_PartyChangeOption(int fd, struct map_session_data *sd)
+{
+ struct party_data *p;
+ int i;
+
+ if( !sd->status.party_id )
+ return;
+
+ p = party_search(sd->status.party_id);
+ if( p == NULL )
+ return;
+
+ ARR_FIND( 0, MAX_PARTY, i, p->data[i].sd == sd );
+ if( i == MAX_PARTY )
+ return; //Shouldn't happen
+
+ if( !p->party.member[i].leader )
+ return;
+
+#if PACKETVER < 20090603
+ //Client can't change the item-field
+ party_changeoption(sd, RFIFOL(fd,2), p->party.item);
+#else
+ party_changeoption(sd, RFIFOL(fd,2), ((RFIFOB(fd,6)?1:0)|(RFIFOB(fd,7)?2:0)));
+#endif
+}
+
+
+/// Validates and processes party messages (CZ_REQUEST_CHAT_PARTY).
+/// 0108 <packet len>.W <text>.?B (<name> : <message>) 00
+void clif_parse_PartyMessage(int fd, struct map_session_data* sd)
+{
+ const char* text = (char*)RFIFOP(fd,4);
+ int textlen = RFIFOW(fd,2) - 4;
+
+ char *name, *message;
+ int namelen, messagelen;
+
+ // validate packet and retrieve name and message
+ if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
+ return;
+
+ if( is_atcommand(fd, sd, message, 1) )
+ return;
+
+ if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) )
+ return;
+
+ if( battle_config.min_chat_delay )
+ { //[Skotlex]
+ if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0)
+ return;
+ sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
+ }
+
+ party_send_message(sd, text, textlen);
+}
+
+
+/// Changes Party Leader (CZ_CHANGE_GROUP_MASTER).
+/// 07da <account id>.L
+void clif_parse_PartyChangeLeader(int fd, struct map_session_data* sd)
+{
+ party_changeleader(sd, map_id2sd(RFIFOL(fd,2)));
+}
+
+
+/// Party Booking in KRO [Spiria]
+///
+
+/// Request to register a party booking advertisment (CZ_PARTY_BOOKING_REQ_REGISTER).
+/// 0802 <level>.W <map id>.W { <job>.W }*6
+void clif_parse_PartyBookingRegisterReq(int fd, struct map_session_data* sd)
+{
+ short level = RFIFOW(fd,2);
+ short mapid = RFIFOW(fd,4);
+ short job[PARTY_BOOKING_JOBS];
+ int i;
+
+ for(i=0; i<PARTY_BOOKING_JOBS; i++)
+ job[i] = RFIFOB(fd,6+i*2);
+
+ party_booking_register(sd, level, mapid, job);
+}
+
+
+/// Result of request to register a party booking advertisment (ZC_PARTY_BOOKING_ACK_REGISTER).
+/// 0803 <result>.W
+/// result:
+/// 0 = success
+/// 1 = failure
+/// 2 = already registered
+void clif_PartyBookingRegisterAck(struct map_session_data *sd, int flag)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x803));
+ WFIFOW(fd,0) = 0x803;
+ WFIFOW(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0x803));
+}
+
+
+/// Request to search for party booking advertisments (CZ_PARTY_BOOKING_REQ_SEARCH).
+/// 0804 <level>.W <map id>.W <job>.W <last index>.L <result count>.W
+void clif_parse_PartyBookingSearchReq(int fd, struct map_session_data* sd)
+{
+ short level = RFIFOW(fd,2);
+ short mapid = RFIFOW(fd,4);
+ short job = RFIFOW(fd,6);
+ unsigned long lastindex = RFIFOL(fd,8);
+ short resultcount = RFIFOW(fd,12);
+
+ party_booking_search(sd, level, mapid, job, lastindex, resultcount);
+}
+
+
+/// Party booking search results (ZC_PARTY_BOOKING_ACK_SEARCH).
+/// 0805 <packet len>.W <more results>.B { <index>.L <char name>.24B <expire time>.L <level>.W <map id>.W { <job>.W }*6 }*
+/// more results:
+/// 0 = no
+/// 1 = yes
+void clif_PartyBookingSearchAck(int fd, struct party_booking_ad_info** results, int count, bool more_result)
+{
+ int i, j;
+ int size = sizeof(struct party_booking_ad_info); // structure size (48)
+ struct party_booking_ad_info *pb_ad;
+ WFIFOHEAD(fd,size*count + 5);
+ WFIFOW(fd,0) = 0x805;
+ WFIFOW(fd,2) = size*count + 5;
+ WFIFOB(fd,4) = more_result;
+ for(i=0; i<count; i++)
+ {
+ pb_ad = results[i];
+ WFIFOL(fd,i*size+5) = pb_ad->index;
+ memcpy(WFIFOP(fd,i*size+9),pb_ad->charname,NAME_LENGTH);
+ WFIFOL(fd,i*size+33) = pb_ad->starttime; // FIXME: This is expire time
+ WFIFOW(fd,i*size+37) = pb_ad->p_detail.level;
+ WFIFOW(fd,i*size+39) = pb_ad->p_detail.mapid;
+ for(j=0; j<PARTY_BOOKING_JOBS; j++)
+ WFIFOW(fd,i*size+41+j*2) = pb_ad->p_detail.job[j];
+ }
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Request to delete own party booking advertisment (CZ_PARTY_BOOKING_REQ_DELETE).
+/// 0806
+void clif_parse_PartyBookingDeleteReq(int fd, struct map_session_data* sd)
+{
+ if(party_booking_delete(sd))
+ clif_PartyBookingDeleteAck(sd, 0);
+}
+
+
+/// Result of request to delete own party booking advertisment (ZC_PARTY_BOOKING_ACK_DELETE).
+/// 0807 <result>.W
+/// result:
+/// 0 = success
+/// 1 = success (auto-removed expired ad)
+/// 2 = failure
+/// 3 = nothing registered
+void clif_PartyBookingDeleteAck(struct map_session_data* sd, int flag)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x807));
+ WFIFOW(fd,0) = 0x807;
+ WFIFOW(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0x807));
+}
+
+
+/// Request to update party booking advertisment (CZ_PARTY_BOOKING_REQ_UPDATE).
+/// 0808 { <job>.W }*6
+void clif_parse_PartyBookingUpdateReq(int fd, struct map_session_data* sd)
+{
+ short job[PARTY_BOOKING_JOBS];
+ int i;
+
+ for(i=0; i<PARTY_BOOKING_JOBS; i++)
+ job[i] = RFIFOW(fd,2+i*2);
+
+ party_booking_update(sd, job);
+}
+
+
+/// Notification about new party booking advertisment (ZC_PARTY_BOOKING_NOTIFY_INSERT).
+/// 0809 <index>.L <char name>.24B <expire time>.L <level>.W <map id>.W { <job>.W }*6
+void clif_PartyBookingInsertNotify(struct map_session_data* sd, struct party_booking_ad_info* pb_ad)
+{
+ int i;
+ uint8 buf[38+PARTY_BOOKING_JOBS*2];
+
+ if(pb_ad == NULL) return;
+
+ WBUFW(buf,0) = 0x809;
+ WBUFL(buf,2) = pb_ad->index;
+ memcpy(WBUFP(buf,6),pb_ad->charname,NAME_LENGTH);
+ WBUFL(buf,30) = pb_ad->starttime; // FIXME: This is expire time
+ WBUFW(buf,34) = pb_ad->p_detail.level;
+ WBUFW(buf,36) = pb_ad->p_detail.mapid;
+ for(i=0; i<PARTY_BOOKING_JOBS; i++)
+ WBUFW(buf,38+i*2) = pb_ad->p_detail.job[i];
+
+ clif_send(buf, packet_len(0x809), &sd->bl, ALL_CLIENT);
+}
+
+
+/// Notification about updated party booking advertisment (ZC_PARTY_BOOKING_NOTIFY_UPDATE).
+/// 080a <index>.L { <job>.W }*6
+void clif_PartyBookingUpdateNotify(struct map_session_data* sd, struct party_booking_ad_info* pb_ad)
+{
+ int i;
+ uint8 buf[6+PARTY_BOOKING_JOBS*2];
+
+ if(pb_ad == NULL) return;
+
+ WBUFW(buf,0) = 0x80a;
+ WBUFL(buf,2) = pb_ad->index;
+ for(i=0; i<PARTY_BOOKING_JOBS; i++)
+ WBUFW(buf,6+i*2) = pb_ad->p_detail.job[i];
+ clif_send(buf,packet_len(0x80a),&sd->bl,ALL_CLIENT); // Now UPDATE all client.
+}
+
+
+/// Notification about deleted party booking advertisment (ZC_PARTY_BOOKING_NOTIFY_DELETE).
+/// 080b <index>.L
+void clif_PartyBookingDeleteNotify(struct map_session_data* sd, int index)
+{
+ uint8 buf[6];
+
+ WBUFW(buf,0) = 0x80b;
+ WBUFL(buf,2) = index;
+
+ clif_send(buf, packet_len(0x80b), &sd->bl, ALL_CLIENT); // Now UPDATE all client.
+}
+
+
+/// Request to close own vending (CZ_REQ_CLOSESTORE).
+/// 012e
+void clif_parse_CloseVending(int fd, struct map_session_data* sd)
+{
+ vending_closevending(sd);
+}
+
+
+/// Request to open a vending shop (CZ_REQ_BUY_FROMMC).
+/// 0130 <account id>.L
+void clif_parse_VendingListReq(int fd, struct map_session_data* sd)
+{
+ if( sd->npc_id )
+ {// using an NPC
+ return;
+ }
+ vending_vendinglistreq(sd,RFIFOL(fd,2));
+}
+
+
+/// Shop item(s) purchase request (CZ_PC_PURCHASE_ITEMLIST_FROMMC).
+/// 0134 <packet len>.W <account id>.L { <amount>.W <index>.W }*
+void clif_parse_PurchaseReq(int fd, struct map_session_data* sd)
+{
+ int len = (int)RFIFOW(fd,2) - 8;
+ int id = (int)RFIFOL(fd,4);
+ const uint8* data = (uint8*)RFIFOP(fd,8);
+
+ vending_purchasereq(sd, id, sd->vended_id, data, len/4);
+
+ // whether it fails or not, the buy window is closed
+ sd->vended_id = 0;
+}
+
+
+/// Shop item(s) purchase request (CZ_PC_PURCHASE_ITEMLIST_FROMMC2).
+/// 0801 <packet len>.W <account id>.L <unique id>.L { <amount>.W <index>.W }*
+void clif_parse_PurchaseReq2(int fd, struct map_session_data* sd)
+{
+ int len = (int)RFIFOW(fd,2) - 12;
+ int aid = (int)RFIFOL(fd,4);
+ int uid = (int)RFIFOL(fd,8);
+ const uint8* data = (uint8*)RFIFOP(fd,12);
+
+ vending_purchasereq(sd, aid, uid, data, len/4);
+
+ // whether it fails or not, the buy window is closed
+ sd->vended_id = 0;
+}
+
+
+/// Confirm or cancel the shop preparation window.
+/// 012f <packet len>.W <shop name>.80B { <index>.W <amount>.W <price>.L }* (CZ_REQ_OPENSTORE)
+/// 01b2 <packet len>.W <shop name>.80B <result>.B { <index>.W <amount>.W <price>.L }* (CZ_REQ_OPENSTORE2)
+/// result:
+/// 0 = canceled
+/// 1 = open
+void clif_parse_OpenVending(int fd, struct map_session_data* sd)
+{
+ short len = (short)RFIFOW(fd,2) - 85;
+ const char* message = (char*)RFIFOP(fd,4);
+ bool flag = (bool)RFIFOB(fd,84);
+ const uint8* data = (uint8*)RFIFOP(fd,85);
+
+ if( sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOROOM )
+ return;
+ if( map[sd->bl.m].flag.novending ) {
+ clif_displaymessage (sd->fd, msg_txt(276)); // "You can't open a shop on this map"
+ return;
+ }
+ if( map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKNOVENDING) ) {
+ clif_displaymessage (sd->fd, msg_txt(204)); // "You can't open a shop on this cell."
+ return;
+ }
+
+ if( vending_checknearnpc(&sd->bl) ) {
+ char output[150];
+ sprintf(output, msg_txt(662), battle_config.min_npc_vending_distance);
+ clif_displaymessage(sd->fd, output);
+ clif_skill_fail(sd, MC_VENDING, USESKILL_FAIL_LEVEL, 0);
+ return;
+ }
+
+ if( message[0] == '\0' ) // invalid input
+ return;
+
+ vending_openvending(sd, message, flag, data, len/8);
+}
+
+
+/// Guild creation request (CZ_REQ_MAKE_GUILD).
+/// 0165 <char id>.L <guild name>.24B
+void clif_parse_CreateGuild(int fd,struct map_session_data *sd)
+{
+ char* name = (char*)RFIFOP(fd,6);
+ name[NAME_LENGTH-1] = '\0';
+
+ if(map[sd->bl.m].flag.guildlock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+
+ guild_create(sd, name);
+}
+
+
+/// Request for guild window interface permissions (CZ_REQ_GUILD_MENUINTERFACE).
+/// 014d
+void clif_parse_GuildCheckMaster(int fd, struct map_session_data *sd)
+{
+ clif_guild_masterormember(sd);
+}
+
+
+/// Request for guild window information (CZ_REQ_GUILD_MENU).
+/// 014f <type>.L
+/// type:
+/// 0 = basic info
+/// 1 = member manager
+/// 2 = positions
+/// 3 = skills
+/// 4 = expulsion list
+/// 5 = unknown (GM_ALLGUILDLIST)
+/// 6 = notice
+void clif_parse_GuildRequestInfo(int fd, struct map_session_data *sd)
+{
+ if( !sd->status.guild_id && !sd->bg_id )
+ return;
+
+ switch( RFIFOL(fd,2) )
+ {
+ case 0: // Basic Information Guild, hostile alliance information
+ clif_guild_basicinfo(sd);
+ clif_guild_allianceinfo(sd);
+ break;
+ case 1: // Members list, list job title
+ clif_guild_positionnamelist(sd);
+ clif_guild_memberlist(sd);
+ break;
+ case 2: // List job title, title information list
+ clif_guild_positionnamelist(sd);
+ clif_guild_positioninfolist(sd);
+ break;
+ case 3: // Skill list
+ clif_guild_skillinfo(sd);
+ break;
+ case 4: // Expulsion list
+ clif_guild_expulsionlist(sd);
+ break;
+ default:
+ ShowError("clif: guild request info: unknown type %d\n", RFIFOL(fd,2));
+ break;
+ }
+}
+
+
+/// Request to update guild positions (CZ_REG_CHANGE_GUILD_POSITIONINFO).
+/// 0161 <packet len>.W { <position id>.L <mode>.L <ranking>.L <pay rate>.L <name>.24B }*
+void clif_parse_GuildChangePositionInfo(int fd, struct map_session_data *sd)
+{
+ int i;
+
+ if(!sd->state.gmaster_flag)
+ return;
+
+ for(i = 4; i < RFIFOW(fd,2); i += 40 ){
+ guild_change_position(sd->status.guild_id, RFIFOL(fd,i), RFIFOL(fd,i+4), RFIFOL(fd,i+12), (char*)RFIFOP(fd,i+16));
+ }
+}
+
+
+/// Request to update the position of guild members (CZ_REQ_CHANGE_MEMBERPOS).
+/// 0155 <packet len>.W { <account id>.L <char id>.L <position id>.L }*
+void clif_parse_GuildChangeMemberPosition(int fd, struct map_session_data *sd)
+{
+ int i;
+
+ if(!sd->state.gmaster_flag)
+ return;
+
+ for(i=4;i<RFIFOW(fd,2);i+=12){
+ guild_change_memberposition(sd->status.guild_id,
+ RFIFOL(fd,i),RFIFOL(fd,i+4),RFIFOL(fd,i+8));
+ }
+}
+
+
+/// Request for guild emblem data (CZ_REQ_GUILD_EMBLEM_IMG).
+/// 0151 <guild id>.L
+void clif_parse_GuildRequestEmblem(int fd,struct map_session_data *sd)
+{
+ struct guild* g;
+ int guild_id = RFIFOL(fd,2);
+
+ if( (g = guild_search(guild_id)) != NULL )
+ clif_guild_emblem(sd,g);
+}
+
+
+/// Validates data of a guild emblem (compressed bitmap)
+static bool clif_validate_emblem(const uint8* emblem, unsigned long emblem_len)
+{
+ bool success;
+ uint8 buf[1800]; // no well-formed emblem bitmap is larger than 1782 (24 bit) / 1654 (8 bit) bytes
+ unsigned long buf_len = sizeof(buf);
+
+ success = ( decode_zip(buf, &buf_len, emblem, emblem_len) == 0 && buf_len >= 18 ) // sizeof(BITMAPFILEHEADER) + sizeof(biSize) of the following info header struct
+ && RBUFW(buf,0) == 0x4d42 // BITMAPFILEHEADER.bfType (signature)
+ && RBUFL(buf,2) == buf_len // BITMAPFILEHEADER.bfSize (file size)
+ && RBUFL(buf,10) < buf_len // BITMAPFILEHEADER.bfOffBits (offset to bitmap bits)
+ ;
+
+ return success;
+}
+
+
+/// Request to update the guild emblem (CZ_REGISTER_GUILD_EMBLEM_IMG).
+/// 0153 <packet len>.W <emblem data>.?B
+void clif_parse_GuildChangeEmblem(int fd,struct map_session_data *sd)
+{
+ unsigned long emblem_len = RFIFOW(fd,2)-4;
+ const uint8* emblem = RFIFOP(fd,4);
+
+ if( !emblem_len || !sd->state.gmaster_flag )
+ return;
+
+ if( !clif_validate_emblem(emblem, emblem_len) )
+ {
+ ShowWarning("clif_parse_GuildChangeEmblem: Rejected malformed guild emblem (size=%lu, accound_id=%d, char_id=%d, guild_id=%d).\n", emblem_len, sd->status.account_id, sd->status.char_id, sd->status.guild_id);
+ return;
+ }
+
+ guild_change_emblem(sd, emblem_len, (const char*)emblem);
+}
+
+
+/// Guild notice update request (CZ_GUILD_NOTICE).
+/// 016e <guild id>.L <msg1>.60B <msg2>.120B
+void clif_parse_GuildChangeNotice(int fd, struct map_session_data* sd)
+{
+ int guild_id = RFIFOL(fd,2);
+ char* msg1 = (char*)RFIFOP(fd,6);
+ char* msg2 = (char*)RFIFOP(fd,66);
+
+ if(!sd->state.gmaster_flag)
+ return;
+
+ // compensate for some client defects when using multilanguage mode
+ if (msg1[0] == '|' && msg1[3] == '|') msg1+= 3; // skip duplicate marker
+ if (msg2[0] == '|' && msg2[3] == '|') msg2+= 3; // skip duplicate marker
+ if (msg2[0] == '|') msg2[strnlen(msg2, MAX_GUILDMES2)-1] = '\0'; // delete extra space at the end of string
+
+ guild_change_notice(sd, guild_id, msg1, msg2);
+}
+
+
+/// Guild invite request (CZ_REQ_JOIN_GUILD).
+/// 0168 <account id>.L <inviter account id>.L <inviter char id>.L
+void clif_parse_GuildInvite(int fd,struct map_session_data *sd)
+{
+ struct map_session_data *t_sd;
+
+ if(map[sd->bl.m].flag.guildlock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+
+ t_sd = map_id2sd(RFIFOL(fd,2));
+
+ // @noask [LuzZza]
+ if(t_sd && t_sd->state.noask) {
+ clif_noask_sub(sd, t_sd, 2);
+ return;
+ }
+
+ guild_invite(sd,t_sd);
+}
+
+
+/// Answer to guild invitation (CZ_JOIN_GUILD).
+/// 016b <guild id>.L <answer>.L
+/// answer:
+/// 0 = refuse
+/// 1 = accept
+void clif_parse_GuildReplyInvite(int fd,struct map_session_data *sd)
+{
+ guild_reply_invite(sd,RFIFOL(fd,2),RFIFOL(fd,6));
+}
+
+
+/// Request to leave guild (CZ_REQ_LEAVE_GUILD).
+/// 0159 <guild id>.L <account id>.L <char id>.L <reason>.40B
+void clif_parse_GuildLeave(int fd,struct map_session_data *sd)
+{
+ if(map[sd->bl.m].flag.guildlock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+ if( sd->bg_id )
+ {
+ clif_displaymessage(fd, msg_txt(670)); //"You can't leave battleground guilds."
+ return;
+ }
+
+ guild_leave(sd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),(char*)RFIFOP(fd,14));
+}
+
+
+/// Request to expel a member of a guild (CZ_REQ_BAN_GUILD).
+/// 015b <guild id>.L <account id>.L <char id>.L <reason>.40B
+void clif_parse_GuildExpulsion(int fd,struct map_session_data *sd)
+{
+ if( map[sd->bl.m].flag.guildlock || sd->bg_id )
+ { // Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+ guild_expulsion(sd,RFIFOL(fd,2),RFIFOL(fd,6),RFIFOL(fd,10),(char*)RFIFOP(fd,14));
+}
+
+
+/// Validates and processes guild messages (CZ_GUILD_CHAT).
+/// 017e <packet len>.W <text>.?B (<name> : <message>) 00
+void clif_parse_GuildMessage(int fd, struct map_session_data* sd)
+{
+ const char* text = (char*)RFIFOP(fd,4);
+ int textlen = RFIFOW(fd,2) - 4;
+
+ char *name, *message;
+ int namelen, messagelen;
+
+ // validate packet and retrieve name and message
+ if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
+ return;
+
+ if( is_atcommand(fd, sd, message, 1) )
+ return;
+
+ if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) )
+ return;
+
+ if( battle_config.min_chat_delay )
+ { //[Skotlex]
+ if (DIFF_TICK(sd->cantalk_tick, gettick()) > 0)
+ return;
+ sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
+ }
+
+ if( sd->bg_id )
+ bg_send_message(sd, text, textlen);
+ else
+ guild_send_message(sd, text, textlen);
+}
+
+
+/// Guild alliance request (CZ_REQ_ALLY_GUILD).
+/// 0170 <account id>.L <inviter account id>.L <inviter char id>.L
+void clif_parse_GuildRequestAlliance(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *t_sd;
+
+ if(!sd->state.gmaster_flag)
+ return;
+
+ if(map[sd->bl.m].flag.guildlock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+
+ t_sd = map_id2sd(RFIFOL(fd,2));
+
+ // @noask [LuzZza]
+ if(t_sd && t_sd->state.noask) {
+ clif_noask_sub(sd, t_sd, 3);
+ return;
+ }
+
+ guild_reqalliance(sd,t_sd);
+}
+
+
+/// Answer to a guild alliance request (CZ_ALLY_GUILD).
+/// 0172 <inviter account id>.L <answer>.L
+/// answer:
+/// 0 = refuse
+/// 1 = accept
+void clif_parse_GuildReplyAlliance(int fd, struct map_session_data *sd)
+{
+ guild_reply_reqalliance(sd,RFIFOL(fd,2),RFIFOL(fd,6));
+}
+
+
+/// Request to delete a guild alliance or opposition (CZ_REQ_DELETE_RELATED_GUILD).
+/// 0183 <opponent guild id>.L <relation>.L
+/// relation:
+/// 0 = Ally
+/// 1 = Enemy
+void clif_parse_GuildDelAlliance(int fd, struct map_session_data *sd)
+{
+ if(!sd->state.gmaster_flag)
+ return;
+
+ if(map[sd->bl.m].flag.guildlock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+ guild_delalliance(sd,RFIFOL(fd,2),RFIFOL(fd,6));
+}
+
+
+/// Request to set a guild as opposition (CZ_REQ_HOSTILE_GUILD).
+/// 0180 <account id>.L
+void clif_parse_GuildOpposition(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *t_sd;
+
+ if(!sd->state.gmaster_flag)
+ return;
+
+ if(map[sd->bl.m].flag.guildlock)
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+
+ t_sd = map_id2sd(RFIFOL(fd,2));
+
+ // @noask [LuzZza]
+ if(t_sd && t_sd->state.noask) {
+ clif_noask_sub(sd, t_sd, 4);
+ return;
+ }
+
+ guild_opposition(sd,t_sd);
+}
+
+
+/// Request to delete own guild (CZ_REQ_DISORGANIZE_GUILD).
+/// 015d <key>.40B
+/// key:
+/// now guild name; might have been (intended) email, since the
+/// field name and size is same as the one in CH_DELETE_CHAR.
+void clif_parse_GuildBreak(int fd, struct map_session_data *sd)
+{
+ if( map[sd->bl.m].flag.guildlock )
+ { //Guild locked.
+ clif_displaymessage(fd, msg_txt(228));
+ return;
+ }
+ guild_break(sd,(char*)RFIFOP(fd,2));
+}
+
+
+/// Pet
+///
+
+/// Request to invoke a pet menu action (CZ_COMMAND_PET).
+/// 01a1 <type>.B
+/// type:
+/// 0 = pet information
+/// 1 = feed
+/// 2 = performance
+/// 3 = return to egg
+/// 4 = unequip accessory
+void clif_parse_PetMenu(int fd, struct map_session_data *sd)
+{
+ pet_menu(sd,RFIFOB(fd,2));
+}
+
+
+/// Attempt to tame a monster (CZ_TRYCAPTURE_MONSTER).
+/// 019f <id>.L
+void clif_parse_CatchPet(int fd, struct map_session_data *sd)
+{
+ pet_catch_process2(sd,RFIFOL(fd,2));
+}
+
+
+/// Answer to pet incubator egg selection dialog (CZ_SELECT_PETEGG).
+/// 01a7 <index>.W
+void clif_parse_SelectEgg(int fd, struct map_session_data *sd)
+{
+ if (sd->menuskill_id != SA_TAMINGMONSTER || sd->menuskill_val != -1)
+ {
+ //Forged packet, disconnect them [Kevin]
+ clif_authfail_fd(fd, 0);
+ return;
+ }
+ pet_select_egg(sd,RFIFOW(fd,2)-2);
+ clif_menuskill_clear(sd);
+}
+
+
+/// Request to display pet's emotion/talk (CZ_PET_ACT).
+/// 01a9 <data>.L
+/// data:
+/// is either emotion (@see enum emotion_type) or a compound value
+/// (((mob id)-100)*100+(act id)*10+(hunger)) that describes an
+/// entry (given in parentheses) in data\pettalktable.xml
+/// act id:
+/// 0 = feeding
+/// 1 = hunting
+/// 2 = danger
+/// 3 = dead
+/// 4 = normal (stand)
+/// 5 = special performance (perfor_s)
+/// 6 = level up (levelup)
+/// 7 = performance 1 (perfor_1)
+/// 8 = performance 2 (perfor_2)
+/// 9 = performance 3 (perfor_3)
+/// 10 = log-in greeting (connect)
+/// hungry value:
+/// 0 = very hungry (hungry)
+/// 1 = hungry (bit_hungry)
+/// 2 = satisfied (noting)
+/// 3 = stuffed (full)
+/// 4 = full (so_full)
+void clif_parse_SendEmotion(int fd, struct map_session_data *sd)
+{
+ if(sd->pd)
+ clif_pet_emotion(sd->pd,RFIFOL(fd,2));
+}
+
+
+/// Request to change pet's name (CZ_RENAME_PET).
+/// 01a5 <name>.24B
+void clif_parse_ChangePetName(int fd, struct map_session_data *sd)
+{
+ pet_change_name(sd,(char*)RFIFOP(fd,2));
+}
+
+
+/// /kill (CZ_DISCONNECT_CHARACTER).
+/// Request to disconnect a character.
+/// 00cc <account id>.L
+/// NOTE: Also sent when using GM right click menu "(name) force to quit"
+void clif_parse_GMKick(int fd, struct map_session_data *sd)
+{
+ struct block_list *target;
+ int tid;
+
+ tid = RFIFOL(fd,2);
+ target = map_id2bl(tid);
+ if (!target) {
+ clif_GM_kickack(sd, 0);
+ return;
+ }
+
+ switch (target->type) {
+ case BL_PC:
+ {
+ char command[NAME_LENGTH+6];
+ sprintf(command, "%ckick %s", atcommand_symbol, status_get_name(target));
+ is_atcommand(fd, sd, command, 1);
+ }
+ break;
+
+ /**
+ * This one does not invoke any atcommand, so we need to check for permissions.
+ */
+ case BL_MOB:
+ {
+ char command[100];
+ if( !pc_can_use_command(sd, "killmonster", COMMAND_ATCOMMAND)) {
+ clif_GM_kickack(sd, 0);
+ return;
+ }
+ sprintf(command, "/kick %s (%d)", status_get_name(target), status_get_class(target));
+ log_atcommand(sd, command);
+ status_percent_damage(&sd->bl, target, 100, 0, true); // can invalidate 'target'
+ }
+ break;
+
+ case BL_NPC:
+ {
+ char command[NAME_LENGTH+11];
+ sprintf(command, "%cunloadnpc %s", atcommand_symbol, status_get_name(target));
+ is_atcommand(fd, sd, command, 1);
+ }
+ break;
+
+ default:
+ clif_GM_kickack(sd, 0);
+ }
+}
+
+
+/// /killall (CZ_DISCONNECT_ALL_CHARACTER).
+/// Request to disconnect all characters.
+/// 00ce
+void clif_parse_GMKickAll(int fd, struct map_session_data* sd) {
+ char cmd[15];
+ sprintf(cmd,"%ckickall",atcommand_symbol);
+ is_atcommand(fd, sd, cmd, 1);
+}
+
+
+/// /remove (CZ_REMOVE_AID).
+/// Request to warp to a character with given login ID.
+/// 01ba <account name>.24B
+
+/// /shift (CZ_SHIFT).
+/// Request to warp to a character with given name.
+/// 01bb <char name>.24B
+void clif_parse_GMShift(int fd, struct map_session_data *sd)
+{// FIXME: remove is supposed to receive account name for clients prior 20100803RE
+ char *player_name;
+ char command[NAME_LENGTH+8];
+
+ player_name = (char*)RFIFOP(fd,2);
+ player_name[NAME_LENGTH-1] = '\0';
+
+ sprintf(command, "%cjumpto %s", atcommand_symbol, player_name);
+ is_atcommand(fd, sd, command, 1);
+}
+
+
+/// /remove (CZ_REMOVE_AID_SSO).
+/// Request to warp to a character with given account ID.
+/// 0843 <account id>.L
+void clif_parse_GMRemove2(int fd, struct map_session_data* sd)
+{
+ int account_id;
+ struct map_session_data* pl_sd;
+
+ account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ if( (pl_sd = map_id2sd(account_id)) != NULL )
+ {
+ char command[NAME_LENGTH+8];
+ sprintf(command, "%cjumpto %s", atcommand_symbol, pl_sd->status.name);
+ is_atcommand(fd, sd, command, 1);
+ }
+}
+
+
+/// /recall (CZ_RECALL).
+/// Request to summon a player with given login ID to own position.
+/// 01bc <account name>.24B
+
+/// /summon (CZ_RECALL_GID).
+/// Request to summon a player with given name to own position.
+/// 01bd <char name>.24B
+void clif_parse_GMRecall(int fd, struct map_session_data *sd)
+{// FIXME: recall is supposed to receive account name for clients prior 20100803RE
+ char *player_name;
+ char command [NAME_LENGTH+8];
+
+ player_name = (char*)RFIFOP(fd,2);
+ player_name[NAME_LENGTH-1] = '\0';
+
+ sprintf(command, "%crecall %s", atcommand_symbol, player_name);
+ is_atcommand(fd, sd, command, 1);
+}
+
+
+/// /recall (CZ_RECALL_SSO).
+/// Request to summon a player with given account ID to own position.
+/// 0842 <account id>.L
+void clif_parse_GMRecall2(int fd, struct map_session_data* sd)
+{
+ int account_id;
+ struct map_session_data* pl_sd;
+
+ account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+ if( (pl_sd = map_id2sd(account_id)) != NULL )
+ {
+ char command[NAME_LENGTH+8];
+ sprintf(command, "%crecall %s", atcommand_symbol, pl_sd->status.name);
+ is_atcommand(fd, sd, command, 1);
+ }
+}
+
+
+/// /item /monster (CZ_ITEM_CREATE).
+/// Request to make items or spawn monsters.
+/// 013f <item/mob name>.24B
+void clif_parse_GM_Monster_Item(int fd, struct map_session_data *sd)
+{
+ char *monster_item_name;
+ char command[NAME_LENGTH+10];
+
+ monster_item_name = (char*)RFIFOP(fd,2);
+ monster_item_name[NAME_LENGTH-1] = '\0';
+
+ // FIXME: Should look for item first, then for monster.
+ // FIXME: /monster takes mob_db Sprite_Name as argument
+ if( mobdb_searchname(monster_item_name) ) {
+ snprintf(command, sizeof(command)-1, "%cmonster %s", atcommand_symbol, monster_item_name);
+ is_atcommand(fd, sd, command, 1);
+ return;
+ }
+ // FIXME: Stackables have a quantity of 20.
+ // FIXME: Equips are supposed to be unidentified.
+
+ if( itemdb_searchname(monster_item_name) ) {
+ snprintf(command, sizeof(command)-1, "%citem %s", atcommand_symbol, monster_item_name);
+ is_atcommand(fd, sd, command, 1);
+ return;
+ }
+}
+
+
+/// /hide (CZ_CHANGE_EFFECTSTATE).
+/// 019d <effect state>.L
+/// effect state:
+/// TODO: Any OPTION_* ?
+void clif_parse_GMHide(int fd, struct map_session_data *sd) {
+ char cmd[6];
+
+ sprintf(cmd,"%chide",atcommand_symbol);
+
+ is_atcommand(fd, sd, cmd, 1);
+}
+
+
+/// Request to adjust player's manner points (CZ_REQ_GIVE_MANNER_POINT).
+/// 0149 <account id>.L <type>.B <value>.W
+/// type:
+/// 0 = positive points
+/// 1 = negative points
+/// 2 = self mute (+10 minutes)
+void clif_parse_GMReqNoChat(int fd,struct map_session_data *sd)
+{
+ int id, type, value;
+ struct map_session_data *dstsd;
+ char command[NAME_LENGTH+15];
+
+ id = RFIFOL(fd,2);
+ type = RFIFOB(fd,6);
+ value = RFIFOW(fd,7);
+
+ if( type == 0 )
+ value = -value;
+
+ //If type is 2 and the ids don't match, this is a crafted hacked packet!
+ //Disabled because clients keep self-muting when you give players public @ commands... [Skotlex]
+ if (type == 2 /* && (pc_get_group_level(sd) > 0 || sd->bl.id != id)*/)
+ return;
+
+ dstsd = map_id2sd(id);
+ if( dstsd == NULL )
+ return;
+
+ sprintf(command, "%cmute %d %s", atcommand_symbol, value, dstsd->status.name);
+ is_atcommand(fd, sd, command, 1);
+}
+
+
+/// /rc (CZ_REQ_GIVE_MANNER_BYNAME).
+/// GM adjustment of a player's manner value by -60.
+/// 0212 <char name>.24B
+void clif_parse_GMRc(int fd, struct map_session_data* sd)
+{
+ char command[NAME_LENGTH+15];
+ char *name = (char*)RFIFOP(fd,2);
+
+ name[NAME_LENGTH-1] = '\0';
+ sprintf(command, "%cmute %d %s", atcommand_symbol, 60, name);
+ is_atcommand(fd, sd, command, 1);
+}
+
+
+/// Result of request to resolve account name (ZC_ACK_ACCOUNTNAME).
+/// 01e0 <account id>.L <account name>.24B
+void clif_account_name(struct map_session_data* sd, int account_id, const char* accname)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x1e0));
+ WFIFOW(fd,0) = 0x1e0;
+ WFIFOL(fd,2) = account_id;
+ safestrncpy((char*)WFIFOP(fd,6), accname, NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x1e0));
+}
+
+
+/// GM requesting account name (for right-click gm menu) (CZ_REQ_ACCOUNTNAME).
+/// 01df <account id>.L
+void clif_parse_GMReqAccountName(int fd, struct map_session_data *sd)
+{
+ int account_id = RFIFOL(fd,2);
+
+ //TODO: find out if this works for any player or only for authorized GMs
+ clif_account_name(sd, account_id, ""); // insert account name here >_<
+}
+
+
+/// /changemaptype <x> <y> <type> (CZ_CHANGE_MAPTYPE).
+/// GM single cell type change request.
+/// 0198 <x>.W <y>.W <type>.W
+/// type:
+/// 0 = not walkable
+/// 1 = walkable
+void clif_parse_GMChangeMapType(int fd, struct map_session_data *sd)
+{
+ int x,y,type;
+
+ if( pc_has_permission(sd, PC_PERM_USE_CHANGEMAPTYPE) )
+ return;
+
+ x = RFIFOW(fd,2);
+ y = RFIFOW(fd,4);
+ type = RFIFOW(fd,6);
+
+ map_setgatcell(sd->bl.m,x,y,type);
+ clif_changemapcell(0,sd->bl.m,x,y,type,ALL_SAMEMAP);
+ //FIXME: once players leave the map, the client 'forgets' this information.
+}
+
+
+/// /in /ex (CZ_SETTING_WHISPER_PC).
+/// Request to allow/deny whispers from a nick.
+/// 00cf <nick>.24B <type>.B
+/// type:
+/// 0 = (/ex nick) deny speech from nick
+/// 1 = (/in nick) allow speech from nick
+void clif_parse_PMIgnore(int fd, struct map_session_data* sd)
+{
+ char* nick;
+ uint8 type;
+ int i;
+
+ nick = (char*)RFIFOP(fd,2); // speed up
+ nick[NAME_LENGTH-1] = '\0'; // to be sure that the player name has at most 23 characters
+ type = RFIFOB(fd,26);
+
+ if( type == 0 )
+ { // Add name to ignore list (block)
+ if (strcmp(wisp_server_name, nick) == 0) {
+ clif_wisexin(sd, type, 1); // fail
+ return;
+ }
+
+ // try to find a free spot, while checking for duplicates at the same time
+ ARR_FIND( 0, MAX_IGNORE_LIST, i, sd->ignore[i].name[0] == '\0' || strcmp(sd->ignore[i].name, nick) == 0 );
+ if( i == MAX_IGNORE_LIST )
+ {// no space for new entry
+ clif_wisexin(sd, type, 2); // too many blocks
+ return;
+ }
+ if( sd->ignore[i].name[0] != '\0' )
+ {// name already exists
+ clif_wisexin(sd, type, 0); // Aegis reports success.
+ return;
+ }
+
+ //Insert in position i
+ safestrncpy(sd->ignore[i].name, nick, NAME_LENGTH);
+ }
+ else
+ { // Remove name from ignore list (unblock)
+
+ // find entry
+ ARR_FIND( 0, MAX_IGNORE_LIST, i, sd->ignore[i].name[0] == '\0' || strcmp(sd->ignore[i].name, nick) == 0 );
+ if( i == MAX_IGNORE_LIST || sd->ignore[i].name[i] == '\0' )
+ { //Not found
+ clif_wisexin(sd, type, 1); // fail
+ return;
+ }
+ // move everything one place down to overwrite removed entry
+ memmove(sd->ignore[i].name, sd->ignore[i+1].name, (MAX_IGNORE_LIST-i-1)*sizeof(sd->ignore[0].name));
+ // wipe last entry
+ memset(sd->ignore[MAX_IGNORE_LIST-1].name, 0, sizeof(sd->ignore[0].name));
+ }
+
+ clif_wisexin(sd, type, 0); // success
+}
+
+
+/// /inall /exall (CZ_SETTING_WHISPER_STATE).
+/// Request to allow/deny all whispers.
+/// 00d0 <type>.B
+/// type:
+/// 0 = (/exall) deny all speech
+/// 1 = (/inall) allow all speech
+void clif_parse_PMIgnoreAll(int fd, struct map_session_data *sd)
+{
+ int type = RFIFOB(fd,2), flag;
+
+ if( type == 0 )
+ {// Deny all
+ if( sd->state.ignoreAll ) {
+ flag = 1; // fail
+ } else {
+ sd->state.ignoreAll = 1;
+ flag = 0; // success
+ }
+ }
+ else
+ {//Unblock everyone
+ if( sd->state.ignoreAll ) {
+ sd->state.ignoreAll = 0;
+ flag = 0; // success
+ } else {
+ if (sd->ignore[0].name[0] != '\0')
+ { //Wipe the ignore list.
+ memset(sd->ignore, 0, sizeof(sd->ignore));
+ flag = 0; // success
+ } else {
+ flag = 1; // fail
+ }
+ }
+ }
+
+ clif_wisall(sd, type, flag);
+}
+
+
+/// Whisper ignore list (ZC_WHISPER_LIST).
+/// 00d4 <packet len>.W { <char name>.24B }*
+void clif_PMIgnoreList(struct map_session_data* sd)
+{
+ int i, fd = sd->fd;
+
+ WFIFOHEAD(fd,4+ARRAYLENGTH(sd->ignore)*NAME_LENGTH);
+ WFIFOW(fd,0) = 0xd4;
+
+ for( i = 0; i < ARRAYLENGTH(sd->ignore) && sd->ignore[i].name[0]; i++ )
+ {
+ memcpy(WFIFOP(fd,4+i*NAME_LENGTH), sd->ignore[i].name, NAME_LENGTH);
+ }
+
+ WFIFOW(fd,2) = 4+i*NAME_LENGTH;
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Whisper ignore list request (CZ_REQ_WHISPER_LIST).
+/// 00d3
+void clif_parse_PMIgnoreList(int fd,struct map_session_data *sd)
+{
+ clif_PMIgnoreList(sd);
+}
+
+
+/// Request to invoke the /doridori recovery bonus (CZ_DORIDORI).
+/// 01e7
+void clif_parse_NoviceDoriDori(int fd, struct map_session_data *sd)
+{
+ if (sd->state.doridori) return;
+
+ switch (sd->class_&MAPID_UPPERMASK)
+ {
+ case MAPID_SOUL_LINKER:
+ case MAPID_STAR_GLADIATOR:
+ case MAPID_TAEKWON:
+ if (!sd->state.rest)
+ break;
+ case MAPID_SUPER_NOVICE:
+ sd->state.doridori=1;
+ break;
+ }
+}
+
+
+/// Request to invoke the effect of super novice's guardian angel prayer (CZ_CHOPOKGI).
+/// 01ed
+/// Note: This packet is caused by 7 lines of any text, followed by
+/// the prayer and an another line of any text. The prayer is
+/// defined by lines 790~793 in data\msgstringtable.txt
+/// "Dear angel, can you hear my voice?"
+/// "I am" (space separated player name) "Super Novice~"
+/// "Help me out~ Please~ T_T"
+void clif_parse_NoviceExplosionSpirits(int fd, struct map_session_data *sd)
+{
+ if( ( sd->class_&MAPID_UPPERMASK ) == MAPID_SUPER_NOVICE )
+ {
+ unsigned int next = pc_nextbaseexp(sd);
+ if( next == 0 ) next = pc_thisbaseexp(sd);
+ if( next )
+ {
+ int percent = (int)( ( (float)sd->status.base_exp/(float)next )*1000. );
+
+ if( percent && ( percent%100 ) == 0 )
+ {// 10.0%, 20.0%, ..., 90.0%
+ sc_start(&sd->bl, status_skill2sc(MO_EXPLOSIONSPIRITS), 100, 17, skill_get_time(MO_EXPLOSIONSPIRITS, 5)); //Lv17-> +50 critical (noted by Poki) [Skotlex]
+ clif_skill_nodamage(&sd->bl, &sd->bl, MO_EXPLOSIONSPIRITS, 5, 1); // prayer always shows successful Lv5 cast and disregards noskill restrictions
+ }
+ }
+ }
+}
+
+
+/// Friends List
+///
+
+/// Toggles a single friend online/offline [Skotlex] (ZC_FRIENDS_STATE).
+/// 0206 <account id>.L <char id>.L <state>.B
+/// state:
+/// 0 = online
+/// 1 = offline
+void clif_friendslist_toggle(struct map_session_data *sd,int account_id, int char_id, int online)
+{
+ int i, fd = sd->fd;
+
+ //Seek friend.
+ for (i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id &&
+ (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++);
+
+ if(i == MAX_FRIENDS || sd->status.friends[i].char_id == 0)
+ return; //Not found
+
+ WFIFOHEAD(fd,packet_len(0x206));
+ WFIFOW(fd, 0) = 0x206;
+ WFIFOL(fd, 2) = sd->status.friends[i].account_id;
+ WFIFOL(fd, 6) = sd->status.friends[i].char_id;
+ WFIFOB(fd,10) = !online; //Yeah, a 1 here means "logged off", go figure...
+ WFIFOSET(fd, packet_len(0x206));
+}
+
+
+//Subfunction called from clif_foreachclient to toggle friends on/off [Skotlex]
+int clif_friendslist_toggle_sub(struct map_session_data *sd,va_list ap)
+{
+ int account_id, char_id, online;
+ account_id = va_arg(ap, int);
+ char_id = va_arg(ap, int);
+ online = va_arg(ap, int);
+ clif_friendslist_toggle(sd, account_id, char_id, online);
+ return 0;
+}
+
+
+/// Sends the whole friends list (ZC_FRIENDS_LIST).
+/// 0201 <packet len>.W { <account id>.L <char id>.L <name>.24B }*
+void clif_friendslist_send(struct map_session_data *sd)
+{
+ int i = 0, n, fd = sd->fd;
+
+ // Send friends list
+ WFIFOHEAD(fd, MAX_FRIENDS * 32 + 4);
+ WFIFOW(fd, 0) = 0x201;
+ for(i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id; i++)
+ {
+ WFIFOL(fd, 4 + 32 * i + 0) = sd->status.friends[i].account_id;
+ WFIFOL(fd, 4 + 32 * i + 4) = sd->status.friends[i].char_id;
+ memcpy(WFIFOP(fd, 4 + 32 * i + 8), &sd->status.friends[i].name, NAME_LENGTH);
+ }
+
+ if (i) {
+ WFIFOW(fd,2) = 4 + 32 * i;
+ WFIFOSET(fd, WFIFOW(fd,2));
+ }
+
+ for (n = 0; n < i; n++)
+ { //Sending the online players
+ if (map_charid2sd(sd->status.friends[n].char_id))
+ clif_friendslist_toggle(sd, sd->status.friends[n].account_id, sd->status.friends[n].char_id, 1);
+ }
+}
+
+
+/// Notification about the result of a friend add request (ZC_ADD_FRIENDS_LIST).
+/// 0209 <result>.W <account id>.L <char id>.L <name>.24B
+/// result:
+/// 0 = MsgStringTable[821]="You have become friends with (%s)."
+/// 1 = MsgStringTable[822]="(%s) does not want to be friends with you."
+/// 2 = MsgStringTable[819]="Your Friend List is full."
+/// 3 = MsgStringTable[820]="(%s)'s Friend List is full."
+void clif_friendslist_reqack(struct map_session_data *sd, struct map_session_data *f_sd, int type)
+{
+ int fd;
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x209));
+ WFIFOW(fd,0) = 0x209;
+ WFIFOW(fd,2) = type;
+ if (f_sd)
+ {
+ WFIFOL(fd,4) = f_sd->status.account_id;
+ WFIFOL(fd,8) = f_sd->status.char_id;
+ memcpy(WFIFOP(fd, 12), f_sd->status.name,NAME_LENGTH);
+ }
+ WFIFOSET(fd, packet_len(0x209));
+}
+
+
+/// Asks a player for permission to be added as friend (ZC_REQ_ADD_FRIENDS).
+/// 0207 <req account id>.L <req char id>.L <req char name>.24B
+void clif_friendlist_req(struct map_session_data* sd, int account_id, int char_id, const char* name)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x207));
+ WFIFOW(fd,0) = 0x207;
+ WFIFOL(fd,2) = account_id;
+ WFIFOL(fd,6) = char_id;
+ memcpy(WFIFOP(fd,10), name, NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x207));
+}
+
+
+/// Request to add a player as friend (CZ_ADD_FRIENDS).
+/// 0202 <name>.24B
+void clif_parse_FriendsListAdd(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *f_sd;
+ int i;
+
+ f_sd = map_nick2sd((char*)RFIFOP(fd,2));
+
+ // ensure that the request player's friend list is not full
+ ARR_FIND(0, MAX_FRIENDS, i, sd->status.friends[i].char_id == 0);
+
+ if( i == MAX_FRIENDS ) {
+ clif_friendslist_reqack(sd, f_sd, 2);
+ return;
+ }
+
+ // Friend doesn't exist (no player with this name)
+ if (f_sd == NULL) {
+ clif_displaymessage(fd, msg_txt(3));
+ return;
+ }
+
+ if( sd->bl.id == f_sd->bl.id )
+ {// adding oneself as friend
+ return;
+ }
+
+ // @noask [LuzZza]
+ if(f_sd->state.noask) {
+ clif_noask_sub(sd, f_sd, 5);
+ return;
+ }
+
+ // Friend already exists
+ for (i = 0; i < MAX_FRIENDS && sd->status.friends[i].char_id != 0; i++) {
+ if (sd->status.friends[i].char_id == f_sd->status.char_id) {
+ clif_displaymessage(fd, msg_txt(671)); //"Friend already exists."
+ return;
+ }
+ }
+
+ f_sd->friend_req = sd->status.char_id;
+ sd->friend_req = f_sd->status.char_id;
+
+ clif_friendlist_req(f_sd, sd->status.account_id, sd->status.char_id, sd->status.name);
+}
+
+
+/// Answer to a friend add request (CZ_ACK_REQ_ADD_FRIENDS).
+/// 0208 <inviter account id>.L <inviter char id>.L <result>.B
+/// 0208 <inviter account id>.L <inviter char id>.L <result>.L (PACKETVER >= 6)
+/// result:
+/// 0 = rejected
+/// 1 = accepted
+void clif_parse_FriendsListReply(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *f_sd;
+ int account_id;
+ char reply;
+
+ account_id = RFIFOL(fd,2);
+ //char_id = RFIFOL(fd,6);
+#if PACKETVER < 6
+ reply = RFIFOB(fd,10);
+#else
+ reply = RFIFOL(fd,10);
+#endif
+
+ if( sd->bl.id == account_id )
+ {// adding oneself as friend
+ return;
+ }
+
+ f_sd = map_id2sd(account_id); //The account id is the same as the bl.id of players.
+ if (f_sd == NULL)
+ return;
+
+ if (reply == 0 || !( sd->friend_req == f_sd->status.char_id && f_sd->friend_req == sd->status.char_id ) )
+ clif_friendslist_reqack(f_sd, sd, 1);
+ else {
+ int i;
+ // Find an empty slot
+ for (i = 0; i < MAX_FRIENDS; i++)
+ if (f_sd->status.friends[i].char_id == 0)
+ break;
+ if (i == MAX_FRIENDS) {
+ clif_friendslist_reqack(f_sd, sd, 2);
+ return;
+ }
+
+ f_sd->status.friends[i].account_id = sd->status.account_id;
+ f_sd->status.friends[i].char_id = sd->status.char_id;
+ memcpy(f_sd->status.friends[i].name, sd->status.name, NAME_LENGTH);
+ clif_friendslist_reqack(f_sd, sd, 0);
+
+ if (battle_config.friend_auto_add) {
+ // Also add f_sd to sd's friendlist.
+ for (i = 0; i < MAX_FRIENDS; i++) {
+ if (sd->status.friends[i].char_id == f_sd->status.char_id)
+ return; //No need to add anything.
+ if (sd->status.friends[i].char_id == 0)
+ break;
+ }
+ if (i == MAX_FRIENDS) {
+ clif_friendslist_reqack(sd, f_sd, 2);
+ return;
+ }
+
+ sd->status.friends[i].account_id = f_sd->status.account_id;
+ sd->status.friends[i].char_id = f_sd->status.char_id;
+ memcpy(sd->status.friends[i].name, f_sd->status.name, NAME_LENGTH);
+ clif_friendslist_reqack(sd, f_sd, 0);
+ }
+ }
+}
+
+
+/// Request to delete a friend (CZ_DELETE_FRIENDS).
+/// 0203 <account id>.L <char id>.L
+void clif_parse_FriendsListRemove(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *f_sd = NULL;
+ int account_id, char_id;
+ int i, j;
+
+ account_id = RFIFOL(fd,2);
+ char_id = RFIFOL(fd,6);
+
+ // Search friend
+ for (i = 0; i < MAX_FRIENDS &&
+ (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++);
+
+ if (i == MAX_FRIENDS) {
+ clif_displaymessage(fd, msg_txt(672)); //"Name not found in list."
+ return;
+ }
+
+ //remove from friend's list first
+ if( (f_sd = map_id2sd(account_id)) && f_sd->status.char_id == char_id) {
+ for (i = 0; i < MAX_FRIENDS &&
+ (f_sd->status.friends[i].char_id != sd->status.char_id || f_sd->status.friends[i].account_id != sd->status.account_id); i++);
+
+ if (i != MAX_FRIENDS) {
+ // move all chars up
+ for(j = i + 1; j < MAX_FRIENDS; j++)
+ memcpy(&f_sd->status.friends[j-1], &f_sd->status.friends[j], sizeof(f_sd->status.friends[0]));
+
+ memset(&f_sd->status.friends[MAX_FRIENDS-1], 0, sizeof(f_sd->status.friends[MAX_FRIENDS-1]));
+ //should the guy be notified of some message? we should add it here if so
+ WFIFOHEAD(f_sd->fd,packet_len(0x20a));
+ WFIFOW(f_sd->fd,0) = 0x20a;
+ WFIFOL(f_sd->fd,2) = sd->status.account_id;
+ WFIFOL(f_sd->fd,6) = sd->status.char_id;
+ WFIFOSET(f_sd->fd, packet_len(0x20a));
+ }
+
+ } else { //friend not online -- ask char server to delete from his friendlist
+ if(chrif_removefriend(char_id,sd->status.char_id)) { // char-server offline, abort
+ clif_displaymessage(fd, msg_txt(673)); //"This action can't be performed at the moment. Please try again later."
+ return;
+ }
+ }
+
+ // We can now delete from original requester
+ for (i = 0; i < MAX_FRIENDS &&
+ (sd->status.friends[i].char_id != char_id || sd->status.friends[i].account_id != account_id); i++);
+ // move all chars up
+ for(j = i + 1; j < MAX_FRIENDS; j++)
+ memcpy(&sd->status.friends[j-1], &sd->status.friends[j], sizeof(sd->status.friends[0]));
+
+ memset(&sd->status.friends[MAX_FRIENDS-1], 0, sizeof(sd->status.friends[MAX_FRIENDS-1]));
+ clif_displaymessage(fd, msg_txt(674)); //"Friend removed"
+
+ WFIFOHEAD(fd,packet_len(0x20a));
+ WFIFOW(fd,0) = 0x20a;
+ WFIFOL(fd,2) = account_id;
+ WFIFOL(fd,6) = char_id;
+ WFIFOSET(fd, packet_len(0x20a));
+}
+
+
+/// /pvpinfo list (ZC_ACK_PVPPOINT).
+/// 0210 <char id>.L <account id>.L <win point>.L <lose point>.L <point>.L
+void clif_PVPInfo(struct map_session_data* sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x210));
+ WFIFOW(fd,0) = 0x210;
+ WFIFOL(fd,2) = sd->status.char_id;
+ WFIFOL(fd,6) = sd->status.account_id;
+ WFIFOL(fd,10) = sd->pvp_won; // times won
+ WFIFOL(fd,14) = sd->pvp_lost; // times lost
+ WFIFOL(fd,18) = sd->pvp_point;
+ WFIFOSET(fd, packet_len(0x210));
+}
+
+
+/// /pvpinfo (CZ_REQ_PVPPOINT).
+/// 020f <char id>.L <account id>.L
+void clif_parse_PVPInfo(int fd,struct map_session_data *sd)
+{
+ // TODO: Is there a way to use this on an another player (char/acc id)?
+ clif_PVPInfo(sd);
+}
+
+
+/// /blacksmith list (ZC_BLACKSMITH_RANK).
+/// 0219 { <name>.24B }*10 { <point>.L }*10
+void clif_blacksmith(struct map_session_data* sd)
+{
+ int i, fd = sd->fd;
+ const char* name;
+
+ WFIFOHEAD(fd,packet_len(0x219));
+ WFIFOW(fd,0) = 0x219;
+ //Packet size limits this list to 10 elements. [Skotlex]
+ for (i = 0; i < 10 && i < MAX_FAME_LIST; i++) {
+ if (smith_fame_list[i].id > 0) {
+ if (strcmp(smith_fame_list[i].name, "-") == 0 &&
+ (name = map_charid2nick(smith_fame_list[i].id)) != NULL)
+ {
+ strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), name, NAME_LENGTH);
+ } else
+ strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), smith_fame_list[i].name, NAME_LENGTH);
+ } else
+ strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), "None", 5);
+ WFIFOL(fd, 242 + i * 4) = smith_fame_list[i].fame;
+ }
+ for(;i < 10; i++) { //In case the MAX is less than 10.
+ strncpy((char *)(WFIFOP(fd, 2 + 24 * i)), "Unavailable", 12);
+ WFIFOL(fd, 242 + i * 4) = 0;
+ }
+
+ WFIFOSET(fd, packet_len(0x219));
+}
+
+
+/// /blacksmith (CZ_BLACKSMITH_RANK).
+/// 0217
+void clif_parse_Blacksmith(int fd,struct map_session_data *sd)
+{
+ clif_blacksmith(sd);
+}
+
+
+/// Notification about backsmith points (ZC_BLACKSMITH_POINT).
+/// 021b <points>.L <total points>.L
+void clif_fame_blacksmith(struct map_session_data *sd, int points)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x21b));
+ WFIFOW(fd,0) = 0x21b;
+ WFIFOL(fd,2) = points;
+ WFIFOL(fd,6) = sd->status.fame;
+ WFIFOSET(fd, packet_len(0x21b));
+}
+
+
+/// /alchemist list (ZC_ALCHEMIST_RANK).
+/// 021a { <name>.24B }*10 { <point>.L }*10
+void clif_alchemist(struct map_session_data* sd)
+{
+ int i, fd = sd->fd;
+ const char* name;
+
+ WFIFOHEAD(fd,packet_len(0x21a));
+ WFIFOW(fd,0) = 0x21a;
+ //Packet size limits this list to 10 elements. [Skotlex]
+ for (i = 0; i < 10 && i < MAX_FAME_LIST; i++) {
+ if (chemist_fame_list[i].id > 0) {
+ if (strcmp(chemist_fame_list[i].name, "-") == 0 &&
+ (name = map_charid2nick(chemist_fame_list[i].id)) != NULL)
+ {
+ memcpy(WFIFOP(fd, 2 + 24 * i), name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), chemist_fame_list[i].name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), "None", NAME_LENGTH);
+ WFIFOL(fd, 242 + i * 4) = chemist_fame_list[i].fame;
+ }
+ for(;i < 10; i++) { //In case the MAX is less than 10.
+ memcpy(WFIFOP(fd, 2 + 24 * i), "Unavailable", NAME_LENGTH);
+ WFIFOL(fd, 242 + i * 4) = 0;
+ }
+
+ WFIFOSET(fd, packet_len(0x21a));
+}
+
+
+/// /alchemist (CZ_ALCHEMIST_RANK).
+/// 0218
+void clif_parse_Alchemist(int fd,struct map_session_data *sd)
+{
+ clif_alchemist(sd);
+}
+
+
+/// Notification about alchemist points (ZC_ALCHEMIST_POINT).
+/// 021c <points>.L <total points>.L
+void clif_fame_alchemist(struct map_session_data *sd, int points)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x21c));
+ WFIFOW(fd,0) = 0x21c;
+ WFIFOL(fd,2) = points;
+ WFIFOL(fd,6) = sd->status.fame;
+ WFIFOSET(fd, packet_len(0x21c));
+}
+
+
+/// /taekwon list (ZC_TAEKWON_RANK).
+/// 0226 { <name>.24B }*10 { <point>.L }*10
+void clif_taekwon(struct map_session_data* sd)
+{
+ int i, fd = sd->fd;
+ const char* name;
+
+ WFIFOHEAD(fd,packet_len(0x226));
+ WFIFOW(fd,0) = 0x226;
+ //Packet size limits this list to 10 elements. [Skotlex]
+ for (i = 0; i < 10 && i < MAX_FAME_LIST; i++) {
+ if (taekwon_fame_list[i].id > 0) {
+ if (strcmp(taekwon_fame_list[i].name, "-") == 0 &&
+ (name = map_charid2nick(taekwon_fame_list[i].id)) != NULL)
+ {
+ memcpy(WFIFOP(fd, 2 + 24 * i), name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), taekwon_fame_list[i].name, NAME_LENGTH);
+ } else
+ memcpy(WFIFOP(fd, 2 + 24 * i), "None", NAME_LENGTH);
+ WFIFOL(fd, 242 + i * 4) = taekwon_fame_list[i].fame;
+ }
+ for(;i < 10; i++) { //In case the MAX is less than 10.
+ memcpy(WFIFOP(fd, 2 + 24 * i), "Unavailable", NAME_LENGTH);
+ WFIFOL(fd, 242 + i * 4) = 0;
+ }
+ WFIFOSET(fd, packet_len(0x226));
+}
+
+
+/// /taekwon (CZ_TAEKWON_RANK).
+/// 0225
+void clif_parse_Taekwon(int fd,struct map_session_data *sd)
+{
+ clif_taekwon(sd);
+}
+
+
+/// Notification about taekwon points (ZC_TAEKWON_POINT).
+/// 0224 <points>.L <total points>.L
+void clif_fame_taekwon(struct map_session_data *sd, int points)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x224));
+ WFIFOW(fd,0) = 0x224;
+ WFIFOL(fd,2) = points;
+ WFIFOL(fd,6) = sd->status.fame;
+ WFIFOSET(fd, packet_len(0x224));
+}
+
+
+/// /pk list (ZC_KILLER_RANK).
+/// 0238 { <name>.24B }*10 { <point>.L }*10
+void clif_ranking_pk(struct map_session_data* sd)
+{
+ int i, fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x238));
+ WFIFOW(fd,0) = 0x238;
+ for(i=0;i<10;i++){
+ memcpy(WFIFOP(fd,i*24+2), "Unknown", NAME_LENGTH);
+ WFIFOL(fd,i*4+242) = 0;
+ }
+ WFIFOSET(fd, packet_len(0x238));
+}
+
+
+/// /pk (CZ_KILLER_RANK).
+/// 0237
+void clif_parse_RankingPk(int fd,struct map_session_data *sd)
+{
+ clif_ranking_pk(sd);
+}
+
+
+/// SG Feel save OK [Komurka] (CZ_AGREE_STARPLACE).
+/// 0254 <which>.B
+/// which:
+/// 0 = sun
+/// 1 = moon
+/// 2 = star
+void clif_parse_FeelSaveOk(int fd,struct map_session_data *sd)
+{
+ int i;
+ if (sd->menuskill_id != SG_FEEL)
+ return;
+ i = sd->menuskill_val-1;
+ if (i<0 || i >= MAX_PC_FEELHATE) return; //Bug?
+
+ sd->feel_map[i].index = map_id2index(sd->bl.m);
+ sd->feel_map[i].m = sd->bl.m;
+ pc_setglobalreg(sd,sg_info[i].feel_var,sd->feel_map[i].index);
+
+//Are these really needed? Shouldn't they show up automatically from the feel save packet?
+// clif_misceffect2(&sd->bl, 0x1b0);
+// clif_misceffect2(&sd->bl, 0x21f);
+ clif_feel_info(sd, i, 0);
+ clif_menuskill_clear(sd);
+}
+
+
+/// Star Gladiator's Feeling map confirmation prompt (ZC_STARPLACE).
+/// 0253 <which>.B
+/// which:
+/// 0 = sun
+/// 1 = moon
+/// 2 = star
+void clif_feel_req(int fd, struct map_session_data *sd, uint16 skill_lv)
+{
+ WFIFOHEAD(fd,packet_len(0x253));
+ WFIFOW(fd,0)=0x253;
+ WFIFOB(fd,2)=TOB(skill_lv-1);
+ WFIFOSET(fd, packet_len(0x253));
+ sd->menuskill_id = SG_FEEL;
+ sd->menuskill_val = skill_lv;
+}
+
+
+/// Request to change homunculus' name (CZ_RENAME_MER).
+/// 0231 <name>.24B
+void clif_parse_ChangeHomunculusName(int fd, struct map_session_data *sd)
+{
+ merc_hom_change_name(sd,(char*)RFIFOP(fd,2));
+}
+
+
+/// Request to warp/move homunculus/mercenary to it's owner (CZ_REQUEST_MOVETOOWNER).
+/// 0234 <id>.L
+void clif_parse_HomMoveToMaster(int fd, struct map_session_data *sd)
+{
+ int id = RFIFOL(fd,2); // Mercenary or Homunculus
+ struct block_list *bl = NULL;
+ struct unit_data *ud = NULL;
+
+ if( sd->md && sd->md->bl.id == id )
+ bl = &sd->md->bl;
+ else if( merc_is_hom_active(sd->hd) && sd->hd->bl.id == id )
+ bl = &sd->hd->bl; // Moving Homunculus
+ else
+ return;
+
+ unit_calc_pos(bl, sd->bl.x, sd->bl.y, sd->ud.dir);
+ ud = unit_bl2ud(bl);
+ unit_walktoxy(bl, ud->to_x, ud->to_y, 4);
+}
+
+
+/// Request to move homunculus/mercenary (CZ_REQUEST_MOVENPC).
+/// 0232 <id>.L <position data>.3B
+void clif_parse_HomMoveTo(int fd, struct map_session_data *sd)
+{
+ int id = RFIFOL(fd,2); // Mercenary or Homunculus
+ struct block_list *bl = NULL;
+ short x, y;
+
+ RFIFOPOS(fd, packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[1], &x, &y, NULL);
+
+ if( sd->md && sd->md->bl.id == id )
+ bl = &sd->md->bl; // Moving Mercenary
+ else if( merc_is_hom_active(sd->hd) && sd->hd->bl.id == id )
+ bl = &sd->hd->bl; // Moving Homunculus
+ else
+ return;
+
+ unit_walktoxy(bl, x, y, 4);
+}
+
+
+/// Request to do an action with homunculus/mercenary (CZ_REQUEST_ACTNPC).
+/// 0233 <id>.L <target id>.L <action>.B
+/// action:
+/// always 0
+void clif_parse_HomAttack(int fd,struct map_session_data *sd)
+{
+ struct block_list *bl = NULL;
+ int id = RFIFOL(fd,2),
+ target_id = RFIFOL(fd,6),
+ action_type = RFIFOB(fd,10);
+
+ if( merc_is_hom_active(sd->hd) && sd->hd->bl.id == id )
+ bl = &sd->hd->bl;
+ else if( sd->md && sd->md->bl.id == id )
+ bl = &sd->md->bl;
+ else return;
+
+ unit_stop_attack(bl);
+ unit_attack(bl, target_id, action_type != 0);
+}
+
+
+/// Request to invoke a homunculus menu action (CZ_COMMAND_MER).
+/// 022d <type>.W <command>.B
+/// type:
+/// always 0
+/// command:
+/// 0 = homunculus information
+/// 1 = feed
+/// 2 = delete
+void clif_parse_HomMenu(int fd, struct map_session_data *sd)
+{ //[orn]
+ int cmd;
+
+ cmd = RFIFOW(fd,0);
+
+ if(!merc_is_hom_active(sd->hd))
+ return;
+
+ merc_menu(sd,RFIFOB(fd,packet_db[sd->packet_ver][cmd].pos[1]));
+}
+
+
+/// Request to resurrect oneself using Token of Siegfried (CZ_STANDING_RESURRECTION).
+/// 0292
+void clif_parse_AutoRevive(int fd, struct map_session_data *sd)
+{
+ int item_position = pc_search_inventory(sd, ITEMID_TOKEN_OF_SIEGFRIED);
+
+ if (item_position < 0)
+ return;
+
+ if (sd->sc.data[SC_HELLPOWER]) //Cannot res while under the effect of SC_HELLPOWER.
+ return;
+
+ if (!status_revive(&sd->bl, 100, 100))
+ return;
+
+ clif_skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,4,1);
+ pc_delitem(sd, item_position, 1, 0, 1, LOG_TYPE_CONSUME);
+}
+
+
+/// Information about character's status values (ZC_ACK_STATUS_GM).
+/// 0214 <str>.B <standardStr>.B <agi>.B <standardAgi>.B <vit>.B <standardVit>.B
+/// <int>.B <standardInt>.B <dex>.B <standardDex>.B <luk>.B <standardLuk>.B
+/// <attPower>.W <refiningPower>.W <max_mattPower>.W <min_mattPower>.W
+/// <itemdefPower>.W <plusdefPower>.W <mdefPower>.W <plusmdefPower>.W
+/// <hitSuccessValue>.W <avoidSuccessValue>.W <plusAvoidSuccessValue>.W
+/// <criticalSuccessValue>.W <ASPD>.W <plusASPD>.W
+void clif_check(int fd, struct map_session_data* pl_sd)
+{
+ WFIFOHEAD(fd,packet_len(0x214));
+ WFIFOW(fd, 0) = 0x214;
+ WFIFOB(fd, 2) = min(pl_sd->status.str, UINT8_MAX);
+ WFIFOB(fd, 3) = pc_need_status_point(pl_sd, SP_STR, 1);
+ WFIFOB(fd, 4) = min(pl_sd->status.agi, UINT8_MAX);
+ WFIFOB(fd, 5) = pc_need_status_point(pl_sd, SP_AGI, 1);
+ WFIFOB(fd, 6) = min(pl_sd->status.vit, UINT8_MAX);
+ WFIFOB(fd, 7) = pc_need_status_point(pl_sd, SP_VIT, 1);
+ WFIFOB(fd, 8) = min(pl_sd->status.int_, UINT8_MAX);
+ WFIFOB(fd, 9) = pc_need_status_point(pl_sd, SP_INT, 1);
+ WFIFOB(fd,10) = min(pl_sd->status.dex, UINT8_MAX);
+ WFIFOB(fd,11) = pc_need_status_point(pl_sd, SP_DEX, 1);
+ WFIFOB(fd,12) = min(pl_sd->status.luk, UINT8_MAX);
+ WFIFOB(fd,13) = pc_need_status_point(pl_sd, SP_LUK, 1);
+ WFIFOW(fd,14) = pl_sd->battle_status.batk+pl_sd->battle_status.rhw.atk+pl_sd->battle_status.lhw.atk;
+ WFIFOW(fd,16) = pl_sd->battle_status.rhw.atk2+pl_sd->battle_status.lhw.atk2;
+ WFIFOW(fd,18) = pl_sd->battle_status.matk_max;
+ WFIFOW(fd,20) = pl_sd->battle_status.matk_min;
+ WFIFOW(fd,22) = pl_sd->battle_status.def;
+ WFIFOW(fd,24) = pl_sd->battle_status.def2;
+ WFIFOW(fd,26) = pl_sd->battle_status.mdef;
+ WFIFOW(fd,28) = pl_sd->battle_status.mdef2;
+ WFIFOW(fd,30) = pl_sd->battle_status.hit;
+ WFIFOW(fd,32) = pl_sd->battle_status.flee;
+ WFIFOW(fd,34) = pl_sd->battle_status.flee2/10;
+ WFIFOW(fd,36) = pl_sd->battle_status.cri/10;
+ WFIFOW(fd,38) = (2000-pl_sd->battle_status.amotion)/10; // aspd
+ WFIFOW(fd,40) = 0; // FIXME: What is 'plusASPD' supposed to be? Maybe adelay?
+ WFIFOSET(fd,packet_len(0x214));
+}
+
+
+/// /check (CZ_REQ_STATUS_GM).
+/// Request character's status values.
+/// 0213 <char name>.24B
+void clif_parse_Check(int fd, struct map_session_data *sd)
+{
+ char charname[NAME_LENGTH];
+ struct map_session_data* pl_sd;
+
+ if(!pc_has_permission(sd, PC_PERM_USE_CHECK))
+ return;
+
+ safestrncpy(charname, (const char*)RFIFOP(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]), sizeof(charname));
+
+ if( ( pl_sd = map_nick2sd(charname) ) == NULL || pc_get_group_level(sd) < pc_get_group_level(pl_sd) )
+ {
+ return;
+ }
+
+ clif_check(fd, pl_sd);
+}
+
+
+
+/// MAIL SYSTEM
+/// By Zephyrus
+///
+
+/// Notification about the result of adding an item to mail (ZC_ACK_MAIL_ADD_ITEM).
+/// 0255 <index>.W <result>.B
+/// result:
+/// 0 = success
+/// 1 = failure
+void clif_Mail_setattachment(int fd, int index, uint8 flag)
+{
+ WFIFOHEAD(fd,packet_len(0x255));
+ WFIFOW(fd,0) = 0x255;
+ WFIFOW(fd,2) = index;
+ WFIFOB(fd,4) = flag;
+ WFIFOSET(fd,packet_len(0x255));
+}
+
+
+/// Notification about the result of retrieving a mail attachment (ZC_MAIL_REQ_GET_ITEM).
+/// 0245 <result>.B
+/// result:
+/// 0 = success
+/// 1 = failure
+/// 2 = too many items
+void clif_Mail_getattachment(int fd, uint8 flag)
+{
+ WFIFOHEAD(fd,packet_len(0x245));
+ WFIFOW(fd,0) = 0x245;
+ WFIFOB(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0x245));
+}
+
+
+/// Notification about the result of sending a mail (ZC_MAIL_REQ_SEND).
+/// 0249 <result>.B
+/// result:
+/// 0 = success
+/// 1 = recipinent does not exist
+void clif_Mail_send(int fd, bool fail)
+{
+ WFIFOHEAD(fd,packet_len(0x249));
+ WFIFOW(fd,0) = 0x249;
+ WFIFOB(fd,2) = fail;
+ WFIFOSET(fd,packet_len(0x249));
+}
+
+
+/// Notification about the result of deleting a mail (ZC_ACK_MAIL_DELETE).
+/// 0257 <mail id>.L <result>.W
+/// result:
+/// 0 = success
+/// 1 = failure
+void clif_Mail_delete(int fd, int mail_id, short fail)
+{
+ WFIFOHEAD(fd, packet_len(0x257));
+ WFIFOW(fd,0) = 0x257;
+ WFIFOL(fd,2) = mail_id;
+ WFIFOW(fd,6) = fail;
+ WFIFOSET(fd, packet_len(0x257));
+}
+
+
+/// Notification about the result of returning a mail (ZC_ACK_MAIL_RETURN).
+/// 0274 <mail id>.L <result>.W
+/// result:
+/// 0 = success
+/// 1 = failure
+void clif_Mail_return(int fd, int mail_id, short fail)
+{
+ WFIFOHEAD(fd,packet_len(0x274));
+ WFIFOW(fd,0) = 0x274;
+ WFIFOL(fd,2) = mail_id;
+ WFIFOW(fd,6) = fail;
+ WFIFOSET(fd,packet_len(0x274));
+}
+
+
+/// Notification about new mail (ZC_MAIL_RECEIVE).
+/// 024a <mail id>.L <title>.40B <sender>.24B
+void clif_Mail_new(int fd, int mail_id, const char *sender, const char *title)
+{
+ WFIFOHEAD(fd,packet_len(0x24a));
+ WFIFOW(fd,0) = 0x24a;
+ WFIFOL(fd,2) = mail_id;
+ safestrncpy((char*)WFIFOP(fd,6), title, MAIL_TITLE_LENGTH);
+ safestrncpy((char*)WFIFOP(fd,46), sender, NAME_LENGTH);
+ WFIFOSET(fd,packet_len(0x24a));
+}
+
+
+/// Opens/closes the mail window (ZC_MAIL_WINDOWS).
+/// 0260 <type>.L
+/// type:
+/// 0 = open
+/// 1 = close
+void clif_Mail_window(int fd, int flag)
+{
+ WFIFOHEAD(fd,packet_len(0x260));
+ WFIFOW(fd,0) = 0x260;
+ WFIFOL(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0x260));
+}
+
+
+/// Lists mails stored in inbox (ZC_MAIL_REQ_GET_LIST).
+/// 0240 <packet len>.W <amount>.L { <mail id>.L <title>.40B <read>.B <sender>.24B <time>.L }*amount
+/// read:
+/// 0 = unread
+/// 1 = read
+void clif_Mail_refreshinbox(struct map_session_data *sd)
+{
+ int fd = sd->fd;
+ struct mail_data *md = &sd->mail.inbox;
+ struct mail_message *msg;
+ int len, i, j;
+
+ len = 8 + (73 * md->amount);
+
+ WFIFOHEAD(fd,len);
+ WFIFOW(fd,0) = 0x240;
+ WFIFOW(fd,2) = len;
+ WFIFOL(fd,4) = md->amount;
+ for( i = j = 0; i < MAIL_MAX_INBOX && j < md->amount; i++ )
+ {
+ msg = &md->msg[i];
+ if (msg->id < 1)
+ continue;
+
+ WFIFOL(fd,8+73*j) = msg->id;
+ memcpy(WFIFOP(fd,12+73*j), msg->title, MAIL_TITLE_LENGTH);
+ WFIFOB(fd,52+73*j) = (msg->status != MAIL_UNREAD);
+ memcpy(WFIFOP(fd,53+73*j), msg->send_name, NAME_LENGTH);
+ WFIFOL(fd,77+73*j) = (uint32)msg->timestamp;
+ j++;
+ }
+ WFIFOSET(fd,len);
+
+ if( md->full )
+ {// TODO: is this official?
+ char output[100];
+ sprintf(output, "Inbox is full (Max %d). Delete some mails.", MAIL_MAX_INBOX);
+ clif_disp_onlyself(sd, output, strlen(output));
+ }
+}
+
+
+/// Mail inbox list request (CZ_MAIL_GET_LIST).
+/// 023f
+void clif_parse_Mail_refreshinbox(int fd, struct map_session_data *sd)
+{
+ struct mail_data* md = &sd->mail.inbox;
+
+ if( md->amount < MAIL_MAX_INBOX && (md->full || sd->mail.changed) )
+ intif_Mail_requestinbox(sd->status.char_id, 1);
+ else
+ clif_Mail_refreshinbox(sd);
+
+ mail_removeitem(sd, 0);
+ mail_removezeny(sd, 0);
+}
+
+
+/// Opens a mail (ZC_MAIL_REQ_OPEN).
+/// 0242 <packet len>.W <mail id>.L <title>.40B <sender>.24B <time>.L <zeny>.L
+/// <amount>.L <name id>.W <item type>.W <identified>.B <damaged>.B <refine>.B
+/// <card1>.W <card2>.W <card3>.W <card4>.W <message>.?B
+void clif_Mail_read(struct map_session_data *sd, int mail_id)
+{
+ int i, fd = sd->fd;
+
+ ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
+ if( i == MAIL_MAX_INBOX )
+ {
+ clif_Mail_return(sd->fd, mail_id, 1); // Mail doesn't exist
+ ShowWarning("clif_parse_Mail_read: char '%s' trying to read a message not the inbox.\n", sd->status.name);
+ return;
+ }
+ else
+ {
+ struct mail_message *msg = &sd->mail.inbox.msg[i];
+ struct item *item = &msg->item;
+ struct item_data *data;
+ int msg_len = strlen(msg->body), len;
+
+ if( msg_len == 0 ) {
+ strcpy(msg->body, "(no message)");
+ msg_len = strlen(msg->body);
+ }
+
+ len = 101 + msg_len;
+
+ WFIFOHEAD(fd,len);
+ WFIFOW(fd,0) = 0x242;
+ WFIFOW(fd,2) = len;
+ WFIFOL(fd,4) = msg->id;
+ safestrncpy((char*)WFIFOP(fd,8), msg->title, MAIL_TITLE_LENGTH + 1);
+ safestrncpy((char*)WFIFOP(fd,48), msg->send_name, NAME_LENGTH + 1);
+ WFIFOL(fd,72) = 0;
+ WFIFOL(fd,76) = msg->zeny;
+
+ if( item->nameid && (data = itemdb_exists(item->nameid)) != NULL )
+ {
+ WFIFOL(fd,80) = item->amount;
+ WFIFOW(fd,84) = (data->view_id)?data->view_id:item->nameid;
+ WFIFOW(fd,86) = data->type;
+ WFIFOB(fd,88) = item->identify;
+ WFIFOB(fd,89) = item->attribute;
+ WFIFOB(fd,90) = item->refine;
+ WFIFOW(fd,91) = item->card[0];
+ WFIFOW(fd,93) = item->card[1];
+ WFIFOW(fd,95) = item->card[2];
+ WFIFOW(fd,97) = item->card[3];
+ }
+ else // no item, set all to zero
+ memset(WFIFOP(fd,80), 0x00, 19);
+
+ WFIFOB(fd,99) = (unsigned char)msg_len;
+ safestrncpy((char*)WFIFOP(fd,100), msg->body, msg_len + 1);
+ WFIFOSET(fd,len);
+
+ if (msg->status == MAIL_UNREAD) {
+ msg->status = MAIL_READ;
+ intif_Mail_read(mail_id);
+ clif_parse_Mail_refreshinbox(fd, sd);
+ }
+ }
+}
+
+
+/// Request to open a mail (CZ_MAIL_OPEN).
+/// 0241 <mail id>.L
+void clif_parse_Mail_read(int fd, struct map_session_data *sd)
+{
+ int mail_id = RFIFOL(fd,2);
+
+ if( mail_id <= 0 )
+ return;
+ if( mail_invalid_operation(sd) )
+ return;
+
+ clif_Mail_read(sd, RFIFOL(fd,2));
+}
+
+
+/// Request to receive mail's attachment (CZ_MAIL_GET_ITEM).
+/// 0244 <mail id>.L
+void clif_parse_Mail_getattach(int fd, struct map_session_data *sd)
+{
+ int mail_id = RFIFOL(fd,2);
+ int i;
+ bool fail = false;
+
+ if( !chrif_isconnected() )
+ return;
+ if( mail_id <= 0 )
+ return;
+ if( mail_invalid_operation(sd) )
+ return;
+
+ ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
+ if( i == MAIL_MAX_INBOX )
+ return;
+
+ if( sd->mail.inbox.msg[i].zeny < 1 && (sd->mail.inbox.msg[i].item.nameid < 1 || sd->mail.inbox.msg[i].item.amount < 1) )
+ return;
+
+ if( sd->mail.inbox.msg[i].zeny + sd->status.zeny > MAX_ZENY )
+ {
+ clif_Mail_getattachment(fd, 1);
+ return;
+ }
+
+ if( sd->mail.inbox.msg[i].item.nameid > 0 )
+ {
+ struct item_data *data;
+ unsigned int weight;
+
+ if ((data = itemdb_exists(sd->mail.inbox.msg[i].item.nameid)) == NULL)
+ return;
+
+ switch( pc_checkadditem(sd, data->nameid, sd->mail.inbox.msg[i].item.amount) )
+ {
+ case ADDITEM_NEW:
+ fail = ( pc_inventoryblank(sd) == 0 );
+ break;
+ case ADDITEM_OVERAMOUNT:
+ fail = true;
+ }
+
+ if( fail )
+ {
+ clif_Mail_getattachment(fd, 1);
+ return;
+ }
+
+ weight = data->weight * sd->mail.inbox.msg[i].item.amount;
+ if( sd->weight + weight > sd->max_weight )
+ {
+ clif_Mail_getattachment(fd, 2);
+ return;
+ }
+ }
+
+ sd->mail.inbox.msg[i].zeny = 0;
+ memset(&sd->mail.inbox.msg[i].item, 0, sizeof(struct item));
+ clif_Mail_read(sd, mail_id);
+
+ intif_Mail_getattach(sd->status.char_id, mail_id);
+}
+
+
+/// Request to delete a mail (CZ_MAIL_DELETE).
+/// 0243 <mail id>.L
+void clif_parse_Mail_delete(int fd, struct map_session_data *sd)
+{
+ int mail_id = RFIFOL(fd,2);
+ int i;
+
+ if( !chrif_isconnected() )
+ return;
+ if( mail_id <= 0 )
+ return;
+ if( mail_invalid_operation(sd) )
+ return;
+
+ ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
+ if (i < MAIL_MAX_INBOX)
+ {
+ struct mail_message *msg = &sd->mail.inbox.msg[i];
+
+ if( (msg->item.nameid > 0 && msg->item.amount > 0) || msg->zeny > 0 )
+ {// can't delete mail without removing attachment first
+ clif_Mail_delete(sd->fd, mail_id, 1);
+ return;
+ }
+
+ intif_Mail_delete(sd->status.char_id, mail_id);
+ }
+}
+
+
+/// Request to return a mail (CZ_REQ_MAIL_RETURN).
+/// 0273 <mail id>.L <receive name>.24B
+void clif_parse_Mail_return(int fd, struct map_session_data *sd)
+{
+ int mail_id = RFIFOL(fd,2);
+ int i;
+
+ if( mail_id <= 0 )
+ return;
+ if( mail_invalid_operation(sd) )
+ return;
+
+ ARR_FIND(0, MAIL_MAX_INBOX, i, sd->mail.inbox.msg[i].id == mail_id);
+ if( i < MAIL_MAX_INBOX && sd->mail.inbox.msg[i].send_id != 0 )
+ intif_Mail_return(sd->status.char_id, mail_id);
+ else
+ clif_Mail_return(sd->fd, mail_id, 1);
+}
+
+
+/// Request to add an item or Zeny to mail (CZ_MAIL_ADD_ITEM).
+/// 0247 <index>.W <amount>.L
+void clif_parse_Mail_setattach(int fd, struct map_session_data *sd)
+{
+ int idx = RFIFOW(fd,2);
+ int amount = RFIFOL(fd,4);
+ unsigned char flag;
+
+ if( !chrif_isconnected() )
+ return;
+ if (idx < 0 || amount < 0)
+ return;
+
+ flag = mail_setitem(sd, idx, amount);
+ clif_Mail_setattachment(fd,idx,flag);
+}
+
+
+/// Request to reset mail item and/or Zeny (CZ_MAIL_RESET_ITEM).
+/// 0246 <type>.W
+/// type:
+/// 0 = reset all
+/// 1 = remove item
+/// 2 = remove zeny
+void clif_parse_Mail_winopen(int fd, struct map_session_data *sd)
+{
+ int flag = RFIFOW(fd,2);
+
+ if (flag == 0 || flag == 1)
+ mail_removeitem(sd, 0);
+ if (flag == 0 || flag == 2)
+ mail_removezeny(sd, 0);
+}
+
+
+/// Request to send mail (CZ_MAIL_SEND).
+/// 0248 <packet len>.W <recipient>.24B <title>.40B <body len>.B <body>.?B
+void clif_parse_Mail_send(int fd, struct map_session_data *sd)
+{
+ struct mail_message msg;
+ int body_len;
+
+ if( !chrif_isconnected() )
+ return;
+ if( sd->state.trading )
+ return;
+
+ if( RFIFOW(fd,2) < 69 ) {
+ ShowWarning("Invalid Msg Len from account %d.\n", sd->status.account_id);
+ return;
+ }
+
+ if( DIFF_TICK(sd->cansendmail_tick, gettick()) > 0 )
+ {
+ clif_displaymessage(sd->fd,msg_txt(675)); //"Cannot send mails too fast!!."
+ clif_Mail_send(fd, true); // fail
+ return;
+ }
+
+ body_len = RFIFOB(fd,68);
+
+ if (body_len > MAIL_BODY_LENGTH)
+ body_len = MAIL_BODY_LENGTH;
+
+ if( !mail_setattachment(sd, &msg) )
+ { // Invalid Append condition
+ clif_Mail_send(sd->fd, true); // fail
+ mail_removeitem(sd,0);
+ mail_removezeny(sd,0);
+ return;
+ }
+
+ msg.id = 0; // id will be assigned by charserver
+ msg.send_id = sd->status.char_id;
+ msg.dest_id = 0; // will attempt to resolve name
+ safestrncpy(msg.send_name, sd->status.name, NAME_LENGTH);
+ safestrncpy(msg.dest_name, (char*)RFIFOP(fd,4), NAME_LENGTH);
+ safestrncpy(msg.title, (char*)RFIFOP(fd,28), MAIL_TITLE_LENGTH);
+
+ if (msg.title[0] == '\0') {
+ return; // Message has no length and somehow client verification was skipped.
+ }
+
+ if (body_len)
+ safestrncpy(msg.body, (char*)RFIFOP(fd,69), body_len + 1);
+ else
+ memset(msg.body, 0x00, MAIL_BODY_LENGTH);
+
+ msg.timestamp = time(NULL);
+ if( !intif_Mail_send(sd->status.account_id, &msg) )
+ mail_deliveryfail(sd, &msg);
+
+ sd->cansendmail_tick = gettick() + 1000; // 1 Second flood Protection
+}
+
+
+/// AUCTION SYSTEM
+/// By Zephyrus
+///
+
+/// Opens/closes the auction window (ZC_AUCTION_WINDOWS).
+/// 025f <type>.L
+/// type:
+/// 0 = open
+/// 1 = close
+void clif_Auction_openwindow(struct map_session_data *sd)
+{
+ int fd = sd->fd;
+
+ if( sd->state.storage_flag || sd->state.vending || sd->state.buyingstore || sd->state.trading )
+ return;
+
+ WFIFOHEAD(fd,packet_len(0x25f));
+ WFIFOW(fd,0) = 0x25f;
+ WFIFOL(fd,2) = 0;
+ WFIFOSET(fd,packet_len(0x25f));
+}
+
+
+/// Returns auction item search results (ZC_AUCTION_ITEM_REQ_SEARCH).
+/// 0252 <packet len>.W <pages>.L <count>.L { <auction id>.L <seller name>.24B <name id>.W <type>.L <amount>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <now price>.L <max price>.L <buyer name>.24B <delete time>.L }*
+void clif_Auction_results(struct map_session_data *sd, short count, short pages, uint8 *buf)
+{
+ int i, fd = sd->fd, len = sizeof(struct auction_data);
+ struct auction_data auction;
+ struct item_data *item;
+ int k;
+
+ WFIFOHEAD(fd,12 + (count * 83));
+ WFIFOW(fd,0) = 0x252;
+ WFIFOW(fd,2) = 12 + (count * 83);
+ WFIFOL(fd,4) = pages;
+ WFIFOL(fd,8) = count;
+
+ for( i = 0; i < count; i++ )
+ {
+ memcpy(&auction, RBUFP(buf,i * len), len);
+ k = 12 + (i * 83);
+
+ WFIFOL(fd,k) = auction.auction_id;
+ safestrncpy((char*)WFIFOP(fd,4+k), auction.seller_name, NAME_LENGTH);
+
+ if( (item = itemdb_exists(auction.item.nameid)) != NULL && item->view_id > 0 )
+ WFIFOW(fd,28+k) = item->view_id;
+ else
+ WFIFOW(fd,28+k) = auction.item.nameid;
+
+ WFIFOL(fd,30+k) = auction.type;
+ WFIFOW(fd,34+k) = auction.item.amount; // Always 1
+ WFIFOB(fd,36+k) = auction.item.identify;
+ WFIFOB(fd,37+k) = auction.item.attribute;
+ WFIFOB(fd,38+k) = auction.item.refine;
+ WFIFOW(fd,39+k) = auction.item.card[0];
+ WFIFOW(fd,41+k) = auction.item.card[1];
+ WFIFOW(fd,43+k) = auction.item.card[2];
+ WFIFOW(fd,45+k) = auction.item.card[3];
+ WFIFOL(fd,47+k) = auction.price;
+ WFIFOL(fd,51+k) = auction.buynow;
+ safestrncpy((char*)WFIFOP(fd,55+k), auction.buyer_name, NAME_LENGTH);
+ WFIFOL(fd,79+k) = (uint32)auction.timestamp;
+ }
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Result from request to add an item (ZC_ACK_AUCTION_ADD_ITEM).
+/// 0256 <index>.W <result>.B
+/// result:
+/// 0 = success
+/// 1 = failure
+static void clif_Auction_setitem(int fd, int index, bool fail)
+{
+ WFIFOHEAD(fd,packet_len(0x256));
+ WFIFOW(fd,0) = 0x256;
+ WFIFOW(fd,2) = index;
+ WFIFOB(fd,4) = fail;
+ WFIFOSET(fd,packet_len(0x256));
+}
+
+
+/// Request to initialize 'new auction' data (CZ_AUCTION_CREATE).
+/// 024b <type>.W
+/// type:
+/// 0 = create (any other action in auction window)
+/// 1 = cancel (cancel pressed on register tab)
+/// ? = junk, uninitialized value (ex. when switching between list filters)
+void clif_parse_Auction_cancelreg(int fd, struct map_session_data *sd)
+{
+ if( sd->auction.amount > 0 )
+ clif_additem(sd, sd->auction.index, sd->auction.amount, 0);
+
+ sd->auction.amount = 0;
+}
+
+
+/// Request to add an item to the action (CZ_AUCTION_ADD_ITEM).
+/// 024c <index>.W <count>.L
+void clif_parse_Auction_setitem(int fd, struct map_session_data *sd)
+{
+ int idx = RFIFOW(fd,2) - 2;
+ int amount = RFIFOL(fd,4); // Always 1
+ struct item_data *item;
+
+ if( sd->auction.amount > 0 )
+ sd->auction.amount = 0;
+
+ if( idx < 0 || idx >= MAX_INVENTORY )
+ {
+ ShowWarning("Character %s trying to set invalid item index in auctions.\n", sd->status.name);
+ return;
+ }
+
+ if( amount != 1 || amount > sd->status.inventory[idx].amount )
+ { // By client, amount is always set to 1. Maybe this is a future implementation.
+ ShowWarning("Character %s trying to set invalid amount in auctions.\n", sd->status.name);
+ return;
+ }
+
+ if( (item = itemdb_exists(sd->status.inventory[idx].nameid)) != NULL && !(item->type == IT_ARMOR || item->type == IT_PETARMOR || item->type == IT_WEAPON || item->type == IT_CARD || item->type == IT_ETC) )
+ { // Consumable or pets are not allowed
+ clif_Auction_setitem(sd->fd, idx, true);
+ return;
+ }
+
+ if( !pc_can_give_items(sd) || sd->status.inventory[idx].expire_time ||
+ !sd->status.inventory[idx].identify ||
+ !itemdb_canauction(&sd->status.inventory[idx],pc_get_group_level(sd)) ) { // Quest Item or something else
+ clif_Auction_setitem(sd->fd, idx, true);
+ return;
+ }
+
+ sd->auction.index = idx;
+ sd->auction.amount = amount;
+ clif_Auction_setitem(fd, idx + 2, false);
+}
+
+/// Result from an auction action (ZC_AUCTION_RESULT).
+/// 0250 <result>.B
+/// result:
+/// 0 = You have failed to bid into the auction
+/// 1 = You have successfully bid in the auction
+/// 2 = The auction has been canceled
+/// 3 = An auction with at least one bidder cannot be canceled
+/// 4 = You cannot register more than 5 items in an auction at a time
+/// 5 = You do not have enough Zeny to pay the Auction Fee
+/// 6 = You have won the auction
+/// 7 = You have failed to win the auction
+/// 8 = You do not have enough Zeny
+/// 9 = You cannot place more than 5 bids at a time
+void clif_Auction_message(int fd, unsigned char flag)
+{
+ WFIFOHEAD(fd,packet_len(0x250));
+ WFIFOW(fd,0) = 0x250;
+ WFIFOB(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0x250));
+}
+
+
+/// Result of the auction close request (ZC_AUCTION_ACK_MY_SELL_STOP).
+/// 025e <result>.W
+/// result:
+/// 0 = You have ended the auction
+/// 1 = You cannot end the auction
+/// 2 = Auction ID is incorrect
+void clif_Auction_close(int fd, unsigned char flag)
+{
+ WFIFOHEAD(fd,packet_len(0x25e));
+ WFIFOW(fd,0) = 0x25d; // BUG: The client identifies this packet as 0x25d (CZ_AUCTION_REQ_MY_SELL_STOP)
+ WFIFOW(fd,2) = flag;
+ WFIFOSET(fd,packet_len(0x25e));
+}
+
+
+/// Request to add an auction (CZ_AUCTION_ADD).
+/// 024d <now money>.L <max money>.L <delete hour>.W
+void clif_parse_Auction_register(int fd, struct map_session_data *sd)
+{
+ struct auction_data auction;
+ struct item_data *item;
+
+ auction.price = RFIFOL(fd,2);
+ auction.buynow = RFIFOL(fd,6);
+ auction.hours = RFIFOW(fd,10);
+
+ // Invalid Situations...
+ if( sd->auction.amount < 1 )
+ {
+ ShowWarning("Character %s trying to register auction without item.\n", sd->status.name);
+ return;
+ }
+
+ if( auction.price >= auction.buynow )
+ {
+ ShowWarning("Character %s trying to alter auction prices.\n", sd->status.name);
+ return;
+ }
+
+ if( auction.hours < 1 || auction.hours > 48 )
+ {
+ ShowWarning("Character %s trying to enter an invalid time for auction.\n", sd->status.name);
+ return;
+ }
+
+ // Auction checks...
+ if( sd->status.zeny < (auction.hours * battle_config.auction_feeperhour) )
+ {
+ clif_Auction_message(fd, 5); // You do not have enough zeny to pay the Auction Fee.
+ return;
+ }
+
+ if( auction.buynow > battle_config.auction_maximumprice )
+ { // Zeny Limits
+ auction.buynow = battle_config.auction_maximumprice;
+ if( auction.price >= auction.buynow )
+ auction.price = auction.buynow - 1;
+ }
+
+ auction.auction_id = 0;
+ auction.seller_id = sd->status.char_id;
+ safestrncpy(auction.seller_name, sd->status.name, sizeof(auction.seller_name));
+ auction.buyer_id = 0;
+ memset(auction.buyer_name, '\0', sizeof(auction.buyer_name));
+
+ if( sd->status.inventory[sd->auction.index].nameid == 0 || sd->status.inventory[sd->auction.index].amount < sd->auction.amount )
+ {
+ clif_Auction_message(fd, 2); // The auction has been canceled
+ return;
+ }
+
+ if( (item = itemdb_exists(sd->status.inventory[sd->auction.index].nameid)) == NULL )
+ { // Just in case
+ clif_Auction_message(fd, 2); // The auction has been canceled
+ return;
+ }
+
+ safestrncpy(auction.item_name, item->jname, sizeof(auction.item_name));
+ auction.type = item->type;
+ memcpy(&auction.item, &sd->status.inventory[sd->auction.index], sizeof(struct item));
+ auction.item.amount = 1;
+ auction.timestamp = 0;
+
+ if( !intif_Auction_register(&auction) )
+ clif_Auction_message(fd, 4); // No Char Server? lets say something to the client
+ else
+ {
+ int zeny = auction.hours*battle_config.auction_feeperhour;
+
+ pc_delitem(sd, sd->auction.index, sd->auction.amount, 1, 6, LOG_TYPE_AUCTION);
+ sd->auction.amount = 0;
+
+ pc_payzeny(sd, zeny, LOG_TYPE_AUCTION, NULL);
+ }
+}
+
+
+/// Cancels an auction (CZ_AUCTION_ADD_CANCEL).
+/// 024e <auction id>.L
+void clif_parse_Auction_cancel(int fd, struct map_session_data *sd)
+{
+ unsigned int auction_id = RFIFOL(fd,2);
+
+ intif_Auction_cancel(sd->status.char_id, auction_id);
+}
+
+
+/// Closes an auction (CZ_AUCTION_REQ_MY_SELL_STOP).
+/// 025d <auction id>.L
+void clif_parse_Auction_close(int fd, struct map_session_data *sd)
+{
+ unsigned int auction_id = RFIFOL(fd,2);
+
+ intif_Auction_close(sd->status.char_id, auction_id);
+}
+
+
+/// Places a bid on an auction (CZ_AUCTION_BUY).
+/// 024f <auction id>.L <money>.L
+void clif_parse_Auction_bid(int fd, struct map_session_data *sd)
+{
+ unsigned int auction_id = RFIFOL(fd,2);
+ int bid = RFIFOL(fd,6);
+
+ if( !pc_can_give_items(sd) ) { //They aren't supposed to give zeny [Inkfish]
+ clif_displaymessage(sd->fd, msg_txt(246));
+ return;
+ }
+
+ if( bid <= 0 )
+ clif_Auction_message(fd, 0); // You have failed to bid into the auction
+ else if( bid > sd->status.zeny )
+ clif_Auction_message(fd, 8); // You do not have enough zeny
+ else if ( CheckForCharServer() ) // char server is down (bugreport:1138)
+ clif_Auction_message(fd, 0); // You have failed to bid into the auction
+ else {
+ pc_payzeny(sd, bid, LOG_TYPE_AUCTION, NULL);
+ intif_Auction_bid(sd->status.char_id, sd->status.name, auction_id, bid);
+ }
+}
+
+
+/// Auction Search (CZ_AUCTION_ITEM_SEARCH).
+/// 0251 <search type>.W <auction id>.L <search text>.24B <page number>.W
+/// search type:
+/// 0 = armor
+/// 1 = weapon
+/// 2 = card
+/// 3 = misc
+/// 4 = name search
+/// 5 = auction id search
+void clif_parse_Auction_search(int fd, struct map_session_data* sd)
+{
+ char search_text[NAME_LENGTH];
+ short type = RFIFOW(fd,2), page = RFIFOW(fd,32);
+ int price = RFIFOL(fd,4); // FIXME: bug #5071
+
+ clif_parse_Auction_cancelreg(fd, sd);
+
+ safestrncpy(search_text, (char*)RFIFOP(fd,8), sizeof(search_text));
+ intif_Auction_requestlist(sd->status.char_id, type, price, search_text, page);
+}
+
+
+/// Requests list of own currently active bids or auctions (CZ_AUCTION_REQ_MY_INFO).
+/// 025c <type>.W
+/// type:
+/// 0 = sell (own auctions)
+/// 1 = buy (own bids)
+void clif_parse_Auction_buysell(int fd, struct map_session_data* sd)
+{
+ short type = RFIFOW(fd,2) + 6;
+ clif_parse_Auction_cancelreg(fd, sd);
+
+ intif_Auction_requestlist(sd->status.char_id, type, 0, "", 1);
+}
+
+
+/// CASH/POINT SHOP
+///
+
+/// List of items offered in a cash shop (ZC_PC_CASH_POINT_ITEMLIST).
+/// 0287 <packet len>.W <cash point>.L { <sell price>.L <discount price>.L <item type>.B <name id>.W }*
+/// 0287 <packet len>.W <cash point>.L <kafra point>.L { <sell price>.L <discount price>.L <item type>.B <name id>.W }* (PACKETVER >= 20070711)
+void clif_cashshop_show(struct map_session_data *sd, struct npc_data *nd)
+{
+ int fd,i;
+#if PACKETVER < 20070711
+ const int offset = 8;
+#else
+ const int offset = 12;
+#endif
+
+ nullpo_retv(sd);
+ nullpo_retv(nd);
+
+ fd = sd->fd;
+ sd->npc_shopid = nd->bl.id;
+ WFIFOHEAD(fd,offset+nd->u.shop.count*11);
+ WFIFOW(fd,0) = 0x287;
+ WFIFOW(fd,2) = offset+nd->u.shop.count*11;
+ WFIFOL(fd,4) = sd->cashPoints; // Cash Points
+#if PACKETVER >= 20070711
+ WFIFOL(fd,8) = sd->kafraPoints; // Kafra Points
+#endif
+
+ for( i = 0; i < nd->u.shop.count; i++ )
+ {
+ struct item_data* id = itemdb_search(nd->u.shop.shop_item[i].nameid);
+ WFIFOL(fd,offset+0+i*11) = nd->u.shop.shop_item[i].value;
+ WFIFOL(fd,offset+4+i*11) = nd->u.shop.shop_item[i].value; // Discount Price
+ WFIFOB(fd,offset+8+i*11) = itemtype(id->type);
+ WFIFOW(fd,offset+9+i*11) = ( id->view_id > 0 ) ? id->view_id : id->nameid;
+ }
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Cashshop Buy Ack (ZC_PC_CASH_POINT_UPDATE).
+/// 0289 <cash point>.L <error>.W
+/// 0289 <cash point>.L <kafra point>.L <error>.W (PACKETVER >= 20070711)
+/// error:
+/// 0 = The deal has successfully completed. (ERROR_TYPE_NONE)
+/// 1 = The Purchase has failed because the NPC does not exist. (ERROR_TYPE_NPC)
+/// 2 = The Purchase has failed because the Kafra Shop System is not working correctly. (ERROR_TYPE_SYSTEM)
+/// 3 = You are over your Weight Limit. (ERROR_TYPE_INVENTORY_WEIGHT)
+/// 4 = You cannot purchase items while you are in a trade. (ERROR_TYPE_EXCHANGE)
+/// 5 = The Purchase has failed because the Item Information was incorrect. (ERROR_TYPE_ITEM_ID)
+/// 6 = You do not have enough Kafra Credit Points. (ERROR_TYPE_MONEY)
+/// 7 = You can purchase up to 10 items.
+/// 8 = Some items could not be purchased.
+void clif_cashshop_ack(struct map_session_data* sd, int error)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x289));
+ WFIFOW(fd,0) = 0x289;
+ WFIFOL(fd,2) = sd->cashPoints;
+#if PACKETVER < 20070711
+ WFIFOW(fd,6) = TOW(error);
+#else
+ WFIFOL(fd,6) = sd->kafraPoints;
+ WFIFOW(fd,10) = TOW(error);
+#endif
+ WFIFOSET(fd, packet_len(0x289));
+}
+
+
+/// Request to buy item(s) from cash shop (CZ_PC_BUY_CASH_POINT_ITEM).
+/// 0288 <name id>.W <amount>.W
+/// 0288 <name id>.W <amount>.W <kafra points>.L (PACKETVER >= 20070711)
+/// 0288 <packet len>.W <kafra points>.L <count>.W { <amount>.W <name id>.W }.4B*count (PACKETVER >= 20100803)
+void clif_parse_cashshop_buy(int fd, struct map_session_data *sd)
+{
+ int fail = 0;
+ nullpo_retv(sd);
+
+ if( sd->state.trading || !sd->npc_shopid )
+ fail = 1;
+ else
+ {
+#if PACKETVER < 20101116
+ short nameid = RFIFOW(fd,2);
+ short amount = RFIFOW(fd,4);
+ int points = RFIFOL(fd,6);
+
+ fail = npc_cashshop_buy(sd, nameid, amount, points);
+#else
+ int len = RFIFOW(fd,2);
+ int points = RFIFOL(fd,4);
+ int count = RFIFOW(fd,8);
+ unsigned short* item_list = (unsigned short*)RFIFOP(fd,10);
+
+ if( len < 10 || len != 10 + count * 4)
+ {
+ ShowWarning("Player %u sent incorrect cash shop buy packet (len %u:%u)!\n", sd->status.char_id, len, 10 + count * 4);
+ return;
+ }
+ fail = npc_cashshop_buylist(sd,points,count,item_list);
+#endif
+ }
+
+ clif_cashshop_ack(sd,fail);
+}
+
+
+/// Adoption System
+///
+
+/// Adoption message (ZC_BABYMSG).
+/// 0216 <msg>.L
+/// msg:
+/// 0 = "You cannot adopt more than 1 child."
+/// 1 = "You must be at least character level 70 in order to adopt someone."
+/// 2 = "You cannot adopt a married person."
+void clif_Adopt_reply(struct map_session_data *sd, int type)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,6);
+ WFIFOW(fd,0) = 0x216;
+ WFIFOL(fd,2) = type;
+ WFIFOSET(fd,6);
+}
+
+
+/// Adoption confirmation (ZC_REQ_BABY).
+/// 01f6 <account id>.L <char id>.L <name>.B
+void clif_Adopt_request(struct map_session_data *sd, struct map_session_data *src, int p_id)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,34);
+ WFIFOW(fd,0) = 0x1f6;
+ WFIFOL(fd,2) = src->status.account_id;
+ WFIFOL(fd,6) = p_id;
+ memcpy(WFIFOP(fd,10), src->status.name, NAME_LENGTH);
+ WFIFOSET(fd,34);
+}
+
+
+/// Request to adopt a player (CZ_REQ_JOIN_BABY).
+/// 01f9 <account id>.L
+void clif_parse_Adopt_request(int fd, struct map_session_data *sd)
+{
+ struct map_session_data *tsd = map_id2sd(RFIFOL(fd,2)), *p_sd = map_charid2sd(sd->status.partner_id);
+
+ if( pc_can_Adopt(sd, p_sd, tsd) )
+ {
+ tsd->adopt_invite = sd->status.account_id;
+ clif_Adopt_request(tsd, sd, p_sd->status.account_id);
+ }
+}
+
+
+/// Answer to adopt confirmation (CZ_JOIN_BABY).
+/// 01f7 <account id>.L <char id>.L <answer>.L
+/// answer:
+/// 0 = rejected
+/// 1 = accepted
+void clif_parse_Adopt_reply(int fd, struct map_session_data *sd)
+{
+ int p1_id = RFIFOL(fd,2);
+ int p2_id = RFIFOL(fd,6);
+ int result = RFIFOL(fd,10);
+ struct map_session_data* p1_sd = map_id2sd(p1_id);
+ struct map_session_data* p2_sd = map_id2sd(p2_id);
+
+ int pid = sd->adopt_invite;
+ sd->adopt_invite = 0;
+
+ if( p1_sd == NULL || p2_sd == NULL )
+ return; // Both players need to be online
+
+ if( pid != p1_sd->status.account_id )
+ return; // Incorrect values
+
+ if( result == 0 )
+ return; // Rejected
+
+ pc_adoption(p1_sd, p2_sd, sd);
+}
+
+
+/// Convex Mirror (ZC_BOSS_INFO).
+/// 0293 <infoType>.B <x>.L <y>.L <minHours>.W <minMinutes>.W <maxHours>.W <maxMinutes>.W <monster name>.51B
+/// infoType:
+/// 0 = No boss on this map (BOSS_INFO_NOT).
+/// 1 = Boss is alive (position update) (BOSS_INFO_ALIVE).
+/// 2 = Boss is alive (initial announce) (BOSS_INFO_ALIVE_WITHMSG).
+/// 3 = Boss is dead (BOSS_INFO_DEAD).
+void clif_bossmapinfo(int fd, struct mob_data *md, short flag)
+{
+ WFIFOHEAD(fd,70);
+ memset(WFIFOP(fd,0),0,70);
+ WFIFOW(fd,0) = 0x293;
+
+ if( md != NULL )
+ {
+ if( md->bl.prev != NULL )
+ { // Boss on This Map
+ if( flag )
+ {
+ WFIFOB(fd,2) = 1;
+ WFIFOL(fd,3) = md->bl.x;
+ WFIFOL(fd,7) = md->bl.y;
+ }
+ else
+ WFIFOB(fd,2) = 2; // First Time
+ }
+ else if (md->spawn_timer != INVALID_TIMER)
+ { // Boss is Dead
+ const struct TimerData * timer_data = get_timer(md->spawn_timer);
+ unsigned int seconds;
+ int hours, minutes;
+
+ seconds = DIFF_TICK(timer_data->tick, gettick()) / 1000 + 60;
+ hours = seconds / (60 * 60);
+ seconds = seconds - (60 * 60 * hours);
+ minutes = seconds / 60;
+
+ WFIFOB(fd,2) = 3;
+ WFIFOW(fd,11) = hours; // Hours
+ WFIFOW(fd,13) = minutes; // Minutes
+ }
+ safestrncpy((char*)WFIFOP(fd,19), md->db->jname, NAME_LENGTH);
+ }
+
+ WFIFOSET(fd,70);
+}
+
+
+/// Requesting equip of a player (CZ_EQUIPWIN_MICROSCOPE).
+/// 02d6 <account id>.L
+void clif_parse_ViewPlayerEquip(int fd, struct map_session_data* sd)
+{
+ int charid = RFIFOL(fd, 2);
+ struct map_session_data* tsd = map_id2sd(charid);
+
+ if (!tsd)
+ return;
+
+ if( tsd->status.show_equip || pc_has_permission(sd, PC_PERM_VIEW_EQUIPMENT) )
+ clif_viewequip_ack(sd, tsd);
+ else
+ clif_viewequip_fail(sd);
+}
+
+
+/// Request to change equip window tick (CZ_CONFIG).
+/// 02d8 <type>.L <value>.L
+/// type:
+/// 0 = open equip window
+/// value:
+/// 0 = disabled
+/// 1 = enabled
+void clif_parse_EquipTick(int fd, struct map_session_data* sd)
+{
+ bool flag = (bool)RFIFOL(fd,6);
+ sd->status.show_equip = flag;
+ clif_equiptickack(sd, flag);
+}
+
+
+/// Questlog System [Kevin] [Inkfish]
+///
+
+/// Sends list of all quest states (ZC_ALL_QUEST_LIST).
+/// 02b1 <packet len>.W <num>.L { <quest id>.L <active>.B }*num
+void clif_quest_send_list(struct map_session_data * sd)
+{
+ int fd = sd->fd;
+ int i;
+ int len = sd->avail_quests*5+8;
+
+ WFIFOHEAD(fd,len);
+ WFIFOW(fd, 0) = 0x2b1;
+ WFIFOW(fd, 2) = len;
+ WFIFOL(fd, 4) = sd->avail_quests;
+
+ for( i = 0; i < sd->avail_quests; i++ )
+ {
+ WFIFOL(fd, i*5+8) = sd->quest_log[i].quest_id;
+ WFIFOB(fd, i*5+12) = sd->quest_log[i].state;
+ }
+
+ WFIFOSET(fd, len);
+}
+
+
+/// Sends list of all quest missions (ZC_ALL_QUEST_MISSION).
+/// 02b2 <packet len>.W <num>.L { <quest id>.L <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3 }*num
+void clif_quest_send_mission(struct map_session_data * sd)
+{
+ int fd = sd->fd;
+ int i, j;
+ int len = sd->avail_quests*104+8;
+ struct mob_db *mob;
+
+ WFIFOHEAD(fd, len);
+ WFIFOW(fd, 0) = 0x2b2;
+ WFIFOW(fd, 2) = len;
+ WFIFOL(fd, 4) = sd->avail_quests;
+
+ for( i = 0; i < sd->avail_quests; i++ )
+ {
+ WFIFOL(fd, i*104+8) = sd->quest_log[i].quest_id;
+ WFIFOL(fd, i*104+12) = sd->quest_log[i].time - quest_db[sd->quest_index[i]].time;
+ WFIFOL(fd, i*104+16) = sd->quest_log[i].time;
+ WFIFOW(fd, i*104+20) = quest_db[sd->quest_index[i]].num_objectives;
+
+ for( j = 0 ; j < quest_db[sd->quest_index[i]].num_objectives; j++ )
+ {
+ WFIFOL(fd, i*104+22+j*30) = quest_db[sd->quest_index[i]].mob[j];
+ WFIFOW(fd, i*104+26+j*30) = sd->quest_log[i].count[j];
+ mob = mob_db(quest_db[sd->quest_index[i]].mob[j]);
+ memcpy(WFIFOP(fd, i*104+28+j*30), mob?mob->jname:"NULL", NAME_LENGTH);
+ }
+ }
+
+ WFIFOSET(fd, len);
+}
+
+
+/// Notification about a new quest (ZC_ADD_QUEST).
+/// 02b3 <quest id>.L <active>.B <start time>.L <expire time>.L <mobs>.W { <mob id>.L <mob count>.W <mob name>.24B }*3
+void clif_quest_add(struct map_session_data * sd, struct quest * qd, int index)
+{
+ int fd = sd->fd;
+ int i;
+ struct mob_db *mob;
+
+ WFIFOHEAD(fd, packet_len(0x2b3));
+ WFIFOW(fd, 0) = 0x2b3;
+ WFIFOL(fd, 2) = qd->quest_id;
+ WFIFOB(fd, 6) = qd->state;
+ WFIFOB(fd, 7) = qd->time - quest_db[index].time;
+ WFIFOL(fd, 11) = qd->time;
+ WFIFOW(fd, 15) = quest_db[index].num_objectives;
+
+ for( i = 0; i < quest_db[index].num_objectives; i++ )
+ {
+ WFIFOL(fd, i*30+17) = quest_db[index].mob[i];
+ WFIFOW(fd, i*30+21) = qd->count[i];
+ mob = mob_db(quest_db[index].mob[i]);
+ memcpy(WFIFOP(fd, i*30+23), mob?mob->jname:"NULL", NAME_LENGTH);
+ }
+
+ WFIFOSET(fd, packet_len(0x2b3));
+}
+
+
+/// Notification about a quest being removed (ZC_DEL_QUEST).
+/// 02b4 <quest id>.L
+void clif_quest_delete(struct map_session_data * sd, int quest_id)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x2b4));
+ WFIFOW(fd, 0) = 0x2b4;
+ WFIFOL(fd, 2) = quest_id;
+ WFIFOSET(fd, packet_len(0x2b4));
+}
+
+
+/// Notification of an update to the hunting mission counter (ZC_UPDATE_MISSION_HUNT).
+/// 02b5 <packet len>.W <mobs>.W { <quest id>.L <mob id>.L <total count>.W <current count>.W }*3
+void clif_quest_update_objective(struct map_session_data * sd, struct quest * qd, int index)
+{
+ int fd = sd->fd;
+ int i;
+ int len = quest_db[index].num_objectives*12+6;
+
+ WFIFOHEAD(fd, len);
+ WFIFOW(fd, 0) = 0x2b5;
+ WFIFOW(fd, 2) = len;
+ WFIFOW(fd, 4) = quest_db[index].num_objectives;
+
+ for( i = 0; i < quest_db[index].num_objectives; i++ )
+ {
+ WFIFOL(fd, i*12+6) = qd->quest_id;
+ WFIFOL(fd, i*12+10) = quest_db[index].mob[i];
+ WFIFOW(fd, i*12+14) = quest_db[index].count[i];
+ WFIFOW(fd, i*12+16) = qd->count[i];
+ }
+
+ WFIFOSET(fd, len);
+}
+
+
+/// Request to change the state of a quest (CZ_ACTIVE_QUEST).
+/// 02b6 <quest id>.L <active>.B
+void clif_parse_questStateAck(int fd, struct map_session_data * sd)
+{
+ quest_update_status(sd, RFIFOL(fd,2), RFIFOB(fd,6)?Q_ACTIVE:Q_INACTIVE);
+}
+
+
+/// Notification about the change of a quest state (ZC_ACTIVE_QUEST).
+/// 02b7 <quest id>.L <active>.B
+void clif_quest_update_status(struct map_session_data * sd, int quest_id, bool active)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x2b7));
+ WFIFOW(fd, 0) = 0x2b7;
+ WFIFOL(fd, 2) = quest_id;
+ WFIFOB(fd, 6) = active;
+ WFIFOSET(fd, packet_len(0x2b7));
+}
+
+
+/// Notification about an NPC's quest state (ZC_QUEST_NOTIFY_EFFECT).
+/// 0446 <npc id>.L <x>.W <y>.W <effect>.W <type>.W
+/// effect:
+/// 0 = none
+/// 1 = exclamation mark icon
+/// 2 = question mark icon
+/// type:
+/// 0 = yellow
+/// 1 = orange
+/// 2 = green
+/// 3 = purple
+void clif_quest_show_event(struct map_session_data *sd, struct block_list *bl, short state, short color)
+{
+#if PACKETVER >= 20090218
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x446));
+ WFIFOW(fd, 0) = 0x446;
+ WFIFOL(fd, 2) = bl->id;
+ WFIFOW(fd, 6) = bl->x;
+ WFIFOW(fd, 8) = bl->y;
+ WFIFOW(fd, 10) = state;
+ WFIFOW(fd, 12) = color;
+ WFIFOSET(fd, packet_len(0x446));
+#endif
+}
+
+
+/// Mercenary System
+///
+
+/// Notification about a mercenary status parameter change (ZC_MER_PAR_CHANGE).
+/// 02a2 <var id>.W <value>.L
+void clif_mercenary_updatestatus(struct map_session_data *sd, int type)
+{
+ struct mercenary_data *md;
+ struct status_data *status;
+ int fd;
+ if( sd == NULL || (md = sd->md) == NULL )
+ return;
+
+ fd = sd->fd;
+ status = &md->battle_status;
+ WFIFOHEAD(fd,packet_len(0x2a2));
+ WFIFOW(fd,0) = 0x2a2;
+ WFIFOW(fd,2) = type;
+ switch( type )
+ {
+ case SP_ATK1:
+ {
+ int atk = rnd()%(status->rhw.atk2 - status->rhw.atk + 1) + status->rhw.atk;
+ WFIFOL(fd,4) = cap_value(atk, 0, INT16_MAX);
+ }
+ break;
+ case SP_MATK1:
+ WFIFOL(fd,4) = cap_value(status->matk_max, 0, INT16_MAX);
+ break;
+ case SP_HIT:
+ WFIFOL(fd,4) = status->hit;
+ break;
+ case SP_CRITICAL:
+ WFIFOL(fd,4) = status->cri/10;
+ break;
+ case SP_DEF1:
+ WFIFOL(fd,4) = status->def;
+ break;
+ case SP_MDEF1:
+ WFIFOL(fd,4) = status->mdef;
+ break;
+ case SP_MERCFLEE:
+ WFIFOL(fd,4) = status->flee;
+ break;
+ case SP_ASPD:
+ WFIFOL(fd,4) = status->amotion;
+ break;
+ case SP_HP:
+ WFIFOL(fd,4) = status->hp;
+ break;
+ case SP_MAXHP:
+ WFIFOL(fd,4) = status->max_hp;
+ break;
+ case SP_SP:
+ WFIFOL(fd,4) = status->sp;
+ break;
+ case SP_MAXSP:
+ WFIFOL(fd,4) = status->max_sp;
+ break;
+ case SP_MERCKILLS:
+ WFIFOL(fd,4) = md->mercenary.kill_count;
+ break;
+ case SP_MERCFAITH:
+ WFIFOL(fd,4) = mercenary_get_faith(md);
+ break;
+ }
+ WFIFOSET(fd,packet_len(0x2a2));
+}
+
+
+/// Mercenary base status data (ZC_MER_INIT).
+/// 029b <id>.L <atk>.W <matk>.W <hit>.W <crit>.W <def>.W <mdef>.W <flee>.W <aspd>.W
+/// <name>.24B <level>.W <hp>.L <maxhp>.L <sp>.L <maxsp>.L <expire time>.L <faith>.W
+/// <calls>.L <kills>.L <atk range>.W
+void clif_mercenary_info(struct map_session_data *sd)
+{
+ int fd;
+ struct mercenary_data *md;
+ struct status_data *status;
+ int atk;
+
+ if( sd == NULL || (md = sd->md) == NULL )
+ return;
+
+ fd = sd->fd;
+ status = &md->battle_status;
+
+ WFIFOHEAD(fd,packet_len(0x29b));
+ WFIFOW(fd,0) = 0x29b;
+ WFIFOL(fd,2) = md->bl.id;
+
+ // Mercenary shows ATK as a random value between ATK ~ ATK2
+ atk = rnd()%(status->rhw.atk2 - status->rhw.atk + 1) + status->rhw.atk;
+ WFIFOW(fd,6) = cap_value(atk, 0, INT16_MAX);
+ WFIFOW(fd,8) = cap_value(status->matk_max, 0, INT16_MAX);
+ WFIFOW(fd,10) = status->hit;
+ WFIFOW(fd,12) = status->cri/10;
+ WFIFOW(fd,14) = status->def;
+ WFIFOW(fd,16) = status->mdef;
+ WFIFOW(fd,18) = status->flee;
+ WFIFOW(fd,20) = status->amotion;
+ safestrncpy((char*)WFIFOP(fd,22), md->db->name, NAME_LENGTH);
+ WFIFOW(fd,46) = md->db->lv;
+ WFIFOL(fd,48) = status->hp;
+ WFIFOL(fd,52) = status->max_hp;
+ WFIFOL(fd,56) = status->sp;
+ WFIFOL(fd,60) = status->max_sp;
+ WFIFOL(fd,64) = (int)time(NULL) + (mercenary_get_lifetime(md) / 1000);
+ WFIFOW(fd,68) = mercenary_get_faith(md);
+ WFIFOL(fd,70) = mercenary_get_calls(md);
+ WFIFOL(fd,74) = md->mercenary.kill_count;
+ WFIFOW(fd,78) = md->battle_status.rhw.range;
+ WFIFOSET(fd,packet_len(0x29b));
+}
+
+
+/// Mercenary skill tree (ZC_MER_SKILLINFO_LIST).
+/// 029d <packet len>.W { <skill id>.W <type>.L <level>.W <sp cost>.W <attack range>.W <skill name>.24B <upgradable>.B }*
+void clif_mercenary_skillblock(struct map_session_data *sd)
+{
+ struct mercenary_data *md;
+ int fd, i, len = 4, id, j;
+
+ if( sd == NULL || (md = sd->md) == NULL )
+ return;
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,4+37*MAX_MERCSKILL);
+ WFIFOW(fd,0) = 0x29d;
+ for( i = 0; i < MAX_MERCSKILL; i++ )
+ {
+ if( (id = md->db->skill[i].id) == 0 )
+ continue;
+ j = id - MC_SKILLBASE;
+ WFIFOW(fd,len) = id;
+ WFIFOL(fd,len+2) = skill_get_inf(id);
+ WFIFOW(fd,len+6) = md->db->skill[j].lv;
+ WFIFOW(fd,len+8) = skill_get_sp(id, md->db->skill[j].lv);
+ WFIFOW(fd,len+10) = skill_get_range2(&md->bl, id, md->db->skill[j].lv);
+ safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
+ WFIFOB(fd,len+36) = 0; // Skillable for Mercenary?
+ len += 37;
+ }
+
+ WFIFOW(fd,2) = len;
+ WFIFOSET(fd,len);
+}
+
+
+/// Request to invoke a mercenary menu action (CZ_MER_COMMAND).
+/// 029f <command>.B
+/// 1 = mercenary information
+/// 2 = delete
+void clif_parse_mercenary_action(int fd, struct map_session_data* sd)
+{
+ int option = RFIFOB(fd,2);
+ if( sd->md == NULL )
+ return;
+
+ if( option == 2 ) merc_delete(sd->md, 2);
+}
+
+
+/// Mercenary Message
+/// message:
+/// 0 = Mercenary soldier's duty hour is over.
+/// 1 = Your mercenary soldier has been killed.
+/// 2 = Your mercenary soldier has been fired.
+/// 3 = Your mercenary soldier has ran away.
+void clif_mercenary_message(struct map_session_data* sd, int message)
+{
+ clif_msg(sd, 1266 + message);
+}
+
+
+/// Notification about the remaining time of a rental item (ZC_CASH_TIME_COUNTER).
+/// 0298 <name id>.W <seconds>.L
+void clif_rental_time(int fd, int nameid, int seconds)
+{ // '<ItemName>' item will disappear in <seconds/60> minutes.
+ WFIFOHEAD(fd,packet_len(0x298));
+ WFIFOW(fd,0) = 0x298;
+ WFIFOW(fd,2) = nameid;
+ WFIFOL(fd,4) = seconds;
+ WFIFOSET(fd,packet_len(0x298));
+}
+
+
+/// Deletes a rental item from client's inventory (ZC_CASH_ITEM_DELETE).
+/// 0299 <index>.W <name id>.W
+void clif_rental_expired(int fd, int index, int nameid)
+{ // '<ItemName>' item has been deleted from the Inventory
+ WFIFOHEAD(fd,packet_len(0x299));
+ WFIFOW(fd,0) = 0x299;
+ WFIFOW(fd,2) = index+2;
+ WFIFOW(fd,4) = nameid;
+ WFIFOSET(fd,packet_len(0x299));
+}
+
+
+/// Book Reading (ZC_READ_BOOK).
+/// 0294 <book id>.L <page>.L
+void clif_readbook(int fd, int book_id, int page)
+{
+ WFIFOHEAD(fd,packet_len(0x294));
+ WFIFOW(fd,0) = 0x294;
+ WFIFOL(fd,2) = book_id;
+ WFIFOL(fd,6) = page;
+ WFIFOSET(fd,packet_len(0x294));
+}
+
+
+/// Battlegrounds
+///
+
+/// Updates HP bar of a camp member (ZC_BATTLEFIELD_NOTIFY_HP).
+/// 02e0 <account id>.L <name>.24B <hp>.W <max hp>.W
+void clif_bg_hp(struct map_session_data *sd)
+{
+ unsigned char buf[34];
+ const int cmd = 0x2e0;
+ nullpo_retv(sd);
+
+ WBUFW(buf,0) = cmd;
+ WBUFL(buf,2) = sd->status.account_id;
+ memcpy(WBUFP(buf,6), sd->status.name, NAME_LENGTH);
+
+ if( sd->battle_status.max_hp > INT16_MAX )
+ { // To correctly display the %hp bar. [Skotlex]
+ WBUFW(buf,30) = sd->battle_status.hp/(sd->battle_status.max_hp/100);
+ WBUFW(buf,32) = 100;
+ }
+ else
+ {
+ WBUFW(buf,30) = sd->battle_status.hp;
+ WBUFW(buf,32) = sd->battle_status.max_hp;
+ }
+
+ clif_send(buf, packet_len(cmd), &sd->bl, BG_AREA_WOS);
+}
+
+
+/// Updates the position of a camp member on the minimap (ZC_BATTLEFIELD_NOTIFY_POSITION).
+/// 02df <account id>.L <name>.24B <class>.W <x>.W <y>.W
+void clif_bg_xy(struct map_session_data *sd)
+{
+ unsigned char buf[36];
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x2df;
+ WBUFL(buf,2)=sd->status.account_id;
+ memcpy(WBUFP(buf,6), sd->status.name, NAME_LENGTH);
+ WBUFW(buf,30)=sd->status.class_;
+ WBUFW(buf,32)=sd->bl.x;
+ WBUFW(buf,34)=sd->bl.y;
+
+ clif_send(buf, packet_len(0x2df), &sd->bl, BG_SAMEMAP_WOS);
+}
+
+void clif_bg_xy_remove(struct map_session_data *sd)
+{
+ unsigned char buf[36];
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x2df;
+ WBUFL(buf,2)=sd->status.account_id;
+ memset(WBUFP(buf,6), 0, NAME_LENGTH);
+ WBUFW(buf,30)=0;
+ WBUFW(buf,32)=-1;
+ WBUFW(buf,34)=-1;
+
+ clif_send(buf, packet_len(0x2df), &sd->bl, BG_SAMEMAP_WOS);
+}
+
+
+/// Notifies clients of a battleground message (ZC_BATTLEFIELD_CHAT).
+/// 02dc <packet len>.W <account id>.L <name>.24B <message>.?B
+void clif_bg_message(struct battleground_data *bg, int src_id, const char *name, const char *mes, int len)
+{
+ struct map_session_data *sd;
+ unsigned char *buf;
+ if( (sd = bg_getavailablesd(bg)) == NULL )
+ return;
+
+ buf = (unsigned char*)aMalloc((len + NAME_LENGTH + 8)*sizeof(unsigned char));
+
+ WBUFW(buf,0) = 0x2dc;
+ WBUFW(buf,2) = len + NAME_LENGTH + 8;
+ WBUFL(buf,4) = src_id;
+ memcpy(WBUFP(buf,8), name, NAME_LENGTH);
+ memcpy(WBUFP(buf,32), mes, len);
+ clif_send(buf,WBUFW(buf,2), &sd->bl, BG);
+
+ if( buf )
+ aFree(buf);
+}
+
+
+/// Validates and processes battlechat messages [pakpil] (CZ_BATTLEFIELD_CHAT).
+/// 0x2db <packet len>.W <text>.?B (<name> : <message>) 00
+void clif_parse_BattleChat(int fd, struct map_session_data* sd)
+{
+ const char* text = (char*)RFIFOP(fd,4);
+ int textlen = RFIFOW(fd,2) - 4;
+
+ char *name, *message;
+ int namelen, messagelen;
+
+ if( !clif_process_message(sd, 0, &name, &namelen, &message, &messagelen) )
+ return;
+
+ if( is_atcommand(fd, sd, message, 1) )
+ return;
+
+ if( sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] || (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) )
+ return;
+
+ if( battle_config.min_chat_delay )
+ {
+ if( DIFF_TICK(sd->cantalk_tick, gettick()) > 0 )
+ return;
+ sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
+ }
+
+ bg_send_message(sd, text, textlen);
+}
+
+
+/// Notifies client of a battleground score change (ZC_BATTLEFIELD_NOTIFY_POINT).
+/// 02de <camp A points>.W <camp B points>.W
+void clif_bg_updatescore(int16 m)
+{
+ struct block_list bl;
+ unsigned char buf[6];
+
+ bl.id = 0;
+ bl.type = BL_NUL;
+ bl.m = m;
+
+ WBUFW(buf,0) = 0x2de;
+ WBUFW(buf,2) = map[m].bgscore_lion;
+ WBUFW(buf,4) = map[m].bgscore_eagle;
+ clif_send(buf,packet_len(0x2de),&bl,ALL_SAMEMAP);
+}
+
+void clif_bg_updatescore_single(struct map_session_data *sd)
+{
+ int fd;
+ nullpo_retv(sd);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x2de));
+ WFIFOW(fd,0) = 0x2de;
+ WFIFOW(fd,2) = map[sd->bl.m].bgscore_lion;
+ WFIFOW(fd,4) = map[sd->bl.m].bgscore_eagle;
+ WFIFOSET(fd,packet_len(0x2de));
+}
+
+
+/// Battleground camp belong-information (ZC_BATTLEFIELD_NOTIFY_CAMPINFO).
+/// 02dd <account id>.L <name>.24B <camp>.W
+void clif_sendbgemblem_area(struct map_session_data *sd)
+{
+ unsigned char buf[33];
+ nullpo_retv(sd);
+
+ WBUFW(buf, 0) = 0x2dd;
+ WBUFL(buf,2) = sd->bl.id;
+ safestrncpy((char*)WBUFP(buf,6), sd->status.name, NAME_LENGTH); // name don't show in screen.
+ WBUFW(buf,30) = sd->bg_id;
+ clif_send(buf,packet_len(0x2dd), &sd->bl, AREA);
+}
+
+void clif_sendbgemblem_single(int fd, struct map_session_data *sd)
+{
+ nullpo_retv(sd);
+ WFIFOHEAD(fd,32);
+ WFIFOW(fd,0) = 0x2dd;
+ WFIFOL(fd,2) = sd->bl.id;
+ safestrncpy((char*)WFIFOP(fd,6), sd->status.name, NAME_LENGTH);
+ WFIFOW(fd,30) = sd->bg_id;
+ WFIFOSET(fd,packet_len(0x2dd));
+}
+
+
+/// Custom Fonts (ZC_NOTIFY_FONT).
+/// 02ef <account_id>.L <font id>.W
+void clif_font(struct map_session_data *sd)
+{
+#if PACKETVER >= 20080102
+ unsigned char buf[8];
+ nullpo_retv(sd);
+ WBUFW(buf,0) = 0x2ef;
+ WBUFL(buf,2) = sd->bl.id;
+ WBUFW(buf,6) = sd->user_font;
+ clif_send(buf, packet_len(0x2ef), &sd->bl, AREA);
+#endif
+}
+
+
+/*==========================================
+ * Instancing Window
+ *------------------------------------------*/
+int clif_instance(int instance_id, int type, int flag)
+{
+ struct map_session_data *sd;
+ struct party_data *p;
+ unsigned char buf[255];
+
+ if( (p = party_search(instance[instance_id].party_id)) == NULL || (sd = party_getavailablesd(p)) == NULL )
+ return 0;
+
+ switch( type )
+ {
+ case 1:
+ // S 0x2cb <Instance name>.61B <Standby Position>.W
+ // Required to start the instancing information window on Client
+ // This window re-appear each "refresh" of client automatically until type 4 is send to client.
+ WBUFW(buf,0) = 0x02CB;
+ memcpy(WBUFP(buf,2),instance[instance_id].name,INSTANCE_NAME_LENGTH);
+ WBUFW(buf,63) = flag;
+ clif_send(buf,packet_len(0x02CB),&sd->bl,PARTY);
+ break;
+ case 2:
+ // S 0x2cc <Standby Position>.W
+ // To announce Instancing queue creation if no maps available
+ WBUFW(buf,0) = 0x02CC;
+ WBUFW(buf,2) = flag;
+ clif_send(buf,packet_len(0x02CC),&sd->bl,PARTY);
+ break;
+ case 3:
+ case 4:
+ // S 0x2cd <Instance Name>.61B <Instance Remaining Time>.L <Instance Noplayers close time>.L
+ WBUFW(buf,0) = 0x02CD;
+ memcpy(WBUFP(buf,2),instance[instance_id].name,61);
+ if( type == 3 )
+ {
+ WBUFL(buf,63) = (uint32)instance[instance_id].progress_timeout;
+ WBUFL(buf,67) = 0;
+ }
+ else
+ {
+ WBUFL(buf,63) = 0;
+ WBUFL(buf,67) = (uint32)instance[instance_id].idle_timeout;
+ }
+ clif_send(buf,packet_len(0x02CD),&sd->bl,PARTY);
+ break;
+ case 5:
+ // S 0x2ce <Message ID>.L
+ // 0 = Notification (EnterLimitDate update?)
+ // 1 = The Memorial Dungeon expired; it has been destroyed
+ // 2 = The Memorial Dungeon's entry time limit expired; it has been destroyed
+ // 3 = The Memorial Dungeon has been removed.
+ // 4 = Create failure (removes the instance window)
+ WBUFW(buf,0) = 0x02CE;
+ WBUFL(buf,2) = flag;
+ //WBUFL(buf,6) = EnterLimitDate;
+ clif_send(buf,packet_len(0x02CE),&sd->bl,PARTY);
+ break;
+ }
+ return 0;
+}
+
+void clif_instance_join(int fd, int instance_id)
+{
+ if( instance[instance_id].idle_timer != INVALID_TIMER )
+ {
+ WFIFOHEAD(fd,packet_len(0x02CD));
+ WFIFOW(fd,0) = 0x02CD;
+ memcpy(WFIFOP(fd,2),instance[instance_id].name,61);
+ WFIFOL(fd,63) = 0;
+ WFIFOL(fd,67) = (uint32)instance[instance_id].idle_timeout;
+ WFIFOSET(fd,packet_len(0x02CD));
+ }
+ else if( instance[instance_id].progress_timer != INVALID_TIMER )
+ {
+ WFIFOHEAD(fd,packet_len(0x02CD));
+ WFIFOW(fd,0) = 0x02CD;
+ memcpy(WFIFOP(fd,2),instance[instance_id].name,61);
+ WFIFOL(fd,63) = (uint32)instance[instance_id].progress_timeout;;
+ WFIFOL(fd,67) = 0;
+ WFIFOSET(fd,packet_len(0x02CD));
+ }
+ else
+ {
+ WFIFOHEAD(fd,packet_len(0x02CB));
+ WFIFOW(fd,0) = 0x02CB;
+ memcpy(WFIFOP(fd,2),instance[instance_id].name,61);
+ WFIFOW(fd,63) = 0;
+ WFIFOSET(fd,packet_len(0x02CB));
+ }
+}
+
+void clif_instance_leave(int fd)
+{
+ WFIFOHEAD(fd,packet_len(0x02CE));
+ WFIFOW(fd,0) = 0x02ce;
+ WFIFOL(fd,2) = 4;
+ WFIFOSET(fd,packet_len(0x02CE));
+}
+
+
+/// Notifies clients about item picked up by a party member (ZC_ITEM_PICKUP_PARTY).
+/// 02b8 <account id>.L <name id>.W <identified>.B <damaged>.B <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W <equip location>.W <item type>.B
+void clif_party_show_picker(struct map_session_data * sd, struct item * item_data)
+{
+#if PACKETVER >= 20071002
+ unsigned char buf[22];
+ struct item_data* id = itemdb_search(item_data->nameid);
+
+ WBUFW(buf,0) = 0x2b8;
+ WBUFL(buf,2) = sd->status.account_id;
+ WBUFW(buf,6) = item_data->nameid;
+ WBUFB(buf,8) = item_data->identify;
+ WBUFB(buf,9) = item_data->attribute;
+ WBUFB(buf,10) = item_data->refine;
+ clif_addcards(WBUFP(buf,11), item_data);
+ WBUFW(buf,19) = id->equip; // equip location
+ WBUFB(buf,21) = itemtype(id->type); // item type
+ clif_send(buf, packet_len(0x2b8), &sd->bl, PARTY_SAMEMAP_WOS);
+#endif
+}
+
+
+/// Display gained exp (ZC_NOTIFY_EXP).
+/// 07f6 <account id>.L <amount>.L <var id>.W <exp type>.W
+/// var id:
+/// SP_BASEEXP, SP_JOBEXP
+/// exp type:
+/// 0 = normal exp gain/loss
+/// 1 = quest exp gain/loss
+void clif_displayexp(struct map_session_data *sd, unsigned int exp, char type, bool quest)
+{
+ int fd;
+
+ nullpo_retv(sd);
+
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, packet_len(0x7f6));
+ WFIFOW(fd,0) = 0x7f6;
+ WFIFOL(fd,2) = sd->bl.id;
+ WFIFOL(fd,6) = exp;
+ WFIFOW(fd,10) = type;
+ WFIFOW(fd,12) = quest?1:0;// Normal exp is shown in yellow, quest exp is shown in purple.
+ WFIFOSET(fd,packet_len(0x7f6));
+}
+
+
+/// Displays digital clock digits on top of the screen (ZC_SHOWDIGIT).
+/// type:
+/// 0 = Displays 'value' for 5 seconds.
+/// 1 = Incremental counter (1 tick/second), negated 'value' specifies start value (e.g. using -10 lets the counter start at 10).
+/// 2 = Decremental counter (1 tick/second), negated 'value' specifies start value (does not stop when reaching 0, but overflows).
+/// 3 = Decremental counter (1 tick/second), 'value' specifies start value (stops when reaching 0, displays at most 2 digits).
+/// value:
+/// Except for type 3 it is interpreted as seconds for displaying as DD:HH:MM:SS, HH:MM:SS, MM:SS or SS (leftmost '00' is not displayed).
+void clif_showdigit(struct map_session_data* sd, unsigned char type, int value)
+{
+ WFIFOHEAD(sd->fd, packet_len(0x1b1));
+ WFIFOW(sd->fd,0) = 0x1b1;
+ WFIFOB(sd->fd,2) = type;
+ WFIFOL(sd->fd,3) = value;
+ WFIFOSET(sd->fd, packet_len(0x1b1));
+}
+
+
+/// Notification of the state of client command /effect (CZ_LESSEFFECT).
+/// 021d <state>.L
+/// state:
+/// 0 = Full effects
+/// 1 = Reduced effects
+///
+/// NOTE: The state is used on Aegis for sending skill unit packet
+/// 0x11f (ZC_SKILL_ENTRY) instead of 0x1c9 (ZC_SKILL_ENTRY2)
+/// whenever possible. Due to the way the decision check is
+/// constructed, this state tracking was rendered useless,
+/// as the only skill unit, that is sent with 0x1c9 is
+/// Graffiti.
+void clif_parse_LessEffect(int fd, struct map_session_data* sd)
+{
+ int isLess = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+
+ sd->state.lesseffect = ( isLess != 0 );
+}
+
+/// S 07e4 <length>.w <option>.l <val>.l {<index>.w <amount>.w).4b*
+void clif_parse_ItemListWindowSelected(int fd, struct map_session_data* sd) {
+ int n = (RFIFOW(fd,2)-12) / 4;
+ int type = RFIFOL(fd,4);
+ int flag = RFIFOL(fd,8); // Button clicked: 0 = Cancel, 1 = OK
+ unsigned short* item_list = (unsigned short*)RFIFOP(fd,12);
+
+ if( sd->state.trading || sd->npc_shopid )
+ return;
+
+ if( flag == 0 || n == 0) {
+ clif_menuskill_clear(sd);
+ return; // Canceled by player.
+ }
+
+ if( sd->menuskill_id != SO_EL_ANALYSIS && sd->menuskill_id != GN_CHANGEMATERIAL ) {
+ clif_menuskill_clear(sd);
+ return; // Prevent hacking.
+ }
+
+ switch( type ) {
+ case 0: // Change Material
+ skill_changematerial(sd,n,item_list);
+ break;
+ case 1: // Level 1: Pure to Rough
+ case 2: // Level 2: Rough to Pure
+ skill_elementalanalysis(sd,n,type,item_list);
+ break;
+ }
+ clif_menuskill_clear(sd);
+
+ return;
+}
+
+/*==========================================
+ * Elemental System
+ *==========================================*/
+void clif_elemental_updatestatus(struct map_session_data *sd, int type) {
+ struct elemental_data *ed;
+ struct status_data *status;
+ int fd;
+
+ if( sd == NULL || (ed = sd->ed) == NULL )
+ return;
+
+ fd = sd->fd;
+ status = &ed->battle_status;
+ WFIFOHEAD(fd,8);
+ WFIFOW(fd,0) = 0x81e;
+ WFIFOW(fd,2) = type;
+ switch( type ) {
+ case SP_HP:
+ WFIFOL(fd,4) = status->hp;
+ break;
+ case SP_MAXHP:
+ WFIFOL(fd,4) = status->max_hp;
+ break;
+ case SP_SP:
+ WFIFOL(fd,4) = status->sp;
+ break;
+ case SP_MAXSP:
+ WFIFOL(fd,4) = status->max_sp;
+ break;
+ }
+ WFIFOSET(fd,8);
+}
+
+void clif_elemental_info(struct map_session_data *sd) {
+ int fd;
+ struct elemental_data *ed;
+ struct status_data *status;
+
+ if( sd == NULL || (ed = sd->ed) == NULL )
+ return;
+
+ fd = sd->fd;
+ status = &ed->battle_status;
+
+ WFIFOHEAD(fd,22);
+ WFIFOW(fd, 0) = 0x81d;
+ WFIFOL(fd, 2) = ed->bl.id;
+ WFIFOL(fd, 6) = status->hp;
+ WFIFOL(fd,10) = status->max_hp;
+ WFIFOL(fd,14) = status->sp;
+ WFIFOL(fd,18) = status->max_sp;
+ WFIFOSET(fd,22);
+}
+
+
+/// Buying Store System
+///
+
+/// Opens preparation window for buying store (ZC_OPEN_BUYING_STORE).
+/// 0810 <slots>.B
+void clif_buyingstore_open(struct map_session_data* sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x810));
+ WFIFOW(fd,0) = 0x810;
+ WFIFOB(fd,2) = sd->buyingstore.slots;
+ WFIFOSET(fd,packet_len(0x810));
+}
+
+
+/// Request to create a buying store (CZ_REQ_OPEN_BUYING_STORE).
+/// 0811 <packet len>.W <limit zeny>.L <result>.B <store name>.80B { <name id>.W <amount>.W <price>.L }*
+/// result:
+/// 0 = cancel
+/// 1 = open
+static void clif_parse_ReqOpenBuyingStore(int fd, struct map_session_data* sd)
+{
+ const unsigned int blocksize = 8;
+ uint8* itemlist;
+ char storename[MESSAGE_SIZE];
+ unsigned char result;
+ int zenylimit;
+ unsigned int count, packet_len;
+ struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+
+ packet_len = RFIFOW(fd,info->pos[0]);
+
+ // TODO: Make this check global for all variable length packets.
+ if( packet_len < 89 )
+ {// minimum packet length
+ ShowError("clif_parse_ReqOpenBuyingStore: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 89, packet_len, sd->bl.id);
+ return;
+ }
+
+ zenylimit = RFIFOL(fd,info->pos[1]);
+ result = RFIFOL(fd,info->pos[2]);
+ safestrncpy(storename, (const char*)RFIFOP(fd,info->pos[3]), sizeof(storename));
+ itemlist = RFIFOP(fd,info->pos[4]);
+
+ // so that buyingstore_create knows, how many elements it has access to
+ packet_len-= info->pos[4];
+
+ if( packet_len%blocksize )
+ {
+ ShowError("clif_parse_ReqOpenBuyingStore: Unexpected item list size %u (account_id=%d, block size=%u)\n", packet_len, sd->bl.id, blocksize);
+ return;
+ }
+ count = packet_len/blocksize;
+
+ buyingstore_create(sd, zenylimit, result, storename, itemlist, count);
+}
+
+
+/// Notification, that the requested buying store could not be created (ZC_FAILED_OPEN_BUYING_STORE_TO_BUYER).
+/// 0812 <result>.W <total weight>.L
+/// result:
+/// 1 = "Failed to open buying store." (0x6cd, MSI_BUYINGSTORE_OPEN_FAILED)
+/// 2 = "Total amount of then possessed items exceeds the weight limit by <weight/10-maxweight*90%>. Please re-enter." (0x6ce, MSI_BUYINGSTORE_OVERWEIGHT)
+/// 8 = "No sale (purchase) information available." (0x705)
+/// ? = nothing
+void clif_buyingstore_open_failed(struct map_session_data* sd, unsigned short result, unsigned int weight)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x812));
+ WFIFOW(fd,0) = 0x812;
+ WFIFOW(fd,2) = result;
+ WFIFOL(fd,4) = weight;
+ WFIFOSET(fd,packet_len(0x812));
+}
+
+
+/// Notification, that the requested buying store was created (ZC_MYITEMLIST_BUYING_STORE).
+/// 0813 <packet len>.W <account id>.L <limit zeny>.L { <price>.L <count>.W <type>.B <name id>.W }*
+void clif_buyingstore_myitemlist(struct map_session_data* sd)
+{
+ int fd = sd->fd;
+ unsigned int i;
+
+ WFIFOHEAD(fd,12+sd->buyingstore.slots*9);
+ WFIFOW(fd,0) = 0x813;
+ WFIFOW(fd,2) = 12+sd->buyingstore.slots*9;
+ WFIFOL(fd,4) = sd->bl.id;
+ WFIFOL(fd,8) = sd->buyingstore.zenylimit;
+
+ for( i = 0; i < sd->buyingstore.slots; i++ )
+ {
+ WFIFOL(fd,12+i*9) = sd->buyingstore.items[i].price;
+ WFIFOW(fd,16+i*9) = sd->buyingstore.items[i].amount;
+ WFIFOB(fd,18+i*9) = itemtype(itemdb_type(sd->buyingstore.items[i].nameid));
+ WFIFOW(fd,19+i*9) = sd->buyingstore.items[i].nameid;
+ }
+
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Notifies clients in area of a buying store (ZC_BUYING_STORE_ENTRY).
+/// 0814 <account id>.L <store name>.80B
+void clif_buyingstore_entry(struct map_session_data* sd)
+{
+ uint8 buf[86];
+
+ WBUFW(buf,0) = 0x814;
+ WBUFL(buf,2) = sd->bl.id;
+ memcpy(WBUFP(buf,6), sd->message, MESSAGE_SIZE);
+
+ clif_send(buf, packet_len(0x814), &sd->bl, AREA_WOS);
+}
+void clif_buyingstore_entry_single(struct map_session_data* sd, struct map_session_data* pl_sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x814));
+ WFIFOW(fd,0) = 0x814;
+ WFIFOL(fd,2) = pl_sd->bl.id;
+ memcpy(WFIFOP(fd,6), pl_sd->message, MESSAGE_SIZE);
+ WFIFOSET(fd,packet_len(0x814));
+}
+
+
+/// Request to close own buying store (CZ_REQ_CLOSE_BUYING_STORE).
+/// 0815
+static void clif_parse_ReqCloseBuyingStore(int fd, struct map_session_data* sd)
+{
+ buyingstore_close(sd);
+}
+
+
+/// Notifies clients in area that a buying store was closed (ZC_DISAPPEAR_BUYING_STORE_ENTRY).
+/// 0816 <account id>.L
+void clif_buyingstore_disappear_entry(struct map_session_data* sd)
+{
+ uint8 buf[6];
+
+ WBUFW(buf,0) = 0x816;
+ WBUFL(buf,2) = sd->bl.id;
+
+ clif_send(buf, packet_len(0x816), &sd->bl, AREA_WOS);
+}
+void clif_buyingstore_disappear_entry_single(struct map_session_data* sd, struct map_session_data* pl_sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x816));
+ WFIFOW(fd,0) = 0x816;
+ WFIFOL(fd,2) = pl_sd->bl.id;
+ WFIFOSET(fd,packet_len(0x816));
+}
+
+
+/// Request to open someone else's buying store (CZ_REQ_CLICK_TO_BUYING_STORE).
+/// 0817 <account id>.L
+static void clif_parse_ReqClickBuyingStore(int fd, struct map_session_data* sd)
+{
+ int account_id;
+
+ account_id = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
+
+ buyingstore_open(sd, account_id);
+}
+
+
+/// Sends buying store item list (ZC_ACK_ITEMLIST_BUYING_STORE).
+/// 0818 <packet len>.W <account id>.L <store id>.L <limit zeny>.L { <price>.L <amount>.W <type>.B <name id>.W }*
+void clif_buyingstore_itemlist(struct map_session_data* sd, struct map_session_data* pl_sd)
+{
+ int fd = sd->fd;
+ unsigned int i;
+
+ WFIFOHEAD(fd,16+pl_sd->buyingstore.slots*9);
+ WFIFOW(fd,0) = 0x818;
+ WFIFOW(fd,2) = 16+pl_sd->buyingstore.slots*9;
+ WFIFOL(fd,4) = pl_sd->bl.id;
+ WFIFOL(fd,8) = pl_sd->buyer_id;
+ WFIFOL(fd,12) = pl_sd->buyingstore.zenylimit;
+
+ for( i = 0; i < pl_sd->buyingstore.slots; i++ )
+ {
+ WFIFOL(fd,16+i*9) = pl_sd->buyingstore.items[i].price;
+ WFIFOW(fd,20+i*9) = pl_sd->buyingstore.items[i].amount; // TODO: Figure out, if no longer needed items (amount == 0) are listed on official.
+ WFIFOB(fd,22+i*9) = itemtype(itemdb_type(pl_sd->buyingstore.items[i].nameid));
+ WFIFOW(fd,23+i*9) = pl_sd->buyingstore.items[i].nameid;
+ }
+
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Request to sell items to a buying store (CZ_REQ_TRADE_BUYING_STORE).
+/// 0819 <packet len>.W <account id>.L <store id>.L { <index>.W <name id>.W <amount>.W }*
+static void clif_parse_ReqTradeBuyingStore(int fd, struct map_session_data* sd)
+{
+ const unsigned int blocksize = 6;
+ uint8* itemlist;
+ int account_id;
+ unsigned int count, packet_len, buyer_id;
+ struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+
+ packet_len = RFIFOW(fd,info->pos[0]);
+
+ if( packet_len < 12 )
+ {// minimum packet length
+ ShowError("clif_parse_ReqTradeBuyingStore: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 12, packet_len, sd->bl.id);
+ return;
+ }
+
+ account_id = RFIFOL(fd,info->pos[1]);
+ buyer_id = RFIFOL(fd,info->pos[2]);
+ itemlist = RFIFOP(fd,info->pos[3]);
+
+ // so that buyingstore_trade knows, how many elements it has access to
+ packet_len-= info->pos[3];
+
+ if( packet_len%blocksize )
+ {
+ ShowError("clif_parse_ReqTradeBuyingStore: Unexpected item list size %u (account_id=%d, buyer_id=%d, block size=%u)\n", packet_len, sd->bl.id, account_id, blocksize);
+ return;
+ }
+ count = packet_len/blocksize;
+
+ buyingstore_trade(sd, account_id, buyer_id, itemlist, count);
+}
+
+
+/// Notifies the buyer, that the buying store has been closed due to a post-trade condition (ZC_FAILED_TRADE_BUYING_STORE_TO_BUYER).
+/// 081a <result>.W
+/// result:
+/// 3 = "All items within the buy limit were purchased." (0x6cf, MSI_BUYINGSTORE_TRADE_OVERLIMITZENY)
+/// 4 = "All items were purchased." (0x6d0, MSI_BUYINGSTORE_TRADE_BUYCOMPLETE)
+/// ? = nothing
+void clif_buyingstore_trade_failed_buyer(struct map_session_data* sd, short result)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x81a));
+ WFIFOW(fd,0) = 0x81a;
+ WFIFOW(fd,2) = result;
+ WFIFOSET(fd,packet_len(0x81a));
+}
+
+
+/// Updates the zeny limit and an item in the buying store item list (ZC_UPDATE_ITEM_FROM_BUYING_STORE).
+/// 081b <name id>.W <amount>.W <limit zeny>.L
+void clif_buyingstore_update_item(struct map_session_data* sd, unsigned short nameid, unsigned short amount)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x81b));
+ WFIFOW(fd,0) = 0x81b;
+ WFIFOW(fd,2) = nameid;
+ WFIFOW(fd,4) = amount; // amount of nameid received
+ WFIFOL(fd,6) = sd->buyingstore.zenylimit;
+ WFIFOSET(fd,packet_len(0x81b));
+}
+
+
+/// Deletes item from inventory, that was sold to a buying store (ZC_ITEM_DELETE_BUYING_STORE).
+/// 081c <index>.W <amount>.W <price>.L
+/// message:
+/// "%s (%d) were sold at %dz." (0x6d2, MSI_BUYINGSTORE_TRADE_SELLCOMPLETE)
+///
+/// NOTE: This function has to be called _instead_ of clif_delitem/clif_dropitem.
+void clif_buyingstore_delete_item(struct map_session_data* sd, short index, unsigned short amount, int price)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x81c));
+ WFIFOW(fd,0) = 0x81c;
+ WFIFOW(fd,2) = index+2;
+ WFIFOW(fd,4) = amount;
+ WFIFOL(fd,6) = price; // price per item, client calculates total Zeny by itself
+ WFIFOSET(fd,packet_len(0x81c));
+}
+
+
+/// Notifies the seller, that a buying store trade failed (ZC_FAILED_TRADE_BUYING_STORE_TO_SELLER).
+/// 0824 <result>.W <name id>.W
+/// result:
+/// 5 = "The deal has failed." (0x39, MSI_DEAL_FAIL)
+/// 6 = "The trade failed, because the entered amount of item %s is higher, than the buyer is willing to buy." (0x6d3, MSI_BUYINGSTORE_TRADE_OVERCOUNT)
+/// 7 = "The trade failed, because the buyer is lacking required balance." (0x6d1, MSI_BUYINGSTORE_TRADE_LACKBUYERZENY)
+/// ? = nothing
+void clif_buyingstore_trade_failed_seller(struct map_session_data* sd, short result, unsigned short nameid)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x824));
+ WFIFOW(fd,0) = 0x824;
+ WFIFOW(fd,2) = result;
+ WFIFOW(fd,4) = nameid;
+ WFIFOSET(fd,packet_len(0x824));
+}
+
+
+/// Search Store Info System
+///
+
+/// Request to search for stores (CZ_SEARCH_STORE_INFO).
+/// 0835 <packet len>.W <type>.B <max price>.L <min price>.L <name id count>.B <card count>.B { <name id>.W }* { <card>.W }*
+/// type:
+/// 0 = Vending
+/// 1 = Buying Store
+///
+/// NOTE: The client determines the item ids by specifying a name and optionally,
+/// amount of card slots. If the client does not know about the item it
+/// cannot be searched.
+static void clif_parse_SearchStoreInfo(int fd, struct map_session_data* sd)
+{
+ const unsigned int blocksize = 2;
+ const uint8* itemlist;
+ const uint8* cardlist;
+ unsigned char type;
+ unsigned int min_price, max_price, packet_len, count, item_count, card_count;
+ struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+
+ packet_len = RFIFOW(fd,info->pos[0]);
+
+ if( packet_len < 15 )
+ {// minimum packet length
+ ShowError("clif_parse_SearchStoreInfo: Malformed packet (expected length=%u, length=%u, account_id=%d).\n", 15, packet_len, sd->bl.id);
+ return;
+ }
+
+ type = RFIFOB(fd,info->pos[1]);
+ max_price = RFIFOL(fd,info->pos[2]);
+ min_price = RFIFOL(fd,info->pos[3]);
+ item_count = RFIFOB(fd,info->pos[4]);
+ card_count = RFIFOB(fd,info->pos[5]);
+ itemlist = RFIFOP(fd,info->pos[6]);
+ cardlist = RFIFOP(fd,info->pos[6]+blocksize*item_count);
+
+ // check, if there is enough data for the claimed count of items
+ packet_len-= info->pos[6];
+
+ if( packet_len%blocksize )
+ {
+ ShowError("clif_parse_SearchStoreInfo: Unexpected item list size %u (account_id=%d, block size=%u)\n", packet_len, sd->bl.id, blocksize);
+ return;
+ }
+ count = packet_len/blocksize;
+
+ if( count < item_count+card_count )
+ {
+ ShowError("clif_parse_SearchStoreInfo: Malformed packet (expected count=%u, count=%u, account_id=%d).\n", item_count+card_count, count, sd->bl.id);
+ return;
+ }
+
+ searchstore_query(sd, type, min_price, max_price, (const unsigned short*)itemlist, item_count, (const unsigned short*)cardlist, card_count);
+}
+
+
+/// Results for a store search request (ZC_SEARCH_STORE_INFO_ACK).
+/// 0836 <packet len>.W <is first page>.B <is next page>.B <remaining uses>.B { <store id>.L <account id>.L <shop name>.80B <nameid>.W <item type>.B <price>.L <amount>.W <refine>.B <card1>.W <card2>.W <card3>.W <card4>.W }*
+/// is first page:
+/// 0 = appends to existing results
+/// 1 = clears previous results before displaying this result set
+/// is next page:
+/// 0 = no "next" label
+/// 1 = "next" label to retrieve more results
+void clif_search_store_info_ack(struct map_session_data* sd)
+{
+ const unsigned int blocksize = MESSAGE_SIZE+26;
+ int fd = sd->fd;
+ unsigned int i, start, end;
+
+ start = sd->searchstore.pages*SEARCHSTORE_RESULTS_PER_PAGE;
+ end = min(sd->searchstore.count, start+SEARCHSTORE_RESULTS_PER_PAGE);
+
+ WFIFOHEAD(fd,7+(end-start)*blocksize);
+ WFIFOW(fd,0) = 0x836;
+ WFIFOW(fd,2) = 7+(end-start)*blocksize;
+ WFIFOB(fd,4) = !sd->searchstore.pages;
+ WFIFOB(fd,5) = searchstore_querynext(sd);
+ WFIFOB(fd,6) = (unsigned char)min(sd->searchstore.uses, UINT8_MAX);
+
+ for( i = start; i < end; i++ )
+ {
+ struct s_search_store_info_item* ssitem = &sd->searchstore.items[i];
+ struct item it;
+
+ WFIFOL(fd,i*blocksize+ 7) = ssitem->store_id;
+ WFIFOL(fd,i*blocksize+11) = ssitem->account_id;
+ memcpy(WFIFOP(fd,i*blocksize+15), ssitem->store_name, MESSAGE_SIZE);
+ WFIFOW(fd,i*blocksize+15+MESSAGE_SIZE) = ssitem->nameid;
+ WFIFOB(fd,i*blocksize+17+MESSAGE_SIZE) = itemtype(itemdb_type(ssitem->nameid));
+ WFIFOL(fd,i*blocksize+18+MESSAGE_SIZE) = ssitem->price;
+ WFIFOW(fd,i*blocksize+22+MESSAGE_SIZE) = ssitem->amount;
+ WFIFOB(fd,i*blocksize+24+MESSAGE_SIZE) = ssitem->refine;
+
+ // make-up an item for clif_addcards
+ memset(&it, 0, sizeof(it));
+ memcpy(&it.card, &ssitem->card, sizeof(it.card));
+ it.nameid = ssitem->nameid;
+ it.amount = ssitem->amount;
+
+ clif_addcards(WFIFOP(fd,i*blocksize+25+MESSAGE_SIZE), &it);
+ }
+
+ WFIFOSET(fd,WFIFOW(fd,2));
+}
+
+
+/// Notification of failure when searching for stores (ZC_SEARCH_STORE_INFO_FAILED).
+/// 0837 <reason>.B
+/// reason:
+/// 0 = "No matching stores were found." (0x70b)
+/// 1 = "There are too many results. Please enter more detailed search term." (0x6f8)
+/// 2 = "You cannot search anymore." (0x706)
+/// 3 = "You cannot search yet." (0x708)
+/// 4 = "No sale (purchase) information available." (0x705)
+void clif_search_store_info_failed(struct map_session_data* sd, unsigned char reason)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x837));
+ WFIFOW(fd,0) = 0x837;
+ WFIFOB(fd,2) = reason;
+ WFIFOSET(fd,packet_len(0x837));
+}
+
+
+/// Request to display next page of results (CZ_SEARCH_STORE_INFO_NEXT_PAGE).
+/// 0838
+static void clif_parse_SearchStoreInfoNextPage(int fd, struct map_session_data* sd)
+{
+ searchstore_next(sd);
+}
+
+
+/// Opens the search store window (ZC_OPEN_SEARCH_STORE_INFO).
+/// 083a <type>.W <remaining uses>.B
+/// type:
+/// 0 = Search Stores
+/// 1 = Search Stores (Cash), asks for confirmation, when clicking a store
+void clif_open_search_store_info(struct map_session_data* sd)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x83a));
+ WFIFOW(fd,0) = 0x83a;
+ WFIFOW(fd,2) = sd->searchstore.effect;
+#if PACKETVER > 20100701
+ WFIFOB(fd,4) = (unsigned char)min(sd->searchstore.uses, UINT8_MAX);
+#endif
+ WFIFOSET(fd,packet_len(0x83a));
+}
+
+
+/// Request to close the store search window (CZ_CLOSE_SEARCH_STORE_INFO).
+/// 083b
+static void clif_parse_CloseSearchStoreInfo(int fd, struct map_session_data* sd)
+{
+ searchstore_close(sd);
+}
+
+
+/// Request to invoke catalog effect on a store from search results (CZ_SSILIST_ITEM_CLICK).
+/// 083c <account id>.L <store id>.L <nameid>.W
+static void clif_parse_SearchStoreInfoListItemClick(int fd, struct map_session_data* sd)
+{
+ unsigned short nameid;
+ int account_id, store_id;
+ struct s_packet_db* info = &packet_db[sd->packet_ver][RFIFOW(fd,0)];
+
+ account_id = RFIFOL(fd,info->pos[0]);
+ store_id = RFIFOL(fd,info->pos[1]);
+ nameid = RFIFOW(fd,info->pos[2]);
+
+ searchstore_click(sd, account_id, store_id, nameid);
+}
+
+
+/// Notification of the store position on current map (ZC_SSILIST_ITEM_CLICK_ACK).
+/// 083d <xPos>.W <yPos>.W
+void clif_search_store_info_click_ack(struct map_session_data* sd, short x, short y)
+{
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x83d));
+ WFIFOW(fd,0) = 0x83d;
+ WFIFOW(fd,2) = x;
+ WFIFOW(fd,4) = y;
+ WFIFOSET(fd,packet_len(0x83d));
+}
+
+
+/// Parse function for packet debugging.
+void clif_parse_debug(int fd,struct map_session_data *sd)
+{
+ int cmd, packet_len;
+
+ // clif_parse ensures, that there is at least 2 bytes of data
+ cmd = RFIFOW(fd,0);
+
+ if( sd )
+ {
+ packet_len = packet_db[sd->packet_ver][cmd].len;
+
+ if( packet_len == 0 )
+ {// unknown
+ packet_len = RFIFOREST(fd);
+ }
+ else if( packet_len == -1 )
+ {// variable length
+ packet_len = RFIFOW(fd,2); // clif_parse ensures, that this amount of data is already received
+ }
+ ShowDebug("Packet debug of 0x%04X (length %d), %s session #%d, %d/%d (AID/CID)\n", cmd, packet_len, sd->state.active ? "authed" : "unauthed", fd, sd->status.account_id, sd->status.char_id);
+ }
+ else
+ {
+ packet_len = RFIFOREST(fd);
+ ShowDebug("Packet debug of 0x%04X (length %d), session #%d\n", cmd, packet_len, fd);
+ }
+
+ ShowDump(RFIFOP(fd,0), packet_len);
+}
+/*==========================================
+ * Server tells client to display a window similar to Magnifier (item) one
+ * Server populates the window with avilable elemental converter options according to player's inventory
+ *------------------------------------------*/
+int clif_elementalconverter_list(struct map_session_data *sd) {
+ int i,c,view,fd;
+
+ nullpo_ret(sd);
+
+
+/// Main client packet processing function
+ fd=sd->fd;
+ WFIFOHEAD(fd, MAX_SKILL_PRODUCE_DB *2+4);
+ WFIFOW(fd, 0)=0x1ad;
+
+ for(i=0,c=0;i<MAX_SKILL_PRODUCE_DB;i++){
+ if( skill_can_produce_mix(sd,skill_produce_db[i].nameid,23, 1) ){
+ if((view = itemdb_viewid(skill_produce_db[i].nameid)) > 0)
+ WFIFOW(fd,c*2+ 4)= view;
+ else
+ WFIFOW(fd,c*2+ 4)= skill_produce_db[i].nameid;
+ c++;
+ }
+ }
+ WFIFOW(fd,2) = c*2+4;
+ WFIFOSET(fd, WFIFOW(fd,2));
+ if (c > 0) {
+ sd->menuskill_id = SA_CREATECON;
+ sd->menuskill_val = c;
+ }
+
+ return 0;
+}
+/**
+ * Rune Knight
+ **/
+void clif_millenniumshield(struct map_session_data *sd, short shields ) {
+#if PACKETVER >= 20081217
+ unsigned char buf[10];
+
+ WBUFW(buf,0) = 0x440;
+ WBUFL(buf,2) = sd->bl.id;
+ WBUFW(buf,6) = shields;
+ WBUFW(buf,8) = 0;
+ clif_send(buf,packet_len(0x440),&sd->bl,AREA);
+#endif
+}
+/**
+ * Warlock
+ **/
+/*==========================================
+ * Spellbook list [LimitLine/3CeAM]
+ *------------------------------------------*/
+int clif_spellbook_list(struct map_session_data *sd)
+{
+ int i, c;
+ int fd;
+
+ nullpo_ret(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, 8 * 8 + 8);
+ WFIFOW(fd,0) = 0x1ad;
+
+ for( i = 0, c = 0; i < MAX_INVENTORY; i ++ )
+ {
+ if( itemdb_is_spellbook(sd->status.inventory[i].nameid) )
+ {
+ WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid;
+ c ++;
+ }
+ }
+
+ if( c > 0 )
+ {
+ WFIFOW(fd,2) = c * 2 + 4;
+ WFIFOSET(fd, WFIFOW(fd, 2));
+ sd->menuskill_id = WL_READING_SB;
+ sd->menuskill_val = c;
+ }
+ else{
+ status_change_end(&sd->bl,SC_STOP,INVALID_TIMER);
+ clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK, 0);
+ }
+
+ return 1;
+}
+/**
+ * Mechanic
+ **/
+/*==========================================
+ * Magic Decoy Material List
+ *------------------------------------------*/
+int clif_magicdecoy_list(struct map_session_data *sd, uint16 skill_lv, short x, short y) {
+ int i, c;
+ int fd;
+
+ nullpo_ret(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, 8 * 8 + 8);
+ WFIFOW(fd,0) = 0x1ad; // This is the official packet. [pakpil]
+
+ for( i = 0, c = 0; i < MAX_INVENTORY; i ++ ) {
+ if( itemdb_is_element(sd->status.inventory[i].nameid) ) {
+ WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid;
+ c ++;
+ }
+ }
+ if( c > 0 ) {
+ sd->menuskill_id = NC_MAGICDECOY;
+ sd->menuskill_val = skill_lv;
+ sd->sc.comet_x = x;
+ sd->sc.comet_y = y;
+ WFIFOW(fd,2) = c * 2 + 4;
+ WFIFOSET(fd, WFIFOW(fd, 2));
+ } else {
+ clif_skill_fail(sd,NC_MAGICDECOY,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+
+ return 1;
+}
+/**
+ * Guilotine Cross
+ **/
+/*==========================================
+ * Guillotine Cross Poisons List
+ *------------------------------------------*/
+int clif_poison_list(struct map_session_data *sd, uint16 skill_lv) {
+ int i, c;
+ int fd;
+
+ nullpo_ret(sd);
+
+ fd = sd->fd;
+ WFIFOHEAD(fd, 8 * 8 + 8);
+ WFIFOW(fd,0) = 0x1ad; // This is the official packet. [pakpil]
+
+ for( i = 0, c = 0; i < MAX_INVENTORY; i ++ ) {
+ if( itemdb_is_poison(sd->status.inventory[i].nameid) ) {
+ WFIFOW(fd, c * 2 + 4) = sd->status.inventory[i].nameid;
+ c ++;
+ }
+ }
+ if( c > 0 ) {
+ sd->menuskill_id = GC_POISONINGWEAPON;
+ sd->menuskill_val = skill_lv;
+ WFIFOW(fd,2) = c * 2 + 4;
+ WFIFOSET(fd, WFIFOW(fd, 2));
+ } else {
+ clif_skill_fail(sd,GC_POISONINGWEAPON,USESKILL_FAIL_GUILLONTINE_POISON,0);
+ return 0;
+ }
+
+ return 1;
+}
+int clif_autoshadowspell_list(struct map_session_data *sd) {
+ int fd, i, c;
+ nullpo_ret(sd);
+ fd = sd->fd;
+ if( !fd ) return 0;
+
+ if( sd->menuskill_id == SC_AUTOSHADOWSPELL )
+ return 0;
+
+ WFIFOHEAD(fd, 2 * 6 + 4);
+ WFIFOW(fd,0) = 0x442;
+ for( i = 0, c = 0; i < MAX_SKILL; i++ )
+ if( sd->status.skill[i].flag == SKILL_FLAG_PLAGIARIZED && sd->status.skill[i].id > 0 &&
+ sd->status.skill[i].id < GS_GLITTERING && skill_get_type(sd->status.skill[i].id) == BF_MAGIC )
+ { // Can't auto cast both Extended class and 3rd class skills.
+ WFIFOW(fd,8+c*2) = sd->status.skill[i].id;
+ c++;
+ }
+
+ if( c > 0 ) {
+ WFIFOW(fd,2) = 8 + c * 2;
+ WFIFOL(fd,4) = c;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ sd->menuskill_id = SC_AUTOSHADOWSPELL;
+ sd->menuskill_val = c;
+ } else {
+ status_change_end(&sd->bl,SC_STOP,INVALID_TIMER);
+ clif_skill_fail(sd,SC_AUTOSHADOWSPELL,USESKILL_FAIL_IMITATION_SKILL_NONE,0);
+ }
+
+ return 1;
+}
+/*===========================================
+ * Skill list for Four Elemental Analysis
+ * and Change Material skills.
+ *------------------------------------------*/
+int clif_skill_itemlistwindow( struct map_session_data *sd, uint16 skill_id, uint16 skill_lv )
+{
+#if PACKETVER >= 20090922
+ int fd;
+
+ nullpo_ret(sd);
+
+ sd->menuskill_id = skill_id; // To prevent hacking.
+ sd->menuskill_val = skill_lv;
+
+ if( skill_id == GN_CHANGEMATERIAL )
+ skill_lv = 0; // Changematerial
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x7e3));
+ WFIFOW(fd,0) = 0x7e3;
+ WFIFOL(fd,2) = skill_lv;
+ WFIFOL(fd,4) = 0;
+ WFIFOSET(fd,packet_len(0x7e3));
+
+#endif
+
+ return 1;
+
+}
+/**
+ * Sends a new status without a tick (currently used by the new mounts)
+ **/
+int clif_status_load_notick(struct block_list *bl,int type,int flag,int val1, int val2, int val3) {
+ unsigned char buf[32];
+
+ nullpo_ret(bl);
+
+ WBUFW(buf,0)=0x043f;
+ WBUFW(buf,2)=type;
+ WBUFL(buf,4)=bl->id;
+ WBUFB(buf,8)=flag;
+ WBUFL(buf,9) = 0;
+ WBUFL(buf,13) = val1;
+ WBUFL(buf,17) = val2;
+ WBUFL(buf,21) = val3;
+
+ clif_send(buf,packet_len(0x043f),bl,AREA);
+ return 0;
+}
+//Notifies FD of ID's type
+int clif_status_load_single(int fd, int id,int type,int flag,int val1, int val2, int val3) {
+ WFIFOHEAD(fd, packet_len(0x043f));
+ WFIFOW(fd,0)=0x043f;
+ WFIFOW(fd,2)=type;
+ WFIFOL(fd,4)=id;
+ WFIFOB(fd,8)=flag;
+ WFIFOL(fd,9) = 0;
+ WFIFOL(fd,13) = val1;
+ WFIFOL(fd,17) = val2;
+ WFIFOL(fd,21) = val3;
+ WFIFOSET(fd, packet_len(0x043f));
+ return 0;
+}
+// msgstringtable.txt
+// 0x291 <line>.W
+void clif_msgtable(int fd, int line) {
+ WFIFOHEAD(fd, packet_len(0x291));
+ WFIFOW(fd, 0) = 0x291;
+ WFIFOW(fd, 2) = line;
+ WFIFOSET(fd, packet_len(0x291));
+}
+
+// msgstringtable.txt
+// 0x7e2 <line>.W <value>.L
+void clif_msgtable_num(int fd, int line, int num) {
+#if PACKETVER >= 20090805
+ WFIFOHEAD(fd, packet_len(0x7e2));
+ WFIFOW(fd, 0) = 0x7e2;
+ WFIFOW(fd, 2) = line;
+ WFIFOL(fd, 4) = num;
+ WFIFOSET(fd, packet_len(0x7e2));
+#endif
+}
+/*==========================================
+ * used by SC_AUTOSHADOWSPELL
+ * RFIFOL(fd,2) - flag (currently not used)
+ *------------------------------------------*/
+void clif_parse_SkillSelectMenu(int fd, struct map_session_data *sd) {
+
+ if( sd->menuskill_id != SC_AUTOSHADOWSPELL )
+ return;
+
+ if( pc_istrading(sd) ) {
+ clif_skill_fail(sd,sd->ud.skill_id,0,0);
+ clif_menuskill_clear(sd);
+ return;
+ }
+
+ skill_select_menu(sd,RFIFOW(fd,6));
+
+ clif_menuskill_clear(sd);
+}
+/*==========================================
+ * Kagerou/Oboro amulet spirit
+ *------------------------------------------*/
+void clif_talisman(struct map_session_data *sd,short type)
+{
+ unsigned char buf[10];
+
+ nullpo_retv(sd);
+
+ WBUFW(buf,0)=0x08cf;
+ WBUFL(buf,2)=sd->bl.id;
+ WBUFW(buf,6)=type;
+ WBUFW(buf,8)=sd->talisman[type];
+ clif_send(buf,packet_len(0x08cf),&sd->bl,AREA);
+}
+/// Move Item from or to Personal Tab (CZ_WHATSOEVER) [FE]
+/// 0907 <index>.W
+///
+/// R 0908 <index>.w <type>.b
+/// type:
+/// 0 = move item to personal tab
+/// 1 = move item to normal tab
+void clif_parse_MoveItem(int fd, struct map_session_data *sd) {
+#if PACKETVER >= 20111122
+ int index;
+
+ /* can't move while dead. */
+ if(pc_isdead(sd)) {
+ return;
+ }
+
+ index = RFIFOW(fd,2)-2;
+
+ if (index < 0 || index >= MAX_INVENTORY)
+ return;
+
+ if ( sd->status.inventory[index].favorite && RFIFOB(fd, 4) == 1 )
+ sd->status.inventory[index].favorite = 0;
+ else if( RFIFOB(fd, 4) == 0 )
+ sd->status.inventory[index].favorite = 1;
+ else
+ return;/* nothing to do. */
+
+ clif_favorite_item(sd, index);
+#endif
+}
+
+
+/// Items that are in favorite tab of inventory (ZC_ITEM_FAVORITE).
+/// 0900 <index>.W <favorite>.B
+void clif_favorite_item(struct map_session_data* sd, unsigned short index) {
+ int fd = sd->fd;
+
+ WFIFOHEAD(fd,packet_len(0x908));
+ WFIFOW(fd,0) = 0x908;
+ WFIFOW(fd,2) = index+2;
+ WFIFOL(fd,4) = (sd->status.inventory[index].favorite == 1) ? 0 : 1;
+ WFIFOSET(fd,packet_len(0x908));
+}
+
+void clif_snap( struct block_list *bl, short x, short y ) {
+ unsigned char buf[10];
+
+ WBUFW(buf,0) = 0x8d2;
+ WBUFL(buf,2) = bl->id;
+ WBUFW(buf,6) = x;
+ WBUFW(buf,8) = y;
+
+ clif_send(buf,packet_len(0x8d2),bl,AREA);
+}
+
+void clif_monster_hp_bar( struct mob_data* md, int fd ) {
+#if PACKETVER >= 20120404
+ WFIFOHEAD(fd,packet_len(0x977));
+
+ WFIFOW(fd,0) = 0x977;
+ WFIFOL(fd,2) = md->bl.id;
+ WFIFOL(fd,6) = md->status.hp;
+ WFIFOL(fd,10) = md->status.max_hp;
+
+ WFIFOSET(fd,packet_len(0x977));
+#endif
+}
+
+/*==========================================
+ * Main client packet processing function
+ *------------------------------------------*/
+static int clif_parse(int fd)
+{
+ int cmd, packet_ver, packet_len, err;
+ TBL_PC* sd;
+ int pnum;
+
+ //TODO apply delays or disconnect based on packet throughput [FlavioJS]
+ // Note: "click masters" can do 80+ clicks in 10 seconds
+
+ for( pnum = 0; pnum < 3; ++pnum )// Limit max packets per cycle to 3 (delay packet spammers) [FlavioJS] -- This actually aids packet spammers, but stuff like /str+ gets slow without it [Ai4rei]
+ { // begin main client packet processing loop
+
+ sd = (TBL_PC *)session[fd]->session_data;
+ if (session[fd]->flag.eof) {
+ if (sd) {
+ if (sd->state.autotrade) {
+ //Disassociate character from the socket connection.
+ session[fd]->session_data = NULL;
+ sd->fd = 0;
+ ShowInfo("Character '"CL_WHITE"%s"CL_RESET"' logged off (using @autotrade).\n", sd->status.name);
+ } else
+ if (sd->state.active) {
+ // Player logout display [Valaris]
+ ShowInfo("Character '"CL_WHITE"%s"CL_RESET"' logged off.\n", sd->status.name);
+ clif_quitsave(fd, sd);
+ } else {
+ //Unusual logout (during log on/off/map-changer procedure)
+ ShowInfo("Player AID:%d/CID:%d logged off.\n", sd->status.account_id, sd->status.char_id);
+ map_quit(sd);
+ }
+ } else {
+ ShowInfo("Closed connection from '"CL_WHITE"%s"CL_RESET"'.\n", ip2str(session[fd]->client_addr, NULL));
+ }
+ do_close(fd);
+ return 0;
+ }
+
+ if (RFIFOREST(fd) < 2)
+ return 0;
+
+ cmd = RFIFOW(fd,0);
+
+ // identify client's packet version
+ if (sd) {
+ packet_ver = sd->packet_ver;
+ } else {
+ // check authentification packet to know packet version
+ packet_ver = clif_guess_PacketVer(fd, 0, &err);
+ if( err ) {// failed to identify packet version
+ ShowInfo("clif_parse: Disconnecting session #%d with unknown packet version%s (p:0x%04x,l:%d).\n", fd, (
+ err == 1 ? "" :
+ err == 2 ? ", possibly for having an invalid account_id" :
+ err == 3 ? ", possibly for having an invalid char_id." :
+ /* Uncomment when checks are added in clif_guess_PacketVer. [FlavioJS]
+ err == 4 ? ", possibly for having an invalid login_id1." :
+ err == 5 ? ", possibly for having an invalid client_tick." :
+ */
+ err == 6 ? ", possibly for having an invalid sex." :
+ ". ERROR invalid error code"), cmd, RFIFOREST(fd));
+ WFIFOHEAD(fd,packet_len(0x6a));
+ WFIFOW(fd,0) = 0x6a;
+ WFIFOB(fd,2) = 3; // Rejected from Server
+ WFIFOSET(fd,packet_len(0x6a));
+
+#ifdef DUMP_INVALID_PACKET
+ ShowDump(RFIFOP(fd,0), RFIFOREST(fd));
+#endif
+
+ RFIFOSKIP(fd, RFIFOREST(fd));
+ set_eof(fd);
+ return 0;
+ }
+ }
+
+ // filter out invalid / unsupported packets
+ if (cmd > MAX_PACKET_DB || packet_db[packet_ver][cmd].len == 0) {
+ ShowWarning("clif_parse: Received unsupported packet (packet 0x%04x, %d bytes received), disconnecting session #%d.\n", cmd, RFIFOREST(fd), fd);
+#ifdef DUMP_INVALID_PACKET
+ ShowDump(RFIFOP(fd,0), RFIFOREST(fd));
+#endif
+ set_eof(fd);
+ return 0;
+ }
+
+ // determine real packet length
+ packet_len = packet_db[packet_ver][cmd].len;
+ if (packet_len == -1) { // variable-length packet
+ if (RFIFOREST(fd) < 4)
+ return 0;
+
+ packet_len = RFIFOW(fd,2);
+ if (packet_len < 4 || packet_len > 32768) {
+ ShowWarning("clif_parse: Received packet 0x%04x specifies invalid packet_len (%d), disconnecting session #%d.\n", cmd, packet_len, fd);
+#ifdef DUMP_INVALID_PACKET
+ ShowDump(RFIFOP(fd,0), RFIFOREST(fd));
+#endif
+ set_eof(fd);
+ return 0;
+ }
+ }
+ if ((int)RFIFOREST(fd) < packet_len)
+ return 0; // not enough data received to form the packet
+
+ if( packet_db[packet_ver][cmd].func == clif_parse_debug )
+ packet_db[packet_ver][cmd].func(fd, sd);
+ else if( packet_db[packet_ver][cmd].func != NULL ) {
+ if( !sd && packet_db[packet_ver][cmd].func != clif_parse_WantToConnection )
+ ; //Only valid packet when there is no session
+ else
+ if( sd && sd->bl.prev == NULL && packet_db[packet_ver][cmd].func != clif_parse_LoadEndAck )
+ ; //Only valid packet when player is not on a map
+ else
+ if( sd && session[sd->fd]->flag.eof )
+ ; //No more packets accepted
+ else
+ packet_db[packet_ver][cmd].func(fd, sd);
+ }
+#ifdef DUMP_UNKNOWN_PACKET
+ else {
+ const char* packet_txt = "save/packet.txt";
+ FILE* fp;
+
+ if( ( fp = fopen( packet_txt , "a" ) ) != NULL ) {
+ if( sd ) {
+ fprintf(fp, "Unknown packet 0x%04X (length %d), %s session #%d, %d/%d (AID/CID)\n", cmd, packet_len, sd->state.active ? "authed" : "unauthed", fd, sd->status.account_id, sd->status.char_id);
+ } else {
+ fprintf(fp, "Unknown packet 0x%04X (length %d), session #%d\n", cmd, packet_len, fd);
+ }
+
+ WriteDump(fp, RFIFOP(fd,0), packet_len);
+ fprintf(fp, "\n");
+ fclose(fp);
+ } else {
+ ShowError("Failed to write '%s'.\n", packet_txt);
+
+ // Dump on console instead
+ if( sd ) {
+ ShowDebug("Unknown packet 0x%04X (length %d), %s session #%d, %d/%d (AID/CID)\n", cmd, packet_len, sd->state.active ? "authed" : "unauthed", fd, sd->status.account_id, sd->status.char_id);
+ } else {
+ ShowDebug("Unknown packet 0x%04X (length %d), session #%d\n", cmd, packet_len, fd);
+ }
+
+ ShowDump(RFIFOP(fd,0), packet_len);
+ }
+ }
+#endif
+
+ RFIFOSKIP(fd, packet_len);
+
+ }; // main loop end
+
+ return 0;
+}
+
+/*==========================================
+ * Reads packet_db.txt and setups its array reference
+ *------------------------------------------*/
+static int packetdb_readdb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int cmd,i,j,packet_ver;
+ int max_cmd=-1;
+ int skip_ver = 0;
+ int warned = 0;
+ char *str[64],*p,*str2[64],*p2,w1[64],w2[64];
+ int packet_len_table[MAX_PACKET_DB] = {
+ 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0040
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+#if PACKETVER <= 20081217
+ 0, 0, 0, 0, 55, 17, 3, 37, 46, -1, 23, -1, 3,110, 3, 2,
+#else
+ 0, 0, 0, 0, 55, 17, 3, 37, 46, -1, 23, -1, 3,114, 3, 2,
+#endif
+#if PACKETVER < 2
+ 3, 28, 19, 11, 3, -1, 9, 5, 52, 51, 56, 58, 41, 2, 6, 6,
+#elif PACKETVER < 20071106 // 78-7b Lv99 effect for later Kameshima
+ 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6,
+#elif PACKETVER <= 20081217 // change in 0x78 and 0x7c
+ 3, 28, 19, 11, 3, -1, 9, 5, 55, 53, 58, 60, 42, 2, 6, 6,
+#else
+ 3, 28, 19, 11, 3, -1, 9, 5, 55, 53, 58, 60, 44, 2, 6, 6,
+#endif
+ //#0x0080
+ 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 2, -1, -1, -1, 0, // 0x8b changed to 2 (was 23)
+ 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6,
+#if PACKETVER <= 20100622
+ 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6,
+#else
+ 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 9, 4, 7, 0, -1, 6, // 0xaa changed to 9 (was 7)
+#endif
+ 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3,
+ //#0x00C0
+ 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 3, 2, 27, // 0xcd change to 3 (was 6)
+ 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1,
+ 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2,
+ 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10,
+ //#0x0100
+ 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1,
+ 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16,
+ 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1,
+ 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26,
+ //#0x0140
+ 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6,
+ 110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42,
+ -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14,186,182,
+ 14, 30, 10, 3, -1, 6,106, -1, 4, 5, 4, -1, 6, 7, -1, -1,
+ //#0x0180
+ 6, 3,106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6,
+#if PACKETVER < 1
+ 90, 86, 24, 6, 30,102, 8, 4, 8, 4, 14, 10, -1, 6, 2, 6,
+#else // 196 comodo icon status display for later
+ 90, 86, 24, 6, 30,102, 9, 4, 8, 4, 14, 10, -1, 6, 2, 6,
+#endif
+#if PACKETVER < 20081126
+ 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4,
+#else // 0x1a2 changed (35->37)
+ 3, 3, 37, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4,
+#endif
+ 11, 7, -1, 67, 12, 18,114, 6, 3, 6, 26, 26, 26, 26, 2, 3,
+ //#0x01C0, Set 0x1d5=-1
+ 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 3, 9, 9, 30, 6, 28,
+ 8, 14, 10, 35, 6, -1, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6,
+ 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1,
+ -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10,
+ //#0x0200
+ 26, -1, 26, 10, 18, 26, 11, 34, 14, 36, 10, 0, 0, -1, 32, 10, // 0x20c change to 0 (was 19)
+ 22, 0, 26, 26, 42, 6, 6, 2, 2,282,282, 10, 10, -1, -1, 66,
+#if PACKETVER < 20071106
+ 10, -1, -1, 8, 10, 2,282, 18, 18, 15, 58, 57, 64, 5, 71, 5,
+#else // 0x22c changed
+ 10, -1, -1, 8, 10, 2,282, 18, 18, 15, 58, 57, 65, 5, 71, 5,
+#endif
+ 12, 26, 9, 11, -1, -1, 10, 2,282, 11, 4, 36, 6, -1, 4, 2,
+ //#0x0240
+ -1, -1, -1, -1, -1, 3, 4, 8, -1, 3, 70, 4, 8, 12, 4, 10,
+ 3, 32, -1, 3, 3, 5, 5, 8, 2, 3, -1, 6, 4, 6, 4, 6,
+ 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0280
+#if PACKETVER < 20070711
+ 0, 0, 0, 6, 14, 0, 0, -1, 6, 8, 18, 0, 0, 0, 0, 0,
+#else
+ 0, 0, 0, 6, 14, 0, 0, -1, 10, 12, 18, 0, 0, 0, 0, 0, // 0x288, 0x289 increase by 4 (kafra points)
+#endif
+ 0, 4, 0, 70, 10, 0, 0, 0, 8, 6, 27, 80, 0, -1, 0, 0,
+ 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 85, -1, -1,107, 6, -1, 7, 7, 22,191, 0, 8, 0, 0, 0, 0,
+ //#0x02C0
+ 0, -1, 0, 0, 0, 30, 30, 0, 0, 3, 0, 65, 4, 71, 10, 0,
+ -1, -1, -1, 0, 29, 0, 6, -1, 10, 10, 3, 0, -1, 32, 6, 36,
+ 34, 33, 0, 0, 0, 0, 0, 0, -1, -1, -1, 13, 67, 59, 60, 8,
+ 10, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0300
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0340
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0380
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x03C0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0400
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 25,
+ //#0x0440
+ 10, 4, -1, 0, 0, 0, 14, 0, 0, 0, 6, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0480
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x04C0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0500
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
+ //#0x0540
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0580
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x05C0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0600
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
+ //#0x0640
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0680
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x06C0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0700
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
+ //#0x0740
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0780
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x07C0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+#if PACKETVER < 20090617
+ 6, 2, -1, 4, 4, 4, 4, 8, 8,254, 6, 8, 6, 54, 30, 54,
+#else // 0x7d9 changed
+ 6, 2, -1, 4, 4, 4, 4, 8, 8,268, 6, 8, 6, 54, 30, 54,
+#endif
+ 0, 15, 8, 6, -1, 8, 8, 32, -1, 5, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 14, -1, -1, -1, 8, 25, 0, 0, 26, 0,
+ //#0x0800
+#if PACKETVER < 20091229
+ -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 20,
+#else // for Party booking ( PACKETVER >= 20091229 )
+ -1, -1, 18, 4, 8, 6, 2, 4, 14, 50, 18, 6, 2, 3, 14, 20,
+#endif
+ 3, -1, 8, -1, 86, 2, 6, 6, -1, -1, 4, 10, 10, 0, 0, 0,
+ 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, -1, -1, 3, 2, 66, 5, 2, 12, 6, 0, 0,
+ //#0x0840
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0880
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x08C0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,
+ 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0900
+ 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ //#0x0940
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ };
+ struct {
+ void (*func)(int, struct map_session_data *);
+ char *name;
+ } clif_parse_func[]={
+ {clif_parse_WantToConnection,"wanttoconnection"},
+ {clif_parse_LoadEndAck,"loadendack"},
+ {clif_parse_TickSend,"ticksend"},
+ {clif_parse_WalkToXY,"walktoxy"},
+ {clif_parse_QuitGame,"quitgame"},
+ {clif_parse_GetCharNameRequest,"getcharnamerequest"},
+ {clif_parse_GlobalMessage,"globalmessage"},
+ {clif_parse_MapMove,"mapmove"},
+ {clif_parse_ChangeDir,"changedir"},
+ {clif_parse_Emotion,"emotion"},
+ {clif_parse_HowManyConnections,"howmanyconnections"},
+ {clif_parse_ActionRequest,"actionrequest"},
+ {clif_parse_Restart,"restart"},
+ {clif_parse_WisMessage,"wis"},
+ {clif_parse_Broadcast,"broadcast"},
+ {clif_parse_TakeItem,"takeitem"},
+ {clif_parse_DropItem,"dropitem"},
+ {clif_parse_UseItem,"useitem"},
+ {clif_parse_EquipItem,"equipitem"},
+ {clif_parse_UnequipItem,"unequipitem"},
+ {clif_parse_NpcClicked,"npcclicked"},
+ {clif_parse_NpcBuySellSelected,"npcbuysellselected"},
+ {clif_parse_NpcBuyListSend,"npcbuylistsend"},
+ {clif_parse_NpcSellListSend,"npcselllistsend"},
+ {clif_parse_CreateChatRoom,"createchatroom"},
+ {clif_parse_ChatAddMember,"chataddmember"},
+ {clif_parse_ChatRoomStatusChange,"chatroomstatuschange"},
+ {clif_parse_ChangeChatOwner,"changechatowner"},
+ {clif_parse_KickFromChat,"kickfromchat"},
+ {clif_parse_ChatLeave,"chatleave"},
+ {clif_parse_TradeRequest,"traderequest"},
+ {clif_parse_TradeAck,"tradeack"},
+ {clif_parse_TradeAddItem,"tradeadditem"},
+ {clif_parse_TradeOk,"tradeok"},
+ {clif_parse_TradeCancel,"tradecancel"},
+ {clif_parse_TradeCommit,"tradecommit"},
+ {clif_parse_StopAttack,"stopattack"},
+ {clif_parse_PutItemToCart,"putitemtocart"},
+ {clif_parse_GetItemFromCart,"getitemfromcart"},
+ {clif_parse_RemoveOption,"removeoption"},
+ {clif_parse_ChangeCart,"changecart"},
+ {clif_parse_StatusUp,"statusup"},
+ {clif_parse_SkillUp,"skillup"},
+ {clif_parse_UseSkillToId,"useskilltoid"},
+ {clif_parse_UseSkillToPos,"useskilltopos"},
+ {clif_parse_UseSkillToPosMoreInfo,"useskilltoposinfo"},
+ {clif_parse_UseSkillMap,"useskillmap"},
+ {clif_parse_RequestMemo,"requestmemo"},
+ {clif_parse_ProduceMix,"producemix"},
+ {clif_parse_Cooking,"cooking"},
+ {clif_parse_NpcSelectMenu,"npcselectmenu"},
+ {clif_parse_NpcNextClicked,"npcnextclicked"},
+ {clif_parse_NpcAmountInput,"npcamountinput"},
+ {clif_parse_NpcStringInput,"npcstringinput"},
+ {clif_parse_NpcCloseClicked,"npccloseclicked"},
+ {clif_parse_ItemIdentify,"itemidentify"},
+ {clif_parse_SelectArrow,"selectarrow"},
+ {clif_parse_AutoSpell,"autospell"},
+ {clif_parse_UseCard,"usecard"},
+ {clif_parse_InsertCard,"insertcard"},
+ {clif_parse_RepairItem,"repairitem"},
+ {clif_parse_WeaponRefine,"weaponrefine"},
+ {clif_parse_SolveCharName,"solvecharname"},
+ {clif_parse_ResetChar,"resetchar"},
+ {clif_parse_LocalBroadcast,"localbroadcast"},
+ {clif_parse_MoveToKafra,"movetokafra"},
+ {clif_parse_MoveFromKafra,"movefromkafra"},
+ {clif_parse_MoveToKafraFromCart,"movetokafrafromcart"},
+ {clif_parse_MoveFromKafraToCart,"movefromkafratocart"},
+ {clif_parse_CloseKafra,"closekafra"},
+ {clif_parse_CreateParty,"createparty"},
+ {clif_parse_CreateParty2,"createparty2"},
+ {clif_parse_PartyInvite,"partyinvite"},
+ {clif_parse_PartyInvite2,"partyinvite2"},
+ {clif_parse_ReplyPartyInvite,"replypartyinvite"},
+ {clif_parse_ReplyPartyInvite2,"replypartyinvite2"},
+ {clif_parse_LeaveParty,"leaveparty"},
+ {clif_parse_RemovePartyMember,"removepartymember"},
+ {clif_parse_PartyChangeOption,"partychangeoption"},
+ {clif_parse_PartyMessage,"partymessage"},
+ {clif_parse_PartyChangeLeader,"partychangeleader"},
+ {clif_parse_CloseVending,"closevending"},
+ {clif_parse_VendingListReq,"vendinglistreq"},
+ {clif_parse_PurchaseReq,"purchasereq"},
+ {clif_parse_PurchaseReq2,"purchasereq2"},
+ {clif_parse_OpenVending,"openvending"},
+ {clif_parse_CreateGuild,"createguild"},
+ {clif_parse_GuildCheckMaster,"guildcheckmaster"},
+ {clif_parse_GuildRequestInfo,"guildrequestinfo"},
+ {clif_parse_GuildChangePositionInfo,"guildchangepositioninfo"},
+ {clif_parse_GuildChangeMemberPosition,"guildchangememberposition"},
+ {clif_parse_GuildRequestEmblem,"guildrequestemblem"},
+ {clif_parse_GuildChangeEmblem,"guildchangeemblem"},
+ {clif_parse_GuildChangeNotice,"guildchangenotice"},
+ {clif_parse_GuildInvite,"guildinvite"},
+ {clif_parse_GuildReplyInvite,"guildreplyinvite"},
+ {clif_parse_GuildLeave,"guildleave"},
+ {clif_parse_GuildExpulsion,"guildexpulsion"},
+ {clif_parse_GuildMessage,"guildmessage"},
+ {clif_parse_GuildRequestAlliance,"guildrequestalliance"},
+ {clif_parse_GuildReplyAlliance,"guildreplyalliance"},
+ {clif_parse_GuildDelAlliance,"guilddelalliance"},
+ {clif_parse_GuildOpposition,"guildopposition"},
+ {clif_parse_GuildBreak,"guildbreak"},
+ {clif_parse_PetMenu,"petmenu"},
+ {clif_parse_CatchPet,"catchpet"},
+ {clif_parse_SelectEgg,"selectegg"},
+ {clif_parse_SendEmotion,"sendemotion"},
+ {clif_parse_ChangePetName,"changepetname"},
+
+ {clif_parse_GMKick,"gmkick"},
+ {clif_parse_GMHide,"gmhide"},
+ {clif_parse_GMReqNoChat,"gmreqnochat"},
+ {clif_parse_GMReqAccountName,"gmreqaccname"},
+ {clif_parse_GMKickAll,"killall"},
+ {clif_parse_GMRecall,"recall"},
+ {clif_parse_GMRecall,"summon"},
+ {clif_parse_GM_Monster_Item,"itemmonster"},
+ {clif_parse_GMShift,"remove"},
+ {clif_parse_GMShift,"shift"},
+ {clif_parse_GMChangeMapType,"changemaptype"},
+ {clif_parse_GMRc,"rc"},
+ {clif_parse_GMRecall2,"recall2"},
+ {clif_parse_GMRemove2,"remove2"},
+
+ {clif_parse_NoviceDoriDori,"sndoridori"},
+ {clif_parse_NoviceExplosionSpirits,"snexplosionspirits"},
+ {clif_parse_PMIgnore,"wisexin"},
+ {clif_parse_PMIgnoreList,"wisexlist"},
+ {clif_parse_PMIgnoreAll,"wisall"},
+ {clif_parse_FriendsListAdd,"friendslistadd"},
+ {clif_parse_FriendsListRemove,"friendslistremove"},
+ {clif_parse_FriendsListReply,"friendslistreply"},
+ {clif_parse_Blacksmith,"blacksmith"},
+ {clif_parse_Alchemist,"alchemist"},
+ {clif_parse_Taekwon,"taekwon"},
+ {clif_parse_RankingPk,"rankingpk"},
+ {clif_parse_FeelSaveOk,"feelsaveok"},
+ {clif_parse_debug,"debug"},
+ {clif_parse_ChangeHomunculusName,"changehomunculusname"},
+ {clif_parse_HomMoveToMaster,"hommovetomaster"},
+ {clif_parse_HomMoveTo,"hommoveto"},
+ {clif_parse_HomAttack,"homattack"},
+ {clif_parse_HomMenu,"hommenu"},
+ {clif_parse_StoragePassword,"storagepassword"},
+ {clif_parse_Hotkey,"hotkey"},
+ {clif_parse_AutoRevive,"autorevive"},
+ {clif_parse_Check,"check"},
+ {clif_parse_Adopt_request,"adoptrequest"},
+ {clif_parse_Adopt_reply,"adoptreply"},
+ // MAIL SYSTEM
+ {clif_parse_Mail_refreshinbox,"mailrefresh"},
+ {clif_parse_Mail_read,"mailread"},
+ {clif_parse_Mail_getattach,"mailgetattach"},
+ {clif_parse_Mail_delete,"maildelete"},
+ {clif_parse_Mail_return,"mailreturn"},
+ {clif_parse_Mail_setattach,"mailsetattach"},
+ {clif_parse_Mail_winopen,"mailwinopen"},
+ {clif_parse_Mail_send,"mailsend"},
+ // AUCTION SYSTEM
+ {clif_parse_Auction_search,"auctionsearch"},
+ {clif_parse_Auction_buysell,"auctionbuysell"},
+ {clif_parse_Auction_setitem,"auctionsetitem"},
+ {clif_parse_Auction_cancelreg,"auctioncancelreg"},
+ {clif_parse_Auction_register,"auctionregister"},
+ {clif_parse_Auction_cancel,"auctioncancel"},
+ {clif_parse_Auction_close,"auctionclose"},
+ {clif_parse_Auction_bid,"auctionbid"},
+ // Quest Log System
+ {clif_parse_questStateAck,"queststate"},
+ {clif_parse_cashshop_buy,"cashshopbuy"},
+ {clif_parse_ViewPlayerEquip,"viewplayerequip"},
+ {clif_parse_EquipTick,"equiptickbox"},
+ {clif_parse_BattleChat,"battlechat"},
+ {clif_parse_mercenary_action,"mermenu"},
+ {clif_parse_progressbar,"progressbar"},
+ {clif_parse_SkillSelectMenu,"skillselectmenu"},
+ {clif_parse_ItemListWindowSelected,"itemlistwindowselected"},
+#if PACKETVER >= 20091229
+ {clif_parse_PartyBookingRegisterReq,"bookingregreq"},
+ {clif_parse_PartyBookingSearchReq,"bookingsearchreq"},
+ {clif_parse_PartyBookingUpdateReq,"bookingupdatereq"},
+ {clif_parse_PartyBookingDeleteReq,"bookingdelreq"},
+#endif
+ {clif_parse_PVPInfo,"pvpinfo"},
+ {clif_parse_LessEffect,"lesseffect"},
+ // Buying Store
+ {clif_parse_ReqOpenBuyingStore,"reqopenbuyingstore"},
+ {clif_parse_ReqCloseBuyingStore,"reqclosebuyingstore"},
+ {clif_parse_ReqClickBuyingStore,"reqclickbuyingstore"},
+ {clif_parse_ReqTradeBuyingStore,"reqtradebuyingstore"},
+ // Store Search
+ {clif_parse_SearchStoreInfo,"searchstoreinfo"},
+ {clif_parse_SearchStoreInfoNextPage,"searchstoreinfonextpage"},
+ {clif_parse_CloseSearchStoreInfo,"closesearchstoreinfo"},
+ {clif_parse_SearchStoreInfoListItemClick,"searchstoreinfolistitemclick"},
+ /* */
+ { clif_parse_MoveItem , "moveitem" },
+ {NULL,NULL}
+ };
+
+ // initialize packet_db[SERVER] from hardcoded packet_len_table[] values
+ memset(packet_db,0,sizeof(packet_db));
+ for( i = 0; i < ARRAYLENGTH(packet_len_table); ++i )
+ packet_len(i) = packet_len_table[i];
+
+ sprintf(line, "%s/packet_db.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowFatalError("can't read %s\n", line);
+ exit(EXIT_FAILURE);
+ }
+
+ clif_config.packet_db_ver = MAX_PACKET_VER;
+ packet_ver = MAX_PACKET_VER; // read into packet_db's version by default
+ while( fgets(line, sizeof(line), fp) )
+ {
+ ln++;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ if (sscanf(line,"%256[^:]: %256[^\r\n]",w1,w2) == 2)
+ {
+ if(strcmpi(w1,"packet_ver")==0) {
+ int prev_ver = packet_ver;
+ skip_ver = 0;
+ packet_ver = atoi(w2);
+ if ( packet_ver > MAX_PACKET_VER )
+ { //Check to avoid overflowing. [Skotlex]
+ if( (warned&1) == 0 )
+ ShowWarning("The packet_db table only has support up to version %d.\n", MAX_PACKET_VER);
+ warned &= 1;
+ skip_ver = 1;
+ }
+ else if( packet_ver < 0 )
+ {
+ if( (warned&2) == 0 )
+ ShowWarning("Negative packet versions are not supported.\n");
+ warned &= 2;
+ skip_ver = 1;
+ }
+ else if( packet_ver == SERVER )
+ {
+ if( (warned&4) == 0 )
+ ShowWarning("Packet version %d is reserved for server use only.\n", SERVER);
+ warned &= 4;
+ skip_ver = 1;
+ }
+
+ if( skip_ver )
+ {
+ ShowWarning("Skipping packet version %d.\n", packet_ver);
+ packet_ver = prev_ver;
+ continue;
+ }
+ // copy from previous version into new version and continue
+ // - indicating all following packets should be read into the newer version
+ memcpy(&packet_db[packet_ver], &packet_db[prev_ver], sizeof(packet_db[0]));
+ continue;
+ } else if(strcmpi(w1,"packet_db_ver")==0) {
+ if(strcmpi(w2,"default")==0) //This is the preferred version.
+ clif_config.packet_db_ver = MAX_PACKET_VER;
+ else // to manually set the packet DB version
+ clif_config.packet_db_ver = cap_value(atoi(w2), 0, MAX_PACKET_VER);
+
+ continue;
+ }
+ }
+
+ if( skip_ver != 0 )
+ continue; // Skipping current packet version
+
+ memset(str,0,sizeof(str));
+ for(j=0,p=line;j<4 && p; ++j)
+ {
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(str[0]==NULL)
+ continue;
+ cmd=strtol(str[0],(char **)NULL,0);
+ if(max_cmd < cmd)
+ max_cmd = cmd;
+ if(cmd <= 0 || cmd > MAX_PACKET_DB)
+ continue;
+ if(str[1]==NULL){
+ ShowError("packet_db: packet len error\n");
+ continue;
+ }
+
+ packet_db[packet_ver][cmd].len = (short)atoi(str[1]);
+
+ if(str[2]==NULL){
+ packet_db[packet_ver][cmd].func = NULL;
+ ln++;
+ continue;
+ }
+
+ // look up processing function by name
+ ARR_FIND( 0, ARRAYLENGTH(clif_parse_func), j, clif_parse_func[j].name != NULL && strcmp(str[2],clif_parse_func[j].name)==0 );
+ if( j < ARRAYLENGTH(clif_parse_func) )
+ packet_db[packet_ver][cmd].func = clif_parse_func[j].func;
+
+ // set the identifying cmd for the packet_db version
+ if (strcmp(str[2],"wanttoconnection")==0)
+ clif_config.connect_cmd[packet_ver] = cmd;
+
+ if(str[3]==NULL){
+ ShowError("packet_db: packet error\n");
+ exit(EXIT_FAILURE);
+ }
+ for(j=0,p2=str[3];p2;j++){
+ short k;
+ str2[j]=p2;
+ p2=strchr(p2,':');
+ if(p2) *p2++=0;
+ k = atoi(str2[j]);
+ // if (packet_db[packet_ver][cmd].pos[j] != k && clif_config.prefer_packet_db) // not used for now
+
+ if( j >= MAX_PACKET_POS )
+ {
+ ShowError("Too many positions found for packet 0x%04x (max=%d).\n", cmd, MAX_PACKET_POS);
+ break;
+ }
+
+ packet_db[packet_ver][cmd].pos[j] = k;
+ }
+ }
+ fclose(fp);
+ if(max_cmd > MAX_PACKET_DB)
+ {
+ ShowWarning("Found packets up to 0x%X, ignored 0x%X and above.\n", max_cmd, MAX_PACKET_DB);
+ ShowWarning("Please increase MAX_PACKET_DB and recompile.\n");
+ }
+ if (!clif_config.connect_cmd[clif_config.packet_db_ver])
+ { //Locate the nearest version that we still support. [Skotlex]
+ for(j = clif_config.packet_db_ver; j >= 0 && !clif_config.connect_cmd[j]; j--);
+
+ clif_config.packet_db_ver = j?j:MAX_PACKET_VER;
+ }
+ ShowStatus("Done reading packet database from '"CL_WHITE"%s"CL_RESET"'. Using default packet version: "CL_WHITE"%d"CL_RESET".\n", "packet_db.txt", clif_config.packet_db_ver);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int do_init_clif(void) {
+ const char* colors[COLOR_MAX] = { "0xFF0000" };
+ int i;
+ /**
+ * Setup Color Table (saves unnecessary load of strtoul on every call)
+ **/
+ for(i = 0; i < COLOR_MAX; i++) {
+ color_table[i] = strtoul(colors[i],NULL,0);
+ color_table[i] = (color_table[i] & 0x0000FF) << 16 | (color_table[i] & 0x00FF00) | (color_table[i] & 0xFF0000) >> 16;//RGB to BGR
+ }
+
+ clif_config.packet_db_ver = -1; // the main packet version of the DB
+ memset(clif_config.connect_cmd, 0, sizeof(clif_config.connect_cmd)); //The default connect command will be determined after reading the packet_db [Skotlex]
+
+ memset(packet_db,0,sizeof(packet_db));
+ //Using the packet_db file is the only way to set up packets now [Skotlex]
+ packetdb_readdb();
+
+ set_defaultparse(clif_parse);
+ if( make_listen_bind(bind_ip,map_port) == -1 ) {
+ ShowFatalError("can't bind game port\n");
+ exit(EXIT_FAILURE);
+ }
+
+ add_timer_func_list(clif_clearunit_delayed_sub, "clif_clearunit_delayed_sub");
+ add_timer_func_list(clif_delayquit, "clif_delayquit");
+
+ delay_clearunit_ers = ers_new(sizeof(struct block_list),"clif.c::delay_clearunit_ers",ERS_OPT_CLEAR);
+
+ return 0;
+}
+
+void do_final_clif(void) {
+ ers_destroy(delay_clearunit_ers);
+}
diff --git a/src/map/script.c b/src/map/script.c
index a918bc853..27e0bb549 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -1,17779 +1,17784 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-//#define DEBUG_DISP
-//#define DEBUG_DISASM
-//#define DEBUG_RUN
-//#define DEBUG_HASH
-//#define DEBUG_DUMP_STACK
-
-#include "../common/cbasetypes.h"
-#include "../common/malloc.h"
-#include "../common/md5calc.h"
-#include "../common/nullpo.h"
-#include "../common/random.h"
-#include "../common/showmsg.h"
-#include "../common/socket.h" // usage: getcharip
-#include "../common/strlib.h"
-#include "../common/timer.h"
-#include "../common/utils.h"
-
-#include "map.h"
-#include "path.h"
-#include "clif.h"
-#include "chrif.h"
-#include "itemdb.h"
-#include "pc.h"
-#include "status.h"
-#include "storage.h"
-#include "mob.h"
-#include "npc.h"
-#include "pet.h"
-#include "mapreg.h"
-#include "homunculus.h"
-#include "instance.h"
-#include "mercenary.h"
-#include "intif.h"
-#include "skill.h"
-#include "status.h"
-#include "chat.h"
-#include "battle.h"
-#include "battleground.h"
-#include "party.h"
-#include "guild.h"
-#include "atcommand.h"
-#include "log.h"
-#include "unit.h"
-#include "pet.h"
-#include "mail.h"
-#include "script.h"
-#include "quest.h"
-#include "elemental.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#ifndef WIN32
- #include <sys/time.h>
-#endif
-#include <time.h>
-#include <setjmp.h>
-#include <errno.h>
-
-#ifdef BETA_THREAD_TEST
- #include "../common/atomic.h"
- #include "../common/spinlock.h"
- #include "../common/thread.h"
- #include "../common/mutex.h"
-#endif
-
-
-///////////////////////////////////////////////////////////////////////////////
-//## TODO possible enhancements: [FlavioJS]
-// - 'callfunc' supporting labels in the current npc "::LabelName"
-// - 'callfunc' supporting labels in other npcs "NpcName::LabelName"
-// - 'function FuncName;' function declarations reverting to global functions
-// if local label isn't found
-// - join callfunc and callsub's functionality
-// - remove dynamic allocation in add_word()
-// - remove GETVALUE / SETVALUE
-// - clean up the set_reg / set_val / setd_sub mess
-// - detect invalid label references at parse-time
-
-//
-// struct script_state* st;
-//
-
-/// Returns the script_data at the target index
-#define script_getdata(st,i) ( &((st)->stack->stack_data[(st)->start + (i)]) )
-/// Returns if the stack contains data at the target index
-#define script_hasdata(st,i) ( (st)->end > (st)->start + (i) )
-/// Returns the index of the last data in the stack
-#define script_lastdata(st) ( (st)->end - (st)->start - 1 )
-/// Pushes an int into the stack
-#define script_pushint(st,val) push_val((st)->stack, C_INT, (val))
-/// Pushes a string into the stack (script engine frees it automatically)
-#define script_pushstr(st,val) push_str((st)->stack, C_STR, (val))
-/// Pushes a copy of a string into the stack
-#define script_pushstrcopy(st,val) push_str((st)->stack, C_STR, aStrdup(val))
-/// Pushes a constant string into the stack (must never change or be freed)
-#define script_pushconststr(st,val) push_str((st)->stack, C_CONSTSTR, (val))
-/// Pushes a nil into the stack
-#define script_pushnil(st) push_val((st)->stack, C_NOP, 0)
-/// Pushes a copy of the data in the target index
-#define script_pushcopy(st,i) push_copy((st)->stack, (st)->start + (i))
-
-#define script_isstring(st,i) data_isstring(script_getdata(st,i))
-#define script_isint(st,i) data_isint(script_getdata(st,i))
-
-#define script_getnum(st,val) conv_num(st, script_getdata(st,val))
-#define script_getstr(st,val) conv_str(st, script_getdata(st,val))
-#define script_getref(st,val) ( script_getdata(st,val)->ref )
-
-// Note: "top" functions/defines use indexes relative to the top of the stack
-// -1 is the index of the data at the top
-
-/// Returns the script_data at the target index relative to the top of the stack
-#define script_getdatatop(st,i) ( &((st)->stack->stack_data[(st)->stack->sp + (i)]) )
-/// Pushes a copy of the data in the target index relative to the top of the stack
-#define script_pushcopytop(st,i) push_copy((st)->stack, (st)->stack->sp + (i))
-/// Removes the range of values [start,end[ relative to the top of the stack
-#define script_removetop(st,start,end) ( pop_stack((st), ((st)->stack->sp + (start)), (st)->stack->sp + (end)) )
-
-//
-// struct script_data* data;
-//
-
-/// Returns if the script data is a string
-#define data_isstring(data) ( (data)->type == C_STR || (data)->type == C_CONSTSTR )
-/// Returns if the script data is an int
-#define data_isint(data) ( (data)->type == C_INT )
-/// Returns if the script data is a reference
-#define data_isreference(data) ( (data)->type == C_NAME )
-/// Returns if the script data is a label
-#define data_islabel(data) ( (data)->type == C_POS )
-/// Returns if the script data is an internal script function label
-#define data_isfunclabel(data) ( (data)->type == C_USERFUNC_POS )
-
-/// Returns if this is a reference to a constant
-#define reference_toconstant(data) ( str_data[reference_getid(data)].type == C_INT )
-/// Returns if this a reference to a param
-#define reference_toparam(data) ( str_data[reference_getid(data)].type == C_PARAM )
-/// Returns if this a reference to a variable
-//##TODO confirm it's C_NAME [FlavioJS]
-#define reference_tovariable(data) ( str_data[reference_getid(data)].type == C_NAME )
-/// Returns the unique id of the reference (id and index)
-#define reference_getuid(data) ( (data)->u.num )
-/// Returns the id of the reference
-#define reference_getid(data) ( (int32)(reference_getuid(data) & 0x00ffffff) )
-/// Returns the array index of the reference
-#define reference_getindex(data) ( (int32)(((uint32)(reference_getuid(data) & 0xff000000)) >> 24) )
-/// Returns the name of the reference
-#define reference_getname(data) ( str_buf + str_data[reference_getid(data)].str )
-/// Returns the linked list of uid-value pairs of the reference (can be NULL)
-#define reference_getref(data) ( (data)->ref )
-/// Returns the value of the constant
-#define reference_getconstant(data) ( str_data[reference_getid(data)].val )
-/// Returns the type of param
-#define reference_getparamtype(data) ( str_data[reference_getid(data)].val )
-
-/// Composes the uid of a reference from the id and the index
-#define reference_uid(id,idx) ( (int32)((((uint32)(id)) & 0x00ffffff) | (((uint32)(idx)) << 24)) )
-
-#define not_server_variable(prefix) ( (prefix) != '$' && (prefix) != '.' && (prefix) != '\'')
-#define not_array_variable(prefix) ( (prefix) != '$' && (prefix) != '@' && (prefix) != '.' && (prefix) != '\'' )
-#define is_string_variable(name) ( (name)[strlen(name) - 1] == '$' )
-
-#define FETCH(n, t) \
- if( script_hasdata(st,n) ) \
- (t)=script_getnum(st,n);
-
-/// Maximum amount of elements in script arrays
-#define SCRIPT_MAX_ARRAYSIZE 128
-
-#define SCRIPT_BLOCK_SIZE 512
-enum { LABEL_NEXTLINE=1,LABEL_START };
-
-/// temporary buffer for passing around compiled bytecode
-/// @see add_scriptb, set_label, parse_script
-static unsigned char* script_buf = NULL;
-static int script_pos = 0, script_size = 0;
-
-static inline int GETVALUE(const unsigned char* buf, int i)
-{
- return (int)MakeDWord(MakeWord(buf[i], buf[i+1]), MakeWord(buf[i+2], 0));
-}
-static inline void SETVALUE(unsigned char* buf, int i, int n)
-{
- buf[i] = GetByte(n, 0);
- buf[i+1] = GetByte(n, 1);
- buf[i+2] = GetByte(n, 2);
-}
-
-// String buffer structures.
-// str_data stores string information
-static struct str_data_struct {
- enum c_op type;
- int str;
- int backpatch;
- int label;
- int (*func)(struct script_state *st);
- int val;
- int next;
-} *str_data = NULL;
-static int str_data_size = 0; // size of the data
-static int str_num = LABEL_START; // next id to be assigned
-
-// str_buf holds the strings themselves
-static char *str_buf;
-static int str_size = 0; // size of the buffer
-static int str_pos = 0; // next position to be assigned
-
-
-// Using a prime number for SCRIPT_HASH_SIZE should give better distributions
-#define SCRIPT_HASH_SIZE 1021
-int str_hash[SCRIPT_HASH_SIZE];
-// Specifies which string hashing method to use
-//#define SCRIPT_HASH_DJB2
-//#define SCRIPT_HASH_SDBM
-#define SCRIPT_HASH_ELF
-
-static DBMap* scriptlabel_db=NULL; // const char* label_name -> int script_pos
-static DBMap* userfunc_db=NULL; // const char* func_name -> struct script_code*
-static int parse_options=0;
-DBMap* script_get_label_db(void){ return scriptlabel_db; }
-DBMap* script_get_userfunc_db(void){ return userfunc_db; }
-
-// important buildin function references for usage in scripts
-static int buildin_set_ref = 0;
-static int buildin_callsub_ref = 0;
-static int buildin_callfunc_ref = 0;
-static int buildin_getelementofarray_ref = 0;
-
-// Caches compiled autoscript item code.
-// Note: This is not cleared when reloading itemdb.
-static DBMap* autobonus_db=NULL; // char* script -> char* bytecode
-
-struct Script_Config script_config = {
- 1, // warn_func_mismatch_argtypes
- 1, 65535, 2048, //warn_func_mismatch_paramnum/check_cmdcount/check_gotocount
- 0, INT_MAX, // input_min_value/input_max_value
- "OnPCDieEvent", //die_event_name
- "OnPCKillEvent", //kill_pc_event_name
- "OnNPCKillEvent", //kill_mob_event_name
- "OnPCLoginEvent", //login_event_name
- "OnPCLogoutEvent", //logout_event_name
- "OnPCLoadMapEvent", //loadmap_event_name
- "OnPCBaseLvUpEvent", //baselvup_event_name
- "OnPCJobLvUpEvent", //joblvup_event_name
- "OnTouch_", //ontouch_name (runs on first visible char to enter area, picks another char if the first char leaves)
- "OnTouch", //ontouch2_name (run whenever a char walks into the OnTouch area)
-};
-
-static jmp_buf error_jump;
-static char* error_msg;
-static const char* error_pos;
-static int error_report; // if the error should produce output
-
-// for advanced scripting support ( nested if, switch, while, for, do-while, function, etc )
-// [Eoe / jA 1080, 1081, 1094, 1164]
-enum curly_type {
- TYPE_NULL = 0,
- TYPE_IF,
- TYPE_SWITCH,
- TYPE_WHILE,
- TYPE_FOR,
- TYPE_DO,
- TYPE_USERFUNC,
- TYPE_ARGLIST // function argument list
-};
-
-enum e_arglist
-{
- ARGLIST_UNDEFINED = 0,
- ARGLIST_NO_PAREN = 1,
- ARGLIST_PAREN = 2,
-};
-
-static struct {
- struct {
- enum curly_type type;
- int index;
- int count;
- int flag;
- struct linkdb_node *case_label;
- } curly[256]; // Information right parenthesis
- int curly_count; // The number of right brackets
- int index; // Number of the syntax used in the script
-} syntax;
-
-const char* parse_curly_close(const char* p);
-const char* parse_syntax_close(const char* p);
-const char* parse_syntax_close_sub(const char* p,int* flag);
-const char* parse_syntax(const char* p);
-static int parse_syntax_for_flag = 0;
-
-extern int current_equip_item_index; //for New CARDS Scripts. It contains Inventory Index of the EQUIP_SCRIPT caller item. [Lupus]
-int potion_flag=0; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex]
-int potion_hp=0, potion_per_hp=0, potion_sp=0, potion_per_sp=0;
-int potion_target=0;
-
-
-c_op get_com(unsigned char *script,int *pos);
-int get_num(unsigned char *script,int *pos);
-
-typedef struct script_function {
- int (*func)(struct script_state *st);
- const char *name;
- const char *arg;
-} script_function;
-
-extern script_function buildin_func[];
-
-static struct linkdb_node* sleep_db;// int oid -> struct script_state*
-
-#ifdef BETA_THREAD_TEST
-/**
- * MySQL Query Slave
- **/
-static SPIN_LOCK queryThreadLock;
-static rAthread queryThread = NULL;
-static ramutex queryThreadMutex = NULL;
-static racond queryThreadCond = NULL;
-static volatile int32 queryThreadTerminate = 0;
-
-struct queryThreadEntry {
- bool ok;
- bool type; /* main db or log db? */
- struct script_state *st;
-};
-
-/* Ladies and Gentleman the Manager! */
-struct {
- struct queryThreadEntry **entry;/* array of structs */
- int count;
- int timer;/* used to receive processed entries */
-} queryThreadData;
-#endif
-
-/*==========================================
- * (Only those needed) local declaration prototype
- *------------------------------------------*/
-const char* parse_subexpr(const char* p,int limit);
-int run_func(struct script_state *st);
-
-enum {
- MF_NOMEMO, //0
- MF_NOTELEPORT,
- MF_NOSAVE,
- MF_NOBRANCH,
- MF_NOPENALTY,
- MF_NOZENYPENALTY,
- MF_PVP,
- MF_PVP_NOPARTY,
- MF_PVP_NOGUILD,
- MF_GVG,
- MF_GVG_NOPARTY, //10
- MF_NOTRADE,
- MF_NOSKILL,
- MF_NOWARP,
- MF_PARTYLOCK,
- MF_NOICEWALL,
- MF_SNOW,
- MF_FOG,
- MF_SAKURA,
- MF_LEAVES,
- /**
- * No longer available, keeping here just in case it's back someday. [Ind]
- **/
- //MF_RAIN, //20
- // 21 free
- MF_NOGO = 22,
- MF_CLOUDS,
- MF_CLOUDS2,
- MF_FIREWORKS,
- MF_GVG_CASTLE,
- MF_GVG_DUNGEON,
- MF_NIGHTENABLED,
- MF_NOBASEEXP,
- MF_NOJOBEXP, //30
- MF_NOMOBLOOT,
- MF_NOMVPLOOT,
- MF_NORETURN,
- MF_NOWARPTO,
- MF_NIGHTMAREDROP,
- MF_RESTRICTED,
- MF_NOCOMMAND,
- MF_NODROP,
- MF_JEXP,
- MF_BEXP, //40
- MF_NOVENDING,
- MF_LOADEVENT,
- MF_NOCHAT,
- MF_NOEXPPENALTY,
- MF_GUILDLOCK,
- MF_TOWN,
- MF_AUTOTRADE,
- MF_ALLOWKS,
- MF_MONSTER_NOTELEPORT,
- MF_PVP_NOCALCRANK, //50
- MF_BATTLEGROUND,
- MF_RESET
-};
-
-const char* script_op2name(int op)
-{
-#define RETURN_OP_NAME(type) case type: return #type
- switch( op )
- {
- RETURN_OP_NAME(C_NOP);
- RETURN_OP_NAME(C_POS);
- RETURN_OP_NAME(C_INT);
- RETURN_OP_NAME(C_PARAM);
- RETURN_OP_NAME(C_FUNC);
- RETURN_OP_NAME(C_STR);
- RETURN_OP_NAME(C_CONSTSTR);
- RETURN_OP_NAME(C_ARG);
- RETURN_OP_NAME(C_NAME);
- RETURN_OP_NAME(C_EOL);
- RETURN_OP_NAME(C_RETINFO);
- RETURN_OP_NAME(C_USERFUNC);
- RETURN_OP_NAME(C_USERFUNC_POS);
-
- // operators
- RETURN_OP_NAME(C_OP3);
- RETURN_OP_NAME(C_LOR);
- RETURN_OP_NAME(C_LAND);
- RETURN_OP_NAME(C_LE);
- RETURN_OP_NAME(C_LT);
- RETURN_OP_NAME(C_GE);
- RETURN_OP_NAME(C_GT);
- RETURN_OP_NAME(C_EQ);
- RETURN_OP_NAME(C_NE);
- RETURN_OP_NAME(C_XOR);
- RETURN_OP_NAME(C_OR);
- RETURN_OP_NAME(C_AND);
- RETURN_OP_NAME(C_ADD);
- RETURN_OP_NAME(C_SUB);
- RETURN_OP_NAME(C_MUL);
- RETURN_OP_NAME(C_DIV);
- RETURN_OP_NAME(C_MOD);
- RETURN_OP_NAME(C_NEG);
- RETURN_OP_NAME(C_LNOT);
- RETURN_OP_NAME(C_NOT);
- RETURN_OP_NAME(C_R_SHIFT);
- RETURN_OP_NAME(C_L_SHIFT);
-
- default:
- ShowDebug("script_op2name: unexpected op=%d\n", op);
- return "???";
- }
-#undef RETURN_OP_NAME
-}
-
-#ifdef DEBUG_DUMP_STACK
-static void script_dump_stack(struct script_state* st)
-{
- int i;
- ShowMessage("\tstart = %d\n", st->start);
- ShowMessage("\tend = %d\n", st->end);
- ShowMessage("\tdefsp = %d\n", st->stack->defsp);
- ShowMessage("\tsp = %d\n", st->stack->sp);
- for( i = 0; i < st->stack->sp; ++i )
- {
- struct script_data* data = &st->stack->stack_data[i];
- ShowMessage("\t[%d] %s", i, script_op2name(data->type));
- switch( data->type )
- {
- case C_INT:
- case C_POS:
- ShowMessage(" %d\n", data->u.num);
- break;
-
- case C_STR:
- case C_CONSTSTR:
- ShowMessage(" \"%s\"\n", data->u.str);
- break;
-
- case C_NAME:
- ShowMessage(" \"%s\" (id=%d ref=%p subtype=%s)\n", reference_getname(data), data->u.num, data->ref, script_op2name(str_data[data->u.num].type));
- break;
-
- case C_RETINFO:
- {
- struct script_retinfo* ri = data->u.ri;
- ShowMessage(" %p {var_function=%p, script=%p, pos=%d, nargs=%d, defsp=%d}\n", ri, ri->var_function, ri->script, ri->pos, ri->nargs, ri->defsp);
- }
- break;
- default:
- ShowMessage("\n");
- break;
- }
- }
-}
-#endif
-
-/// Reports on the console the src of a script error.
-static void script_reportsrc(struct script_state *st)
-{
- struct block_list* bl;
-
- if( st->oid == 0 )
- return; //Can't report source.
-
- bl = map_id2bl(st->oid);
- if( bl == NULL )
- return;
-
- switch( bl->type )
- {
- case BL_NPC:
- if( bl->m >= 0 )
- ShowDebug("Source (NPC): %s at %s (%d,%d)\n", ((struct npc_data *)bl)->name, map[bl->m].name, bl->x, bl->y);
- else
- ShowDebug("Source (NPC): %s (invisible/not on a map)\n", ((struct npc_data *)bl)->name);
- break;
- default:
- if( bl->m >= 0 )
- ShowDebug("Source (Non-NPC type %d): name %s at %s (%d,%d)\n", bl->type, status_get_name(bl), map[bl->m].name, bl->x, bl->y);
- else
- ShowDebug("Source (Non-NPC type %d): name %s (invisible/not on a map)\n", bl->type, status_get_name(bl));
- break;
- }
-}
-
-/// Reports on the console information about the script data.
-static void script_reportdata(struct script_data* data)
-{
- if( data == NULL )
- return;
- switch( data->type )
- {
- case C_NOP:// no value
- ShowDebug("Data: nothing (nil)\n");
- break;
- case C_INT:// number
- ShowDebug("Data: number value=%d\n", data->u.num);
- break;
- case C_STR:
- case C_CONSTSTR:// string
- if( data->u.str )
- {
- ShowDebug("Data: string value=\"%s\"\n", data->u.str);
- }
- else
- {
- ShowDebug("Data: string value=NULL\n");
- }
- break;
- case C_NAME:// reference
- if( reference_tovariable(data) )
- {// variable
- const char* name = reference_getname(data);
- if( not_array_variable(*name) )
- ShowDebug("Data: variable name='%s'\n", name);
- else
- ShowDebug("Data: variable name='%s' index=%d\n", name, reference_getindex(data));
- }
- else if( reference_toconstant(data) )
- {// constant
- ShowDebug("Data: constant name='%s' value=%d\n", reference_getname(data), reference_getconstant(data));
- }
- else if( reference_toparam(data) )
- {// param
- ShowDebug("Data: param name='%s' type=%d\n", reference_getname(data), reference_getparamtype(data));
- }
- else
- {// ???
- ShowDebug("Data: reference name='%s' type=%s\n", reference_getname(data), script_op2name(data->type));
- ShowDebug("Please report this!!! - str_data.type=%s\n", script_op2name(str_data[reference_getid(data)].type));
- }
- break;
- case C_POS:// label
- ShowDebug("Data: label pos=%d\n", data->u.num);
- break;
- default:
- ShowDebug("Data: %s\n", script_op2name(data->type));
- break;
- }
-}
-
-
-/// Reports on the console information about the current built-in function.
-static void script_reportfunc(struct script_state* st)
-{
- int i, params, id;
- struct script_data* data;
-
- if( !script_hasdata(st,0) )
- {// no stack
- return;
- }
-
- data = script_getdata(st,0);
-
- if( !data_isreference(data) || str_data[reference_getid(data)].type != C_FUNC )
- {// script currently not executing a built-in function or corrupt stack
- return;
- }
-
- id = reference_getid(data);
- params = script_lastdata(st)-1;
-
- if( params > 0 )
- {
- ShowDebug("Function: %s (%d parameter%s):\n", get_str(id), params, ( params == 1 ) ? "" : "s");
-
- for( i = 2; i <= script_lastdata(st); i++ )
- {
- script_reportdata(script_getdata(st,i));
- }
- }
- else
- {
- ShowDebug("Function: %s (no parameters)\n", get_str(id));
- }
-}
-
-
-/*==========================================
- * Output error message
- *------------------------------------------*/
-static void disp_error_message2(const char *mes,const char *pos,int report)
-{
- error_msg = aStrdup(mes);
- error_pos = pos;
- error_report = report;
- longjmp( error_jump, 1 );
-}
-#define disp_error_message(mes,pos) disp_error_message2(mes,pos,1)
-
-/// Checks event parameter validity
-static void check_event(struct script_state *st, const char *evt)
-{
- if( evt && evt[0] && !stristr(evt, "::On") )
- {
- ShowWarning("NPC event parameter deprecated! Please use 'NPCNAME::OnEVENT' instead of '%s'.\n", evt);
- script_reportsrc(st);
- }
-}
-
-/*==========================================
- * Hashes the input string
- *------------------------------------------*/
-static unsigned int calc_hash(const char* p)
-{
- unsigned int h;
-
-#if defined(SCRIPT_HASH_DJB2)
- h = 5381;
- while( *p ) // hash*33 + c
- h = ( h << 5 ) + h + ((unsigned char)TOLOWER(*p++));
-#elif defined(SCRIPT_HASH_SDBM)
- h = 0;
- while( *p ) // hash*65599 + c
- h = ( h << 6 ) + ( h << 16 ) - h + ((unsigned char)TOLOWER(*p++));
-#elif defined(SCRIPT_HASH_ELF) // UNIX ELF hash
- h = 0;
- while( *p ){
- unsigned int g;
- h = ( h << 4 ) + ((unsigned char)TOLOWER(*p++));
- g = h & 0xF0000000;
- if( g )
- {
- h ^= g >> 24;
- h &= ~g;
- }
- }
-#else // athena hash
- h = 0;
- while( *p )
- h = ( h << 1 ) + ( h >> 3 ) + ( h >> 5 ) + ( h >> 8 ) + (unsigned char)TOLOWER(*p++);
-#endif
-
- return h % SCRIPT_HASH_SIZE;
-}
-
-
-/*==========================================
- * str_data manipulation functions
- *------------------------------------------*/
-
-/// Looks up string using the provided id.
-const char* get_str(int id)
-{
- Assert( id >= LABEL_START && id < str_size );
- return str_buf+str_data[id].str;
-}
-
-/// Returns the uid of the string, or -1.
-static int search_str(const char* p)
-{
- int i;
-
- for( i = str_hash[calc_hash(p)]; i != 0; i = str_data[i].next )
- if( strcasecmp(get_str(i),p) == 0 )
- return i;
-
- return -1;
-}
-
-/// Stores a copy of the string and returns its id.
-/// If an identical string is already present, returns its id instead.
-int add_str(const char* p)
-{
- int i, h;
- int len;
-
- h = calc_hash(p);
-
- if( str_hash[h] == 0 )
- {// empty bucket, add new node here
- str_hash[h] = str_num;
- }
- else
- {// scan for end of list, or occurence of identical string
- for( i = str_hash[h]; ; i = str_data[i].next )
- {
- if( strcasecmp(get_str(i),p) == 0 )
- return i; // string already in list
- if( str_data[i].next == 0 )
- break; // reached the end
- }
-
- // append node to end of list
- str_data[i].next = str_num;
- }
-
- // grow list if neccessary
- if( str_num >= str_data_size )
- {
- str_data_size += 128;
- RECREATE(str_data,struct str_data_struct,str_data_size);
- memset(str_data + (str_data_size - 128), '\0', 128);
- }
-
- len=(int)strlen(p);
-
- // grow string buffer if neccessary
- while( str_pos+len+1 >= str_size )
- {
- str_size += 256;
- RECREATE(str_buf,char,str_size);
- memset(str_buf + (str_size - 256), '\0', 256);
- }
-
- safestrncpy(str_buf+str_pos, p, len+1);
- str_data[str_num].type = C_NOP;
- str_data[str_num].str = str_pos;
- str_data[str_num].next = 0;
- str_data[str_num].func = NULL;
- str_data[str_num].backpatch = -1;
- str_data[str_num].label = -1;
- str_pos += len+1;
-
- return str_num++;
-}
-
-
-/// Appends 1 byte to the script buffer.
-static void add_scriptb(int a)
-{
- if( script_pos+1 >= script_size )
- {
- script_size += SCRIPT_BLOCK_SIZE;
- RECREATE(script_buf,unsigned char,script_size);
- }
- script_buf[script_pos++] = (uint8)(a);
-}
-
-/// Appends a c_op value to the script buffer.
-/// The value is variable-length encoded into 8-bit blocks.
-/// The encoding scheme is ( 01?????? )* 00??????, LSB first.
-/// All blocks but the last hold 7 bits of data, topmost bit is always 1 (carries).
-static void add_scriptc(int a)
-{
- while( a >= 0x40 )
- {
- add_scriptb((a&0x3f)|0x40);
- a = (a - 0x40) >> 6;
- }
-
- add_scriptb(a);
-}
-
-/// Appends an integer value to the script buffer.
-/// The value is variable-length encoded into 8-bit blocks.
-/// The encoding scheme is ( 11?????? )* 10??????, LSB first.
-/// All blocks but the last hold 7 bits of data, topmost bit is always 1 (carries).
-static void add_scripti(int a)
-{
- while( a >= 0x40 )
- {
- add_scriptb((a&0x3f)|0xc0);
- a = (a - 0x40) >> 6;
- }
- add_scriptb(a|0x80);
-}
-
-/// Appends a str_data object (label/function/variable/integer) to the script buffer.
-
-///
-/// @param l The id of the str_data entry
-// Maximum up to 16M
-static void add_scriptl(int l)
-{
- int backpatch = str_data[l].backpatch;
-
- switch(str_data[l].type){
- case C_POS:
- case C_USERFUNC_POS:
- add_scriptc(C_POS);
- add_scriptb(str_data[l].label);
- add_scriptb(str_data[l].label>>8);
- add_scriptb(str_data[l].label>>16);
- break;
- case C_NOP:
- case C_USERFUNC:
- // Embedded data backpatch there is a possibility of label
- add_scriptc(C_NAME);
- str_data[l].backpatch = script_pos;
- add_scriptb(backpatch);
- add_scriptb(backpatch>>8);
- add_scriptb(backpatch>>16);
- break;
- case C_INT:
- add_scripti(abs(str_data[l].val));
- if( str_data[l].val < 0 ) //Notice that this is negative, from jA (Rayce)
- add_scriptc(C_NEG);
- break;
- default: // assume C_NAME
- add_scriptc(C_NAME);
- add_scriptb(l);
- add_scriptb(l>>8);
- add_scriptb(l>>16);
- break;
- }
-}
-
-/*==========================================
- * Resolve the label
- *------------------------------------------*/
-void set_label(int l,int pos, const char* script_pos)
-{
- int i,next;
-
- if(str_data[l].type==C_INT || str_data[l].type==C_PARAM || str_data[l].type==C_FUNC)
- { //Prevent overwriting constants values, parameters and built-in functions [Skotlex]
- disp_error_message("set_label: invalid label name",script_pos);
- return;
- }
- if(str_data[l].label!=-1){
- disp_error_message("set_label: dup label ",script_pos);
- return;
- }
- str_data[l].type=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS);
- str_data[l].label=pos;
- for(i=str_data[l].backpatch;i>=0 && i!=0x00ffffff;){
- next=GETVALUE(script_buf,i);
- script_buf[i-1]=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS);
- SETVALUE(script_buf,i,pos);
- i=next;
- }
-}
-
-/// Skips spaces and/or comments.
-const char* skip_space(const char* p)
-{
- if( p == NULL )
- return NULL;
- for(;;)
- {
- while( ISSPACE(*p) )
- ++p;
- if( *p == '/' && p[1] == '/' )
- {// line comment
- while(*p && *p!='\n')
- ++p;
- }
- else if( *p == '/' && p[1] == '*' )
- {// block comment
- p += 2;
- for(;;)
- {
- if( *p == '\0' )
- return p;//disp_error_message("script:skip_space: end of file while parsing block comment. expected "CL_BOLD"*/"CL_NORM, p);
- if( *p == '*' && p[1] == '/' )
- {// end of block comment
- p += 2;
- break;
- }
- ++p;
- }
- }
- else
- break;
- }
- return p;
-}
-
-/// Skips a word.
-/// A word consists of undercores and/or alfanumeric characters,
-/// and valid variable prefixes/postfixes.
-static
-const char* skip_word(const char* p)
-{
- // prefix
- switch( *p )
- {
- case '@':// temporary char variable
- ++p; break;
- case '#':// account variable
- p += ( p[1] == '#' ? 2 : 1 ); break;
- case '\'':// instance variable
- ++p; break;
- case '.':// npc variable
- p += ( p[1] == '@' ? 2 : 1 ); break;
- case '$':// global variable
- p += ( p[1] == '@' ? 2 : 1 ); break;
- }
-
- while( ISALNUM(*p) || *p == '_' )
- ++p;
-
- // postfix
- if( *p == '$' )// string
- p++;
-
- return p;
-}
-
-/// Adds a word to str_data.
-/// @see skip_word
-/// @see add_str
-static
-int add_word(const char* p)
-{
- char* word;
- int len;
- int i;
-
- // Check for a word
- len = skip_word(p) - p;
- if( len == 0 )
- disp_error_message("script:add_word: invalid word. A word consists of undercores and/or alfanumeric characters, and valid variable prefixes/postfixes.", p);
-
- // Duplicate the word
- word = (char*)aMalloc(len+1);
- memcpy(word, p, len);
- word[len] = 0;
-
- // add the word
- i = add_str(word);
- aFree(word);
- return i;
-}
-
-/// Parses a function call.
-/// The argument list can have parenthesis or not.
-/// The number of arguments is checked.
-static
-const char* parse_callfunc(const char* p, int require_paren, int is_custom)
-{
- const char* p2;
- const char* arg=NULL;
- int func;
-
- func = add_word(p);
- if( str_data[func].type == C_FUNC ){
- // buildin function
- add_scriptl(func);
- add_scriptc(C_ARG);
- arg = buildin_func[str_data[func].val].arg;
- } else if( str_data[func].type == C_USERFUNC || str_data[func].type == C_USERFUNC_POS ){
- // script defined function
- add_scriptl(buildin_callsub_ref);
- add_scriptc(C_ARG);
- add_scriptl(func);
- arg = buildin_func[str_data[buildin_callsub_ref].val].arg;
- if( *arg == 0 )
- disp_error_message("parse_callfunc: callsub has no arguments, please review it's definition",p);
- if( *arg != '*' )
- ++arg; // count func as argument
- } else {
-#ifdef SCRIPT_CALLFUNC_CHECK
- const char* name = get_str(func);
- if( !is_custom && strdb_get(userfunc_db, name) == NULL ) {
-#endif
- disp_error_message("parse_line: expect command, missing function name or calling undeclared function",p);
-#ifdef SCRIPT_CALLFUNC_CHECK
- } else {;
- add_scriptl(buildin_callfunc_ref);
- add_scriptc(C_ARG);
- add_scriptc(C_STR);
- while( *name ) add_scriptb(*name ++);
- add_scriptb(0);
- arg = buildin_func[str_data[buildin_callfunc_ref].val].arg;
- if( *arg != '*' ) ++ arg;
- }
-#endif
- }
-
- p = skip_word(p);
- p = skip_space(p);
- syntax.curly[syntax.curly_count].type = TYPE_ARGLIST;
- syntax.curly[syntax.curly_count].count = 0;
- if( *p == ';' )
- {// <func name> ';'
- syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN;
- } else if( *p == '(' && *(p2=skip_space(p+1)) == ')' )
- {// <func name> '(' ')'
- syntax.curly[syntax.curly_count].flag = ARGLIST_PAREN;
- p = p2;
- /*
- } else if( 0 && require_paren && *p != '(' )
- {// <func name>
- syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN;
- */
- } else
- {// <func name> <arg list>
- if( require_paren ){
- if( *p != '(' )
- disp_error_message("need '('",p);
- ++p; // skip '('
- syntax.curly[syntax.curly_count].flag = ARGLIST_PAREN;
- } else if( *p == '(' ){
- syntax.curly[syntax.curly_count].flag = ARGLIST_UNDEFINED;
- } else {
- syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN;
- }
- ++syntax.curly_count;
- while( *arg ) {
- p2=parse_subexpr(p,-1);
- if( p == p2 )
- break; // not an argument
- if( *arg != '*' )
- ++arg; // next argument
-
- p=skip_space(p2);
- if( *arg == 0 || *p != ',' )
- break; // no more arguments
- ++p; // skip comma
- }
- --syntax.curly_count;
- }
- if( *arg && *arg != '?' && *arg != '*' )
- disp_error_message2("parse_callfunc: not enough arguments, expected ','", p, script_config.warn_func_mismatch_paramnum);
- if( syntax.curly[syntax.curly_count].type != TYPE_ARGLIST )
- disp_error_message("parse_callfunc: DEBUG last curly is not an argument list",p);
- if( syntax.curly[syntax.curly_count].flag == ARGLIST_PAREN ){
- if( *p != ')' )
- disp_error_message("parse_callfunc: expected ')' to close argument list",p);
- ++p;
- }
- add_scriptc(C_FUNC);
- return p;
-}
-
-/// Processes end of logical script line.
-/// @param first When true, only fix up scheduling data is initialized
-/// @param p Script position for error reporting in set_label
-static void parse_nextline(bool first, const char* p)
-{
- if( !first )
- {
- add_scriptc(C_EOL); // mark end of line for stack cleanup
- set_label(LABEL_NEXTLINE, script_pos, p); // fix up '-' labels
- }
-
- // initialize data for new '-' label fix up scheduling
- str_data[LABEL_NEXTLINE].type = C_NOP;
- str_data[LABEL_NEXTLINE].backpatch = -1;
- str_data[LABEL_NEXTLINE].label = -1;
-}
-
-/// Parse a variable assignment using the direct equals operator
-/// @param p script position where the function should run from
-/// @return NULL if not a variable assignment, the new position otherwise
-const char* parse_variable(const char* p) {
- int i, j, word;
- c_op type = C_NOP;
- const char *p2 = NULL;
- const char *var = p;
-
- // skip the variable where applicable
- p = skip_word(p);
- p = skip_space(p);
-
- if( p == NULL ) {// end of the line or invalid buffer
- return NULL;
- }
-
- if( *p == '[' ) {// array variable so process the array as appropriate
- for( p2 = p, i = 0, j = 1; p; ++ i ) {
- if( *p ++ == ']' && --(j) == 0 ) break;
- if( *p == '[' ) ++ j;
- }
-
- if( !(p = skip_space(p)) ) {// end of line or invalid characters remaining
- disp_error_message("Missing right expression or closing bracket for variable.", p);
- }
- }
-
- if( type == C_NOP &&
- !( ( p[0] == '=' && p[1] != '=' && (type = C_EQ) ) // =
- || ( p[0] == '+' && p[1] == '=' && (type = C_ADD) ) // +=
- || ( p[0] == '-' && p[1] == '=' && (type = C_SUB) ) // -=
- || ( p[0] == '^' && p[1] == '=' && (type = C_XOR) ) // ^=
- || ( p[0] == '|' && p[1] == '=' && (type = C_OR ) ) // |=
- || ( p[0] == '&' && p[1] == '=' && (type = C_AND) ) // &=
- || ( p[0] == '*' && p[1] == '=' && (type = C_MUL) ) // *=
- || ( p[0] == '/' && p[1] == '=' && (type = C_DIV) ) // /=
- || ( p[0] == '%' && p[1] == '=' && (type = C_MOD) ) // %=
- || ( p[0] == '~' && p[1] == '=' && (type = C_NOT) ) // ~=
- || ( p[0] == '+' && p[1] == '+' && (type = C_ADD_PP) ) // ++
- || ( p[0] == '-' && p[1] == '-' && (type = C_SUB_PP) ) // --
- || ( p[0] == '<' && p[1] == '<' && p[2] == '=' && (type = C_L_SHIFT) ) // <<=
- || ( p[0] == '>' && p[1] == '>' && p[2] == '=' && (type = C_R_SHIFT) ) // >>=
- ) )
- {// failed to find a matching operator combination so invalid
- return NULL;
- }
-
- switch( type ) {
- case C_EQ: {// incremental modifier
- p = skip_space( &p[1] );
- }
- break;
-
- case C_L_SHIFT:
- case C_R_SHIFT: {// left or right shift modifier
- p = skip_space( &p[3] );
- }
- break;
-
- default: {// normal incremental command
- p = skip_space( &p[2] );
- }
- }
-
- if( p == NULL ) {// end of line or invalid buffer
- return NULL;
- }
-
- // push the set function onto the stack
- add_scriptl(buildin_set_ref);
- add_scriptc(C_ARG);
-
- // always append parenthesis to avoid errors
- syntax.curly[syntax.curly_count].type = TYPE_ARGLIST;
- syntax.curly[syntax.curly_count].count = 0;
- syntax.curly[syntax.curly_count].flag = ARGLIST_PAREN;
-
- // increment the total curly count for the position in the script
- ++ syntax.curly_count;
-
- // parse the variable currently being modified
- word = add_word(var);
-
- if( str_data[word].type == C_FUNC || str_data[word].type == C_USERFUNC || str_data[word].type == C_USERFUNC_POS )
- {// cannot assign a variable which exists as a function or label
- disp_error_message("Cannot modify a variable which has the same name as a function or label.", p);
- }
-
- if( p2 ) {// process the variable index
- const char* p3 = NULL;
-
- // push the getelementofarray method into the stack
- add_scriptl(buildin_getelementofarray_ref);
- add_scriptc(C_ARG);
- add_scriptl(word);
-
- // process the sub-expression for this assignment
- p3 = parse_subexpr(p2 + 1, 1);
- p3 = skip_space(p3);
-
- if( *p3 != ']' ) {// closing parenthesis is required for this script
- disp_error_message("Missing closing ']' parenthesis for the variable assignment.", p3);
- }
-
- // push the closing function stack operator onto the stack
- add_scriptc(C_FUNC);
- p3 ++;
- } else {// simply push the variable or value onto the stack
- add_scriptl(word);
- }
-
- if( type != C_EQ )
- add_scriptc(C_REF);
-
- if( type == C_ADD_PP || type == C_SUB_PP ) {// incremental operator for the method
- add_scripti(1);
- add_scriptc(type == C_ADD_PP ? C_ADD : C_SUB);
- } else {// process the value as an expression
- p = parse_subexpr(p, -1);
-
- if( type != C_EQ )
- {// push the type of modifier onto the stack
- add_scriptc(type);
- }
- }
-
- // decrement the curly count for the position within the script
- -- syntax.curly_count;
-
- // close the script by appending the function operator
- add_scriptc(C_FUNC);
-
- // push the buffer from the method
- return p;
-}
-
-/*==========================================
- * Analysis section
- *------------------------------------------*/
-const char* parse_simpleexpr(const char *p)
-{
- int i;
- p=skip_space(p);
-
- if(*p==';' || *p==',')
- disp_error_message("parse_simpleexpr: unexpected expr end",p);
- if(*p=='('){
- if( (i=syntax.curly_count-1) >= 0 && syntax.curly[i].type == TYPE_ARGLIST )
- ++syntax.curly[i].count;
- p=parse_subexpr(p+1,-1);
- p=skip_space(p);
- if( (i=syntax.curly_count-1) >= 0 && syntax.curly[i].type == TYPE_ARGLIST &&
- syntax.curly[i].flag == ARGLIST_UNDEFINED && --syntax.curly[i].count == 0
- ){
- if( *p == ',' ){
- syntax.curly[i].flag = ARGLIST_PAREN;
- return p;
- } else
- syntax.curly[i].flag = ARGLIST_NO_PAREN;
- }
- if( *p != ')' )
- disp_error_message("parse_simpleexpr: unmatch ')'",p);
- ++p;
- } else if(ISDIGIT(*p) || ((*p=='-' || *p=='+') && ISDIGIT(p[1]))){
- char *np;
- while(*p == '0' && ISDIGIT(p[1])) p++;
- i=strtoul(p,&np,0);
- add_scripti(i);
- p=np;
- } else if(*p=='"'){
- add_scriptc(C_STR);
- p++;
- while( *p && *p != '"' ){
- if( (unsigned char)p[-1] <= 0x7e && *p == '\\' )
- {
- char buf[8];
- size_t len = skip_escaped_c(p) - p;
- size_t n = sv_unescape_c(buf, p, len);
- if( n != 1 )
- ShowDebug("parse_simpleexpr: unexpected length %d after unescape (\"%.*s\" -> %.*s)\n", (int)n, (int)len, p, (int)n, buf);
- p += len;
- add_scriptb(*buf);
- continue;
- }
- else if( *p == '\n' )
- disp_error_message("parse_simpleexpr: unexpected newline @ string",p);
- add_scriptb(*p++);
- }
- if(!*p)
- disp_error_message("parse_simpleexpr: unexpected eof @ string",p);
- add_scriptb(0);
- p++; //'"'
- } else {
- int l;
- const char* pv;
-
- // label , register , function etc
- if(skip_word(p)==p)
- disp_error_message("parse_simpleexpr: unexpected character",p);
-
- l=add_word(p);
- if( str_data[l].type == C_FUNC || str_data[l].type == C_USERFUNC || str_data[l].type == C_USERFUNC_POS)
- return parse_callfunc(p,1,0);
-#ifdef SCRIPT_CALLFUNC_CHECK
- else {
- const char* name = get_str(l);
- if( strdb_get(userfunc_db,name) != NULL ) {
- return parse_callfunc(p,1,1);
- }
- }
-#endif
-
- if( (pv = parse_variable(p)) )
- {// successfully processed a variable assignment
- return pv;
- }
-
- p=skip_word(p);
- if( *p == '[' ){
- // array(name[i] => getelementofarray(name,i) )
- add_scriptl(buildin_getelementofarray_ref);
- add_scriptc(C_ARG);
- add_scriptl(l);
-
- p=parse_subexpr(p+1,-1);
- p=skip_space(p);
- if( *p != ']' )
- disp_error_message("parse_simpleexpr: unmatch ']'",p);
- ++p;
- add_scriptc(C_FUNC);
- }else
- add_scriptl(l);
-
- }
-
- return p;
-}
-
-/*==========================================
- * Analysis of the expression
- *------------------------------------------*/
-const char* parse_subexpr(const char* p,int limit)
-{
- int op,opl,len;
- const char* tmpp;
-
- p=skip_space(p);
-
- if( *p == '-' ){
- tmpp = skip_space(p+1);
- if( *tmpp == ';' || *tmpp == ',' ){
- add_scriptl(LABEL_NEXTLINE);
- p++;
- return p;
- }
- }
-
- if((op=C_NEG,*p=='-') || (op=C_LNOT,*p=='!') || (op=C_NOT,*p=='~')){
- p=parse_subexpr(p+1,10);
- add_scriptc(op);
- } else
- p=parse_simpleexpr(p);
- p=skip_space(p);
- while((
- (op=C_OP3,opl=0,len=1,*p=='?') ||
- (op=C_ADD,opl=8,len=1,*p=='+') ||
- (op=C_SUB,opl=8,len=1,*p=='-') ||
- (op=C_MUL,opl=9,len=1,*p=='*') ||
- (op=C_DIV,opl=9,len=1,*p=='/') ||
- (op=C_MOD,opl=9,len=1,*p=='%') ||
- (op=C_LAND,opl=2,len=2,*p=='&' && p[1]=='&') ||
- (op=C_AND,opl=6,len=1,*p=='&') ||
- (op=C_LOR,opl=1,len=2,*p=='|' && p[1]=='|') ||
- (op=C_OR,opl=5,len=1,*p=='|') ||
- (op=C_XOR,opl=4,len=1,*p=='^') ||
- (op=C_EQ,opl=3,len=2,*p=='=' && p[1]=='=') ||
- (op=C_NE,opl=3,len=2,*p=='!' && p[1]=='=') ||
- (op=C_R_SHIFT,opl=7,len=2,*p=='>' && p[1]=='>') ||
- (op=C_GE,opl=3,len=2,*p=='>' && p[1]=='=') ||
- (op=C_GT,opl=3,len=1,*p=='>') ||
- (op=C_L_SHIFT,opl=7,len=2,*p=='<' && p[1]=='<') ||
- (op=C_LE,opl=3,len=2,*p=='<' && p[1]=='=') ||
- (op=C_LT,opl=3,len=1,*p=='<')) && opl>limit){
- p+=len;
- if(op == C_OP3) {
- p=parse_subexpr(p,-1);
- p=skip_space(p);
- if( *(p++) != ':')
- disp_error_message("parse_subexpr: need ':'", p-1);
- p=parse_subexpr(p,-1);
- } else {
- p=parse_subexpr(p,opl);
- }
- add_scriptc(op);
- p=skip_space(p);
- }
-
- return p; /* return first untreated operator */
-}
-
-/*==========================================
- * Evaluation of the expression
- *------------------------------------------*/
-const char* parse_expr(const char *p)
-{
- switch(*p){
- case ')': case ';': case ':': case '[': case ']':
- case '}':
- disp_error_message("parse_expr: unexpected char",p);
- }
- p=parse_subexpr(p,-1);
- return p;
-}
-
-/*==========================================
- * Analysis of the line
- *------------------------------------------*/
-const char* parse_line(const char* p)
-{
- const char* p2;
-
- p=skip_space(p);
- if(*p==';') {
- //Close decision for if(); for(); while();
- p = parse_syntax_close(p + 1);
- return p;
- }
- if(*p==')' && parse_syntax_for_flag)
- return p+1;
-
- p = skip_space(p);
- if(p[0] == '{') {
- syntax.curly[syntax.curly_count].type = TYPE_NULL;
- syntax.curly[syntax.curly_count].count = -1;
- syntax.curly[syntax.curly_count].index = -1;
- syntax.curly_count++;
- return p + 1;
- } else if(p[0] == '}') {
- return parse_curly_close(p);
- }
-
- // Syntax-related processing
- p2 = parse_syntax(p);
- if(p2 != NULL)
- return p2;
-
- // attempt to process a variable assignment
- p2 = parse_variable(p);
-
- if( p2 != NULL )
- {// variable assignment processed so leave the method
- return parse_syntax_close(p2 + 1);
- }
-
- p = parse_callfunc(p,0,0);
- p = skip_space(p);
-
- if(parse_syntax_for_flag) {
- if( *p != ')' )
- disp_error_message("parse_line: need ')'",p);
- } else {
- if( *p != ';' )
- disp_error_message("parse_line: need ';'",p);
- }
-
- //Binding decision for if(), for(), while()
- p = parse_syntax_close(p+1);
-
- return p;
-}
-
-// { ... } Closing process
-const char* parse_curly_close(const char* p)
-{
- if(syntax.curly_count <= 0) {
- disp_error_message("parse_curly_close: unexpected string",p);
- return p + 1;
- } else if(syntax.curly[syntax.curly_count-1].type == TYPE_NULL) {
- syntax.curly_count--;
- //Close decision if, for , while
- p = parse_syntax_close(p + 1);
- return p;
- } else if(syntax.curly[syntax.curly_count-1].type == TYPE_SWITCH) {
- //Closing switch()
- int pos = syntax.curly_count-1;
- char label[256];
- int l;
- // Remove temporary variables
- sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index);
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
-
- // Go to the end pointer unconditionally
- sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index);
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
-
- // You are here labeled
- sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
- l=add_str(label);
- set_label(l,script_pos, p);
-
- if(syntax.curly[pos].flag) {
- //Exists default
- sprintf(label,"goto __SW%x_DEF;",syntax.curly[pos].index);
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
- }
-
- // Label end
- sprintf(label,"__SW%x_FIN",syntax.curly[pos].index);
- l=add_str(label);
- set_label(l,script_pos, p);
- linkdb_final(&syntax.curly[pos].case_label); // free the list of case label
- syntax.curly_count--;
- //Closing decision if, for , while
- p = parse_syntax_close(p + 1);
- return p;
- } else {
- disp_error_message("parse_curly_close: unexpected string",p);
- return p + 1;
- }
-}
-
-// Syntax-related processing
-// break, case, continue, default, do, for, function,
-// if, switch, while ? will handle this internally.
-const char* parse_syntax(const char* p)
-{
- const char *p2 = skip_word(p);
-
- switch(*p) {
- case 'B':
- case 'b':
- if(p2 - p == 5 && !strncasecmp(p,"break",5)) {
- // break Processing
- char label[256];
- int pos = syntax.curly_count - 1;
- while(pos >= 0) {
- if(syntax.curly[pos].type == TYPE_DO) {
- sprintf(label,"goto __DO%x_FIN;",syntax.curly[pos].index);
- break;
- } else if(syntax.curly[pos].type == TYPE_FOR) {
- sprintf(label,"goto __FR%x_FIN;",syntax.curly[pos].index);
- break;
- } else if(syntax.curly[pos].type == TYPE_WHILE) {
- sprintf(label,"goto __WL%x_FIN;",syntax.curly[pos].index);
- break;
- } else if(syntax.curly[pos].type == TYPE_SWITCH) {
- sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index);
- break;
- }
- pos--;
- }
- if(pos < 0) {
- disp_error_message("parse_syntax: unexpected 'break'",p);
- } else {
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
- }
- p = skip_space(p2);
- if(*p != ';')
- disp_error_message("parse_syntax: need ';'",p);
- // Closing decision if, for , while
- p = parse_syntax_close(p + 1);
- return p;
- }
- break;
- case 'c':
- case 'C':
- if(p2 - p == 4 && !strncasecmp(p,"case",4)) {
- //Processing case
- int pos = syntax.curly_count-1;
- if(pos < 0 || syntax.curly[pos].type != TYPE_SWITCH) {
- disp_error_message("parse_syntax: unexpected 'case' ",p);
- return p+1;
- } else {
- char label[256];
- int l,v;
- char *np;
- if(syntax.curly[pos].count != 1) {
- //Jump for FALLTHRU
- sprintf(label,"goto __SW%x_%xJ;",syntax.curly[pos].index,syntax.curly[pos].count);
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
-
- // You are here labeled
- sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
- l=add_str(label);
- set_label(l,script_pos, p);
- }
- //Decision statement switch
- p = skip_space(p2);
- if(p == p2) {
- disp_error_message("parse_syntax: expect space ' '",p);
- }
- // check whether case label is integer or not
- v = strtol(p,&np,0);
- if(np == p) { //Check for constants
- p2 = skip_word(p);
- v = p2-p; // length of word at p2
- memcpy(label,p,v);
- label[v]='\0';
- if( !script_get_constant(label, &v) )
- disp_error_message("parse_syntax: 'case' label not integer",p);
- p = skip_word(p);
- } else { //Numeric value
- if((*p == '-' || *p == '+') && ISDIGIT(p[1])) // pre-skip because '-' can not skip_word
- p++;
- p = skip_word(p);
- if(np != p)
- disp_error_message("parse_syntax: 'case' label not integer",np);
- }
- p = skip_space(p);
- if(*p != ':')
- disp_error_message("parse_syntax: expect ':'",p);
- sprintf(label,"if(%d != $@__SW%x_VAL) goto __SW%x_%x;",
- v,syntax.curly[pos].index,syntax.curly[pos].index,syntax.curly[pos].count+1);
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- // Bad I do not parse twice
- p2 = parse_line(label);
- parse_line(p2);
- syntax.curly_count--;
- if(syntax.curly[pos].count != 1) {
- // Label after the completion of FALLTHRU
- sprintf(label,"__SW%x_%xJ",syntax.curly[pos].index,syntax.curly[pos].count);
- l=add_str(label);
- set_label(l,script_pos,p);
- }
- // check duplication of case label [Rayce]
- if(linkdb_search(&syntax.curly[pos].case_label, (void*)__64BPRTSIZE(v)) != NULL)
- disp_error_message("parse_syntax: dup 'case'",p);
- linkdb_insert(&syntax.curly[pos].case_label, (void*)__64BPRTSIZE(v), (void*)1);
-
- sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index);
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
-
- parse_line(label);
- syntax.curly_count--;
- syntax.curly[pos].count++;
- }
- return p + 1;
- } else if(p2 - p == 8 && !strncasecmp(p,"continue",8)) {
- // Processing continue
- char label[256];
- int pos = syntax.curly_count - 1;
- while(pos >= 0) {
- if(syntax.curly[pos].type == TYPE_DO) {
- sprintf(label,"goto __DO%x_NXT;",syntax.curly[pos].index);
- syntax.curly[pos].flag = 1; //Flag put the link for continue
- break;
- } else if(syntax.curly[pos].type == TYPE_FOR) {
- sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index);
- break;
- } else if(syntax.curly[pos].type == TYPE_WHILE) {
- sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index);
- break;
- }
- pos--;
- }
- if(pos < 0) {
- disp_error_message("parse_syntax: unexpected 'continue'",p);
- } else {
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
- }
- p = skip_space(p2);
- if(*p != ';')
- disp_error_message("parse_syntax: need ';'",p);
- //Closing decision if, for , while
- p = parse_syntax_close(p + 1);
- return p;
- }
- break;
- case 'd':
- case 'D':
- if(p2 - p == 7 && !strncasecmp(p,"default",7)) {
- // Switch - default processing
- int pos = syntax.curly_count-1;
- if(pos < 0 || syntax.curly[pos].type != TYPE_SWITCH) {
- disp_error_message("parse_syntax: unexpected 'default'",p);
- } else if(syntax.curly[pos].flag) {
- disp_error_message("parse_syntax: dup 'default'",p);
- } else {
- char label[256];
- int l;
- // Put the label location
- p = skip_space(p2);
- if(*p != ':') {
- disp_error_message("parse_syntax: need ':'",p);
- }
- sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
- l=add_str(label);
- set_label(l,script_pos,p);
-
- // Skip to the next link w/o condition
- sprintf(label,"goto __SW%x_%x;",syntax.curly[pos].index,syntax.curly[pos].count+1);
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
-
- // The default label
- sprintf(label,"__SW%x_DEF",syntax.curly[pos].index);
- l=add_str(label);
- set_label(l,script_pos,p);
-
- syntax.curly[syntax.curly_count - 1].flag = 1;
- syntax.curly[pos].count++;
- }
- return p + 1;
- } else if(p2 - p == 2 && !strncasecmp(p,"do",2)) {
- int l;
- char label[256];
- p=skip_space(p2);
-
- syntax.curly[syntax.curly_count].type = TYPE_DO;
- syntax.curly[syntax.curly_count].count = 1;
- syntax.curly[syntax.curly_count].index = syntax.index++;
- syntax.curly[syntax.curly_count].flag = 0;
- // Label of the (do) form here
- sprintf(label,"__DO%x_BGN",syntax.curly[syntax.curly_count].index);
- l=add_str(label);
- set_label(l,script_pos,p);
- syntax.curly_count++;
- return p;
- }
- break;
- case 'f':
- case 'F':
- if(p2 - p == 3 && !strncasecmp(p,"for",3)) {
- int l;
- char label[256];
- int pos = syntax.curly_count;
- syntax.curly[syntax.curly_count].type = TYPE_FOR;
- syntax.curly[syntax.curly_count].count = 1;
- syntax.curly[syntax.curly_count].index = syntax.index++;
- syntax.curly[syntax.curly_count].flag = 0;
- syntax.curly_count++;
-
- p=skip_space(p2);
-
- if(*p != '(')
- disp_error_message("parse_syntax: need '('",p);
- p++;
-
- // Execute the initialization statement
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- p=parse_line(p);
- syntax.curly_count--;
-
- // Form the start of label decision
- sprintf(label,"__FR%x_J",syntax.curly[pos].index);
- l=add_str(label);
- set_label(l,script_pos,p);
-
- p=skip_space(p);
- if(*p == ';') {
- // For (; Because the pattern of always true ;)
- ;
- } else {
- // Skip to the end point if the condition is false
- sprintf(label,"__FR%x_FIN",syntax.curly[pos].index);
- add_scriptl(add_str("jump_zero"));
- add_scriptc(C_ARG);
- p=parse_expr(p);
- p=skip_space(p);
- add_scriptl(add_str(label));
- add_scriptc(C_FUNC);
- }
- if(*p != ';')
- disp_error_message("parse_syntax: need ';'",p);
- p++;
-
- // Skip to the beginning of the loop
- sprintf(label,"goto __FR%x_BGN;",syntax.curly[pos].index);
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
-
- // Labels to form the next loop
- sprintf(label,"__FR%x_NXT",syntax.curly[pos].index);
- l=add_str(label);
- set_label(l,script_pos,p);
-
- // Process the next time you enter the loop
- // A ')' last for; flag to be treated as'
- parse_syntax_for_flag = 1;
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- p=parse_line(p);
- syntax.curly_count--;
- parse_syntax_for_flag = 0;
-
- // Skip to the determination process conditions
- sprintf(label,"goto __FR%x_J;",syntax.curly[pos].index);
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
-
- // Loop start labeling
- sprintf(label,"__FR%x_BGN",syntax.curly[pos].index);
- l=add_str(label);
- set_label(l,script_pos,p);
- return p;
- }
- else if( p2 - p == 8 && strncasecmp(p,"function",8) == 0 )
- {// internal script function
- const char *func_name;
-
- func_name = skip_space(p2);
- p = skip_word(func_name);
- if( p == func_name )
- disp_error_message("parse_syntax:function: function name is missing or invalid", p);
- p2 = skip_space(p);
- if( *p2 == ';' )
- {// function <name> ;
- // function declaration - just register the name
- int l;
- l = add_word(func_name);
- if( str_data[l].type == C_NOP )// register only, if the name was not used by something else
- str_data[l].type = C_USERFUNC;
- else if( str_data[l].type == C_USERFUNC )
- ; // already registered
- else
- disp_error_message("parse_syntax:function: function name is invalid", func_name);
-
- // Close condition of if, for, while
- p = parse_syntax_close(p2 + 1);
- return p;
- }
- else if(*p2 == '{')
- {// function <name> <line/block of code>
- char label[256];
- int l;
-
- syntax.curly[syntax.curly_count].type = TYPE_USERFUNC;
- syntax.curly[syntax.curly_count].count = 1;
- syntax.curly[syntax.curly_count].index = syntax.index++;
- syntax.curly[syntax.curly_count].flag = 0;
- ++syntax.curly_count;
-
- // Jump over the function code
- sprintf(label, "goto __FN%x_FIN;", syntax.curly[syntax.curly_count-1].index);
- syntax.curly[syntax.curly_count].type = TYPE_NULL;
- ++syntax.curly_count;
- parse_line(label);
- --syntax.curly_count;
-
- // Set the position of the function (label)
- l=add_word(func_name);
- if( str_data[l].type == C_NOP || str_data[l].type == C_USERFUNC )// register only, if the name was not used by something else
- {
- str_data[l].type = C_USERFUNC;
- set_label(l, script_pos, p);
- if( parse_options&SCRIPT_USE_LABEL_DB )
- strdb_iput(scriptlabel_db, get_str(l), script_pos);
- }
- else
- disp_error_message("parse_syntax:function: function name is invalid", func_name);
-
- return skip_space(p);
- }
- else
- {
- disp_error_message("expect ';' or '{' at function syntax",p);
- }
- }
- break;
- case 'i':
- case 'I':
- if(p2 - p == 2 && !strncasecmp(p,"if",2)) {
- // If process
- char label[256];
- p=skip_space(p2);
- if(*p != '(') { //Prevent if this {} non-c syntax. from Rayce (jA)
- disp_error_message("need '('",p);
- }
- syntax.curly[syntax.curly_count].type = TYPE_IF;
- syntax.curly[syntax.curly_count].count = 1;
- syntax.curly[syntax.curly_count].index = syntax.index++;
- syntax.curly[syntax.curly_count].flag = 0;
- sprintf(label,"__IF%x_%x",syntax.curly[syntax.curly_count].index,syntax.curly[syntax.curly_count].count);
- syntax.curly_count++;
- add_scriptl(add_str("jump_zero"));
- add_scriptc(C_ARG);
- p=parse_expr(p);
- p=skip_space(p);
- add_scriptl(add_str(label));
- add_scriptc(C_FUNC);
- return p;
- }
- break;
- case 's':
- case 'S':
- if(p2 - p == 6 && !strncasecmp(p,"switch",6)) {
- // Processing of switch ()
- char label[256];
- p=skip_space(p2);
- if(*p != '(') {
- disp_error_message("need '('",p);
- }
- syntax.curly[syntax.curly_count].type = TYPE_SWITCH;
- syntax.curly[syntax.curly_count].count = 1;
- syntax.curly[syntax.curly_count].index = syntax.index++;
- syntax.curly[syntax.curly_count].flag = 0;
- sprintf(label,"$@__SW%x_VAL",syntax.curly[syntax.curly_count].index);
- syntax.curly_count++;
- add_scriptl(add_str("set"));
- add_scriptc(C_ARG);
- add_scriptl(add_str(label));
- p=parse_expr(p);
- p=skip_space(p);
- if(*p != '{') {
- disp_error_message("parse_syntax: need '{'",p);
- }
- add_scriptc(C_FUNC);
- return p + 1;
- }
- break;
- case 'w':
- case 'W':
- if(p2 - p == 5 && !strncasecmp(p,"while",5)) {
- int l;
- char label[256];
- p=skip_space(p2);
- if(*p != '(') {
- disp_error_message("need '('",p);
- }
- syntax.curly[syntax.curly_count].type = TYPE_WHILE;
- syntax.curly[syntax.curly_count].count = 1;
- syntax.curly[syntax.curly_count].index = syntax.index++;
- syntax.curly[syntax.curly_count].flag = 0;
- // Form the start of label decision
- sprintf(label,"__WL%x_NXT",syntax.curly[syntax.curly_count].index);
- l=add_str(label);
- set_label(l,script_pos,p);
-
- // Skip to the end point if the condition is false
- sprintf(label,"__WL%x_FIN",syntax.curly[syntax.curly_count].index);
- syntax.curly_count++;
- add_scriptl(add_str("jump_zero"));
- add_scriptc(C_ARG);
- p=parse_expr(p);
- p=skip_space(p);
- add_scriptl(add_str(label));
- add_scriptc(C_FUNC);
- return p;
- }
- break;
- }
- return NULL;
-}
-
-const char* parse_syntax_close(const char *p) {
- // If (...) for (...) hoge (); as to make sure closed closed once again
- int flag;
-
- do {
- p = parse_syntax_close_sub(p,&flag);
- } while(flag);
- return p;
-}
-
-// Close judgment if, for, while, of do
-// flag == 1 : closed
-// flag == 0 : not closed
-const char* parse_syntax_close_sub(const char* p,int* flag)
-{
- char label[256];
- int pos = syntax.curly_count - 1;
- int l;
- *flag = 1;
-
- if(syntax.curly_count <= 0) {
- *flag = 0;
- return p;
- } else if(syntax.curly[pos].type == TYPE_IF) {
- const char *bp = p;
- const char *p2;
-
- // if-block and else-block end is a new line
- parse_nextline(false, p);
-
- // Skip to the last location if
- sprintf(label,"goto __IF%x_FIN;",syntax.curly[pos].index);
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
-
- // Put the label of the location
- sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
- l=add_str(label);
- set_label(l,script_pos,p);
-
- syntax.curly[pos].count++;
- p = skip_space(p);
- p2 = skip_word(p);
- if(!syntax.curly[pos].flag && p2 - p == 4 && !strncasecmp(p,"else",4)) {
- // else or else - if
- p = skip_space(p2);
- p2 = skip_word(p);
- if(p2 - p == 2 && !strncasecmp(p,"if",2)) {
- // else - if
- p=skip_space(p2);
- if(*p != '(') {
- disp_error_message("need '('",p);
- }
- sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
- add_scriptl(add_str("jump_zero"));
- add_scriptc(C_ARG);
- p=parse_expr(p);
- p=skip_space(p);
- add_scriptl(add_str(label));
- add_scriptc(C_FUNC);
- *flag = 0;
- return p;
- } else {
- // else
- if(!syntax.curly[pos].flag) {
- syntax.curly[pos].flag = 1;
- *flag = 0;
- return p;
- }
- }
- }
- // Close if
- syntax.curly_count--;
- // Put the label of the final location
- sprintf(label,"__IF%x_FIN",syntax.curly[pos].index);
- l=add_str(label);
- set_label(l,script_pos,p);
- if(syntax.curly[pos].flag == 1) {
- // Because the position of the pointer is the same if not else for this
- return bp;
- }
- return p;
- } else if(syntax.curly[pos].type == TYPE_DO) {
- int l;
- char label[256];
- const char *p2;
-
- if(syntax.curly[pos].flag) {
- // (Come here continue) to form the label here
- sprintf(label,"__DO%x_NXT",syntax.curly[pos].index);
- l=add_str(label);
- set_label(l,script_pos,p);
- }
-
- // Skip to the end point if the condition is false
- p = skip_space(p);
- p2 = skip_word(p);
- if(p2 - p != 5 || strncasecmp(p,"while",5))
- disp_error_message("parse_syntax: need 'while'",p);
-
- p = skip_space(p2);
- if(*p != '(') {
- disp_error_message("need '('",p);
- }
-
- // do-block end is a new line
- parse_nextline(false, p);
-
- sprintf(label,"__DO%x_FIN",syntax.curly[pos].index);
- add_scriptl(add_str("jump_zero"));
- add_scriptc(C_ARG);
- p=parse_expr(p);
- p=skip_space(p);
- add_scriptl(add_str(label));
- add_scriptc(C_FUNC);
-
- // Skip to the starting point
- sprintf(label,"goto __DO%x_BGN;",syntax.curly[pos].index);
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
-
- // Form label of the end point conditions
- sprintf(label,"__DO%x_FIN",syntax.curly[pos].index);
- l=add_str(label);
- set_label(l,script_pos,p);
- p = skip_space(p);
- if(*p != ';') {
- disp_error_message("parse_syntax: need ';'",p);
- return p+1;
- }
- p++;
- syntax.curly_count--;
- return p;
- } else if(syntax.curly[pos].type == TYPE_FOR) {
- // for-block end is a new line
- parse_nextline(false, p);
-
- // Skip to the next loop
- sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index);
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
-
- // End for labeling
- sprintf(label,"__FR%x_FIN",syntax.curly[pos].index);
- l=add_str(label);
- set_label(l,script_pos,p);
- syntax.curly_count--;
- return p;
- } else if(syntax.curly[pos].type == TYPE_WHILE) {
- // while-block end is a new line
- parse_nextline(false, p);
-
- // Skip to the decision while
- sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index);
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
-
- // End while labeling
- sprintf(label,"__WL%x_FIN",syntax.curly[pos].index);
- l=add_str(label);
- set_label(l,script_pos,p);
- syntax.curly_count--;
- return p;
- } else if(syntax.curly[syntax.curly_count-1].type == TYPE_USERFUNC) {
- int pos = syntax.curly_count-1;
- char label[256];
- int l;
- // Back
- sprintf(label,"return;");
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
-
- // Put the label of the location
- sprintf(label,"__FN%x_FIN",syntax.curly[pos].index);
- l=add_str(label);
- set_label(l,script_pos,p);
- syntax.curly_count--;
- return p;
- } else {
- *flag = 0;
- return p;
- }
-}
-
-/*==========================================
- * Added built-in functions
- *------------------------------------------*/
-static void add_buildin_func(void)
-{
- int i,n;
- const char* p;
- for( i = 0; buildin_func[i].func; i++ )
- {
- // arg must follow the pattern: (v|s|i|r|l)*\?*\*?
- // 'v' - value (either string or int or reference)
- // 's' - string
- // 'i' - int
- // 'r' - reference (of a variable)
- // 'l' - label
- // '?' - one optional parameter
- // '*' - unknown number of optional parameters
- p = buildin_func[i].arg;
- while( *p == 'v' || *p == 's' || *p == 'i' || *p == 'r' || *p == 'l' ) ++p;
- while( *p == '?' ) ++p;
- if( *p == '*' ) ++p;
- if( *p != 0){
- ShowWarning("add_buildin_func: ignoring function \"%s\" with invalid arg \"%s\".\n", buildin_func[i].name, buildin_func[i].arg);
- } else if( *skip_word(buildin_func[i].name) != 0 ){
- ShowWarning("add_buildin_func: ignoring function with invalid name \"%s\" (must be a word).\n", buildin_func[i].name);
- } else {
- n = add_str(buildin_func[i].name);
- str_data[n].type = C_FUNC;
- str_data[n].val = i;
- str_data[n].func = buildin_func[i].func;
-
- if (!strcmp(buildin_func[i].name, "set")) buildin_set_ref = n;
- else if (!strcmp(buildin_func[i].name, "callsub")) buildin_callsub_ref = n;
- else if (!strcmp(buildin_func[i].name, "callfunc")) buildin_callfunc_ref = n;
- else if( !strcmp(buildin_func[i].name, "getelementofarray") ) buildin_getelementofarray_ref = n;
- }
- }
-}
-
-/// Retrieves the value of a constant.
-bool script_get_constant(const char* name, int* value)
-{
- int n = search_str(name);
-
- if( n == -1 || str_data[n].type != C_INT )
- {// not found or not a constant
- return false;
- }
- value[0] = str_data[n].val;
-
- return true;
-}
-
-/// Creates new constant or parameter with given value.
-void script_set_constant(const char* name, int value, bool isparameter)
-{
- int n = add_str(name);
-
- if( str_data[n].type == C_NOP )
- {// new
- str_data[n].type = isparameter ? C_PARAM : C_INT;
- str_data[n].val = value;
- }
- else if( str_data[n].type == C_PARAM || str_data[n].type == C_INT )
- {// existing parameter or constant
- ShowError("script_set_constant: Attempted to overwrite existing %s '%s' (old value=%d, new value=%d).\n", ( str_data[n].type == C_PARAM ) ? "parameter" : "constant", name, str_data[n].val, value);
- }
- else
- {// existing name
- ShowError("script_set_constant: Invalid name for %s '%s' (already defined as %s).\n", isparameter ? "parameter" : "constant", name, script_op2name(str_data[n].type));
- }
-}
-
-/*==========================================
- * Reading constant databases
- * const.txt
- *------------------------------------------*/
-static void read_constdb(void)
-{
- FILE *fp;
- char line[1024],name[1024],val[1024];
- int type;
-
- sprintf(line, "%s/const.txt", db_path);
- fp=fopen(line, "r");
- if(fp==NULL){
- ShowError("can't read %s\n", line);
- return ;
- }
- while(fgets(line, sizeof(line), fp))
- {
- if(line[0]=='/' && line[1]=='/')
- continue;
- type=0;
- if(sscanf(line,"%[A-Za-z0-9_],%[-0-9xXA-Fa-f],%d",name,val,&type)>=2 ||
- sscanf(line,"%[A-Za-z0-9_] %[-0-9xXA-Fa-f] %d",name,val,&type)>=2){
- script_set_constant(name, (int)strtol(val, NULL, 0), (bool)type);
- }
- }
- fclose(fp);
-}
-
-/*==========================================
- * Display emplacement line of script
- *------------------------------------------*/
-static const char* script_print_line(StringBuf* buf, const char* p, const char* mark, int line)
-{
- int i;
- if( p == NULL || !p[0] ) return NULL;
- if( line < 0 )
- StringBuf_Printf(buf, "*% 5d : ", -line);
- else
- StringBuf_Printf(buf, " % 5d : ", line);
- for(i=0;p[i] && p[i] != '\n';i++){
- if(p + i != mark)
- StringBuf_Printf(buf, "%c", p[i]);
- else
- StringBuf_Printf(buf, "\'%c\'", p[i]);
- }
- StringBuf_AppendStr(buf, "\n");
- return p+i+(p[i] == '\n' ? 1 : 0);
-}
-
-void script_error(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos)
-{
- // Find the line where the error occurred
- int j;
- int line = start_line;
- const char *p;
- const char *linestart[5] = { NULL, NULL, NULL, NULL, NULL };
- StringBuf buf;
-
- for(p=src;p && *p;line++){
- const char *lineend=strchr(p,'\n');
- if(lineend==NULL || error_pos<lineend){
- break;
- }
- for( j = 0; j < 4; j++ ) {
- linestart[j] = linestart[j+1];
- }
- linestart[4] = p;
- p=lineend+1;
- }
-
- StringBuf_Init(&buf);
- StringBuf_AppendStr(&buf, "\a\n");
- StringBuf_Printf(&buf, "script error on %s line %d\n", file, line);
- StringBuf_Printf(&buf, " %s\n", error_msg);
- for(j = 0; j < 5; j++ ) {
- script_print_line(&buf, linestart[j], NULL, line + j - 5);
- }
- p = script_print_line(&buf, p, error_pos, -line);
- for(j = 0; j < 5; j++) {
- p = script_print_line(&buf, p, NULL, line + j + 1 );
- }
- ShowError("%s", StringBuf_Value(&buf));
- StringBuf_Destroy(&buf);
-}
-
-/*==========================================
- * Analysis of the script
- *------------------------------------------*/
-struct script_code* parse_script(const char *src,const char *file,int line,int options)
-{
- const char *p,*tmpp;
- int i;
- struct script_code* code = NULL;
- static int first=1;
- char end;
- bool unresolved_names = false;
-
- if( src == NULL )
- return NULL;// empty script
-
- memset(&syntax,0,sizeof(syntax));
- if(first){
- add_buildin_func();
- read_constdb();
- first=0;
- }
-
- script_buf=(unsigned char *)aMalloc(SCRIPT_BLOCK_SIZE*sizeof(unsigned char));
- script_pos=0;
- script_size=SCRIPT_BLOCK_SIZE;
- parse_nextline(true, NULL);
-
- // who called parse_script is responsible for clearing the database after using it, but just in case... lets clear it here
- if( options&SCRIPT_USE_LABEL_DB )
- db_clear(scriptlabel_db);
- parse_options = options;
-
- if( setjmp( error_jump ) != 0 ) {
- //Restore program state when script has problems. [from jA]
- int i;
- const int size = ARRAYLENGTH(syntax.curly);
- if( error_report )
- script_error(src,file,line,error_msg,error_pos);
- aFree( error_msg );
- aFree( script_buf );
- script_pos = 0;
- script_size = 0;
- script_buf = NULL;
- for(i=LABEL_START;i<str_num;i++)
- if(str_data[i].type == C_NOP) str_data[i].type = C_NAME;
- for(i=0; i<size; i++)
- linkdb_final(&syntax.curly[i].case_label);
- return NULL;
- }
-
- parse_syntax_for_flag=0;
- p=src;
- p=skip_space(p);
- if( options&SCRIPT_IGNORE_EXTERNAL_BRACKETS )
- {// does not require brackets around the script
- if( *p == '\0' && !(options&SCRIPT_RETURN_EMPTY_SCRIPT) )
- {// empty script and can return NULL
- aFree( script_buf );
- script_pos = 0;
- script_size = 0;
- script_buf = NULL;
- return NULL;
- }
- end = '\0';
- }
- else
- {// requires brackets around the script
- if( *p != '{' )
- disp_error_message("not found '{'",p);
- p = skip_space(p+1);
- if( *p == '}' && !(options&SCRIPT_RETURN_EMPTY_SCRIPT) )
- {// empty script and can return NULL
- aFree( script_buf );
- script_pos = 0;
- script_size = 0;
- script_buf = NULL;
- return NULL;
- }
- end = '}';
- }
-
- // clear references of labels, variables and internal functions
- for(i=LABEL_START;i<str_num;i++){
- if(
- str_data[i].type==C_POS || str_data[i].type==C_NAME ||
- str_data[i].type==C_USERFUNC || str_data[i].type == C_USERFUNC_POS
- ){
- str_data[i].type=C_NOP;
- str_data[i].backpatch=-1;
- str_data[i].label=-1;
- }
- }
-
- while( syntax.curly_count != 0 || *p != end )
- {
- if( *p == '\0' )
- disp_error_message("unexpected end of script",p);
- // Special handling only label
- tmpp=skip_space(skip_word(p));
- if(*tmpp==':' && !(!strncasecmp(p,"default:",8) && p + 7 == tmpp)){
- i=add_word(p);
- set_label(i,script_pos,p);
- if( parse_options&SCRIPT_USE_LABEL_DB )
- strdb_iput(scriptlabel_db, get_str(i), script_pos);
- p=tmpp+1;
- p=skip_space(p);
- continue;
- }
-
- // All other lumped
- p=parse_line(p);
- p=skip_space(p);
-
- parse_nextline(false, p);
- }
-
- add_scriptc(C_NOP);
-
- // trim code to size
- script_size = script_pos;
- RECREATE(script_buf,unsigned char,script_pos);
-
- // default unknown references to variables
- for(i=LABEL_START;i<str_num;i++){
- if(str_data[i].type==C_NOP){
- int j,next;
- str_data[i].type=C_NAME;
- str_data[i].label=i;
- for(j=str_data[i].backpatch;j>=0 && j!=0x00ffffff;){
- next=GETVALUE(script_buf,j);
- SETVALUE(script_buf,j,i);
- j=next;
- }
- }
- else if( str_data[i].type == C_USERFUNC )
- {// 'function name;' without follow-up code
- ShowError("parse_script: function '%s' declared but not defined.\n", str_buf+str_data[i].str);
- unresolved_names = true;
- }
- }
-
- if( unresolved_names )
- {
- disp_error_message("parse_script: unresolved function references", p);
- }
-
-#ifdef DEBUG_DISP
- for(i=0;i<script_pos;i++){
- if((i&15)==0) ShowMessage("%04x : ",i);
- ShowMessage("%02x ",script_buf[i]);
- if((i&15)==15) ShowMessage("\n");
- }
- ShowMessage("\n");
-#endif
-#ifdef DEBUG_DISASM
- {
- int i = 0,j;
- while(i < script_pos) {
- c_op op = get_com(script_buf,&i);
-
- ShowMessage("%06x %s", i, script_op2name(op));
- j = i;
- switch(op) {
- case C_INT:
- ShowMessage(" %d", get_num(script_buf,&i));
- break;
- case C_POS:
- ShowMessage(" 0x%06x", *(int*)(script_buf+i)&0xffffff);
- i += 3;
- break;
- case C_NAME:
- j = (*(int*)(script_buf+i)&0xffffff);
- ShowMessage(" %s", ( j == 0xffffff ) ? "?? unknown ??" : get_str(j));
- i += 3;
- break;
- case C_STR:
- j = strlen(script_buf + i);
- ShowMessage(" %s", script_buf + i);
- i += j+1;
- break;
- }
- ShowMessage(CL_CLL"\n");
- }
- }
-#endif
-
- CREATE(code,struct script_code,1);
- code->script_buf = script_buf;
- code->script_size = script_size;
- code->script_vars = idb_alloc(DB_OPT_RELEASE_DATA);
- return code;
-}
-
-/// Returns the player attached to this script, identified by the rid.
-/// If there is no player attached, the script is terminated.
-TBL_PC *script_rid2sd(struct script_state *st)
-{
- TBL_PC *sd=map_id2sd(st->rid);
- if(!sd){
- ShowError("script_rid2sd: fatal error ! player not attached!\n");
- script_reportfunc(st);
- script_reportsrc(st);
- st->state = END;
- }
- return sd;
-}
-
-/// Dereferences a variable/constant, replacing it with a copy of the value.
-///
-/// @param st Script state
-/// @param data Variable/constant
-void get_val(struct script_state* st, struct script_data* data)
-{
- const char* name;
- char prefix;
- char postfix;
- TBL_PC* sd = NULL;
-
- if( !data_isreference(data) )
- return;// not a variable/constant
-
- name = reference_getname(data);
- prefix = name[0];
- postfix = name[strlen(name) - 1];
-
- //##TODO use reference_tovariable(data) when it's confirmed that it works [FlavioJS]
- if( !reference_toconstant(data) && not_server_variable(prefix) )
- {
- sd = script_rid2sd(st);
- if( sd == NULL )
- {// needs player attached
- if( postfix == '$' )
- {// string variable
- ShowWarning("script:get_val: cannot access player variable '%s', defaulting to \"\"\n", name);
- data->type = C_CONSTSTR;
- data->u.str = "";
- }
- else
- {// integer variable
- ShowWarning("script:get_val: cannot access player variable '%s', defaulting to 0\n", name);
- data->type = C_INT;
- data->u.num = 0;
- }
- return;
- }
- }
-
- if( postfix == '$' )
- {// string variable
-
- switch( prefix )
- {
- case '@':
- data->u.str = pc_readregstr(sd, data->u.num);
- break;
- case '$':
- data->u.str = mapreg_readregstr(data->u.num);
- break;
- case '#':
- if( name[1] == '#' )
- data->u.str = pc_readaccountreg2str(sd, name);// global
- else
- data->u.str = pc_readaccountregstr(sd, name);// local
- break;
- case '.':
- {
- struct DBMap* n =
- data->ref ? *data->ref:
- name[1] == '@' ? st->stack->var_function:// instance/scope variable
- st->script->script_vars;// npc variable
- if( n )
- data->u.str = (char*)idb_get(n,reference_getuid(data));
- else
- data->u.str = NULL;
- }
- break;
- case '\'':
- if (st->instance_id) {
- data->u.str = (char*)idb_get(instance[st->instance_id].vars,reference_getuid(data));
- } else {
- ShowWarning("script:get_val: cannot access instance variable '%s', defaulting to \"\"\n", name);
- data->u.str = NULL;
- }
- break;
- default:
- data->u.str = pc_readglobalreg_str(sd, name);
- break;
- }
-
- if( data->u.str == NULL || data->u.str[0] == '\0' )
- {// empty string
- data->type = C_CONSTSTR;
- data->u.str = "";
- }
- else
- {// duplicate string
- data->type = C_STR;
- data->u.str = aStrdup(data->u.str);
- }
-
- }
- else
- {// integer variable
-
- data->type = C_INT;
-
- if( reference_toconstant(data) )
- {
- data->u.num = reference_getconstant(data);
- }
- else if( reference_toparam(data) )
- {
- data->u.num = pc_readparam(sd, reference_getparamtype(data));
- }
- else
- switch( prefix )
- {
- case '@':
- data->u.num = pc_readreg(sd, data->u.num);
- break;
- case '$':
- data->u.num = mapreg_readreg(data->u.num);
- break;
- case '#':
- if( name[1] == '#' )
- data->u.num = pc_readaccountreg2(sd, name);// global
- else
- data->u.num = pc_readaccountreg(sd, name);// local
- break;
- case '.':
- {
- struct DBMap* n =
- data->ref ? *data->ref:
- name[1] == '@' ? st->stack->var_function:// instance/scope variable
- st->script->script_vars;// npc variable
- if( n )
- data->u.num = (int)idb_iget(n,reference_getuid(data));
- else
- data->u.num = 0;
- }
- break;
- case '\'':
- if( st->instance_id )
- data->u.num = (int)idb_iget(instance[st->instance_id].vars,reference_getuid(data));
- else {
- ShowWarning("script:get_val: cannot access instance variable '%s', defaulting to 0\n", name);
- data->u.num = 0;
- }
- break;
- default:
- data->u.num = pc_readglobalreg(sd, name);
- break;
- }
-
- }
-
- return;
-}
-
-struct script_data* push_val2(struct script_stack* stack, enum c_op type, int val, struct DBMap** ref);
-
-/// Retrieves the value of a reference identified by uid (variable, constant, param)
-/// The value is left in the top of the stack and needs to be removed manually.
-void* get_val2(struct script_state* st, int uid, struct DBMap** ref)
-{
- struct script_data* data;
- push_val2(st->stack, C_NAME, uid, ref);
- data = script_getdatatop(st, -1);
- get_val(st, data);
- return (data->type == C_INT ? (void*)__64BPRTSIZE(data->u.num) : (void*)__64BPRTSIZE(data->u.str));
-}
-
-/*==========================================
- * Stores the value of a script variable
- * Return value is 0 on fail, 1 on success.
- *------------------------------------------*/
-static int set_reg(struct script_state* st, TBL_PC* sd, int num, const char* name, const void* value, struct DBMap** ref)
-{
- char prefix = name[0];
-
- if( is_string_variable(name) )
- {// string variable
- const char* str = (const char*)value;
- switch (prefix) {
- case '@':
- return pc_setregstr(sd, num, str);
- case '$':
- return mapreg_setregstr(num, str);
- case '#':
- return (name[1] == '#') ?
- pc_setaccountreg2str(sd, name, str) :
- pc_setaccountregstr(sd, name, str);
- case '.':
- {
- struct DBMap* n;
- n = (ref) ? *ref : (name[1] == '@') ? st->stack->var_function : st->script->script_vars;
- if( n ) {
- idb_remove(n, num);
- if (str[0]) idb_put(n, num, aStrdup(str));
- }
- }
- return 1;
- case '\'':
- if( st->instance_id ) {
- idb_remove(instance[st->instance_id].vars, num);
- if( str[0] ) idb_put(instance[st->instance_id].vars, num, aStrdup(str));
- }
- return 1;
- default:
- return pc_setglobalreg_str(sd, name, str);
- }
- }
- else
- {// integer variable
- int val = (int)__64BPRTSIZE(value);
- if(str_data[num&0x00ffffff].type == C_PARAM)
- {
- if( pc_setparam(sd, str_data[num&0x00ffffff].val, val) == 0 )
- {
- if( st != NULL )
- {
- ShowError("script:set_reg: failed to set param '%s' to %d.\n", name, val);
- script_reportsrc(st);
- st->state = END;
- }
- return 0;
- }
- return 1;
- }
-
- switch (prefix) {
- case '@':
- return pc_setreg(sd, num, val);
- case '$':
- return mapreg_setreg(num, val);
- case '#':
- return (name[1] == '#') ?
- pc_setaccountreg2(sd, name, val) :
- pc_setaccountreg(sd, name, val);
- case '.':
- {
- struct DBMap* n;
- n = (ref) ? *ref : (name[1] == '@') ? st->stack->var_function : st->script->script_vars;
- if( n ) {
- idb_remove(n, num);
- if( val != 0 )
- idb_iput(n, num, val);
- }
- }
- return 1;
- case '\'':
- if( st->instance_id ) {
- idb_remove(instance[st->instance_id].vars, num);
- if( val != 0 )
- idb_iput(instance[st->instance_id].vars, num, val);
- }
- return 1;
- default:
- return pc_setglobalreg(sd, name, val);
- }
- }
-}
-
-int set_var(TBL_PC* sd, char* name, void* val)
-{
- return set_reg(NULL, sd, reference_uid(add_str(name),0), name, val, NULL);
-}
-
-void setd_sub(struct script_state *st, TBL_PC *sd, const char *varname, int elem, void *value, struct DBMap **ref)
-{
- set_reg(st, sd, reference_uid(add_str(varname),elem), varname, value, ref);
-}
-
-/// Converts the data to a string
-const char* conv_str(struct script_state* st, struct script_data* data)
-{
- char* p;
-
- get_val(st, data);
- if( data_isstring(data) )
- {// nothing to convert
- }
- else if( data_isint(data) )
- {// int -> string
- CREATE(p, char, ITEM_NAME_LENGTH);
- snprintf(p, ITEM_NAME_LENGTH, "%d", data->u.num);
- p[ITEM_NAME_LENGTH-1] = '\0';
- data->type = C_STR;
- data->u.str = p;
- }
- else if( data_isreference(data) )
- {// reference -> string
- //##TODO when does this happen (check get_val) [FlavioJS]
- data->type = C_CONSTSTR;
- data->u.str = reference_getname(data);
- }
- else
- {// unsupported data type
- ShowError("script:conv_str: cannot convert to string, defaulting to \"\"\n");
- script_reportdata(data);
- script_reportsrc(st);
- data->type = C_CONSTSTR;
- data->u.str = "";
- }
- return data->u.str;
-}
-
-/// Converts the data to an int
-int conv_num(struct script_state* st, struct script_data* data)
-{
- char* p;
- long num;
-
- get_val(st, data);
- if( data_isint(data) )
- {// nothing to convert
- }
- else if( data_isstring(data) )
- {// string -> int
- // the result does not overflow or underflow, it is capped instead
- // ex: 999999999999 is capped to INT_MAX (2147483647)
- p = data->u.str;
- errno = 0;
- num = strtol(data->u.str, NULL, 10);// change radix to 0 to support octal numbers "o377" and hex numbers "0xFF"
- if( errno == ERANGE
-#if LONG_MAX > INT_MAX
- || num < INT_MIN || num > INT_MAX
-#endif
- )
- {
- if( num <= INT_MIN )
- {
- num = INT_MIN;
- ShowError("script:conv_num: underflow detected, capping to %ld\n", num);
- }
- else//if( num >= INT_MAX )
- {
- num = INT_MAX;
- ShowError("script:conv_num: overflow detected, capping to %ld\n", num);
- }
- script_reportdata(data);
- script_reportsrc(st);
- }
- if( data->type == C_STR )
- aFree(p);
- data->type = C_INT;
- data->u.num = (int)num;
- }
-#if 0
- // FIXME this function is being used to retrieve the position of labels and
- // probably other stuff [FlavioJS]
- else
- {// unsupported data type
- ShowError("script:conv_num: cannot convert to number, defaulting to 0\n");
- script_reportdata(data);
- script_reportsrc(st);
- data->type = C_INT;
- data->u.num = 0;
- }
-#endif
- return data->u.num;
-}
-
-//
-// Stack operations
-//
-
-/// Increases the size of the stack
-void stack_expand(struct script_stack* stack)
-{
- stack->sp_max += 64;
- stack->stack_data = (struct script_data*)aRealloc(stack->stack_data,
- stack->sp_max * sizeof(stack->stack_data[0]) );
- memset(stack->stack_data + (stack->sp_max - 64), 0,
- 64 * sizeof(stack->stack_data[0]) );
-}
-
-/// Pushes a value into the stack
-#define push_val(stack,type,val) push_val2(stack, type, val, NULL)
-
-/// Pushes a value into the stack (with reference)
-struct script_data* push_val2(struct script_stack* stack, enum c_op type, int val, struct DBMap** ref)
-{
- if( stack->sp >= stack->sp_max )
- stack_expand(stack);
- stack->stack_data[stack->sp].type = type;
- stack->stack_data[stack->sp].u.num = val;
- stack->stack_data[stack->sp].ref = ref;
- stack->sp++;
- return &stack->stack_data[stack->sp-1];
-}
-
-/// Pushes a string into the stack
-struct script_data* push_str(struct script_stack* stack, enum c_op type, char* str)
-{
- if( stack->sp >= stack->sp_max )
- stack_expand(stack);
- stack->stack_data[stack->sp].type = type;
- stack->stack_data[stack->sp].u.str = str;
- stack->stack_data[stack->sp].ref = NULL;
- stack->sp++;
- return &stack->stack_data[stack->sp-1];
-}
-
-/// Pushes a retinfo into the stack
-struct script_data* push_retinfo(struct script_stack* stack, struct script_retinfo* ri, DBMap **ref)
-{
- if( stack->sp >= stack->sp_max )
- stack_expand(stack);
- stack->stack_data[stack->sp].type = C_RETINFO;
- stack->stack_data[stack->sp].u.ri = ri;
- stack->stack_data[stack->sp].ref = ref;
- stack->sp++;
- return &stack->stack_data[stack->sp-1];
-}
-
-/// Pushes a copy of the target position into the stack
-struct script_data* push_copy(struct script_stack* stack, int pos)
-{
- switch( stack->stack_data[pos].type )
- {
- case C_CONSTSTR:
- return push_str(stack, C_CONSTSTR, stack->stack_data[pos].u.str);
- break;
- case C_STR:
- return push_str(stack, C_STR, aStrdup(stack->stack_data[pos].u.str));
- break;
- case C_RETINFO:
- ShowFatalError("script:push_copy: can't create copies of C_RETINFO. Exiting...\n");
- exit(1);
- break;
- default:
- return push_val2(
- stack,stack->stack_data[pos].type,
- stack->stack_data[pos].u.num,
- stack->stack_data[pos].ref
- );
- break;
- }
-}
-
-/// Removes the values in indexes [start,end[ from the stack.
-/// Adjusts all stack pointers.
-void pop_stack(struct script_state* st, int start, int end)
-{
- struct script_stack* stack = st->stack;
- struct script_data* data;
- int i;
-
- if( start < 0 )
- start = 0;
- if( end > stack->sp )
- end = stack->sp;
- if( start >= end )
- return;// nothing to pop
-
- // free stack elements
- for( i = start; i < end; i++ )
- {
- data = &stack->stack_data[i];
- if( data->type == C_STR )
- aFree(data->u.str);
- if( data->type == C_RETINFO )
- {
- struct script_retinfo* ri = data->u.ri;
- if( ri->var_function )
- script_free_vars(ri->var_function);
- if( data->ref )
- aFree(data->ref);
- aFree(ri);
- }
- data->type = C_NOP;
- }
- // move the rest of the elements
- if( stack->sp > end )
- {
- memmove(&stack->stack_data[start], &stack->stack_data[end], sizeof(stack->stack_data[0])*(stack->sp - end));
- for( i = start + stack->sp - end; i < stack->sp; ++i )
- stack->stack_data[i].type = C_NOP;
- }
- // adjust stack pointers
- if( st->start > end ) st->start -= end - start;
- else if( st->start > start ) st->start = start;
- if( st->end > end ) st->end -= end - start;
- else if( st->end > start ) st->end = start;
- if( stack->defsp > end ) stack->defsp -= end - start;
- else if( stack->defsp > start ) stack->defsp = start;
- stack->sp -= end - start;
-}
-
-///
-///
-///
-
-/*==========================================
- * Release script dependent variable, dependent variable of function
- *------------------------------------------*/
-void script_free_vars(struct DBMap* storage)
-{
- if( storage )
- {// destroy the storage construct containing the variables
- db_destroy(storage);
- }
-}
-
-void script_free_code(struct script_code* code)
-{
- script_free_vars( code->script_vars );
- aFree( code->script_buf );
- aFree( code );
-}
-
-/// Creates a new script state.
-///
-/// @param script Script code
-/// @param pos Position in the code
-/// @param rid Who is running the script (attached player)
-/// @param oid Where the code is being run (npc 'object')
-/// @return Script state
-struct script_state* script_alloc_state(struct script_code* script, int pos, int rid, int oid)
-{
- struct script_state* st;
- CREATE(st, struct script_state, 1);
- st->stack = (struct script_stack*)aMalloc(sizeof(struct script_stack));
- st->stack->sp = 0;
- st->stack->sp_max = 64;
- CREATE(st->stack->stack_data, struct script_data, st->stack->sp_max);
- st->stack->defsp = st->stack->sp;
- st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA);
- st->state = RUN;
- st->script = script;
- //st->scriptroot = script;
- st->pos = pos;
- st->rid = rid;
- st->oid = oid;
- st->sleep.timer = INVALID_TIMER;
- return st;
-}
-
-/// Frees a script state.
-///
-/// @param st Script state
-void script_free_state(struct script_state* st)
-{
- if(st->bk_st)
- {// backup was not restored
- ShowDebug("script_free_state: Previous script state lost (rid=%d, oid=%d, state=%d, bk_npcid=%d).\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid);
- }
- if( st->sleep.timer != INVALID_TIMER )
- delete_timer(st->sleep.timer, run_script_timer);
- script_free_vars(st->stack->var_function);
- pop_stack(st, 0, st->stack->sp);
- aFree(st->stack->stack_data);
- aFree(st->stack);
- st->stack = NULL;
- st->pos = -1;
- aFree(st);
-}
-
-//
-// Main execution unit
-//
-/*==========================================
- * Read command
- *------------------------------------------*/
-c_op get_com(unsigned char *script,int *pos)
-{
- int i = 0, j = 0;
-
- if(script[*pos]>=0x80){
- return C_INT;
- }
- while(script[*pos]>=0x40){
- i=script[(*pos)++]<<j;
- j+=6;
- }
- return (c_op)(i+(script[(*pos)++]<<j));
-}
-
-/*==========================================
- * Income figures
- *------------------------------------------*/
-int get_num(unsigned char *script,int *pos)
-{
- int i,j;
- i=0; j=0;
- while(script[*pos]>=0xc0){
- i+=(script[(*pos)++]&0x7f)<<j;
- j+=6;
- }
- return i+((script[(*pos)++]&0x7f)<<j);
-}
-
-/*==========================================
- * Remove the value from the stack
- *------------------------------------------*/
-int pop_val(struct script_state* st)
-{
- if(st->stack->sp<=0)
- return 0;
- st->stack->sp--;
- get_val(st,&(st->stack->stack_data[st->stack->sp]));
- if(st->stack->stack_data[st->stack->sp].type==C_INT)
- return st->stack->stack_data[st->stack->sp].u.num;
- return 0;
-}
-
-/// Ternary operators
-/// test ? if_true : if_false
-void op_3(struct script_state* st, int op)
-{
- struct script_data* data;
- int flag = 0;
-
- data = script_getdatatop(st, -3);
- get_val(st, data);
-
- if( data_isstring(data) )
- flag = data->u.str[0];// "" -> false
- else if( data_isint(data) )
- flag = data->u.num;// 0 -> false
- else
- {
- ShowError("script:op_3: invalid data for the ternary operator test\n");
- script_reportdata(data);
- script_reportsrc(st);
- script_removetop(st, -3, 0);
- script_pushnil(st);
- return;
- }
- if( flag )
- script_pushcopytop(st, -2);
- else
- script_pushcopytop(st, -1);
- script_removetop(st, -4, -1);
-}
-
-/// Binary string operators
-/// s1 EQ s2 -> i
-/// s1 NE s2 -> i
-/// s1 GT s2 -> i
-/// s1 GE s2 -> i
-/// s1 LT s2 -> i
-/// s1 LE s2 -> i
-/// s1 ADD s2 -> s
-void op_2str(struct script_state* st, int op, const char* s1, const char* s2)
-{
- int a = 0;
-
- switch(op){
- case C_EQ: a = (strcmp(s1,s2) == 0); break;
- case C_NE: a = (strcmp(s1,s2) != 0); break;
- case C_GT: a = (strcmp(s1,s2) > 0); break;
- case C_GE: a = (strcmp(s1,s2) >= 0); break;
- case C_LT: a = (strcmp(s1,s2) < 0); break;
- case C_LE: a = (strcmp(s1,s2) <= 0); break;
- case C_ADD:
- {
- char* buf = (char *)aMalloc((strlen(s1)+strlen(s2)+1)*sizeof(char));
- strcpy(buf, s1);
- strcat(buf, s2);
- script_pushstr(st, buf);
- return;
- }
- default:
- ShowError("script:op2_str: unexpected string operator %s\n", script_op2name(op));
- script_reportsrc(st);
- script_pushnil(st);
- st->state = END;
- return;
- }
-
- script_pushint(st,a);
-}
-
-/// Binary number operators
-/// i OP i -> i
-void op_2num(struct script_state* st, int op, int i1, int i2)
-{
- int ret;
- double ret_double;
-
- switch( op )
- {
- case C_AND: ret = i1 & i2; break;
- case C_OR: ret = i1 | i2; break;
- case C_XOR: ret = i1 ^ i2; break;
- case C_LAND: ret = (i1 && i2); break;
- case C_LOR: ret = (i1 || i2); break;
- case C_EQ: ret = (i1 == i2); break;
- case C_NE: ret = (i1 != i2); break;
- case C_GT: ret = (i1 > i2); break;
- case C_GE: ret = (i1 >= i2); break;
- case C_LT: ret = (i1 < i2); break;
- case C_LE: ret = (i1 <= i2); break;
- case C_R_SHIFT: ret = i1>>i2; break;
- case C_L_SHIFT: ret = i1<<i2; break;
- case C_DIV:
- case C_MOD:
- if( i2 == 0 )
- {
- ShowError("script:op_2num: division by zero detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2);
- script_reportsrc(st);
- script_pushnil(st);
- st->state = END;
- return;
- }
- else if( op == C_DIV )
- ret = i1 / i2;
- else//if( op == C_MOD )
- ret = i1 % i2;
- break;
- default:
- switch( op )
- {// operators that can overflow/underflow
- case C_ADD: ret = i1 + i2; ret_double = (double)i1 + (double)i2; break;
- case C_SUB: ret = i1 - i2; ret_double = (double)i1 - (double)i2; break;
- case C_MUL: ret = i1 * i2; ret_double = (double)i1 * (double)i2; break;
- default:
- ShowError("script:op_2num: unexpected number operator %s i1=%d i2=%d\n", script_op2name(op), i1, i2);
- script_reportsrc(st);
- script_pushnil(st);
- return;
- }
- if( ret_double < (double)INT_MIN )
- {
- ShowWarning("script:op_2num: underflow detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2);
- script_reportsrc(st);
- ret = INT_MIN;
- }
- else if( ret_double > (double)INT_MAX )
- {
- ShowWarning("script:op_2num: overflow detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2);
- script_reportsrc(st);
- ret = INT_MAX;
- }
- }
- script_pushint(st, ret);
-}
-
-/// Binary operators
-void op_2(struct script_state *st, int op)
-{
- struct script_data* left, leftref;
- struct script_data* right;
-
- leftref.type = C_NOP;
-
- left = script_getdatatop(st, -2);
- right = script_getdatatop(st, -1);
-
- if (st->op2ref) {
- if (data_isreference(left)) {
- leftref = *left;
- }
-
- st->op2ref = 0;
- }
-
- get_val(st, left);
- get_val(st, right);
-
- // automatic conversions
- switch( op )
- {
- case C_ADD:
- if( data_isint(left) && data_isstring(right) )
- {// convert int-string to string-string
- conv_str(st, left);
- }
- else if( data_isstring(left) && data_isint(right) )
- {// convert string-int to string-string
- conv_str(st, right);
- }
- break;
- }
-
- if( data_isstring(left) && data_isstring(right) )
- {// ss => op_2str
- op_2str(st, op, left->u.str, right->u.str);
- script_removetop(st, leftref.type == C_NOP ? -3 : -2, -1);// pop the two values before the top one
-
- if (leftref.type != C_NOP)
- {
- aFree(left->u.str);
- *left = leftref;
- }
- }
- else if( data_isint(left) && data_isint(right) )
- {// ii => op_2num
- int i1 = left->u.num;
- int i2 = right->u.num;
-
- script_removetop(st, leftref.type == C_NOP ? -2 : -1, 0);
- op_2num(st, op, i1, i2);
-
- if (leftref.type != C_NOP)
- *left = leftref;
- }
- else
- {// invalid argument
- ShowError("script:op_2: invalid data for operator %s\n", script_op2name(op));
- script_reportdata(left);
- script_reportdata(right);
- script_reportsrc(st);
- script_removetop(st, -2, 0);
- script_pushnil(st);
- st->state = END;
- }
-}
-
-/// Unary operators
-/// NEG i -> i
-/// NOT i -> i
-/// LNOT i -> i
-void op_1(struct script_state* st, int op)
-{
- struct script_data* data;
- int i1;
-
- data = script_getdatatop(st, -1);
- get_val(st, data);
-
- if( !data_isint(data) )
- {// not a number
- ShowError("script:op_1: argument is not a number (op=%s)\n", script_op2name(op));
- script_reportdata(data);
- script_reportsrc(st);
- script_pushnil(st);
- st->state = END;
- return;
- }
-
- i1 = data->u.num;
- script_removetop(st, -1, 0);
- switch( op )
- {
- case C_NEG: i1 = -i1; break;
- case C_NOT: i1 = ~i1; break;
- case C_LNOT: i1 = !i1; break;
- default:
- ShowError("script:op_1: unexpected operator %s i1=%d\n", script_op2name(op), i1);
- script_reportsrc(st);
- script_pushnil(st);
- st->state = END;
- return;
- }
- script_pushint(st, i1);
-}
-
-
-/// Checks the type of all arguments passed to a built-in function.
-///
-/// @param st Script state whose stack arguments should be inspected.
-/// @param func Built-in function for which the arguments are intended.
-static void script_check_buildin_argtype(struct script_state* st, int func)
-{
- char type;
- int idx, invalid = 0;
- script_function* sf = &buildin_func[str_data[func].val];
-
- for( idx = 2; script_hasdata(st, idx); idx++ )
- {
- struct script_data* data = script_getdata(st, idx);
-
- type = sf->arg[idx-2];
-
- if( type == '?' || type == '*' )
- {// optional argument or unknown number of optional parameters ( no types are after this )
- break;
- }
- else if( type == 0 )
- {// more arguments than necessary ( should not happen, as it is checked before )
- ShowWarning("Found more arguments than necessary. unexpected arg type %s\n",script_op2name(data->type));
- invalid++;
- break;
- }
- else
- {
- const char* name = NULL;
-
- if( data_isreference(data) )
- {// get name for variables to determine the type they refer to
- name = reference_getname(data);
- }
-
- switch( type )
- {
- case 'v':
- if( !data_isstring(data) && !data_isint(data) && !data_isreference(data) )
- {// variant
- ShowWarning("Unexpected type for argument %d. Expected string, number or variable.\n", idx-1);
- script_reportdata(data);
- invalid++;
- }
- break;
- case 's':
- if( !data_isstring(data) && !( data_isreference(data) && is_string_variable(name) ) )
- {// string
- ShowWarning("Unexpected type for argument %d. Expected string.\n", idx-1);
- script_reportdata(data);
- invalid++;
- }
- break;
- case 'i':
- if( !data_isint(data) && !( data_isreference(data) && ( reference_toparam(data) || reference_toconstant(data) || !is_string_variable(name) ) ) )
- {// int ( params and constants are always int )
- ShowWarning("Unexpected type for argument %d. Expected number.\n", idx-1);
- script_reportdata(data);
- invalid++;
- }
- break;
- case 'r':
- if( !data_isreference(data) )
- {// variables
- ShowWarning("Unexpected type for argument %d. Expected variable, got %s.\n", idx-1,script_op2name(data->type));
- script_reportdata(data);
- invalid++;
- }
- break;
- case 'l':
- if( !data_islabel(data) && !data_isfunclabel(data) )
- {// label
- ShowWarning("Unexpected type for argument %d. Expected label, got %s\n", idx-1,script_op2name(data->type));
- script_reportdata(data);
- invalid++;
- }
- break;
- }
- }
- }
-
- if(invalid)
- {
- ShowDebug("Function: %s\n", get_str(func));
- script_reportsrc(st);
- }
-}
-
-
-/// Executes a buildin command.
-/// Stack: C_NAME(<command>) C_ARG <arg0> <arg1> ... <argN>
-int run_func(struct script_state *st)
-{
- struct script_data* data;
- int i,start_sp,end_sp,func;
-
- end_sp = st->stack->sp;// position after the last argument
- for( i = end_sp-1; i > 0 ; --i )
- if( st->stack->stack_data[i].type == C_ARG )
- break;
- if( i == 0 )
- {
- ShowError("script:run_func: C_ARG not found. please report this!!!\n");
- st->state = END;
- script_reportsrc(st);
- return 1;
- }
- start_sp = i-1;// C_NAME of the command
- st->start = start_sp;
- st->end = end_sp;
-
- data = &st->stack->stack_data[st->start];
- if( data->type == C_NAME && str_data[data->u.num].type == C_FUNC )
- func = data->u.num;
- else
- {
- ShowError("script:run_func: not a buildin command.\n");
- script_reportdata(data);
- script_reportsrc(st);
- st->state = END;
- return 1;
- }
-
- if( script_config.warn_func_mismatch_argtypes )
- {
- script_check_buildin_argtype(st, func);
- }
-
- if(str_data[func].func){
- if (str_data[func].func(st)) //Report error
- script_reportsrc(st);
- } else {
- ShowError("script:run_func: '%s' (id=%d type=%s) has no C function. please report this!!!\n", get_str(func), func, script_op2name(str_data[func].type));
- script_reportsrc(st);
- st->state = END;
- }
-
- // Stack's datum are used when re-running functions [Eoe]
- if( st->state == RERUNLINE )
- return 0;
-
- pop_stack(st, st->start, st->end);
- if( st->state == RETFUNC )
- {// return from a user-defined function
- struct script_retinfo* ri;
- int olddefsp = st->stack->defsp;
- int nargs;
-
- pop_stack(st, st->stack->defsp, st->start);// pop distractions from the stack
- if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp-1].type != C_RETINFO )
- {
- ShowWarning("script:run_func: return without callfunc or callsub!\n");
- script_reportsrc(st);
- st->state = END;
- return 1;
- }
- script_free_vars( st->stack->var_function );
-
- ri = st->stack->stack_data[st->stack->defsp-1].u.ri;
- nargs = ri->nargs;
- st->pos = ri->pos;
- st->script = ri->script;
- st->stack->var_function = ri->var_function;
- st->stack->defsp = ri->defsp;
- memset(ri, 0, sizeof(struct script_retinfo));
-
- pop_stack(st, olddefsp-nargs-1, olddefsp);// pop arguments and retinfo
-
- st->state = GOTO;
- }
-
- return 0;
-}
-
-/*==========================================
- * script execution
- *------------------------------------------*/
-void run_script(struct script_code *rootscript,int pos,int rid,int oid)
-{
- struct script_state *st;
-
- if( rootscript == NULL || pos < 0 )
- return;
-
- // TODO In jAthena, this function can take over the pending script in the player. [FlavioJS]
- // It is unclear how that can be triggered, so it needs the be traced/checked in more detail.
- // NOTE At the time of this change, this function wasn't capable of taking over the script state because st->scriptroot was never set.
- st = script_alloc_state(rootscript, pos, rid, oid);
- run_script_main(st);
-}
-
-void script_stop_sleeptimers(int id)
-{
- struct script_state* st;
- for(;;)
- {
- st = (struct script_state*)linkdb_erase(&sleep_db,(void*)__64BPRTSIZE(id));
- if( st == NULL )
- break; // no more sleep timers
- script_free_state(st);
- }
-}
-
-/*==========================================
- * Delete the specified node from sleep_db
- *------------------------------------------*/
-struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n)
-{
- struct linkdb_node *retnode;
-
- if( n == NULL)
- return NULL;
- if( n->prev == NULL )
- sleep_db = n->next;
- else
- n->prev->next = n->next;
- if( n->next )
- n->next->prev = n->prev;
- retnode = n->next;
- aFree( n );
- return retnode; // The following; return retnode
-}
-
-/*==========================================
- * Timer function for sleep
- *------------------------------------------*/
-int run_script_timer(int tid, unsigned int tick, int id, intptr_t data)
-{
- struct script_state *st = (struct script_state *)data;
- struct linkdb_node *node = (struct linkdb_node *)sleep_db;
- TBL_PC *sd = map_id2sd(st->rid);
-
- if((sd && sd->status.char_id != id) || (st->rid && !sd))
- { //Character mismatch. Cancel execution.
- st->rid = 0;
- st->state = END;
- }
- while( node && st->sleep.timer != INVALID_TIMER ) {
- if( (int)__64BPRTSIZE(node->key) == st->oid && ((struct script_state *)node->data)->sleep.timer == st->sleep.timer ) {
- script_erase_sleepdb(node);
- st->sleep.timer = INVALID_TIMER;
- break;
- }
- node = node->next;
- }
- if(st->state != RERUNLINE)
- st->sleep.tick = 0;
- run_script_main(st);
- return 0;
-}
-
-/// Detaches script state from possibly attached character and restores it's previous script if any.
-///
-/// @param st Script state to detach.
-/// @param dequeue_event Whether to schedule any queued events, when there was no previous script.
-static void script_detach_state(struct script_state* st, bool dequeue_event)
-{
- struct map_session_data* sd;
-
- if(st->rid && (sd = map_id2sd(st->rid))!=NULL)
- {
- sd->st = st->bk_st;
- sd->npc_id = st->bk_npcid;
- /**
- * For the Secure NPC Timeout option (check config/Secure.h) [RR]
- **/
- #if SECURE_NPCTIMEOUT
- /**
- * We're done with this NPC session, so we cancel the timer (if existent) and move on
- **/
- if( sd->npc_idle_timer != INVALID_TIMER ) {
- delete_timer(sd->npc_idle_timer,npc_rr_secure_timeout_timer);
- sd->npc_idle_timer = INVALID_TIMER;
- }
- #endif
- if(st->bk_st)
- {
- //Remove tag for removal.
- st->bk_st = NULL;
- st->bk_npcid = 0;
- }
- else if(dequeue_event)
- {
- npc_event_dequeue(sd);
- }
- }
- else if(st->bk_st)
- {// rid was set to 0, before detaching the script state
- ShowError("script_detach_state: Found previous script state without attached player (rid=%d, oid=%d, state=%d, bk_npcid=%d)\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid);
- script_reportsrc(st->bk_st);
-
- script_free_state(st->bk_st);
- st->bk_st = NULL;
- }
-}
-
-/// Attaches script state to possibly attached character and backups it's previous script, if any.
-///
-/// @param st Script state to attach.
-static void script_attach_state(struct script_state* st)
-{
- struct map_session_data* sd;
-
- if(st->rid && (sd = map_id2sd(st->rid))!=NULL)
- {
- if(st!=sd->st)
- {
- if(st->bk_st)
- {// there is already a backup
- ShowDebug("script_free_state: Previous script state lost (rid=%d, oid=%d, state=%d, bk_npcid=%d).\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid);
- }
- st->bk_st = sd->st;
- st->bk_npcid = sd->npc_id;
- }
- sd->st = st;
- sd->npc_id = st->oid;
-/**
- * For the Secure NPC Timeout option (check config/Secure.h) [RR]
- **/
-#if SECURE_NPCTIMEOUT
- if( sd->npc_idle_timer == INVALID_TIMER )
- sd->npc_idle_timer = add_timer(gettick() + (SECURE_NPCTIMEOUT_INTERVAL*1000),npc_rr_secure_timeout_timer,sd->bl.id,0);
- sd->npc_idle_tick = gettick();
-#endif
- }
-}
-
-/*==========================================
- * The main part of the script execution
- *------------------------------------------*/
-void run_script_main(struct script_state *st)
-{
- int cmdcount = script_config.check_cmdcount;
- int gotocount = script_config.check_gotocount;
- TBL_PC *sd;
- struct script_stack *stack=st->stack;
- struct npc_data *nd;
-
- script_attach_state(st);
-
- nd = map_id2nd(st->oid);
- if( nd && map[nd->bl.m].instance_id > 0 )
- st->instance_id = map[nd->bl.m].instance_id;
-
- if(st->state == RERUNLINE) {
- run_func(st);
- if(st->state == GOTO)
- st->state = RUN;
- } else if(st->state != END)
- st->state = RUN;
-
- while(st->state == RUN)
- {
- enum c_op c = get_com(st->script->script_buf,&st->pos);
- switch(c){
- case C_EOL:
- if( stack->defsp > stack->sp )
- ShowError("script:run_script_main: unexpected stack position (defsp=%d sp=%d). please report this!!!\n", stack->defsp, stack->sp);
- else
- pop_stack(st, stack->defsp, stack->sp);// pop unused stack data. (unused return value)
- break;
- case C_INT:
- push_val(stack,C_INT,get_num(st->script->script_buf,&st->pos));
- break;
- case C_POS:
- case C_NAME:
- push_val(stack,c,GETVALUE(st->script->script_buf,st->pos));
- st->pos+=3;
- break;
- case C_ARG:
- push_val(stack,c,0);
- break;
- case C_STR:
- push_str(stack,C_CONSTSTR,(char*)(st->script->script_buf+st->pos));
- while(st->script->script_buf[st->pos++]);
- break;
- case C_FUNC:
- run_func(st);
- if(st->state==GOTO){
- st->state = RUN;
- if( !st->freeloop && gotocount>0 && (--gotocount)<=0 ){
- ShowError("run_script: infinity loop !\n");
- script_reportsrc(st);
- st->state=END;
- }
- }
- break;
-
- case C_REF:
- st->op2ref = 1;
- break;
-
- case C_NEG:
- case C_NOT:
- case C_LNOT:
- op_1(st ,c);
- break;
-
- case C_ADD:
- case C_SUB:
- case C_MUL:
- case C_DIV:
- case C_MOD:
- case C_EQ:
- case C_NE:
- case C_GT:
- case C_GE:
- case C_LT:
- case C_LE:
- case C_AND:
- case C_OR:
- case C_XOR:
- case C_LAND:
- case C_LOR:
- case C_R_SHIFT:
- case C_L_SHIFT:
- op_2(st, c);
- break;
-
- case C_OP3:
- op_3(st, c);
- break;
-
- case C_NOP:
- st->state=END;
- break;
-
- default:
- ShowError("unknown command : %d @ %d\n",c,st->pos);
- st->state=END;
- break;
- }
- if( !st->freeloop && cmdcount>0 && (--cmdcount)<=0 ){
- ShowError("run_script: infinity loop !\n");
- script_reportsrc(st);
- st->state=END;
- }
- }
-
- if(st->sleep.tick > 0) {
- //Restore previous script
- script_detach_state(st, false);
- //Delay execution
- sd = map_id2sd(st->rid); // Get sd since script might have attached someone while running. [Inkfish]
- st->sleep.charid = sd?sd->status.char_id:0;
- st->sleep.timer = add_timer(gettick()+st->sleep.tick,
- run_script_timer, st->sleep.charid, (intptr_t)st);
- linkdb_insert(&sleep_db, (void*)__64BPRTSIZE(st->oid), st);
- }
- else if(st->state != END && st->rid){
- //Resume later (st is already attached to player).
- if(st->bk_st) {
- ShowWarning("Unable to restore stack! Double continuation!\n");
- //Report BOTH scripts to see if that can help somehow.
- ShowDebug("Previous script (lost):\n");
- script_reportsrc(st->bk_st);
- ShowDebug("Current script:\n");
- script_reportsrc(st);
-
- script_free_state(st->bk_st);
- st->bk_st = NULL;
- }
- } else {
- //Dispose of script.
- if ((sd = map_id2sd(st->rid))!=NULL)
- { //Restore previous stack and save char.
- if(sd->state.using_fake_npc){
- clif_clearunit_single(sd->npc_id, CLR_OUTSIGHT, sd->fd);
- sd->state.using_fake_npc = 0;
- }
- //Restore previous script if any.
- script_detach_state(st, true);
- if (sd->state.reg_dirty&2)
- intif_saveregistry(sd,2);
- if (sd->state.reg_dirty&1)
- intif_saveregistry(sd,1);
- }
- script_free_state(st);
- st = NULL;
- }
-}
-
-int script_config_read(char *cfgName)
-{
- int i;
- char line[1024],w1[1024],w2[1024];
- FILE *fp;
-
-
- fp=fopen(cfgName,"r");
- if(fp==NULL){
- ShowError("File not found: %s\n", cfgName);
- return 1;
- }
- while(fgets(line, sizeof(line), fp))
- {
- if(line[0] == '/' && line[1] == '/')
- continue;
- i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2);
- if(i!=2)
- continue;
-
- if(strcmpi(w1,"warn_func_mismatch_paramnum")==0) {
- script_config.warn_func_mismatch_paramnum = config_switch(w2);
- }
- else if(strcmpi(w1,"check_cmdcount")==0) {
- script_config.check_cmdcount = config_switch(w2);
- }
- else if(strcmpi(w1,"check_gotocount")==0) {
- script_config.check_gotocount = config_switch(w2);
- }
- else if(strcmpi(w1,"input_min_value")==0) {
- script_config.input_min_value = config_switch(w2);
- }
- else if(strcmpi(w1,"input_max_value")==0) {
- script_config.input_max_value = config_switch(w2);
- }
- else if(strcmpi(w1,"warn_func_mismatch_argtypes")==0) {
- script_config.warn_func_mismatch_argtypes = config_switch(w2);
- }
- else if(strcmpi(w1,"import")==0){
- script_config_read(w2);
- }
- else {
- ShowWarning("Unknown setting '%s' in file %s\n", w1, cfgName);
- }
- }
- fclose(fp);
-
- return 0;
-}
-
-/**
- * @see DBApply
- */
-static int db_script_free_code_sub(DBKey key, DBData *data, va_list ap)
-{
- struct script_code *code = db_data2ptr(data);
- if (code)
- script_free_code(code);
- return 0;
-}
-
-void script_run_autobonus(const char *autobonus, int id, int pos)
-{
- struct script_code *script = (struct script_code *)strdb_get(autobonus_db, autobonus);
-
- if( script )
- {
- current_equip_item_index = pos;
- run_script(script,0,id,0);
- }
-}
-
-void script_add_autobonus(const char *autobonus)
-{
- if( strdb_get(autobonus_db, autobonus) == NULL )
- {
- struct script_code *script = parse_script(autobonus, "autobonus", 0, 0);
-
- if( script )
- strdb_put(autobonus_db, autobonus, script);
- }
-}
-
-
-/// resets a temporary character array variable to given value
-void script_cleararray_pc(struct map_session_data* sd, const char* varname, void* value)
-{
- int key;
- uint8 idx;
-
- if( not_array_variable(varname[0]) || !not_server_variable(varname[0]) )
- {
- ShowError("script_cleararray_pc: Variable '%s' has invalid scope (char_id=%d).\n", varname, sd->status.char_id);
- return;
- }
-
- key = add_str(varname);
-
- if( is_string_variable(varname) )
- {
- for( idx = 0; idx < SCRIPT_MAX_ARRAYSIZE; idx++ )
- {
- pc_setregstr(sd, reference_uid(key, idx), (const char*)value);
- }
- }
- else
- {
- for( idx = 0; idx < SCRIPT_MAX_ARRAYSIZE; idx++ )
- {
- pc_setreg(sd, reference_uid(key, idx), (int)__64BPRTSIZE(value));
- }
- }
-}
-
-
-/// sets a temporary character array variable element idx to given value
-/// @param refcache Pointer to an int variable, which keeps a copy of the reference to varname and must be initialized to 0. Can be NULL if only one element is set.
-void script_setarray_pc(struct map_session_data* sd, const char* varname, uint8 idx, void* value, int* refcache)
-{
- int key;
-
- if( not_array_variable(varname[0]) || !not_server_variable(varname[0]) )
- {
- ShowError("script_setarray_pc: Variable '%s' has invalid scope (char_id=%d).\n", varname, sd->status.char_id);
- return;
- }
-
- if( idx >= SCRIPT_MAX_ARRAYSIZE )
- {
- ShowError("script_setarray_pc: Variable '%s' has invalid index '%d' (char_id=%d).\n", varname, (int)idx, sd->status.char_id);
- return;
- }
-
- key = ( refcache && refcache[0] ) ? refcache[0] : add_str(varname);
-
- if( is_string_variable(varname) )
- {
- pc_setregstr(sd, reference_uid(key, idx), (const char*)value);
- }
- else
- {
- pc_setreg(sd, reference_uid(key, idx), (int)__64BPRTSIZE(value));
- }
-
- if( refcache )
- {// save to avoid repeated add_str calls
- refcache[0] = key;
- }
-}
-#ifdef BETA_THREAD_TEST
-int buildin_query_sql_sub(struct script_state* st, Sql* handle);
-
-/* used to receive items the queryThread has already processed */
-int queryThread_timer(int tid, unsigned int tick, int id, intptr_t data) {
- int i, cursor = 0;
- bool allOk = true;
-
- EnterSpinLock(&queryThreadLock);
-
- for( i = 0; i < queryThreadData.count; i++ ) {
- struct queryThreadEntry *entry = queryThreadData.entry[i];
-
- if( !entry->ok ) {
- allOk = false;
- continue;
- }
-
- run_script_main(entry->st);
-
- entry->st = NULL;/* empty entries */
- aFree(entry);
- queryThreadData.entry[i] = NULL;
- }
-
-
- if( allOk ) {
- /* cancel the repeating timer -- it'll re-create itself when necessary, dont need to remain looping */
- delete_timer(queryThreadData.timer, queryThread_timer);
- queryThreadData.timer = INVALID_TIMER;
- }
-
- /* now lets clear the mess. */
- for( i = 0; i < queryThreadData.count; i++ ) {
- struct queryThreadEntry *entry = queryThreadData.entry[i];
- if( entry == NULL )
- continue;/* entry on hold */
-
- /* move */
- memmove(&queryThreadData.entry[cursor], &queryThreadData.entry[i], sizeof(struct queryThreadEntry*));
-
- cursor++;
- }
-
- queryThreadData.count = cursor;
-
- LeaveSpinLock(&queryThreadLock);
-
- return 0;
-}
-
-void queryThread_add(struct script_state *st, bool type) {
- int idx = 0;
- struct queryThreadEntry* entry = NULL;
-
- EnterSpinLock(&queryThreadLock);
-
- if( queryThreadData.count++ != 0 )
- RECREATE(queryThreadData.entry, struct queryThreadEntry* , queryThreadData.count);
-
- idx = queryThreadData.count-1;
-
- CREATE(queryThreadData.entry[idx],struct queryThreadEntry,1);
-
- entry = queryThreadData.entry[idx];
-
- entry->st = st;
- entry->ok = false;
- entry->type = type;
- if( queryThreadData.timer == INVALID_TIMER ) { /* start the receiver timer */
- queryThreadData.timer = add_timer_interval(gettick() + 100, queryThread_timer, 0, 0, 100);
- }
-
- LeaveSpinLock(&queryThreadLock);
-
- /* unlock the queryThread */
- racond_signal(queryThreadCond);
-}
-/* adds a new log to the queue */
-void queryThread_log(char * entry, int length) {
- int idx = logThreadData.count;
-
- EnterSpinLock(&queryThreadLock);
-
- if( logThreadData.count++ != 0 )
- RECREATE(logThreadData.entry, char* , logThreadData.count);
-
- CREATE(logThreadData.entry[idx], char, length + 1 );
- safestrncpy(logThreadData.entry[idx], entry, length + 1 );
-
- LeaveSpinLock(&queryThreadLock);
-
- /* unlock the queryThread */
- racond_signal(queryThreadCond);
-}
-
-/* queryThread_main */
-static void *queryThread_main(void *x) {
- Sql *queryThread_handle = Sql_Malloc();
- int i;
-
- if ( SQL_ERROR == Sql_Connect(queryThread_handle, map_server_id, map_server_pw, map_server_ip, map_server_port, map_server_db) )
- exit(EXIT_FAILURE);
-
- if( strlen(default_codepage) > 0 )
- if ( SQL_ERROR == Sql_SetEncoding(queryThread_handle, default_codepage) )
- Sql_ShowDebug(queryThread_handle);
-
- if( log_config.sql_logs ) {
- logmysql_handle = Sql_Malloc();
-
- if ( SQL_ERROR == Sql_Connect(logmysql_handle, log_db_id, log_db_pw, log_db_ip, log_db_port, log_db_db) )
- exit(EXIT_FAILURE);
-
- if( strlen(default_codepage) > 0 )
- if ( SQL_ERROR == Sql_SetEncoding(logmysql_handle, default_codepage) )
- Sql_ShowDebug(logmysql_handle);
- }
-
- while( 1 ) {
-
- if(queryThreadTerminate > 0)
- break;
-
- EnterSpinLock(&queryThreadLock);
-
- /* mess with queryThreadData within the lock */
- for( i = 0; i < queryThreadData.count; i++ ) {
- struct queryThreadEntry *entry = queryThreadData.entry[i];
-
- if( entry->ok )
- continue;
- else if ( !entry->st || !entry->st->stack ) {
- entry->ok = true;/* dispose */
- continue;
- }
-
- buildin_query_sql_sub(entry->st, entry->type ? logmysql_handle : queryThread_handle);
-
- entry->ok = true;/* we're done with this */
- }
-
- /* also check for any logs in need to be sent */
- if( log_config.sql_logs ) {
- for( i = 0; i < logThreadData.count; i++ ) {
- if( SQL_ERROR == Sql_Query(logmysql_handle, logThreadData.entry[i]) )
- Sql_ShowDebug(logmysql_handle);
- aFree(logThreadData.entry[i]);
- }
- logThreadData.count = 0;
- }
-
- LeaveSpinLock(&queryThreadLock);
-
- ramutex_lock( queryThreadMutex );
- racond_wait( queryThreadCond, queryThreadMutex, -1 );
- ramutex_unlock( queryThreadMutex );
-
- }
-
- Sql_Free(queryThread_handle);
-
- if( log_config.sql_logs ) {
- Sql_Free(logmysql_handle);
- }
-
- return NULL;
-}
-#endif
-/*==========================================
- * Destructor
- *------------------------------------------*/
-int do_final_script() {
- int i;
-#ifdef DEBUG_HASH
- if (battle_config.etc_log)
- {
- FILE *fp = fopen("hash_dump.txt","wt");
- if(fp) {
- int count[SCRIPT_HASH_SIZE];
- int count2[SCRIPT_HASH_SIZE]; // number of buckets with a certain number of items
- int n=0;
- int min=INT_MAX,max=0,zero=0;
- double mean=0.0f;
- double median=0.0f;
-
- ShowNotice("Dumping script str hash information to hash_dump.txt\n");
- memset(count, 0, sizeof(count));
- fprintf(fp,"num : hash : data_name\n");
- fprintf(fp,"---------------------------------------------------------------\n");
- for(i=LABEL_START; i<str_num; i++) {
- unsigned int h = calc_hash(get_str(i));
- fprintf(fp,"%04d : %4u : %s\n",i,h, get_str(i));
- ++count[h];
- }
- fprintf(fp,"--------------------\n\n");
- memset(count2, 0, sizeof(count2));
- for(i=0; i<SCRIPT_HASH_SIZE; i++) {
- fprintf(fp," hash %3d = %d\n",i,count[i]);
- if(min > count[i])
- min = count[i]; // minimun count of collision
- if(max < count[i])
- max = count[i]; // maximun count of collision
- if(count[i] == 0)
- zero++;
- ++count2[count[i]];
- }
- fprintf(fp,"\n--------------------\n items : buckets\n--------------------\n");
- for( i=min; i <= max; ++i ){
- fprintf(fp," %5d : %7d\n",i,count2[i]);
- mean += 1.0f*i*count2[i]/SCRIPT_HASH_SIZE; // Note: this will always result in <nr labels>/<nr buckets>
- }
- for( i=min; i <= max; ++i ){
- n += count2[i];
- if( n*2 >= SCRIPT_HASH_SIZE )
- {
- if( SCRIPT_HASH_SIZE%2 == 0 && SCRIPT_HASH_SIZE/2 == n )
- median = (i+i+1)/2.0f;
- else
- median = i;
- break;
- }
- }
- fprintf(fp,"--------------------\n min = %d, max = %d, zero = %d\n mean = %lf, median = %lf\n",min,max,zero,mean,median);
- fclose(fp);
- }
- }
-#endif
-
- mapreg_final();
-
- db_destroy(scriptlabel_db);
- userfunc_db->destroy(userfunc_db, db_script_free_code_sub);
- autobonus_db->destroy(autobonus_db, db_script_free_code_sub);
- if(sleep_db) {
- struct linkdb_node *n = (struct linkdb_node *)sleep_db;
- while(n) {
- struct script_state *st = (struct script_state *)n->data;
- script_free_state(st);
- n = n->next;
- }
- linkdb_final(&sleep_db);
- }
-
- if (str_data)
- aFree(str_data);
- if (str_buf)
- aFree(str_buf);
-
- for( i = 0; i < atcmd_binding_count; i++ ) {
- aFree(atcmd_binding[i]);
- }
-
- if( atcmd_binding_count != 0 )
- aFree(atcmd_binding);
-#ifdef BETA_THREAD_TEST
- /* QueryThread */
- InterlockedIncrement(&queryThreadTerminate);
- racond_signal(queryThreadCond);
- rathread_wait(queryThread, NULL);
-
- // Destroy cond var and mutex.
- racond_destroy( queryThreadCond );
- ramutex_destroy( queryThreadMutex );
-
- /* Clear missing vars */
- for( i = 0; i < queryThreadData.count; i++ ) {
- aFree(queryThreadData.entry[i]);
- }
-
- aFree(queryThreadData.entry);
-
- for( i = 0; i < logThreadData.count; i++ ) {
- aFree(logThreadData.entry[i]);
- }
-
- aFree(logThreadData.entry);
-#endif
-
- return 0;
-}
-/*==========================================
- * Initialization
- *------------------------------------------*/
-int do_init_script() {
- userfunc_db=strdb_alloc(DB_OPT_DUP_KEY,0);
- scriptlabel_db=strdb_alloc(DB_OPT_DUP_KEY,50);
- autobonus_db = strdb_alloc(DB_OPT_DUP_KEY,0);
-
- mapreg_init();
-#ifdef BETA_THREAD_TEST
- CREATE(queryThreadData.entry, struct queryThreadEntry*, 1);
- queryThreadData.count = 0;
- CREATE(logThreadData.entry, char *, 1);
- logThreadData.count = 0;
- /* QueryThread Start */
-
- InitializeSpinLock(&queryThreadLock);
-
- queryThreadData.timer = INVALID_TIMER;
- queryThreadTerminate = 0;
- queryThreadMutex = ramutex_create();
- queryThreadCond = racond_create();
-
- queryThread = rathread_create(queryThread_main, NULL);
-
- if(queryThread == NULL){
- ShowFatalError("do_init_script: cannot spawn Query Thread.\n");
- exit(EXIT_FAILURE);
- }
-
- add_timer_func_list(queryThread_timer, "queryThread_timer");
-#endif
- return 0;
-}
-
-int script_reload() {
- int i;
-
-#ifdef BETA_THREAD_TEST
- /* we're reloading so any queries undergoing should be...exterminated. */
- EnterSpinLock(&queryThreadLock);
-
- for( i = 0; i < queryThreadData.count; i++ ) {
- aFree(queryThreadData.entry[i]);
- }
- queryThreadData.count = 0;
-
- if( queryThreadData.timer != INVALID_TIMER ) {
- delete_timer(queryThreadData.timer, queryThread_timer);
- queryThreadData.timer = INVALID_TIMER;
- }
-
- LeaveSpinLock(&queryThreadLock);
-#endif
-
-
- userfunc_db->clear(userfunc_db, db_script_free_code_sub);
- db_clear(scriptlabel_db);
-
- // @commands (script based)
- // Clear bindings
- for( i = 0; i < atcmd_binding_count; i++ ) {
- aFree(atcmd_binding[i]);
- }
-
- if( atcmd_binding_count != 0 )
- aFree(atcmd_binding);
-
- atcmd_binding_count = 0;
-
- if(sleep_db) {
- struct linkdb_node *n = (struct linkdb_node *)sleep_db;
- while(n) {
- struct script_state *st = (struct script_state *)n->data;
- script_free_state(st);
- n = n->next;
- }
- linkdb_final(&sleep_db);
- }
- mapreg_reload();
- return 0;
-}
-
-//-----------------------------------------------------------------------------
-// buildin functions
-//
-
-#define BUILDIN_DEF(x,args) { buildin_ ## x , #x , args }
-#define BUILDIN_DEF2(x,x2,args) { buildin_ ## x , x2 , args }
-#define BUILDIN_FUNC(x) int buildin_ ## x (struct script_state* st)
-
-/////////////////////////////////////////////////////////////////////
-// NPC interaction
-//
-
-/// Appends a message to the npc dialog.
-/// If a dialog doesn't exist yet, one is created.
-///
-/// mes "<message>";
-BUILDIN_FUNC(mes)
-{
- TBL_PC* sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if( !script_hasdata(st, 3) )
- {// only a single line detected in the script
- clif_scriptmes(sd, st->oid, script_getstr(st, 2));
- }
- else
- {// parse multiple lines as they exist
- int i;
-
- for( i = 2; script_hasdata(st, i); i++ )
- {
- // send the message to the client
- clif_scriptmes(sd, st->oid, script_getstr(st, i));
- }
- }
-
- return 0;
-}
-
-/// Displays the button 'next' in the npc dialog.
-/// The dialog text is cleared and the script continues when the button is pressed.
-///
-/// next;
-BUILDIN_FUNC(next)
-{
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- st->state = STOP;
- clif_scriptnext(sd, st->oid);
- return 0;
-}
-
-/// Ends the script and displays the button 'close' on the npc dialog.
-/// The dialog is closed when the button is pressed.
-///
-/// close;
-BUILDIN_FUNC(close)
-{
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- st->state = END;
- clif_scriptclose(sd, st->oid);
- return 0;
-}
-
-/// Displays the button 'close' on the npc dialog.
-/// The dialog is closed and the script continues when the button is pressed.
-///
-/// close2;
-BUILDIN_FUNC(close2)
-{
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- st->state = STOP;
- clif_scriptclose(sd, st->oid);
- return 0;
-}
-
-/// Counts the number of valid and total number of options in 'str'
-/// If max_count > 0 the counting stops when that valid option is reached
-/// total is incremented for each option (NULL is supported)
-static int menu_countoptions(const char* str, int max_count, int* total)
-{
- int count = 0;
- int bogus_total;
-
- if( total == NULL )
- total = &bogus_total;
- ++(*total);
-
- // initial empty options
- while( *str == ':' )
- {
- ++str;
- ++(*total);
- }
- // count menu options
- while( *str != '\0' )
- {
- ++count;
- --max_count;
- if( max_count == 0 )
- break;
- while( *str != ':' && *str != '\0' )
- ++str;
- while( *str == ':' )
- {
- ++str;
- ++(*total);
- }
- }
- return count;
-}
-
-/// Displays a menu with options and goes to the target label.
-/// The script is stopped if cancel is pressed.
-/// Options with no text are not displayed in the client.
-///
-/// Options can be grouped together, separated by the character ':' in the text:
-/// ex: menu "A:B:C",L_target;
-/// All these options go to the specified target label.
-///
-/// The index of the selected option is put in the variable @menu.
-/// Indexes start with 1 and are consistent with grouped and empty options.
-/// ex: menu "A::B",-,"",L_Impossible,"C",-;
-/// // displays "A", "B" and "C", corresponding to indexes 1, 3 and 5
-///
-/// NOTE: the client closes the npc dialog when cancel is pressed
-///
-/// menu "<option_text>",<target_label>{,"<option_text>",<target_label>,...};
-BUILDIN_FUNC(menu)
-{
- int i;
- const char* text;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- // TODO detect multiple scripts waiting for input at the same time, and what to do when that happens
- if( sd->state.menu_or_input == 0 )
- {
- struct StringBuf buf;
- struct script_data* data;
-
- if( script_lastdata(st) % 2 == 0 )
- {// argument count is not even (1st argument is at index 2)
- ShowError("script:menu: illegal number of arguments (%d).\n", (script_lastdata(st) - 1));
- st->state = END;
- return 1;
- }
-
- StringBuf_Init(&buf);
- sd->npc_menu = 0;
- for( i = 2; i < script_lastdata(st); i += 2 )
- {
- // menu options
- text = script_getstr(st, i);
-
- // target label
- data = script_getdata(st, i+1);
- if( !data_islabel(data) )
- {// not a label
- StringBuf_Destroy(&buf);
- ShowError("script:menu: argument #%d (from 1) is not a label or label not found.\n", i);
- script_reportdata(data);
- st->state = END;
- return 1;
- }
-
- // append option(s)
- if( text[0] == '\0' )
- continue;// empty string, ignore
- if( sd->npc_menu > 0 )
- StringBuf_AppendStr(&buf, ":");
- StringBuf_AppendStr(&buf, text);
- sd->npc_menu += menu_countoptions(text, 0, NULL);
- }
- st->state = RERUNLINE;
- sd->state.menu_or_input = 1;
-
- /**
- * menus beyond this length crash the client (see bugreport:6402)
- **/
- if( StringBuf_Length(&buf) >= 2047 ) {
- struct npc_data * nd = map_id2nd(st->oid);
- char* menu;
- CREATE(menu, char, 2048);
- safestrncpy(menu, StringBuf_Value(&buf), 2047);
- ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StringBuf_Length(&buf));
- clif_scriptmenu(sd, st->oid, menu);
- aFree(menu);
- } else
- clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf));
-
- StringBuf_Destroy(&buf);
-
- if( sd->npc_menu >= 0xff )
- {// client supports only up to 254 entries; 0 is not used and 255 is reserved for cancel; excess entries are displayed but cause 'uint8' overflow
- ShowWarning("buildin_menu: Too many options specified (current=%d, max=254).\n", sd->npc_menu);
- script_reportsrc(st);
- }
- }
- else if( sd->npc_menu == 0xff )
- {// Cancel was pressed
- sd->state.menu_or_input = 0;
- st->state = END;
- }
- else
- {// goto target label
- int menu = 0;
-
- sd->state.menu_or_input = 0;
- if( sd->npc_menu <= 0 )
- {
- ShowDebug("script:menu: unexpected selection (%d)\n", sd->npc_menu);
- st->state = END;
- return 1;
- }
-
- // get target label
- for( i = 2; i < script_lastdata(st); i += 2 )
- {
- text = script_getstr(st, i);
- sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu);
- if( sd->npc_menu <= 0 )
- break;// entry found
- }
- if( sd->npc_menu > 0 )
- {// Invalid selection
- ShowDebug("script:menu: selection is out of range (%d pairs are missing?) - please report this\n", sd->npc_menu);
- st->state = END;
- return 1;
- }
- if( !data_islabel(script_getdata(st, i + 1)) )
- {// TODO remove this temporary crash-prevention code (fallback for multiple scripts requesting user input)
- ShowError("script:menu: unexpected data in label argument\n");
- script_reportdata(script_getdata(st, i + 1));
- st->state = END;
- return 1;
- }
- pc_setreg(sd, add_str("@menu"), menu);
- st->pos = script_getnum(st, i + 1);
- st->state = GOTO;
- }
- return 0;
-}
-
-/// Displays a menu with options and returns the selected option.
-/// Behaves like 'menu' without the target labels.
-///
-/// select(<option_text>{,<option_text>,...}) -> <selected_option>
-///
-/// @see menu
-BUILDIN_FUNC(select)
-{
- int i;
- const char* text;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if( sd->state.menu_or_input == 0 ) {
- struct StringBuf buf;
-
- StringBuf_Init(&buf);
- sd->npc_menu = 0;
- for( i = 2; i <= script_lastdata(st); ++i ) {
- text = script_getstr(st, i);
-
- if( sd->npc_menu > 0 )
- StringBuf_AppendStr(&buf, ":");
-
- StringBuf_AppendStr(&buf, text);
- sd->npc_menu += menu_countoptions(text, 0, NULL);
- }
-
- st->state = RERUNLINE;
- sd->state.menu_or_input = 1;
-
- /**
- * menus beyond this length crash the client (see bugreport:6402)
- **/
- if( StringBuf_Length(&buf) >= 2047 ) {
- struct npc_data * nd = map_id2nd(st->oid);
- char* menu;
- CREATE(menu, char, 2048);
- safestrncpy(menu, StringBuf_Value(&buf), 2047);
- ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StringBuf_Length(&buf));
- clif_scriptmenu(sd, st->oid, menu);
- aFree(menu);
- } else
- clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf));
- StringBuf_Destroy(&buf);
-
- if( sd->npc_menu >= 0xff ) {
- ShowWarning("buildin_select: Too many options specified (current=%d, max=254).\n", sd->npc_menu);
- script_reportsrc(st);
- }
- } else if( sd->npc_menu == 0xff ) {// Cancel was pressed
- sd->state.menu_or_input = 0;
- st->state = END;
- } else {// return selected option
- int menu = 0;
-
- sd->state.menu_or_input = 0;
- for( i = 2; i <= script_lastdata(st); ++i ) {
- text = script_getstr(st, i);
- sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu);
- if( sd->npc_menu <= 0 )
- break;// entry found
- }
- pc_setreg(sd, add_str("@menu"), menu);
- script_pushint(st, menu);
- st->state = RUN;
- }
- return 0;
-}
-
-/// Displays a menu with options and returns the selected option.
-/// Behaves like 'menu' without the target labels, except when cancel is
-/// pressed.
-/// When cancel is pressed, the script continues and 255 is returned.
-///
-/// prompt(<option_text>{,<option_text>,...}) -> <selected_option>
-///
-/// @see menu
-BUILDIN_FUNC(prompt)
-{
- int i;
- const char *text;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if( sd->state.menu_or_input == 0 )
- {
- struct StringBuf buf;
-
- StringBuf_Init(&buf);
- sd->npc_menu = 0;
- for( i = 2; i <= script_lastdata(st); ++i )
- {
- text = script_getstr(st, i);
- if( sd->npc_menu > 0 )
- StringBuf_AppendStr(&buf, ":");
- StringBuf_AppendStr(&buf, text);
- sd->npc_menu += menu_countoptions(text, 0, NULL);
- }
-
- st->state = RERUNLINE;
- sd->state.menu_or_input = 1;
-
- /**
- * menus beyond this length crash the client (see bugreport:6402)
- **/
- if( StringBuf_Length(&buf) >= 2047 ) {
- struct npc_data * nd = map_id2nd(st->oid);
- char* menu;
- CREATE(menu, char, 2048);
- safestrncpy(menu, StringBuf_Value(&buf), 2047);
- ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StringBuf_Length(&buf));
- clif_scriptmenu(sd, st->oid, menu);
- aFree(menu);
- } else
- clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf));
- StringBuf_Destroy(&buf);
-
- if( sd->npc_menu >= 0xff )
- {
- ShowWarning("buildin_prompt: Too many options specified (current=%d, max=254).\n", sd->npc_menu);
- script_reportsrc(st);
- }
- }
- else if( sd->npc_menu == 0xff )
- {// Cancel was pressed
- sd->state.menu_or_input = 0;
- pc_setreg(sd, add_str("@menu"), 0xff);
- script_pushint(st, 0xff);
- st->state = RUN;
- }
- else
- {// return selected option
- int menu = 0;
-
- sd->state.menu_or_input = 0;
- for( i = 2; i <= script_lastdata(st); ++i )
- {
- text = script_getstr(st, i);
- sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu);
- if( sd->npc_menu <= 0 )
- break;// entry found
- }
- pc_setreg(sd, add_str("@menu"), menu);
- script_pushint(st, menu);
- st->state = RUN;
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////
-// ...
-//
-
-/// Jumps to the target script label.
-///
-/// goto <label>;
-BUILDIN_FUNC(goto)
-{
- if( !data_islabel(script_getdata(st,2)) )
- {
- ShowError("script:goto: not a label\n");
- script_reportdata(script_getdata(st,2));
- st->state = END;
- return 1;
- }
-
- st->pos = script_getnum(st,2);
- st->state = GOTO;
- return 0;
-}
-
-/*==========================================
- * user-defined function call
- *------------------------------------------*/
-BUILDIN_FUNC(callfunc)
-{
- int i, j;
- struct script_retinfo* ri;
- struct script_code* scr;
- const char* str = script_getstr(st,2);
- DBMap **ref = NULL;
-
- scr = (struct script_code*)strdb_get(userfunc_db, str);
- if( !scr )
- {
- ShowError("script:callfunc: function not found! [%s]\n", str);
- st->state = END;
- return 1;
- }
-
- for( i = st->start+3, j = 0; i < st->end; i++, j++ )
- {
- struct script_data* data = push_copy(st->stack,i);
- if( data_isreference(data) && !data->ref )
- {
- const char* name = reference_getname(data);
- if( name[0] == '.' ) {
- if ( !ref ) {
- ref = (struct DBMap**)aCalloc(sizeof(struct DBMap*), 1);
- ref[0] = (name[1] == '@' ? st->stack->var_function : st->script->script_vars);
- }
- data->ref = ref;
- }
- }
- }
-
- CREATE(ri, struct script_retinfo, 1);
- ri->script = st->script;// script code
- ri->var_function = st->stack->var_function;// scope variables
- ri->pos = st->pos;// script location
- ri->nargs = j;// argument count
- ri->defsp = st->stack->defsp;// default stack pointer
- push_retinfo(st->stack, ri, ref);
-
- st->pos = 0;
- st->script = scr;
- st->stack->defsp = st->stack->sp;
- st->state = GOTO;
- st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA);
-
- return 0;
-}
-/*==========================================
- * subroutine call
- *------------------------------------------*/
-BUILDIN_FUNC(callsub)
-{
- int i,j;
- struct script_retinfo* ri;
- int pos = script_getnum(st,2);
- DBMap **ref = NULL;
-
- if( !data_islabel(script_getdata(st,2)) && !data_isfunclabel(script_getdata(st,2)) )
- {
- ShowError("script:callsub: argument is not a label\n");
- script_reportdata(script_getdata(st,2));
- st->state = END;
- return 1;
- }
-
- for( i = st->start+3, j = 0; i < st->end; i++, j++ )
- {
- struct script_data* data = push_copy(st->stack,i);
- if( data_isreference(data) && !data->ref )
- {
- const char* name = reference_getname(data);
- if( name[0] == '.' && name[1] == '@' ) {
- if ( !ref ) {
- ref = (struct DBMap**)aCalloc(sizeof(struct DBMap*), 1);
- ref[0] = st->stack->var_function;
- }
- data->ref = ref;
- }
- }
- }
-
- CREATE(ri, struct script_retinfo, 1);
- ri->script = st->script;// script code
- ri->var_function = st->stack->var_function;// scope variables
- ri->pos = st->pos;// script location
- ri->nargs = j;// argument count
- ri->defsp = st->stack->defsp;// default stack pointer
- push_retinfo(st->stack, ri, ref);
-
- st->pos = pos;
- st->stack->defsp = st->stack->sp;
- st->state = GOTO;
- st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA);
-
- return 0;
-}
-
-/// Retrieves an argument provided to callfunc/callsub.
-/// If the argument doesn't exist
-///
-/// getarg(<index>{,<default_value>}) -> <value>
-BUILDIN_FUNC(getarg)
-{
- struct script_retinfo* ri;
- int idx;
-
- if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp - 1].type != C_RETINFO )
- {
- ShowError("script:getarg: no callfunc or callsub!\n");
- st->state = END;
- return 1;
- }
- ri = st->stack->stack_data[st->stack->defsp - 1].u.ri;
-
- idx = script_getnum(st,2);
-
- if( idx >= 0 && idx < ri->nargs )
- push_copy(st->stack, st->stack->defsp - 1 - ri->nargs + idx);
- else if( script_hasdata(st,3) )
- script_pushcopy(st, 3);
- else
- {
- ShowError("script:getarg: index (idx=%d) out of range (nargs=%d) and no default value found\n", idx, ri->nargs);
- st->state = END;
- return 1;
- }
-
- return 0;
-}
-
-/// Returns from the current function, optionaly returning a value from the functions.
-/// Don't use outside script functions.
-///
-/// return;
-/// return <value>;
-BUILDIN_FUNC(return)
-{
- if( script_hasdata(st,2) )
- {// return value
- struct script_data* data;
- script_pushcopy(st, 2);
- data = script_getdatatop(st, -1);
- if( data_isreference(data) )
- {
- const char* name = reference_getname(data);
- if( name[0] == '.' && name[1] == '@' )
- {// scope variable
- if( !data->ref || data->ref == (DBMap**)&st->stack->var_function )
- get_val(st, data);// current scope, convert to value
- }
- else if( name[0] == '.' && !data->ref )
- {// script variable, link to current script
- data->ref = &st->script->script_vars;
- }
- }
- }
- else
- {// no return value
- script_pushnil(st);
- }
- st->state = RETFUNC;
- return 0;
-}
-
-/// Returns a random number from 0 to <range>-1.
-/// Or returns a random number from <min> to <max>.
-/// If <min> is greater than <max>, their numbers are switched.
-/// rand(<range>) -> <int>
-/// rand(<min>,<max>) -> <int>
-BUILDIN_FUNC(rand)
-{
- int range;
- int min;
- int max;
-
- if( script_hasdata(st,3) )
- {// min,max
- min = script_getnum(st,2);
- max = script_getnum(st,3);
- if( max < min )
- swap(min, max);
- range = max - min + 1;
- }
- else
- {// range
- min = 0;
- range = script_getnum(st,2);
- }
- if( range <= 1 )
- script_pushint(st, min);
- else
- script_pushint(st, rnd()%range + min);
-
- return 0;
-}
-
-/*==========================================
- * Warp sd to str,x,y or Random or SavePoint/Save
- *------------------------------------------*/
-BUILDIN_FUNC(warp)
-{
- int ret;
- int x,y;
- const char* str;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- str = script_getstr(st,2);
- x = script_getnum(st,3);
- y = script_getnum(st,4);
-
- if(strcmp(str,"Random")==0)
- ret = pc_randomwarp(sd,CLR_TELEPORT);
- else if(strcmp(str,"SavePoint")==0 || strcmp(str,"Save")==0)
- ret = pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
- else
- ret = pc_setpos(sd,mapindex_name2id(str),x,y,CLR_OUTSIGHT);
-
- if( ret ) {
- ShowError("buildin_warp: moving player '%s' to \"%s\",%d,%d failed.\n", sd->status.name, str, x, y);
- script_reportsrc(st);
- }
-
- return 0;
-}
-/*==========================================
- * Warp a specified area
- *------------------------------------------*/
-static int buildin_areawarp_sub(struct block_list *bl,va_list ap)
-{
- int x2,y2,x3,y3;
- unsigned int index;
-
- index = va_arg(ap,unsigned int);
- x2 = va_arg(ap,int);
- y2 = va_arg(ap,int);
- x3 = va_arg(ap,int);
- y3 = va_arg(ap,int);
-
- if(index == 0)
- pc_randomwarp((TBL_PC *)bl,CLR_TELEPORT);
- else if(x3 && y3) {
- int max, tx, ty, j = 0;
-
- // choose a suitable max number of attempts
- if( (max = (y3-y2+1)*(x3-x2+1)*3) > 1000 )
- max = 1000;
-
- // find a suitable map cell
- do {
- tx = rnd()%(x3-x2+1)+x2;
- ty = rnd()%(y3-y2+1)+y2;
- j++;
- } while( map_getcell(index,tx,ty,CELL_CHKNOPASS) && j < max );
-
- pc_setpos((TBL_PC *)bl,index,tx,ty,CLR_OUTSIGHT);
- }
- else
- pc_setpos((TBL_PC *)bl,index,x2,y2,CLR_OUTSIGHT);
- return 0;
-}
-BUILDIN_FUNC(areawarp)
-{
- int16 m, x0,y0,x1,y1, x2,y2,x3=0,y3=0;
- unsigned int index;
- const char *str;
- const char *mapname;
-
- mapname = script_getstr(st,2);
- x0 = script_getnum(st,3);
- y0 = script_getnum(st,4);
- x1 = script_getnum(st,5);
- y1 = script_getnum(st,6);
- str = script_getstr(st,7);
- x2 = script_getnum(st,8);
- y2 = script_getnum(st,9);
-
- if( script_hasdata(st,10) && script_hasdata(st,11) ) { // Warp area to area
- if( (x3 = script_getnum(st,10)) < 0 || (y3 = script_getnum(st,11)) < 0 ){
- x3 = 0;
- y3 = 0;
- } else if( x3 && y3 ) {
- // normalize x3/y3 coordinates
- if( x3 < x2 ) swap(x3,x2);
- if( y3 < y2 ) swap(y3,y2);
- }
- }
-
- if( (m = map_mapname2mapid(mapname)) < 0 )
- return 0;
-
- if( strcmp(str,"Random") == 0 )
- index = 0;
- else if( !(index=mapindex_name2id(str)) )
- return 0;
-
- map_foreachinarea(buildin_areawarp_sub, m,x0,y0,x1,y1, BL_PC, index,x2,y2,x3,y3);
- return 0;
-}
-
-/*==========================================
- * areapercentheal <map>,<x1>,<y1>,<x2>,<y2>,<hp>,<sp>
- *------------------------------------------*/
-static int buildin_areapercentheal_sub(struct block_list *bl,va_list ap)
-{
- int hp, sp;
- hp = va_arg(ap, int);
- sp = va_arg(ap, int);
- pc_percentheal((TBL_PC *)bl,hp,sp);
- return 0;
-}
-BUILDIN_FUNC(areapercentheal)
-{
- int hp,sp,m;
- const char *mapname;
- int x0,y0,x1,y1;
-
- mapname=script_getstr(st,2);
- x0=script_getnum(st,3);
- y0=script_getnum(st,4);
- x1=script_getnum(st,5);
- y1=script_getnum(st,6);
- hp=script_getnum(st,7);
- sp=script_getnum(st,8);
-
- if( (m=map_mapname2mapid(mapname))< 0)
- return 0;
-
- map_foreachinarea(buildin_areapercentheal_sub,m,x0,y0,x1,y1,BL_PC,hp,sp);
- return 0;
-}
-
-/*==========================================
- * warpchar [LuzZza]
- * Useful for warp one player from
- * another player npc-session.
- * Using: warpchar "mapname",x,y,Char_ID;
- *------------------------------------------*/
-BUILDIN_FUNC(warpchar)
-{
- int x,y,a;
- const char *str;
- TBL_PC *sd;
-
- str=script_getstr(st,2);
- x=script_getnum(st,3);
- y=script_getnum(st,4);
- a=script_getnum(st,5);
-
- sd = map_charid2sd(a);
- if( sd == NULL )
- return 0;
-
- if(strcmp(str, "Random") == 0)
- pc_randomwarp(sd, CLR_TELEPORT);
- else
- if(strcmp(str, "SavePoint") == 0)
- pc_setpos(sd, sd->status.save_point.map,sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT);
- else
- pc_setpos(sd, mapindex_name2id(str), x, y, CLR_TELEPORT);
-
- return 0;
-}
-/*==========================================
- * Warpparty - [Fredzilla] [Paradox924X]
- * Syntax: warpparty "to_mapname",x,y,Party_ID,{"from_mapname"};
- * If 'from_mapname' is specified, only the party members on that map will be warped
- *------------------------------------------*/
-BUILDIN_FUNC(warpparty)
-{
- TBL_PC *sd = NULL;
- TBL_PC *pl_sd;
- struct party_data* p;
- int type;
- int mapindex;
- int i;
-
- const char* str = script_getstr(st,2);
- int x = script_getnum(st,3);
- int y = script_getnum(st,4);
- int p_id = script_getnum(st,5);
- const char* str2 = NULL;
- if ( script_hasdata(st,6) )
- str2 = script_getstr(st,6);
-
- p = party_search(p_id);
- if(!p)
- return 0;
-
- type = ( strcmp(str,"Random")==0 ) ? 0
- : ( strcmp(str,"SavePointAll")==0 ) ? 1
- : ( strcmp(str,"SavePoint")==0 ) ? 2
- : ( strcmp(str,"Leader")==0 ) ? 3
- : 4;
-
- switch (type)
- {
- case 3:
- for(i = 0; i < MAX_PARTY && !p->party.member[i].leader; i++);
- if (i == MAX_PARTY || !p->data[i].sd) //Leader not found / not online
- return 0;
- pl_sd = p->data[i].sd;
- mapindex = pl_sd->mapindex;
- x = pl_sd->bl.x;
- y = pl_sd->bl.y;
- break;
- case 4:
- mapindex = mapindex_name2id(str);
- break;
- case 2:
- //"SavePoint" uses save point of the currently attached player
- if (( sd = script_rid2sd(st) ) == NULL )
- return 0;
- default:
- mapindex = 0;
- break;
- }
-
- for (i = 0; i < MAX_PARTY; i++)
- {
- if( !(pl_sd = p->data[i].sd) || pl_sd->status.party_id != p_id )
- continue;
-
- if( str2 && strcmp(str2, map[pl_sd->bl.m].name) != 0 )
- continue;
-
- if( pc_isdead(pl_sd) )
- continue;
-
- switch( type )
- {
- case 0: // Random
- if(!map[pl_sd->bl.m].flag.nowarp)
- pc_randomwarp(pl_sd,CLR_TELEPORT);
- break;
- case 1: // SavePointAll
- if(!map[pl_sd->bl.m].flag.noreturn)
- pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,CLR_TELEPORT);
- break;
- case 2: // SavePoint
- if(!map[pl_sd->bl.m].flag.noreturn)
- pc_setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
- break;
- case 3: // Leader
- case 4: // m,x,y
- if(!map[pl_sd->bl.m].flag.noreturn && !map[pl_sd->bl.m].flag.nowarp)
- pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT);
- break;
- }
- }
-
- return 0;
-}
-/*==========================================
- * Warpguild - [Fredzilla]
- * Syntax: warpguild "mapname",x,y,Guild_ID;
- *------------------------------------------*/
-BUILDIN_FUNC(warpguild)
-{
- TBL_PC *sd = NULL;
- TBL_PC *pl_sd;
- struct guild* g;
- struct s_mapiterator* iter;
- int type;
-
- const char* str = script_getstr(st,2);
- int x = script_getnum(st,3);
- int y = script_getnum(st,4);
- int gid = script_getnum(st,5);
-
- g = guild_search(gid);
- if( g == NULL )
- return 0;
-
- type = ( strcmp(str,"Random")==0 ) ? 0
- : ( strcmp(str,"SavePointAll")==0 ) ? 1
- : ( strcmp(str,"SavePoint")==0 ) ? 2
- : 3;
-
- if( type == 2 && ( sd = script_rid2sd(st) ) == NULL )
- {// "SavePoint" uses save point of the currently attached player
- return 0;
- }
-
- iter = mapit_getallusers();
- for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) )
- {
- if( pl_sd->status.guild_id != gid )
- continue;
-
- switch( type )
- {
- case 0: // Random
- if(!map[pl_sd->bl.m].flag.nowarp)
- pc_randomwarp(pl_sd,CLR_TELEPORT);
- break;
- case 1: // SavePointAll
- if(!map[pl_sd->bl.m].flag.noreturn)
- pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,CLR_TELEPORT);
- break;
- case 2: // SavePoint
- if(!map[pl_sd->bl.m].flag.noreturn)
- pc_setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
- break;
- case 3: // m,x,y
- if(!map[pl_sd->bl.m].flag.noreturn && !map[pl_sd->bl.m].flag.nowarp)
- pc_setpos(pl_sd,mapindex_name2id(str),x,y,CLR_TELEPORT);
- break;
- }
- }
- mapit_free(iter);
-
- return 0;
-}
-/*==========================================
- * Force Heal a player (hp and sp)
- *------------------------------------------*/
-BUILDIN_FUNC(heal)
-{
- TBL_PC *sd;
- int hp,sp;
-
- sd = script_rid2sd(st);
- if (!sd) return 0;
-
- hp=script_getnum(st,2);
- sp=script_getnum(st,3);
- status_heal(&sd->bl, hp, sp, 1);
- return 0;
-}
-/*==========================================
- * Heal a player by item (get vit bonus etc)
- *------------------------------------------*/
-BUILDIN_FUNC(itemheal)
-{
- TBL_PC *sd;
- int hp,sp;
-
- hp=script_getnum(st,2);
- sp=script_getnum(st,3);
-
- if(potion_flag==1) {
- potion_hp = hp;
- potion_sp = sp;
- return 0;
- }
-
- sd = script_rid2sd(st);
- if (!sd) return 0;
- pc_itemheal(sd,sd->itemid,hp,sp);
- return 0;
-}
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(percentheal)
-{
- int hp,sp;
- TBL_PC* sd;
-
- hp=script_getnum(st,2);
- sp=script_getnum(st,3);
-
- if(potion_flag==1) {
- potion_per_hp = hp;
- potion_per_sp = sp;
- return 0;
- }
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-#ifdef RENEWAL
- if( sd->sc.data[SC_EXTREMITYFIST2] )
- sp = 0;
-#endif
- pc_percentheal(sd,hp,sp);
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(jobchange)
-{
- int job, upper=-1;
-
- job=script_getnum(st,2);
- if( script_hasdata(st,3) )
- upper=script_getnum(st,3);
-
- if (pcdb_checkid(job))
- {
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- pc_jobchange(sd, job, upper);
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(jobname)
-{
- int class_=script_getnum(st,2);
- script_pushconststr(st, (char*)job_name(class_));
- return 0;
-}
-
-/// Get input from the player.
-/// For numeric inputs the value is capped to the range [min,max]. Returns 1 if
-/// the value was higher than 'max', -1 if lower than 'min' and 0 otherwise.
-/// For string inputs it returns 1 if the string was longer than 'max', -1 is
-/// shorter than 'min' and 0 otherwise.
-///
-/// input(<var>{,<min>{,<max>}}) -> <int>
-BUILDIN_FUNC(input)
-{
- TBL_PC* sd;
- struct script_data* data;
- int uid;
- const char* name;
- int min;
- int max;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- data = script_getdata(st,2);
- if( !data_isreference(data) ){
- ShowError("script:input: not a variable\n");
- script_reportdata(data);
- st->state = END;
- return 1;
- }
- uid = reference_getuid(data);
- name = reference_getname(data);
- min = (script_hasdata(st,3) ? script_getnum(st,3) : script_config.input_min_value);
- max = (script_hasdata(st,4) ? script_getnum(st,4) : script_config.input_max_value);
-
- if( !sd->state.menu_or_input )
- { // first invocation, display npc input box
- sd->state.menu_or_input = 1;
- st->state = RERUNLINE;
- if( is_string_variable(name) )
- clif_scriptinputstr(sd,st->oid);
- else
- clif_scriptinput(sd,st->oid);
- }
- else
- { // take received text/value and store it in the designated variable
- sd->state.menu_or_input = 0;
- if( is_string_variable(name) )
- {
- int len = (int)strlen(sd->npc_str);
- set_reg(st, sd, uid, name, (void*)sd->npc_str, script_getref(st,2));
- script_pushint(st, (len > max ? 1 : len < min ? -1 : 0));
- }
- else
- {
- int amount = sd->npc_amount;
- set_reg(st, sd, uid, name, (void*)__64BPRTSIZE(cap_value(amount,min,max)), script_getref(st,2));
- script_pushint(st, (amount > max ? 1 : amount < min ? -1 : 0));
- }
- st->state = RUN;
- }
- return 0;
-}
-
-// declare the copyarray method here for future reference
-BUILDIN_FUNC(copyarray);
-
-/// Sets the value of a variable.
-/// The value is converted to the type of the variable.
-///
-/// set(<variable>,<value>) -> <variable>
-BUILDIN_FUNC(set)
-{
- TBL_PC* sd = NULL;
- struct script_data* data;
- //struct script_data* datavalue;
- int num;
- const char* name;
- char prefix;
-
- data = script_getdata(st,2);
- //datavalue = script_getdata(st,3);
- if( !data_isreference(data) )
- {
- ShowError("script:set: not a variable\n");
- script_reportdata(script_getdata(st,2));
- st->state = END;
- return 1;
- }
-
- num = reference_getuid(data);
- name = reference_getname(data);
- prefix = *name;
-
- if( not_server_variable(prefix) )
- {
- sd = script_rid2sd(st);
- if( sd == NULL )
- {
- ShowError("script:set: no player attached for player variable '%s'\n", name);
- return 0;
- }
- }
-
-#if 0
- if( data_isreference(datavalue) )
- {// the value being referenced is a variable
- const char* namevalue = reference_getname(datavalue);
-
- if( !not_array_variable(*namevalue) )
- {// array variable being copied into another array variable
- if( sd == NULL && not_server_variable(*namevalue) && !(sd = script_rid2sd(st)) )
- {// player must be attached in order to copy a player variable
- ShowError("script:set: no player attached for player variable '%s'\n", namevalue);
- return 0;
- }
-
- if( is_string_variable(namevalue) != is_string_variable(name) )
- {// non-matching array value types
- ShowWarning("script:set: two array variables do not match in type.\n");
- return 0;
- }
-
- // push the maximum number of array values to the stack
- push_val(st->stack, C_INT, SCRIPT_MAX_ARRAYSIZE);
-
- // call the copy array method directly
- return buildin_copyarray(st);
- }
- }
-#endif
-
- if( is_string_variable(name) )
- set_reg(st,sd,num,name,(void*)script_getstr(st,3),script_getref(st,2));
- else
- set_reg(st,sd,num,name,(void*)__64BPRTSIZE(script_getnum(st,3)),script_getref(st,2));
-
- // return a copy of the variable reference
- script_pushcopy(st,2);
-
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////
-/// Array variables
-///
-
-/// Returns the size of the specified array
-static int32 getarraysize(struct script_state* st, int32 id, int32 idx, int isstring, struct DBMap** ref)
-{
- int32 ret = idx;
-
- if( isstring )
- {
- for( ; idx < SCRIPT_MAX_ARRAYSIZE; ++idx )
- {
- char* str = (char*)get_val2(st, reference_uid(id, idx), ref);
- if( str && *str )
- ret = idx + 1;
- script_removetop(st, -1, 0);
- }
- }
- else
- {
- for( ; idx < SCRIPT_MAX_ARRAYSIZE; ++idx )
- {
- int32 num = (int32)__64BPRTSIZE(get_val2(st, reference_uid(id, idx), ref));
- if( num )
- ret = idx + 1;
- script_removetop(st, -1, 0);
- }
- }
- return ret;
-}
-
-/// Sets values of an array, from the starting index.
-/// ex: setarray arr[1],1,2,3;
-///
-/// setarray <array variable>,<value1>{,<value2>...};
-BUILDIN_FUNC(setarray)
-{
- struct script_data* data;
- const char* name;
- int32 start;
- int32 end;
- int32 id;
- int32 i;
- TBL_PC* sd = NULL;
-
- data = script_getdata(st, 2);
- if( !data_isreference(data) )
- {
- ShowError("script:setarray: not a variable\n");
- script_reportdata(data);
- st->state = END;
- return 1;// not a variable
- }
-
- id = reference_getid(data);
- start = reference_getindex(data);
- name = reference_getname(data);
- if( not_array_variable(*name) )
- {
- ShowError("script:setarray: illegal scope\n");
- script_reportdata(data);
- st->state = END;
- return 1;// not supported
- }
-
- if( not_server_variable(*name) )
- {
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached
- }
-
- end = start + script_lastdata(st) - 2;
- if( end > SCRIPT_MAX_ARRAYSIZE )
- end = SCRIPT_MAX_ARRAYSIZE;
-
- if( is_string_variable(name) )
- {// string array
- for( i = 3; start < end; ++start, ++i )
- set_reg(st, sd, reference_uid(id, start), name, (void*)script_getstr(st,i), reference_getref(data));
- }
- else
- {// int array
- for( i = 3; start < end; ++start, ++i )
- set_reg(st, sd, reference_uid(id, start), name, (void*)__64BPRTSIZE(script_getnum(st,i)), reference_getref(data));
- }
- return 0;
-}
-
-/// Sets count values of an array, from the starting index.
-/// ex: cleararray arr[0],0,1;
-///
-/// cleararray <array variable>,<value>,<count>;
-BUILDIN_FUNC(cleararray)
-{
- struct script_data* data;
- const char* name;
- int32 start;
- int32 end;
- int32 id;
- void* v;
- TBL_PC* sd = NULL;
-
- data = script_getdata(st, 2);
- if( !data_isreference(data) )
- {
- ShowError("script:cleararray: not a variable\n");
- script_reportdata(data);
- st->state = END;
- return 1;// not a variable
- }
-
- id = reference_getid(data);
- start = reference_getindex(data);
- name = reference_getname(data);
- if( not_array_variable(*name) )
- {
- ShowError("script:cleararray: illegal scope\n");
- script_reportdata(data);
- st->state = END;
- return 1;// not supported
- }
-
- if( not_server_variable(*name) )
- {
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached
- }
-
- if( is_string_variable(name) )
- v = (void*)script_getstr(st, 3);
- else
- v = (void*)__64BPRTSIZE(script_getnum(st, 3));
-
- end = start + script_getnum(st, 4);
- if( end > SCRIPT_MAX_ARRAYSIZE )
- end = SCRIPT_MAX_ARRAYSIZE;
-
- for( ; start < end; ++start )
- set_reg(st, sd, reference_uid(id, start), name, v, script_getref(st,2));
- return 0;
-}
-
-/// Copies data from one array to another.
-/// ex: copyarray arr[0],arr[2],2;
-///
-/// copyarray <destination array variable>,<source array variable>,<count>;
-BUILDIN_FUNC(copyarray)
-{
- struct script_data* data1;
- struct script_data* data2;
- const char* name1;
- const char* name2;
- int32 idx1;
- int32 idx2;
- int32 id1;
- int32 id2;
- void* v;
- int32 i;
- int32 count;
- TBL_PC* sd = NULL;
-
- data1 = script_getdata(st, 2);
- data2 = script_getdata(st, 3);
- if( !data_isreference(data1) || !data_isreference(data2) )
- {
- ShowError("script:copyarray: not a variable\n");
- script_reportdata(data1);
- script_reportdata(data2);
- st->state = END;
- return 1;// not a variable
- }
-
- id1 = reference_getid(data1);
- id2 = reference_getid(data2);
- idx1 = reference_getindex(data1);
- idx2 = reference_getindex(data2);
- name1 = reference_getname(data1);
- name2 = reference_getname(data2);
- if( not_array_variable(*name1) || not_array_variable(*name2) )
- {
- ShowError("script:copyarray: illegal scope\n");
- script_reportdata(data1);
- script_reportdata(data2);
- st->state = END;
- return 1;// not supported
- }
-
- if( is_string_variable(name1) != is_string_variable(name2) )
- {
- ShowError("script:copyarray: type mismatch\n");
- script_reportdata(data1);
- script_reportdata(data2);
- st->state = END;
- return 1;// data type mismatch
- }
-
- if( not_server_variable(*name1) || not_server_variable(*name2) )
- {
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached
- }
-
- count = script_getnum(st, 4);
- if( count > SCRIPT_MAX_ARRAYSIZE - idx1 )
- count = SCRIPT_MAX_ARRAYSIZE - idx1;
- if( count <= 0 || (id1 == id2 && idx1 == idx2) )
- return 0;// nothing to copy
-
- if( id1 == id2 && idx1 > idx2 )
- {// destination might be overlapping the source - copy in reverse order
- for( i = count - 1; i >= 0; --i )
- {
- v = get_val2(st, reference_uid(id2, idx2 + i), reference_getref(data2));
- set_reg(st, sd, reference_uid(id1, idx1 + i), name1, v, reference_getref(data1));
- script_removetop(st, -1, 0);
- }
- }
- else
- {// normal copy
- for( i = 0; i < count; ++i )
- {
- if( idx2 + i < SCRIPT_MAX_ARRAYSIZE )
- {
- v = get_val2(st, reference_uid(id2, idx2 + i), reference_getref(data2));
- set_reg(st, sd, reference_uid(id1, idx1 + i), name1, v, reference_getref(data1));
- script_removetop(st, -1, 0);
- }
- else// out of range - assume ""/0
- set_reg(st, sd, reference_uid(id1, idx1 + i), name1, (is_string_variable(name1)?(void*)"":(void*)0), reference_getref(data1));
- }
- }
- return 0;
-}
-
-/// Returns the size of the array.
-/// Assumes that everything before the starting index exists.
-/// ex: getarraysize(arr[3])
-///
-/// getarraysize(<array variable>) -> <int>
-BUILDIN_FUNC(getarraysize)
-{
- struct script_data* data;
- const char* name;
-
- data = script_getdata(st, 2);
- if( !data_isreference(data) )
- {
- ShowError("script:getarraysize: not a variable\n");
- script_reportdata(data);
- script_pushnil(st);
- st->state = END;
- return 1;// not a variable
- }
-
- name = reference_getname(data);
- if( not_array_variable(*name) )
- {
- ShowError("script:getarraysize: illegal scope\n");
- script_reportdata(data);
- script_pushnil(st);
- st->state = END;
- return 1;// not supported
- }
-
- script_pushint(st, getarraysize(st, reference_getid(data), reference_getindex(data), is_string_variable(name), reference_getref(data)));
- return 0;
-}
-
-/// Deletes count or all the elements in an array, from the starting index.
-/// ex: deletearray arr[4],2;
-///
-/// deletearray <array variable>;
-/// deletearray <array variable>,<count>;
-BUILDIN_FUNC(deletearray)
-{
- struct script_data* data;
- const char* name;
- int start;
- int end;
- int id;
- TBL_PC *sd = NULL;
-
- data = script_getdata(st, 2);
- if( !data_isreference(data) )
- {
- ShowError("script:deletearray: not a variable\n");
- script_reportdata(data);
- st->state = END;
- return 1;// not a variable
- }
-
- id = reference_getid(data);
- start = reference_getindex(data);
- name = reference_getname(data);
- if( not_array_variable(*name) )
- {
- ShowError("script:deletearray: illegal scope\n");
- script_reportdata(data);
- st->state = END;
- return 1;// not supported
- }
-
- if( not_server_variable(*name) )
- {
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached
- }
-
- end = SCRIPT_MAX_ARRAYSIZE;
-
- if( start >= end )
- return 0;// nothing to free
-
- if( script_hasdata(st,3) )
- {
- int count = script_getnum(st, 3);
- if( count > end - start )
- count = end - start;
- if( count <= 0 )
- return 0;// nothing to free
-
- // move rest of the elements backward
- for( ; start + count < end; ++start )
- {
- void* v = get_val2(st, reference_uid(id, start + count), reference_getref(data));
- set_reg(st, sd, reference_uid(id, start), name, v, reference_getref(data));
- script_removetop(st, -1, 0);
- }
- }
-
- // clear the rest of the array
- if( is_string_variable(name) )
- {
- for( ; start < end; ++start )
- set_reg(st, sd, reference_uid(id, start), name, (void *)"", reference_getref(data));
- }
- else
- {
- for( ; start < end; ++start )
- set_reg(st, sd, reference_uid(id, start), name, (void*)0, reference_getref(data));
- }
- return 0;
-}
-
-/// Returns a reference to the target index of the array variable.
-/// Equivalent to var[index].
-///
-/// getelementofarray(<array variable>,<index>) -> <variable reference>
-BUILDIN_FUNC(getelementofarray)
-{
- struct script_data* data;
- const char* name;
- int32 id;
- int i;
-
- data = script_getdata(st, 2);
- if( !data_isreference(data) )
- {
- ShowError("script:getelementofarray: not a variable\n");
- script_reportdata(data);
- script_pushnil(st);
- st->state = END;
- return 1;// not a variable
- }
-
- id = reference_getid(data);
- name = reference_getname(data);
- if( not_array_variable(*name) )
- {
- ShowError("script:getelementofarray: illegal scope\n");
- script_reportdata(data);
- script_pushnil(st);
- st->state = END;
- return 1;// not supported
- }
-
- i = script_getnum(st, 3);
- if( i < 0 || i >= SCRIPT_MAX_ARRAYSIZE )
- {
- ShowWarning("script:getelementofarray: index out of range (%d)\n", i);
- script_reportdata(data);
- script_pushnil(st);
- st->state = END;
- return 1;// out of range
- }
-
- push_val2(st->stack, C_NAME, reference_uid(id, i), reference_getref(data));
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////
-/// ...
-///
-
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(setlook)
-{
- int type,val;
- TBL_PC* sd;
-
- type=script_getnum(st,2);
- val=script_getnum(st,3);
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- pc_changelook(sd,type,val);
-
- return 0;
-}
-
-BUILDIN_FUNC(changelook)
-{ // As setlook but only client side
- int type,val;
- TBL_PC* sd;
-
- type=script_getnum(st,2);
- val=script_getnum(st,3);
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- clif_changelook(&sd->bl,type,val);
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(cutin)
-{
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- clif_cutin(sd,script_getstr(st,2),script_getnum(st,3));
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(viewpoint)
-{
- int type,x,y,id,color;
- TBL_PC* sd;
-
- type=script_getnum(st,2);
- x=script_getnum(st,3);
- y=script_getnum(st,4);
- id=script_getnum(st,5);
- color=script_getnum(st,6);
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- clif_viewpoint(sd,st->oid,type,x,y,id,color);
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(countitem)
-{
- int nameid, i;
- int count = 0;
- struct item_data* id = NULL;
- struct script_data* data;
-
- TBL_PC* sd = script_rid2sd(st);
- if (!sd) {
- script_pushint(st,0);
- return 0;
- }
-
- data = script_getdata(st,2);
- get_val(st, data); // convert into value in case of a variable
-
- if( data_isstring(data) )
- {// item name
- id = itemdb_searchname(conv_str(st, data));
- }
- else
- {// item id
- id = itemdb_exists(conv_num(st, data));
- }
-
- if( id == NULL )
- {
- ShowError("buildin_countitem: Invalid item '%s'.\n", script_getstr(st,2)); // returns string, regardless of what it was
- script_pushint(st,0);
- return 1;
- }
-
- nameid = id->nameid;
-
- for(i = 0; i < MAX_INVENTORY; i++)
- if(sd->status.inventory[i].nameid == nameid)
- count += sd->status.inventory[i].amount;
-
- script_pushint(st,count);
- return 0;
-}
-
-/*==========================================
- * countitem2(nameID,Identified,Refine,Attribute,Card0,Card1,Card2,Card3) [Lupus]
- * returns number of items that meet the conditions
- *------------------------------------------*/
-BUILDIN_FUNC(countitem2)
-{
- int nameid, iden, ref, attr, c1, c2, c3, c4;
- int count = 0;
- int i;
- struct item_data* id = NULL;
- struct script_data* data;
-
- TBL_PC* sd = script_rid2sd(st);
- if (!sd) {
- script_pushint(st,0);
- return 0;
- }
-
- data = script_getdata(st,2);
- get_val(st, data); // convert into value in case of a variable
-
- if( data_isstring(data) )
- {// item name
- id = itemdb_searchname(conv_str(st, data));
- }
- else
- {// item id
- id = itemdb_exists(conv_num(st, data));
- }
-
- if( id == NULL )
- {
- ShowError("buildin_countitem2: Invalid item '%s'.\n", script_getstr(st,2)); // returns string, regardless of what it was
- script_pushint(st,0);
- return 1;
- }
-
- nameid = id->nameid;
- iden = script_getnum(st,3);
- ref = script_getnum(st,4);
- attr = script_getnum(st,5);
- c1 = (short)script_getnum(st,6);
- c2 = (short)script_getnum(st,7);
- c3 = (short)script_getnum(st,8);
- c4 = (short)script_getnum(st,9);
-
- for(i = 0; i < MAX_INVENTORY; i++)
- if (sd->status.inventory[i].nameid > 0 && sd->inventory_data[i] != NULL &&
- sd->status.inventory[i].amount > 0 && sd->status.inventory[i].nameid == nameid &&
- sd->status.inventory[i].identify == iden && sd->status.inventory[i].refine == ref &&
- sd->status.inventory[i].attribute == attr && sd->status.inventory[i].card[0] == c1 &&
- sd->status.inventory[i].card[1] == c2 && sd->status.inventory[i].card[2] == c3 &&
- sd->status.inventory[i].card[3] == c4
- )
- count += sd->status.inventory[i].amount;
-
- script_pushint(st,count);
- return 0;
-}
-
-/*==========================================
- * Check if item with this amount can fit in inventory
- * Checking : weight, stack amount >32k, slots amount >(MAX_INVENTORY)
- * Return
- * 0 : fail
- * 1 : success (npc side only)
- *------------------------------------------*/
-BUILDIN_FUNC(checkweight)
-{
- int nameid, amount, slots, amount2=0;
- unsigned int weight=0, i, nbargs;
- struct item_data* id = NULL;
- struct map_session_data* sd;
- struct script_data* data;
-
- if( ( sd = script_rid2sd(st) ) == NULL ){
- return 0;
- }
- nbargs = script_lastdata(st)+1;
- if(nbargs%2){
- ShowError("buildin_checkweight: Invalid nb of args should be a multiple of 2.\n");
- script_pushint(st,0);
- return 1;
- }
- slots = pc_inventoryblank(sd); //nb of empty slot
-
- for(i=2; i<nbargs; i=i+2){
- data = script_getdata(st,i);
- get_val(st, data); // convert into value in case of a variable
- if( data_isstring(data) ){// item name
- id = itemdb_searchname(conv_str(st, data));
- } else {// item id
- id = itemdb_exists(conv_num(st, data));
- }
- if( id == NULL ) {
- ShowError("buildin_checkweight: Invalid item '%s'.\n", script_getstr(st,i)); // returns string, regardless of what it was
- script_pushint(st,0);
- return 1;
- }
- nameid = id->nameid;
-
- amount = script_getnum(st,i+1);
- if( amount < 1 ) {
- ShowError("buildin_checkweight: Invalid amount '%d'.\n", amount);
- script_pushint(st,0);
- return 1;
- }
-
- weight += itemdb_weight(nameid)*amount; //total weight for all chk
- if( weight + sd->weight > sd->max_weight )
- {// too heavy
- script_pushint(st,0);
- return 0;
- }
-
- switch( pc_checkadditem(sd, nameid, amount) )
- {
- case ADDITEM_EXIST:
- // item is already in inventory, but there is still space for the requested amount
- break;
- case ADDITEM_NEW:
- if( itemdb_isstackable(nameid) ) {// stackable
- amount2++;
- if( slots < amount2 ) {
- script_pushint(st,0);
- return 0;
- }
- }
- else {// non-stackable
- amount2 += amount;
- if( slots < amount2){
- script_pushint(st,0);
- return 0;
- }
- }
- break;
- case ADDITEM_OVERAMOUNT:
- script_pushint(st,0);
- return 0;
- }
- }
- script_pushint(st,1);
- return 0;
-}
-
-BUILDIN_FUNC(checkweight2)
-{
- //variable sub checkweight
- int32 nameid=-1, amount=-1;
- int i=0, amount2=0, slots=0, weight=0;
- short fail=0;
-
- //variable for array parsing
- struct script_data* data_it;
- struct script_data* data_nb;
- const char* name_it;
- const char* name_nb;
- int32 id_it, id_nb;
- int32 idx_it, idx_nb;
- int nb_it, nb_nb; //array size
-
- TBL_PC *sd = script_rid2sd(st);
- nullpo_retr(1,sd);
-
- data_it = script_getdata(st, 2);
- data_nb = script_getdata(st, 3);
-
- if( !data_isreference(data_it) || !data_isreference(data_nb))
- {
- ShowError("script:checkweight2: parameter not a variable\n");
- script_pushint(st,0);
- return 1;// not a variable
- }
- id_it = reference_getid(data_it);
- id_nb = reference_getid(data_nb);
- idx_it = reference_getindex(data_it);
- idx_nb = reference_getindex(data_nb);
- name_it = reference_getname(data_it);
- name_nb = reference_getname(data_nb);
-
- if( not_array_variable(*name_it) || not_array_variable(*name_nb))
- {
- ShowError("script:checkweight2: illegal scope\n");
- script_pushint(st,0);
- return 1;// not supported
- }
- if(is_string_variable(name_it) || is_string_variable(name_nb)){
- ShowError("script:checkweight2: illegal type, need int\n");
- script_pushint(st,0);
- return 1;// not supported
- }
- nb_it = getarraysize(st, id_it, idx_it, 0, reference_getref(data_it));
- nb_nb = getarraysize(st, id_nb, idx_nb, 0, reference_getref(data_nb));
- if(nb_it != nb_nb){
- ShowError("Size mistmatch: nb_it=%d, nb_nb=%d\n",nb_it,nb_nb);
- fail = 1;
- }
-
- slots = pc_inventoryblank(sd);
- for(i=0; i<nb_it; i++){
- nameid = (int32)__64BPRTSIZE(get_val2(st,reference_uid(id_it,idx_it+i),reference_getref(data_it)));
- script_removetop(st, -1, 0);
- amount = (int32)__64BPRTSIZE(get_val2(st,reference_uid(id_nb,idx_nb+i),reference_getref(data_nb)));
- script_removetop(st, -1, 0);
- if(fail) continue; //cpntonie to depop rest
-
- if(itemdb_exists(nameid) == NULL ){
- ShowError("buildin_checkweight2: Invalid item '%d'.\n", nameid);
- fail=1;
- continue;
- }
- if(amount < 0 ){
- ShowError("buildin_checkweight2: Invalid amount '%d'.\n", amount);
- fail = 1;
- continue;
- }
- weight += itemdb_weight(nameid)*amount;
- if( weight + sd->weight > sd->max_weight ){
- fail = 1;
- continue;
- }
- switch( pc_checkadditem(sd, nameid, amount) ) {
- case ADDITEM_EXIST:
- // item is already in inventory, but there is still space for the requested amount
- break;
- case ADDITEM_NEW:
- if( itemdb_isstackable(nameid) ){// stackable
- amount2++;
- if( slots < amount2 )
- fail = 1;
- }
- else {// non-stackable
- amount2 += amount;
- if( slots < amount2 ){
- fail = 1;
- }
- }
- break;
- case ADDITEM_OVERAMOUNT:
- fail = 1;
- } //end switch
- } //end loop DO NOT break it prematurly we need to depop all stack
-
- fail?script_pushint(st,0):script_pushint(st,1);
- return 0;
-}
-
-/*==========================================
- * getitem <item id>,<amount>{,<account ID>};
- * getitem "<item name>",<amount>{,<account ID>};
- *------------------------------------------*/
-BUILDIN_FUNC(getitem)
-{
- int nameid,amount,get_count,i,flag = 0;
- struct item it;
- TBL_PC *sd;
- struct script_data *data;
-
- data=script_getdata(st,2);
- get_val(st,data);
- if( data_isstring(data) )
- {// "<item name>"
- const char *name=conv_str(st,data);
- struct item_data *item_data = itemdb_searchname(name);
- if( item_data == NULL ){
- ShowError("buildin_getitem: Nonexistant item %s requested.\n", name);
- return 1; //No item created.
- }
- nameid=item_data->nameid;
- } else if( data_isint(data) )
- {// <item id>
- nameid=conv_num(st,data);
- //Violet Box, Blue Box, etc - random item pick
- if( nameid < 0 ) {
- nameid = -nameid;
- flag = 1;
- }
- if( nameid <= 0 || !itemdb_exists(nameid) ){
- ShowError("buildin_getitem: Nonexistant item %d requested.\n", nameid);
- return 1; //No item created.
- }
- } else {
- ShowError("buildin_getitem: invalid data type for argument #1 (%d).", data->type);
- return 1;
- }
-
- // <amount>
- if( (amount=script_getnum(st,3)) <= 0)
- return 0; //return if amount <=0, skip the useles iteration
-
- memset(&it,0,sizeof(it));
- it.nameid=nameid;
- if(!flag)
- it.identify=1;
- else
- it.identify=itemdb_isidentified(nameid);
-
- if( script_hasdata(st,4) )
- sd=map_id2sd(script_getnum(st,4)); // <Account ID>
- else
- sd=script_rid2sd(st); // Attached player
-
- if( sd == NULL ) // no target
- return 0;
-
- //Check if it's stackable.
- if (!itemdb_isstackable(nameid))
- get_count = 1;
- else
- get_count = amount;
-
- for (i = 0; i < amount; i += get_count)
- {
- // if not pet egg
- if (!pet_create_egg(sd, nameid))
- {
- if ((flag = pc_additem(sd, &it, get_count, LOG_TYPE_SCRIPT)))
- {
- clif_additem(sd, 0, 0, flag);
- if( pc_candrop(sd,&it) )
- map_addflooritem(&it,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
- }
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(getitem2)
-{
- int nameid,amount,get_count,i,flag = 0;
- int iden,ref,attr,c1,c2,c3,c4;
- struct item_data *item_data;
- struct item item_tmp;
- TBL_PC *sd;
- struct script_data *data;
-
- if( script_hasdata(st,11) )
- sd=map_id2sd(script_getnum(st,11)); // <Account ID>
- else
- sd=script_rid2sd(st); // Attached player
-
- if( sd == NULL ) // no target
- return 0;
-
- data=script_getdata(st,2);
- get_val(st,data);
- if( data_isstring(data) ){
- const char *name=conv_str(st,data);
- struct item_data *item_data = itemdb_searchname(name);
- if( item_data )
- nameid=item_data->nameid;
- else
- nameid=UNKNOWN_ITEM_ID;
- }else
- nameid=conv_num(st,data);
-
- amount=script_getnum(st,3);
- iden=script_getnum(st,4);
- ref=script_getnum(st,5);
- attr=script_getnum(st,6);
- c1=(short)script_getnum(st,7);
- c2=(short)script_getnum(st,8);
- c3=(short)script_getnum(st,9);
- c4=(short)script_getnum(st,10);
-
- if(nameid<0) { // Invalide nameid
- nameid = -nameid;
- flag = 1;
- }
-
- if(nameid > 0) {
- memset(&item_tmp,0,sizeof(item_tmp));
- item_data=itemdb_exists(nameid);
- if (item_data == NULL)
- return -1;
- if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR){
- if(ref > MAX_REFINE) ref = MAX_REFINE;
- }
- else if(item_data->type==IT_PETEGG) {
- iden = 1;
- ref = 0;
- }
- else {
- iden = 1;
- ref = attr = 0;
- }
-
- item_tmp.nameid=nameid;
- if(!flag)
- item_tmp.identify=iden;
- else if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR)
- item_tmp.identify=0;
- item_tmp.refine=ref;
- item_tmp.attribute=attr;
- item_tmp.card[0]=(short)c1;
- item_tmp.card[1]=(short)c2;
- item_tmp.card[2]=(short)c3;
- item_tmp.card[3]=(short)c4;
-
- //Check if it's stackable.
- if (!itemdb_isstackable(nameid))
- get_count = 1;
- else
- get_count = amount;
-
- for (i = 0; i < amount; i += get_count)
- {
- // if not pet egg
- if (!pet_create_egg(sd, nameid))
- {
- if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_SCRIPT)))
- {
- clif_additem(sd, 0, 0, flag);
- if( pc_candrop(sd,&item_tmp) )
- map_addflooritem(&item_tmp,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
- }
- }
- }
-
- return 0;
-}
-
-/*==========================================
- * rentitem <item id>,<seconds>
- * rentitem "<item name>",<seconds>
- *------------------------------------------*/
-BUILDIN_FUNC(rentitem)
-{
- struct map_session_data *sd;
- struct script_data *data;
- struct item it;
- int seconds;
- int nameid = 0, flag;
-
- data = script_getdata(st,2);
- get_val(st,data);
-
- if( (sd = script_rid2sd(st)) == NULL )
- return 0;
-
- if( data_isstring(data) )
- {
- const char *name = conv_str(st,data);
- struct item_data *itd = itemdb_searchname(name);
- if( itd == NULL )
- {
- ShowError("buildin_rentitem: Nonexistant item %s requested.\n", name);
- return 1;
- }
- nameid = itd->nameid;
- }
- else if( data_isint(data) )
- {
- nameid = conv_num(st,data);
- if( nameid <= 0 || !itemdb_exists(nameid) )
- {
- ShowError("buildin_rentitem: Nonexistant item %d requested.\n", nameid);
- return 1;
- }
- }
- else
- {
- ShowError("buildin_rentitem: invalid data type for argument #1 (%d).\n", data->type);
- return 1;
- }
-
- seconds = script_getnum(st,3);
- memset(&it, 0, sizeof(it));
- it.nameid = nameid;
- it.identify = 1;
- it.expire_time = (unsigned int)(time(NULL) + seconds);
-
- if( (flag = pc_additem(sd, &it, 1, LOG_TYPE_SCRIPT)) )
- {
- clif_additem(sd, 0, 0, flag);
- return 1;
- }
-
- return 0;
-}
-
-/*==========================================
- * gets an item with someone's name inscribed [Skotlex]
- * getinscribeditem item_num, character_name
- * Returned Qty is always 1, only works on equip-able
- * equipment
- *------------------------------------------*/
-BUILDIN_FUNC(getnameditem)
-{
- int nameid;
- struct item item_tmp;
- TBL_PC *sd, *tsd;
- struct script_data *data;
-
- sd = script_rid2sd(st);
- if (sd == NULL)
- { //Player not attached!
- script_pushint(st,0);
- return 0;
- }
-
- data=script_getdata(st,2);
- get_val(st,data);
- if( data_isstring(data) ){
- const char *name=conv_str(st,data);
- struct item_data *item_data = itemdb_searchname(name);
- if( item_data == NULL)
- { //Failed
- script_pushint(st,0);
- return 0;
- }
- nameid = item_data->nameid;
- }else
- nameid = conv_num(st,data);
-
- if(!itemdb_exists(nameid)/* || itemdb_isstackable(nameid)*/)
- { //Even though named stackable items "could" be risky, they are required for certain quests.
- script_pushint(st,0);
- return 0;
- }
-
- data=script_getdata(st,3);
- get_val(st,data);
- if( data_isstring(data) ) //Char Name
- tsd=map_nick2sd(conv_str(st,data));
- else //Char Id was given
- tsd=map_charid2sd(conv_num(st,data));
-
- if( tsd == NULL )
- { //Failed
- script_pushint(st,0);
- return 0;
- }
-
- memset(&item_tmp,0,sizeof(item_tmp));
- item_tmp.nameid=nameid;
- item_tmp.amount=1;
- item_tmp.identify=1;
- item_tmp.card[0]=CARD0_CREATE; //we don't use 255! because for example SIGNED WEAPON shouldn't get TOP10 BS Fame bonus [Lupus]
- item_tmp.card[2]=tsd->status.char_id;
- item_tmp.card[3]=tsd->status.char_id >> 16;
- if(pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT)) {
- script_pushint(st,0);
- return 0; //Failed to add item, we will not drop if they don't fit
- }
-
- script_pushint(st,1);
- return 0;
-}
-
-/*==========================================
- * gets a random item ID from an item group [Skotlex]
- * groupranditem group_num
- *------------------------------------------*/
-BUILDIN_FUNC(grouprandomitem)
-{
- int group;
-
- group = script_getnum(st,2);
- script_pushint(st,itemdb_searchrandomid(group));
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(makeitem)
-{
- int nameid,amount,flag = 0;
- int x,y,m;
- const char *mapname;
- struct item item_tmp;
- struct script_data *data;
-
- data=script_getdata(st,2);
- get_val(st,data);
- if( data_isstring(data) ){
- const char *name=conv_str(st,data);
- struct item_data *item_data = itemdb_searchname(name);
- if( item_data )
- nameid=item_data->nameid;
- else
- nameid=UNKNOWN_ITEM_ID;
- }else
- nameid=conv_num(st,data);
-
- amount=script_getnum(st,3);
- mapname =script_getstr(st,4);
- x =script_getnum(st,5);
- y =script_getnum(st,6);
-
- if(strcmp(mapname,"this")==0)
- {
- TBL_PC *sd;
- sd = script_rid2sd(st);
- if (!sd) return 0; //Failed...
- m=sd->bl.m;
- } else
- m=map_mapname2mapid(mapname);
-
- if(nameid<0) {
- nameid = -nameid;
- flag = 1;
- }
-
- if(nameid > 0) {
- memset(&item_tmp,0,sizeof(item_tmp));
- item_tmp.nameid=nameid;
- if(!flag)
- item_tmp.identify=1;
- else
- item_tmp.identify=itemdb_isidentified(nameid);
-
- map_addflooritem(&item_tmp,amount,m,x,y,0,0,0,0);
- }
-
- return 0;
-}
-
-
-/// Counts / deletes the current item given by idx.
-/// Used by buildin_delitem_search
-/// Relies on all input data being already fully valid.
-static void buildin_delitem_delete(struct map_session_data* sd, int idx, int* amount, bool delete_items)
-{
- int delamount;
- struct item* inv = &sd->status.inventory[idx];
-
- delamount = ( amount[0] < inv->amount ) ? amount[0] : inv->amount;
-
- if( delete_items )
- {
- if( sd->inventory_data[idx]->type == IT_PETEGG && inv->card[0] == CARD0_PET )
- {// delete associated pet
- intif_delete_petdata(MakeDWord(inv->card[1], inv->card[2]));
- }
- pc_delitem(sd, idx, delamount, 0, 0, LOG_TYPE_SCRIPT);
- }
-
- amount[0]-= delamount;
-}
-
-
-/// Searches for item(s) and checks, if there is enough of them.
-/// Used by delitem and delitem2
-/// Relies on all input data being already fully valid.
-/// @param exact_match will also match item attributes and cards, not just name id
-/// @return true when all items could be deleted, false when there were not enough items to delete
-static bool buildin_delitem_search(struct map_session_data* sd, struct item* it, bool exact_match)
-{
- bool delete_items = false;
- int i, amount, important;
- struct item* inv;
-
- // prefer always non-equipped items
- it->equip = 0;
-
- // when searching for nameid only, prefer additionally
- if( !exact_match )
- {
- // non-refined items
- it->refine = 0;
- // card-less items
- memset(it->card, 0, sizeof(it->card));
- }
-
- for(;;)
- {
- amount = it->amount;
- important = 0;
-
- // 1st pass -- less important items / exact match
- for( i = 0; amount && i < ARRAYLENGTH(sd->status.inventory); i++ )
- {
- inv = &sd->status.inventory[i];
-
- if( !inv->nameid || !sd->inventory_data[i] || inv->nameid != it->nameid )
- {// wrong/invalid item
- continue;
- }
-
- if( inv->equip != it->equip || inv->refine != it->refine )
- {// not matching attributes
- important++;
- continue;
- }
-
- if( exact_match )
- {
- if( inv->identify != it->identify || inv->attribute != it->attribute || memcmp(inv->card, it->card, sizeof(inv->card)) )
- {// not matching exact attributes
- continue;
- }
- }
- else
- {
- if( sd->inventory_data[i]->type == IT_PETEGG )
- {
- if( inv->card[0] == CARD0_PET && CheckForCharServer() )
- {// pet which cannot be deleted
- continue;
- }
- }
- else if( memcmp(inv->card, it->card, sizeof(inv->card)) )
- {// named/carded item
- important++;
- continue;
- }
- }
-
- // count / delete item
- buildin_delitem_delete(sd, i, &amount, delete_items);
- }
-
- // 2nd pass -- any matching item
- if( amount == 0 || important == 0 )
- {// either everything was already consumed or no items were skipped
- ;
- }
- else for( i = 0; amount && i < ARRAYLENGTH(sd->status.inventory); i++ )
- {
- inv = &sd->status.inventory[i];
-
- if( !inv->nameid || !sd->inventory_data[i] || inv->nameid != it->nameid )
- {// wrong/invalid item
- continue;
- }
-
- if( sd->inventory_data[i]->type == IT_PETEGG && inv->card[0] == CARD0_PET && CheckForCharServer() )
- {// pet which cannot be deleted
- continue;
- }
-
- if( exact_match )
- {
- if( inv->refine != it->refine || inv->identify != it->identify || inv->attribute != it->attribute || memcmp(inv->card, it->card, sizeof(inv->card)) )
- {// not matching attributes
- continue;
- }
- }
-
- // count / delete item
- buildin_delitem_delete(sd, i, &amount, delete_items);
- }
-
- if( amount )
- {// not enough items
- return false;
- }
- else if( delete_items )
- {// we are done with the work
- return true;
- }
- else
- {// get rid of the items now
- delete_items = true;
- }
- }
-}
-
-
-/// Deletes items from the target/attached player.
-/// Prioritizes ordinary items.
-///
-/// delitem <item id>,<amount>{,<account id>}
-/// delitem "<item name>",<amount>{,<account id>}
-BUILDIN_FUNC(delitem)
-{
- TBL_PC *sd;
- struct item it;
- struct script_data *data;
-
- if( script_hasdata(st,4) )
- {
- int account_id = script_getnum(st,4);
- sd = map_id2sd(account_id); // <account id>
- if( sd == NULL )
- {
- ShowError("script:delitem: player not found (AID=%d).\n", account_id);
- st->state = END;
- return 1;
- }
- }
- else
- {
- sd = script_rid2sd(st);// attached player
- if( sd == NULL )
- return 0;
- }
-
- data = script_getdata(st,2);
- get_val(st,data);
- if( data_isstring(data) )
- {
- const char* item_name = conv_str(st,data);
- struct item_data* id = itemdb_searchname(item_name);
- if( id == NULL )
- {
- ShowError("script:delitem: unknown item \"%s\".\n", item_name);
- st->state = END;
- return 1;
- }
- it.nameid = id->nameid;// "<item name>"
- }
- else
- {
- it.nameid = conv_num(st,data);// <item id>
- if( !itemdb_exists( it.nameid ) )
- {
- ShowError("script:delitem: unknown item \"%d\".\n", it.nameid);
- st->state = END;
- return 1;
- }
- }
-
- it.amount=script_getnum(st,3);
-
- if( it.amount <= 0 )
- return 0;// nothing to do
-
- if( buildin_delitem_search(sd, &it, false) )
- {// success
- return 0;
- }
-
- ShowError("script:delitem: failed to delete %d items (AID=%d item_id=%d).\n", it.amount, sd->status.account_id, it.nameid);
- st->state = END;
- clif_scriptclose(sd, st->oid);
- return 1;
-}
-
-/// Deletes items from the target/attached player.
-///
-/// delitem2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>}
-/// delitem2 "<Item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>}
-BUILDIN_FUNC(delitem2)
-{
- TBL_PC *sd;
- struct item it;
- struct script_data *data;
-
- if( script_hasdata(st,11) )
- {
- int account_id = script_getnum(st,11);
- sd = map_id2sd(account_id); // <account id>
- if( sd == NULL )
- {
- ShowError("script:delitem2: player not found (AID=%d).\n", account_id);
- st->state = END;
- return 1;
- }
- }
- else
- {
- sd = script_rid2sd(st);// attached player
- if( sd == NULL )
- return 0;
- }
-
- data = script_getdata(st,2);
- get_val(st,data);
- if( data_isstring(data) )
- {
- const char* item_name = conv_str(st,data);
- struct item_data* id = itemdb_searchname(item_name);
- if( id == NULL )
- {
- ShowError("script:delitem2: unknown item \"%s\".\n", item_name);
- st->state = END;
- return 1;
- }
- it.nameid = id->nameid;// "<item name>"
- }
- else
- {
- it.nameid = conv_num(st,data);// <item id>
- if( !itemdb_exists( it.nameid ) )
- {
- ShowError("script:delitem: unknown item \"%d\".\n", it.nameid);
- st->state = END;
- return 1;
- }
- }
-
- it.amount=script_getnum(st,3);
- it.identify=script_getnum(st,4);
- it.refine=script_getnum(st,5);
- it.attribute=script_getnum(st,6);
- it.card[0]=(short)script_getnum(st,7);
- it.card[1]=(short)script_getnum(st,8);
- it.card[2]=(short)script_getnum(st,9);
- it.card[3]=(short)script_getnum(st,10);
-
- if( it.amount <= 0 )
- return 0;// nothing to do
-
- if( buildin_delitem_search(sd, &it, true) )
- {// success
- return 0;
- }
-
- ShowError("script:delitem2: failed to delete %d items (AID=%d item_id=%d).\n", it.amount, sd->status.account_id, it.nameid);
- st->state = END;
- clif_scriptclose(sd, st->oid);
- return 1;
-}
-
-/*==========================================
- * Enables/Disables use of items while in an NPC [Skotlex]
- *------------------------------------------*/
-BUILDIN_FUNC(enableitemuse)
-{
- TBL_PC *sd;
- sd=script_rid2sd(st);
- if (sd)
- sd->npc_item_flag = st->oid;
- return 0;
-}
-
-BUILDIN_FUNC(disableitemuse)
-{
- TBL_PC *sd;
- sd=script_rid2sd(st);
- if (sd)
- sd->npc_item_flag = 0;
- return 0;
-}
-
-/*==========================================
- * return the basic stats of sd
- * chk pc_readparam for available type
- *------------------------------------------*/
-BUILDIN_FUNC(readparam)
-{
- int type;
- TBL_PC *sd;
-
- type=script_getnum(st,2);
- if( script_hasdata(st,3) )
- sd=map_nick2sd(script_getstr(st,3));
- else
- sd=script_rid2sd(st);
-
- if(sd==NULL){
- script_pushint(st,-1);
- return 0;
- }
-
- script_pushint(st,pc_readparam(sd,type));
-
- return 0;
-}
-
-/*==========================================
- * Return charid identification
- * return by @num :
- * 0 : char_id
- * 1 : party_id
- * 2 : guild_id
- * 3 : account_id
- * 4 : bg_id
- *------------------------------------------*/
-BUILDIN_FUNC(getcharid)
-{
- int num;
- TBL_PC *sd;
-
- num = script_getnum(st,2);
- if( script_hasdata(st,3) )
- sd=map_nick2sd(script_getstr(st,3));
- else
- sd=script_rid2sd(st);
-
- if(sd==NULL){
- script_pushint(st,0); //return 0, according docs
- return 0;
- }
-
- switch( num ) {
- case 0: script_pushint(st,sd->status.char_id); break;
- case 1: script_pushint(st,sd->status.party_id); break;
- case 2: script_pushint(st,sd->status.guild_id); break;
- case 3: script_pushint(st,sd->status.account_id); break;
- case 4: script_pushint(st,sd->bg_id); break;
- default:
- ShowError("buildin_getcharid: invalid parameter (%d).\n", num);
- script_pushint(st,0);
- break;
- }
-
- return 0;
-}
-/*==========================================
- * returns the GID of an NPC
- *------------------------------------------*/
-BUILDIN_FUNC(getnpcid)
-{
- int num = script_getnum(st,2);
- struct npc_data* nd = NULL;
-
- if( script_hasdata(st,3) )
- {// unique npc name
- if( ( nd = npc_name2id(script_getstr(st,3)) ) == NULL )
- {
- ShowError("buildin_getnpcid: No such NPC '%s'.\n", script_getstr(st,3));
- script_pushint(st,0);
- return 1;
- }
- }
-
- switch (num) {
- case 0:
- script_pushint(st,nd ? nd->bl.id : st->oid);
- break;
- default:
- ShowError("buildin_getnpcid: invalid parameter (%d).\n", num);
- script_pushint(st,0);
- return 1;
- }
-
- return 0;
-}
-
-/*==========================================
- * Return the name of the party_id
- * null if not found
- *------------------------------------------*/
-BUILDIN_FUNC(getpartyname)
-{
- int party_id;
- struct party_data* p;
-
- party_id = script_getnum(st,2);
-
- if( ( p = party_search(party_id) ) != NULL )
- {
- script_pushstrcopy(st,p->party.name);
- }
- else
- {
- script_pushconststr(st,"null");
- }
- return 0;
-}
-
-/*==========================================
- * Get the information of the members of a party by type
- * @party_id, @type
- * return by @type :
- * - : nom des membres
- * 1 : char_id des membres
- * 2 : account_id des membres
- *------------------------------------------*/
-BUILDIN_FUNC(getpartymember)
-{
- struct party_data *p;
- int i,j=0,type=0;
-
- p=party_search(script_getnum(st,2));
-
- if( script_hasdata(st,3) )
- type=script_getnum(st,3);
-
- if(p!=NULL){
- for(i=0;i<MAX_PARTY;i++){
- if(p->party.member[i].account_id){
- switch (type) {
- case 2:
- mapreg_setreg(reference_uid(add_str("$@partymemberaid"), j),p->party.member[i].account_id);
- break;
- case 1:
- mapreg_setreg(reference_uid(add_str("$@partymembercid"), j),p->party.member[i].char_id);
- break;
- default:
- mapreg_setregstr(reference_uid(add_str("$@partymembername$"), j),p->party.member[i].name);
- }
- j++;
- }
- }
- }
- mapreg_setreg(add_str("$@partymembercount"),j);
-
- return 0;
-}
-
-/*==========================================
- * Retrieves party leader. if flag is specified,
- * return some of the leader data. Otherwise, return name.
- *------------------------------------------*/
-BUILDIN_FUNC(getpartyleader)
-{
- int party_id, type = 0, i=0;
- struct party_data *p;
-
- party_id=script_getnum(st,2);
- if( script_hasdata(st,3) )
- type=script_getnum(st,3);
-
- p=party_search(party_id);
-
- if (p) //Search leader
- for(i = 0; i < MAX_PARTY && !p->party.member[i].leader; i++);
-
- if (!p || i == MAX_PARTY) { //leader not found
- if (type)
- script_pushint(st,-1);
- else
- script_pushconststr(st,"null");
- return 0;
- }
-
- switch (type) {
- case 1: script_pushint(st,p->party.member[i].account_id); break;
- case 2: script_pushint(st,p->party.member[i].char_id); break;
- case 3: script_pushint(st,p->party.member[i].class_); break;
- case 4: script_pushstrcopy(st,mapindex_id2name(p->party.member[i].map)); break;
- case 5: script_pushint(st,p->party.member[i].lv); break;
- default: script_pushstrcopy(st,p->party.member[i].name); break;
- }
- return 0;
-}
-
-/*==========================================
- * Return the name of the @guild_id
- * null if not found
- *------------------------------------------*/
-BUILDIN_FUNC(getguildname)
-{
- int guild_id;
- struct guild* g;
-
- guild_id = script_getnum(st,2);
-
- if( ( g = guild_search(guild_id) ) != NULL )
- {
- script_pushstrcopy(st,g->name);
- }
- else
- {
- script_pushconststr(st,"null");
- }
- return 0;
-}
-
-/*==========================================
- * Return the name of the guild master of @guild_id
- * null if not found
- *------------------------------------------*/
-BUILDIN_FUNC(getguildmaster)
-{
- int guild_id;
- struct guild* g;
-
- guild_id = script_getnum(st,2);
-
- if( ( g = guild_search(guild_id) ) != NULL )
- {
- script_pushstrcopy(st,g->member[0].name);
- }
- else
- {
- script_pushconststr(st,"null");
- }
- return 0;
-}
-
-BUILDIN_FUNC(getguildmasterid)
-{
- int guild_id;
- struct guild* g;
-
- guild_id = script_getnum(st,2);
-
- if( ( g = guild_search(guild_id) ) != NULL )
- {
- script_pushint(st,g->member[0].char_id);
- }
- else
- {
- script_pushint(st,0);
- }
- return 0;
-}
-
-/*==========================================
- * Get char string information by type :
- * Return by @type :
- * 0 : char_name
- * 1 : party_name or ""
- * 2 : guild_name or ""
- * 3 : map_name
- * - : ""
- *------------------------------------------*/
-BUILDIN_FUNC(strcharinfo)
-{
- TBL_PC *sd;
- int num;
- struct guild* g;
- struct party_data* p;
-
- sd=script_rid2sd(st);
- if (!sd) { //Avoid crashing....
- script_pushconststr(st,"");
- return 0;
- }
- num=script_getnum(st,2);
- switch(num){
- case 0:
- script_pushstrcopy(st,sd->status.name);
- break;
- case 1:
- if( ( p = party_search(sd->status.party_id) ) != NULL )
- {
- script_pushstrcopy(st,p->party.name);
- }
- else
- {
- script_pushconststr(st,"");
- }
- break;
- case 2:
- if( ( g = guild_search(sd->status.guild_id) ) != NULL )
- {
- script_pushstrcopy(st,g->name);
- }
- else
- {
- script_pushconststr(st,"");
- }
- break;
- case 3:
- script_pushconststr(st,map[sd->bl.m].name);
- break;
- default:
- ShowWarning("buildin_strcharinfo: unknown parameter.\n");
- script_pushconststr(st,"");
- break;
- }
-
- return 0;
-}
-
-/*==========================================
- * Get npc string information by type
- * return by @type:
- * 0 : name
- * 1 : str#
- * 2 : #str
- * 3 : ::str
- * 4 : map name
- *------------------------------------------*/
-BUILDIN_FUNC(strnpcinfo)
-{
- TBL_NPC* nd;
- int num;
- char *buf,*name=NULL;
-
- nd = map_id2nd(st->oid);
- if (!nd) {
- script_pushconststr(st, "");
- return 0;
- }
-
- num = script_getnum(st,2);
- switch(num){
- case 0: // display name
- name = aStrdup(nd->name);
- break;
- case 1: // visible part of display name
- if((buf = strchr(nd->name,'#')) != NULL)
- {
- name = aStrdup(nd->name);
- name[buf - nd->name] = 0;
- } else // Return the name, there is no '#' present
- name = aStrdup(nd->name);
- break;
- case 2: // # fragment
- if((buf = strchr(nd->name,'#')) != NULL)
- name = aStrdup(buf+1);
- break;
- case 3: // unique name
- name = aStrdup(nd->exname);
- break;
- case 4: // map name
- name = aStrdup(map[nd->bl.m].name);
- break;
- }
-
- if(name)
- script_pushstr(st, name);
- else
- script_pushconststr(st, "");
-
- return 0;
-}
-
-
-// aegis->athena slot position conversion table
-static unsigned int equip[] = {EQP_HEAD_TOP,EQP_ARMOR,EQP_HAND_L,EQP_HAND_R,EQP_GARMENT,EQP_SHOES,EQP_ACC_L,EQP_ACC_R,EQP_HEAD_MID,EQP_HEAD_LOW};
-
-/*==========================================
- * GetEquipID(Pos); Pos: 1-10
- *------------------------------------------*/
-BUILDIN_FUNC(getequipid)
-{
- int i, num;
- TBL_PC* sd;
- struct item_data* item;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- num = script_getnum(st,2) - 1;
- if( num < 0 || num >= ARRAYLENGTH(equip) )
- {
- script_pushint(st,-1);
- return 0;
- }
-
- // get inventory position of item
- i = pc_checkequip(sd,equip[num]);
- if( i < 0 )
- {
- script_pushint(st,-1);
- return 0;
- }
-
- item = sd->inventory_data[i];
- if( item != 0 )
- script_pushint(st,item->nameid);
- else
- script_pushint(st,0);
-
- return 0;
-}
-
-/*==========================================
- * Get the equipement name at pos
- * return item jname or ""
- *------------------------------------------*/
-BUILDIN_FUNC(getequipname)
-{
- int i, num;
- TBL_PC* sd;
- struct item_data* item;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- num = script_getnum(st,2) - 1;
- if( num < 0 || num >= ARRAYLENGTH(equip) )
- {
- script_pushconststr(st,"");
- return 0;
- }
-
- // get inventory position of item
- i = pc_checkequip(sd,equip[num]);
- if( i < 0 )
- {
- script_pushint(st,-1);
- return 0;
- }
-
- item = sd->inventory_data[i];
- if( item != 0 )
- script_pushstrcopy(st,item->jname);
- else
- script_pushconststr(st,"");
-
- return 0;
-}
-
-/*==========================================
- * getbrokenid [Valaris]
- *------------------------------------------*/
-BUILDIN_FUNC(getbrokenid)
-{
- int i,num,id=0,brokencounter=0;
- TBL_PC *sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- num=script_getnum(st,2);
- for(i=0; i<MAX_INVENTORY; i++) {
- if(sd->status.inventory[i].attribute){
- brokencounter++;
- if(num==brokencounter){
- id=sd->status.inventory[i].nameid;
- break;
- }
- }
- }
-
- script_pushint(st,id);
-
- return 0;
-}
-
-/*==========================================
- * repair [Valaris]
- *------------------------------------------*/
-BUILDIN_FUNC(repair)
-{
- int i,num;
- int repaircounter=0;
- TBL_PC *sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- num=script_getnum(st,2);
- for(i=0; i<MAX_INVENTORY; i++) {
- if(sd->status.inventory[i].attribute){
- repaircounter++;
- if(num==repaircounter){
- sd->status.inventory[i].attribute=0;
- clif_equiplist(sd);
- clif_produceeffect(sd, 0, sd->status.inventory[i].nameid);
- clif_misceffect(&sd->bl, 3);
- break;
- }
- }
- }
-
- return 0;
-}
-
-/*==========================================
- * repairall
- *------------------------------------------*/
-BUILDIN_FUNC(repairall)
-{
- int i, repaircounter = 0;
- TBL_PC *sd;
-
- sd = script_rid2sd(st);
- if(sd == NULL)
- return 0;
-
- for(i = 0; i < MAX_INVENTORY; i++)
- {
- if(sd->status.inventory[i].nameid && sd->status.inventory[i].attribute)
- {
- sd->status.inventory[i].attribute = 0;
- clif_produceeffect(sd,0,sd->status.inventory[i].nameid);
- repaircounter++;
- }
- }
-
- if(repaircounter)
- {
- clif_misceffect(&sd->bl, 3);
- clif_equiplist(sd);
- }
-
- return 0;
-}
-
-/*==========================================
- * Chk if player have something equiped at pos
- *------------------------------------------*/
-BUILDIN_FUNC(getequipisequiped)
-{
- int i = -1,num;
- TBL_PC *sd;
-
- num = script_getnum(st,2);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if (num > 0 && num <= ARRAYLENGTH(equip))
- i=pc_checkequip(sd,equip[num-1]);
-
- if(i >= 0)
- script_pushint(st,1);
- else
- script_pushint(st,0);
- return 0;
-}
-
-/*==========================================
- * Chk if the player have something equiped at pos
- * if so chk if this item ain't marked not refinable or rental
- * return (npc)
- * 1 : true
- * 0 : false
- *------------------------------------------*/
-BUILDIN_FUNC(getequipisenableref)
-{
- int i = -1,num;
- TBL_PC *sd;
-
- num = script_getnum(st,2);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if( num > 0 && num <= ARRAYLENGTH(equip) )
- i = pc_checkequip(sd,equip[num-1]);
- if( i >= 0 && sd->inventory_data[i] && !sd->inventory_data[i]->flag.no_refine && !sd->status.inventory[i].expire_time )
- script_pushint(st,1);
- else
- script_pushint(st,0);
-
- return 0;
-}
-
-/*==========================================
- * Chk if the item equiped at pos is identify (huh ?)
- * return (npc)
- * 1 : true
- * 0 : false
- *------------------------------------------*/
-BUILDIN_FUNC(getequipisidentify)
-{
- int i = -1,num;
- TBL_PC *sd;
-
- num = script_getnum(st,2);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if (num > 0 && num <= ARRAYLENGTH(equip))
- i=pc_checkequip(sd,equip[num-1]);
- if(i >= 0)
- script_pushint(st,sd->status.inventory[i].identify);
- else
- script_pushint(st,0);
-
- return 0;
-}
-
-/*==========================================
- * Get the item refined value at pos
- * return (npc)
- * x : refine amount
- * 0 : false (not refined)
- *------------------------------------------*/
-BUILDIN_FUNC(getequiprefinerycnt)
-{
- int i = -1,num;
- TBL_PC *sd;
-
- num = script_getnum(st,2);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if (num > 0 && num <= ARRAYLENGTH(equip))
- i=pc_checkequip(sd,equip[num-1]);
- if(i >= 0)
- script_pushint(st,sd->status.inventory[i].refine);
- else
- script_pushint(st,0);
-
- return 0;
-}
-
-/*==========================================
- * Get the weapon level value at pos
- * (pos should normally only be EQI_HAND_L or EQI_HAND_R)
- * return (npc)
- * x : weapon level
- * 0 : false
- *------------------------------------------*/
-BUILDIN_FUNC(getequipweaponlv)
-{
- int i = -1,num;
- TBL_PC *sd;
-
- num = script_getnum(st,2);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if (num > 0 && num <= ARRAYLENGTH(equip))
- i=pc_checkequip(sd,equip[num-1]);
- if(i >= 0 && sd->inventory_data[i])
- script_pushint(st,sd->inventory_data[i]->wlv);
- else
- script_pushint(st,0);
-
- return 0;
-}
-
-/*==========================================
- * Get the item refine chance (from refine.txt) for item at pos
- * return (npc)
- * x : refine chance
- * 0 : false (max refine level or unequip..)
- *------------------------------------------*/
-BUILDIN_FUNC(getequippercentrefinery)
-{
- int i = -1,num;
- TBL_PC *sd;
-
- num = script_getnum(st,2);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if (num > 0 && num <= ARRAYLENGTH(equip))
- i=pc_checkequip(sd,equip[num-1]);
- if(i >= 0 && sd->status.inventory[i].nameid && sd->status.inventory[i].refine < MAX_REFINE)
- script_pushint(st,status_get_refine_chance(itemdb_wlv(sd->status.inventory[i].nameid), (int)sd->status.inventory[i].refine));
- else
- script_pushint(st,0);
-
- return 0;
-}
-
-/*==========================================
- * Refine +1 item at pos and log and display refine
- *------------------------------------------*/
-BUILDIN_FUNC(successrefitem)
-{
- int i=-1,num,ep;
- TBL_PC *sd;
-
- num = script_getnum(st,2);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if (num > 0 && num <= ARRAYLENGTH(equip))
- i=pc_checkequip(sd,equip[num-1]);
- if(i >= 0) {
- ep=sd->status.inventory[i].equip;
-
- //Logs items, got from (N)PC scripts [Lupus]
- log_pick_pc(sd, LOG_TYPE_SCRIPT, -1, &sd->status.inventory[i]);
-
- sd->status.inventory[i].refine++;
- pc_unequipitem(sd,i,2); // status calc will happen in pc_equipitem() below
-
- clif_refine(sd->fd,0,i,sd->status.inventory[i].refine);
- clif_delitem(sd,i,1,3);
-
- //Logs items, got from (N)PC scripts [Lupus]
- log_pick_pc(sd, LOG_TYPE_SCRIPT, 1, &sd->status.inventory[i]);
-
- clif_additem(sd,i,1,0);
- pc_equipitem(sd,i,ep);
- clif_misceffect(&sd->bl,3);
- if(sd->status.inventory[i].refine == MAX_REFINE &&
- sd->status.inventory[i].card[0] == CARD0_FORGE &&
- sd->status.char_id == (int)MakeDWord(sd->status.inventory[i].card[2],sd->status.inventory[i].card[3])
- ){ // Fame point system [DracoRPG]
- switch (sd->inventory_data[i]->wlv){
- case 1:
- pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point
- break;
- case 2:
- pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point
- break;
- case 3:
- pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point
- break;
- }
- }
- }
-
- return 0;
-}
-
-/*==========================================
- * Show a failed Refine +1 attempt
- *------------------------------------------*/
-BUILDIN_FUNC(failedrefitem)
-{
- int i=-1,num;
- TBL_PC *sd;
-
- num = script_getnum(st,2);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if (num > 0 && num <= ARRAYLENGTH(equip))
- i=pc_checkequip(sd,equip[num-1]);
- if(i >= 0) {
- sd->status.inventory[i].refine = 0;
- pc_unequipitem(sd,i,3); //recalculate bonus
- clif_refine(sd->fd,1,i,sd->status.inventory[i].refine); //notify client of failure
-
- pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT);
-
- clif_misceffect(&sd->bl,2); // display failure effect
- }
-
- return 0;
-}
-
-/*==========================================
- * Downgrades an Equipment Part by -1 . [Masao]
- *------------------------------------------*/
-BUILDIN_FUNC(downrefitem)
-{
- int i = -1,num,ep;
- TBL_PC *sd;
-
- num = script_getnum(st,2);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if (num > 0 && num <= ARRAYLENGTH(equip))
- i = pc_checkequip(sd,equip[num-1]);
- if(i >= 0) {
- ep = sd->status.inventory[i].equip;
-
- //Logs items, got from (N)PC scripts [Lupus]
- log_pick_pc(sd, LOG_TYPE_SCRIPT, -1, &sd->status.inventory[i]);
-
- sd->status.inventory[i].refine++;
- pc_unequipitem(sd,i,2); // status calc will happen in pc_equipitem() below
-
- clif_refine(sd->fd,2,i,sd->status.inventory[i].refine = sd->status.inventory[i].refine - 2);
- clif_delitem(sd,i,1,3);
-
- //Logs items, got from (N)PC scripts [Lupus]
- log_pick_pc(sd, LOG_TYPE_SCRIPT, 1, &sd->status.inventory[i]);
-
- clif_additem(sd,i,1,0);
- pc_equipitem(sd,i,ep);
- clif_misceffect(&sd->bl,2);
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(statusup)
-{
- int type;
- TBL_PC *sd;
-
- type=script_getnum(st,2);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- pc_statusup(sd,type);
-
- return 0;
-}
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(statusup2)
-{
- int type,val;
- TBL_PC *sd;
-
- type=script_getnum(st,2);
- val=script_getnum(st,3);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- pc_statusup2(sd,type,val);
-
- return 0;
-}
-
-/// See 'doc/item_bonus.txt'
-///
-/// bonus <bonus type>,<val1>;
-/// bonus2 <bonus type>,<val1>,<val2>;
-/// bonus3 <bonus type>,<val1>,<val2>,<val3>;
-/// bonus4 <bonus type>,<val1>,<val2>,<val3>,<val4>;
-/// bonus5 <bonus type>,<val1>,<val2>,<val3>,<val4>,<val5>;
-BUILDIN_FUNC(bonus)
-{
- int type;
- int val1;
- int val2 = 0;
- int val3 = 0;
- int val4 = 0;
- int val5 = 0;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0; // no player attached
-
- type = script_getnum(st,2);
- switch( type ) {
- case SP_AUTOSPELL:
- case SP_AUTOSPELL_WHENHIT:
- case SP_AUTOSPELL_ONSKILL:
- case SP_SKILL_ATK:
- case SP_SKILL_HEAL:
- case SP_SKILL_HEAL2:
- case SP_ADD_SKILL_BLOW:
- case SP_CASTRATE:
- case SP_ADDEFF_ONSKILL:
- case SP_SKILL_USE_SP_RATE:
- case SP_SKILL_COOLDOWN:
- case SP_SKILL_FIXEDCAST:
- case SP_SKILL_VARIABLECAST:
- case SP_VARCASTRATE:
- case SP_SKILL_USE_SP:
- // these bonuses support skill names
- val1 = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) );
- break;
- default:
- val1 = script_getnum(st,3);
- break;
- }
-
- switch( script_lastdata(st)-2 ) {
- case 1:
- pc_bonus(sd, type, val1);
- break;
- case 2:
- val2 = script_getnum(st,4);
- pc_bonus2(sd, type, val1, val2);
- break;
- case 3:
- val2 = script_getnum(st,4);
- val3 = script_getnum(st,5);
- pc_bonus3(sd, type, val1, val2, val3);
- break;
- case 4:
- if( type == SP_AUTOSPELL_ONSKILL && script_isstring(st,4) )
- val2 = skill_name2id(script_getstr(st,4)); // 2nd value can be skill name
- else
- val2 = script_getnum(st,4);
-
- val3 = script_getnum(st,5);
- val4 = script_getnum(st,6);
- pc_bonus4(sd, type, val1, val2, val3, val4);
- break;
- case 5:
- if( type == SP_AUTOSPELL_ONSKILL && script_isstring(st,4) )
- val2 = skill_name2id(script_getstr(st,4)); // 2nd value can be skill name
- else
- val2 = script_getnum(st,4);
-
- val3 = script_getnum(st,5);
- val4 = script_getnum(st,6);
- val5 = script_getnum(st,7);
- pc_bonus5(sd, type, val1, val2, val3, val4, val5);
- break;
- default:
- ShowDebug("buildin_bonus: unexpected number of arguments (%d)\n", (script_lastdata(st) - 1));
- break;
- }
-
- return 0;
-}
-
-BUILDIN_FUNC(autobonus)
-{
- unsigned int dur;
- short rate;
- short atk_type = 0;
- TBL_PC* sd;
- const char *bonus_script, *other_script = NULL;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0; // no player attached
-
- if( sd->state.autobonus&sd->status.inventory[current_equip_item_index].equip )
- return 0;
-
- rate = script_getnum(st,3);
- dur = script_getnum(st,4);
- bonus_script = script_getstr(st,2);
- if( !rate || !dur || !bonus_script )
- return 0;
-
- if( script_hasdata(st,5) )
- atk_type = script_getnum(st,5);
- if( script_hasdata(st,6) )
- other_script = script_getstr(st,6);
-
- if( pc_addautobonus(sd->autobonus,ARRAYLENGTH(sd->autobonus),
- bonus_script,rate,dur,atk_type,other_script,sd->status.inventory[current_equip_item_index].equip,false) )
- {
- script_add_autobonus(bonus_script);
- if( other_script )
- script_add_autobonus(other_script);
- }
-
- return 0;
-}
-
-BUILDIN_FUNC(autobonus2)
-{
- unsigned int dur;
- short rate;
- short atk_type = 0;
- TBL_PC* sd;
- const char *bonus_script, *other_script = NULL;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0; // no player attached
-
- if( sd->state.autobonus&sd->status.inventory[current_equip_item_index].equip )
- return 0;
-
- rate = script_getnum(st,3);
- dur = script_getnum(st,4);
- bonus_script = script_getstr(st,2);
- if( !rate || !dur || !bonus_script )
- return 0;
-
- if( script_hasdata(st,5) )
- atk_type = script_getnum(st,5);
- if( script_hasdata(st,6) )
- other_script = script_getstr(st,6);
-
- if( pc_addautobonus(sd->autobonus2,ARRAYLENGTH(sd->autobonus2),
- bonus_script,rate,dur,atk_type,other_script,sd->status.inventory[current_equip_item_index].equip,false) )
- {
- script_add_autobonus(bonus_script);
- if( other_script )
- script_add_autobonus(other_script);
- }
-
- return 0;
-}
-
-BUILDIN_FUNC(autobonus3)
-{
- unsigned int dur;
- short rate,atk_type;
- TBL_PC* sd;
- const char *bonus_script, *other_script = NULL;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0; // no player attached
-
- if( sd->state.autobonus&sd->status.inventory[current_equip_item_index].equip )
- return 0;
-
- rate = script_getnum(st,3);
- dur = script_getnum(st,4);
- atk_type = ( script_isstring(st,5) ? skill_name2id(script_getstr(st,5)) : script_getnum(st,5) );
- bonus_script = script_getstr(st,2);
- if( !rate || !dur || !atk_type || !bonus_script )
- return 0;
-
- if( script_hasdata(st,6) )
- other_script = script_getstr(st,6);
-
- if( pc_addautobonus(sd->autobonus3,ARRAYLENGTH(sd->autobonus3),
- bonus_script,rate,dur,atk_type,other_script,sd->status.inventory[current_equip_item_index].equip,true) )
- {
- script_add_autobonus(bonus_script);
- if( other_script )
- script_add_autobonus(other_script);
- }
-
- return 0;
-}
-
-/// Changes the level of a player skill.
-/// <flag> defaults to 1
-/// <flag>=0 : set the level of the skill
-/// <flag>=1 : set the temporary level of the skill
-/// <flag>=2 : add to the level of the skill
-///
-/// skill <skill id>,<level>,<flag>
-/// skill <skill id>,<level>
-/// skill "<skill name>",<level>,<flag>
-/// skill "<skill name>",<level>
-BUILDIN_FUNC(skill)
-{
- int id;
- int level;
- int flag = 1;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
- level = script_getnum(st,3);
- if( script_hasdata(st,4) )
- flag = script_getnum(st,4);
- pc_skill(sd, id, level, flag);
-
- return 0;
-}
-
-/// Changes the level of a player skill.
-/// like skill, but <flag> defaults to 2
-///
-/// addtoskill <skill id>,<amount>,<flag>
-/// addtoskill <skill id>,<amount>
-/// addtoskill "<skill name>",<amount>,<flag>
-/// addtoskill "<skill name>",<amount>
-///
-/// @see skill
-BUILDIN_FUNC(addtoskill)
-{
- int id;
- int level;
- int flag = 2;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
- level = script_getnum(st,3);
- if( script_hasdata(st,4) )
- flag = script_getnum(st,4);
- pc_skill(sd, id, level, flag);
-
- return 0;
-}
-
-/// Increases the level of a guild skill.
-///
-/// guildskill <skill id>,<amount>;
-/// guildskill "<skill name>",<amount>;
-BUILDIN_FUNC(guildskill)
-{
- int id;
- int level;
- TBL_PC* sd;
- int i;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
- level = script_getnum(st,3);
- for( i=0; i < level; i++ )
- guild_skillup(sd, id);
-
- return 0;
-}
-
-/// Returns the level of the player skill.
-///
-/// getskilllv(<skill id>) -> <level>
-/// getskilllv("<skill name>") -> <level>
-BUILDIN_FUNC(getskilllv)
-{
- int id;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
- script_pushint(st, pc_checkskill(sd,id));
-
- return 0;
-}
-
-/// Returns the level of the guild skill.
-///
-/// getgdskilllv(<guild id>,<skill id>) -> <level>
-/// getgdskilllv(<guild id>,"<skill name>") -> <level>
-BUILDIN_FUNC(getgdskilllv)
-{
- int guild_id;
- uint16 skill_id;
- struct guild* g;
-
- guild_id = script_getnum(st,2);
- skill_id = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) );
- g = guild_search(guild_id);
- if( g == NULL )
- script_pushint(st, -1);
- else
- script_pushint(st, guild_checkskill(g,skill_id));
-
- return 0;
-}
-
-/// Returns the 'basic_skill_check' setting.
-/// This config determines if the server checks the skill level of NV_BASIC
-/// before allowing the basic actions.
-///
-/// basicskillcheck() -> <bool>
-BUILDIN_FUNC(basicskillcheck)
-{
- script_pushint(st, battle_config.basic_skill_check);
- return 0;
-}
-
-/// Returns the GM level of the player.
-///
-/// getgmlevel() -> <level>
-BUILDIN_FUNC(getgmlevel)
-{
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- script_pushint(st, pc_get_group_level(sd));
-
- return 0;
-}
-
-/// Returns the group ID of the player.
-///
-/// getgroupid() -> <int>
-BUILDIN_FUNC(getgroupid)
-{
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if (sd == NULL)
- return 1; // no player attached, report source
- script_pushint(st, pc_get_group_id(sd));
-
- return 0;
-}
-
-/// Terminates the execution of this script instance.
-///
-/// end
-BUILDIN_FUNC(end)
-{
- st->state = END;
- return 0;
-}
-
-/// Checks if the player has that effect state (option).
-///
-/// checkoption(<option>) -> <bool>
-BUILDIN_FUNC(checkoption)
-{
- int option;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- option = script_getnum(st,2);
- if( sd->sc.option&option )
- script_pushint(st, 1);
- else
- script_pushint(st, 0);
-
- return 0;
-}
-
-/// Checks if the player is in that body state (opt1).
-///
-/// checkoption1(<opt1>) -> <bool>
-BUILDIN_FUNC(checkoption1)
-{
- int opt1;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- opt1 = script_getnum(st,2);
- if( sd->sc.opt1 == opt1 )
- script_pushint(st, 1);
- else
- script_pushint(st, 0);
-
- return 0;
-}
-
-/// Checks if the player has that health state (opt2).
-///
-/// checkoption2(<opt2>) -> <bool>
-BUILDIN_FUNC(checkoption2)
-{
- int opt2;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- opt2 = script_getnum(st,2);
- if( sd->sc.opt2&opt2 )
- script_pushint(st, 1);
- else
- script_pushint(st, 0);
-
- return 0;
-}
-
-/// Changes the effect state (option) of the player.
-/// <flag> defaults to 1
-/// <flag>=0 : removes the option
-/// <flag>=other : adds the option
-///
-/// setoption <option>,<flag>;
-/// setoption <option>;
-BUILDIN_FUNC(setoption)
-{
- int option;
- int flag = 1;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- option = script_getnum(st,2);
- if( script_hasdata(st,3) )
- flag = script_getnum(st,3);
- else if( !option ){// Request to remove everything.
- flag = 0;
- option = OPTION_FALCON|OPTION_RIDING;
-#ifndef NEW_CARTS
- option |= OPTION_CART;
-#endif
- }
- if( flag ){// Add option
- if( option&OPTION_WEDDING && !battle_config.wedding_modifydisplay )
- option &= ~OPTION_WEDDING;// Do not show the wedding sprites
- pc_setoption(sd, sd->sc.option|option);
- } else// Remove option
- pc_setoption(sd, sd->sc.option&~option);
-
- return 0;
-}
-
-/// Returns if the player has a cart.
-///
-/// checkcart() -> <bool>
-///
-/// @author Valaris
-BUILDIN_FUNC(checkcart)
-{
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- if( pc_iscarton(sd) )
- script_pushint(st, 1);
- else
- script_pushint(st, 0);
-
- return 0;
-}
-
-/// Sets the cart of the player.
-/// <type> defaults to 1
-/// <type>=0 : removes the cart
-/// <type>=1 : Normal cart
-/// <type>=2 : Wooden cart
-/// <type>=3 : Covered cart with flowers and ferns
-/// <type>=4 : Wooden cart with a Panda doll on the back
-/// <type>=5 : Normal cart with bigger wheels, a roof and a banner on the back
-///
-/// setcart <type>;
-/// setcart;
-BUILDIN_FUNC(setcart)
-{
- int type = 1;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- if( script_hasdata(st,2) )
- type = script_getnum(st,2);
- pc_setcart(sd, type);
-
- return 0;
-}
-
-/// Returns if the player has a falcon.
-///
-/// checkfalcon() -> <bool>
-///
-/// @author Valaris
-BUILDIN_FUNC(checkfalcon)
-{
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- if( pc_isfalcon(sd) )
- script_pushint(st, 1);
- else
- script_pushint(st, 0);
-
- return 0;
-}
-
-/// Sets if the player has a falcon or not.
-/// <flag> defaults to 1
-///
-/// setfalcon <flag>;
-/// setfalcon;
-BUILDIN_FUNC(setfalcon)
-{
- int flag = 1;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- if( script_hasdata(st,2) )
- flag = script_getnum(st,2);
-
- pc_setfalcon(sd, flag);
-
- return 0;
-}
-
-/// Returns if the player is riding.
-///
-/// checkriding() -> <bool>
-///
-/// @author Valaris
-BUILDIN_FUNC(checkriding)
-{
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- if( pc_isriding(sd) || pc_isridingwug(sd) || pc_isridingdragon(sd) )
- script_pushint(st, 1);
- else
- script_pushint(st, 0);
-
- return 0;
-}
-
-/// Sets if the player is riding.
-/// <flag> defaults to 1
-///
-/// setriding <flag>;
-/// setriding;
-BUILDIN_FUNC(setriding)
-{
- int flag = 1;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- if( script_hasdata(st,2) )
- flag = script_getnum(st,2);
- pc_setriding(sd, flag);
-
- return 0;
-}
-
-/// Returns if the player has a warg.
-///
-/// checkwug() -> <bool>
-///
-BUILDIN_FUNC(checkwug)
-{
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- if( pc_iswug(sd) || pc_isridingwug(sd) )
- script_pushint(st, 1);
- else
- script_pushint(st, 0);
-
- return 0;
-}
-
-/// Returns if the player is wearing MADO Gear.
-///
-/// checkmadogear() -> <bool>
-///
-BUILDIN_FUNC(checkmadogear)
-{
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- if( pc_ismadogear(sd) )
- script_pushint(st, 1);
- else
- script_pushint(st, 0);
-
- return 0;
-}
-
-/// Sets if the player is riding MADO Gear.
-/// <flag> defaults to 1
-///
-/// setmadogear <flag>;
-/// setmadogear;
-BUILDIN_FUNC(setmadogear)
-{
- int flag = 1;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- if( script_hasdata(st,2) )
- flag = script_getnum(st,2);
- pc_setmadogear(sd, flag);
-
- return 0;
-}
-
-/// Sets the save point of the player.
-///
-/// save "<map name>",<x>,<y>
-/// savepoint "<map name>",<x>,<y>
-BUILDIN_FUNC(savepoint)
-{
- int x;
- int y;
- short map;
- const char* str;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached, report source
-
- str = script_getstr(st, 2);
- x = script_getnum(st,3);
- y = script_getnum(st,4);
- map = mapindex_name2id(str);
- if( map )
- pc_setsavepoint(sd, map, x, y);
-
- return 0;
-}
-
-/*==========================================
- * GetTimeTick(0: System Tick, 1: Time Second Tick)
- *------------------------------------------*/
-BUILDIN_FUNC(gettimetick) /* Asgard Version */
-{
- int type;
- time_t timer;
- struct tm *t;
-
- type=script_getnum(st,2);
-
- switch(type){
- case 2:
- //type 2:(Get the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC
- // from the system clock.)
- script_pushint(st,(int)time(NULL));
- break;
- case 1:
- //type 1:(Second Ticks: 0-86399, 00:00:00-23:59:59)
- time(&timer);
- t=localtime(&timer);
- script_pushint(st,((t->tm_hour)*3600+(t->tm_min)*60+t->tm_sec));
- break;
- case 0:
- default:
- //type 0:(System Ticks)
- script_pushint(st,gettick());
- break;
- }
- return 0;
-}
-
-/*==========================================
- * GetTime(Type);
- * 1: Sec 2: Min 3: Hour
- * 4: WeekDay 5: MonthDay 6: Month
- * 7: Year
- *------------------------------------------*/
-BUILDIN_FUNC(gettime) /* Asgard Version */
-{
- int type;
- time_t timer;
- struct tm *t;
-
- type=script_getnum(st,2);
-
- time(&timer);
- t=localtime(&timer);
-
- switch(type){
- case 1://Sec(0~59)
- script_pushint(st,t->tm_sec);
- break;
- case 2://Min(0~59)
- script_pushint(st,t->tm_min);
- break;
- case 3://Hour(0~23)
- script_pushint(st,t->tm_hour);
- break;
- case 4://WeekDay(0~6)
- script_pushint(st,t->tm_wday);
- break;
- case 5://MonthDay(01~31)
- script_pushint(st,t->tm_mday);
- break;
- case 6://Month(01~12)
- script_pushint(st,t->tm_mon+1);
- break;
- case 7://Year(20xx)
- script_pushint(st,t->tm_year+1900);
- break;
- case 8://Year Day(01~366)
- script_pushint(st,t->tm_yday+1);
- break;
- default://(format error)
- script_pushint(st,-1);
- break;
- }
- return 0;
-}
-
-/*==========================================
- * GetTimeStr("TimeFMT", Length);
- *------------------------------------------*/
-BUILDIN_FUNC(gettimestr)
-{
- char *tmpstr;
- const char *fmtstr;
- int maxlen;
- time_t now = time(NULL);
-
- fmtstr=script_getstr(st,2);
- maxlen=script_getnum(st,3);
-
- tmpstr=(char *)aMalloc((maxlen+1)*sizeof(char));
- strftime(tmpstr,maxlen,fmtstr,localtime(&now));
- tmpstr[maxlen]='\0';
-
- script_pushstr(st,tmpstr);
- return 0;
-}
-
-/*==========================================
- * Open player storage
- *------------------------------------------*/
-BUILDIN_FUNC(openstorage)
-{
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- storage_storageopen(sd);
- return 0;
-}
-
-BUILDIN_FUNC(guildopenstorage)
-{
- TBL_PC* sd;
- int ret;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- ret = storage_guild_storageopen(sd);
- script_pushint(st,ret);
- return 0;
-}
-
-/*==========================================
- * Make player use a skill trought item usage
- *------------------------------------------*/
-/// itemskill <skill id>,<level>
-/// itemskill "<skill name>",<level>
-BUILDIN_FUNC(itemskill)
-{
- int id;
- int lv;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL || sd->ud.skilltimer != INVALID_TIMER )
- return 0;
-
- id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
- lv = script_getnum(st,3);
-
- sd->skillitem=id;
- sd->skillitemlv=lv;
- clif_item_skill(sd,id,lv);
- return 0;
-}
-/*==========================================
- * Attempt to create an item
- *------------------------------------------*/
-BUILDIN_FUNC(produce)
-{
- int trigger;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- trigger=script_getnum(st,2);
- clif_skill_produce_mix_list(sd, -1, trigger);
- return 0;
-}
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(cooking)
-{
- int trigger;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- trigger=script_getnum(st,2);
- clif_cooking_list(sd, trigger, AM_PHARMACY, 1, 1);
- return 0;
-}
-/*==========================================
- * Create a pet
- *------------------------------------------*/
-BUILDIN_FUNC(makepet)
-{
- TBL_PC* sd;
- int id,pet_id;
-
- id=script_getnum(st,2);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- pet_id = search_petDB_index(id, PET_CLASS);
-
- if (pet_id < 0)
- pet_id = search_petDB_index(id, PET_EGG);
- if (pet_id >= 0 && sd) {
- sd->catch_target_class = pet_db[pet_id].class_;
- intif_create_pet(
- sd->status.account_id, sd->status.char_id,
- (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv,
- (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate,
- 100, 0, 1, pet_db[pet_id].jname);
- }
-
- return 0;
-}
-/*==========================================
- * Give player exp base,job * quest_exp_rate/100
- *------------------------------------------*/
-BUILDIN_FUNC(getexp)
-{
- TBL_PC* sd;
- int base=0,job=0;
- double bonus;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- base=script_getnum(st,2);
- job =script_getnum(st,3);
- if(base<0 || job<0)
- return 0;
-
- // bonus for npc-given exp
- bonus = battle_config.quest_exp_rate / 100.;
- base = (int) cap_value(base * bonus, 0, INT_MAX);
- job = (int) cap_value(job * bonus, 0, INT_MAX);
-
- pc_gainexp(sd, NULL, base, job, true);
-
- return 0;
-}
-
-/*==========================================
- * Gain guild exp [Celest]
- *------------------------------------------*/
-BUILDIN_FUNC(guildgetexp)
-{
- TBL_PC* sd;
- int exp;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- exp = script_getnum(st,2);
- if(exp < 0)
- return 0;
- if(sd && sd->status.guild_id > 0)
- guild_getexp (sd, exp);
-
- return 0;
-}
-
-/*==========================================
- * Changes the guild master of a guild [Skotlex]
- *------------------------------------------*/
-BUILDIN_FUNC(guildchangegm)
-{
- TBL_PC *sd;
- int guild_id;
- const char *name;
-
- guild_id = script_getnum(st,2);
- name = script_getstr(st,3);
- sd=map_nick2sd(name);
-
- if (!sd)
- script_pushint(st,0);
- else
- script_pushint(st,guild_gm_change(guild_id, sd));
-
- return 0;
-}
-
-/*==========================================
- * Spawn a monster :
- @mapn,x,y : location
- @str : monster name
- @class_ : mob_id
- @amount : nb to spawn
- @event : event to attach to mob
- *------------------------------------------*/
-BUILDIN_FUNC(monster)
-{
- const char* mapn = script_getstr(st,2);
- int x = script_getnum(st,3);
- int y = script_getnum(st,4);
- const char* str = script_getstr(st,5);
- int class_ = script_getnum(st,6);
- int amount = script_getnum(st,7);
- const char* event = "";
- unsigned int size = SZ_SMALL;
- unsigned int ai = AI_NONE;
-
- struct map_session_data* sd;
- int16 m;
-
- if (script_hasdata(st, 8))
- {
- event = script_getstr(st, 8);
- check_event(st, event);
- }
-
- if (script_hasdata(st, 9))
- {
- size = script_getnum(st, 9);
- if (size > 3)
- {
- ShowWarning("buildin_monster: Attempted to spawn non-existing size %d for monster class %d\n", size, class_);
- return 1;
- }
- }
-
- if (script_hasdata(st, 10))
- {
- ai = script_getnum(st, 10);
- if (ai > 4)
- {
- ShowWarning("buildin_monster: Attempted to spawn non-existing ai %d for monster class %d\n", ai, class_);
- return 1;
- }
- }
-
- if (class_ >= 0 && !mobdb_checkid(class_))
- {
- ShowWarning("buildin_monster: Attempted to spawn non-existing monster class %d\n", class_);
- return 1;
- }
-
- sd = map_id2sd(st->rid);
-
- if (sd && strcmp(mapn, "this") == 0)
- m = sd->bl.m;
- else
- {
- m = map_mapname2mapid(mapn);
- if (map[m].flag.src4instance && st->instance_id)
- { // Try to redirect to the instance map, not the src map
- if ((m = instance_mapid2imapid(m, st->instance_id)) < 0)
- {
- ShowError("buildin_monster: Trying to spawn monster (%d) on instance map (%s) without instance attached.\n", class_, mapn);
- return 1;
- }
- }
- }
-
- mob_once_spawn(sd, m, x, y, str, class_, amount, event, size, ai);
- return 0;
-}
-/*==========================================
- * Request List of Monster Drops
- *------------------------------------------*/
-BUILDIN_FUNC(getmobdrops)
-{
- int class_ = script_getnum(st,2);
- int i, j = 0;
- struct mob_db *mob;
-
- if( !mobdb_checkid(class_) )
- {
- script_pushint(st, 0);
- return 0;
- }
-
- mob = mob_db(class_);
-
- for( i = 0; i < MAX_MOB_DROP; i++ )
- {
- if( mob->dropitem[i].nameid < 1 )
- continue;
- if( itemdb_exists(mob->dropitem[i].nameid) == NULL )
- continue;
-
- mapreg_setreg(reference_uid(add_str("$@MobDrop_item"), j), mob->dropitem[i].nameid);
- mapreg_setreg(reference_uid(add_str("$@MobDrop_rate"), j), mob->dropitem[i].p);
-
- j++;
- }
-
- mapreg_setreg(add_str("$@MobDrop_count"), j);
- script_pushint(st, 1);
-
- return 0;
-}
-/*==========================================
- * Same as monster but randomize location in x0,x1,y0,y1 area
- *------------------------------------------*/
-BUILDIN_FUNC(areamonster)
-{
- const char* mapn = script_getstr(st,2);
- int x0 = script_getnum(st,3);
- int y0 = script_getnum(st,4);
- int x1 = script_getnum(st,5);
- int y1 = script_getnum(st,6);
- const char* str = script_getstr(st,7);
- int class_ = script_getnum(st,8);
- int amount = script_getnum(st,9);
- const char* event = "";
- unsigned int size = SZ_SMALL;
- unsigned int ai = AI_NONE;
-
- struct map_session_data* sd;
- int16 m;
-
- if (script_hasdata(st,10))
- {
- event = script_getstr(st, 10);
- check_event(st, event);
- }
-
- if (script_hasdata(st, 11))
- {
- size = script_getnum(st, 11);
- if (size > 3)
- {
- ShowWarning("buildin_monster: Attempted to spawn non-existing size %d for monster class %d\n", size, class_);
- return 1;
- }
- }
-
- if (script_hasdata(st, 12))
- {
- ai = script_getnum(st, 12);
- if (ai > 4)
- {
- ShowWarning("buildin_monster: Attempted to spawn non-existing ai %d for monster class %d\n", ai, class_);
- return 1;
- }
- }
-
- sd = map_id2sd(st->rid);
-
- if (sd && strcmp(mapn, "this") == 0)
- m = sd->bl.m;
- else
- {
- m = map_mapname2mapid(mapn);
- if (map[m].flag.src4instance && st->instance_id)
- { // Try to redirect to the instance map, not the src map
- if ((m = instance_mapid2imapid(m, st->instance_id)) < 0)
- {
- ShowError("buildin_areamonster: Trying to spawn monster (%d) on instance map (%s) without instance attached.\n", class_, mapn);
- return 1;
- }
- }
- }
-
- mob_once_spawn_area(sd, m, x0, y0, x1, y1, str, class_, amount, event, size, ai);
- return 0;
-}
-/*==========================================
- * KillMonster subcheck, verify if mob to kill ain't got an even to handle, could be force kill by allflag
- *------------------------------------------*/
- static int buildin_killmonster_sub_strip(struct block_list *bl,va_list ap)
-{ //same fix but with killmonster instead - stripping events from mobs.
- TBL_MOB* md = (TBL_MOB*)bl;
- char *event=va_arg(ap,char *);
- int allflag=va_arg(ap,int);
-
- md->state.npc_killmonster = 1;
-
- if(!allflag){
- if(strcmp(event,md->npc_event)==0)
- status_kill(bl);
- }else{
- if(!md->spawn)
- status_kill(bl);
- }
- md->state.npc_killmonster = 0;
- return 0;
-}
-static int buildin_killmonster_sub(struct block_list *bl,va_list ap)
-{
- TBL_MOB* md = (TBL_MOB*)bl;
- char *event=va_arg(ap,char *);
- int allflag=va_arg(ap,int);
-
- if(!allflag){
- if(strcmp(event,md->npc_event)==0)
- status_kill(bl);
- }else{
- if(!md->spawn)
- status_kill(bl);
- }
- return 0;
-}
-BUILDIN_FUNC(killmonster)
-{
- const char *mapname,*event;
- int16 m,allflag=0;
- mapname=script_getstr(st,2);
- event=script_getstr(st,3);
- if(strcmp(event,"All")==0)
- allflag = 1;
- else
- check_event(st, event);
-
- if( (m=map_mapname2mapid(mapname))<0 )
- return 0;
-
- if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 )
- return 0;
-
- if( script_hasdata(st,4) ) {
- if ( script_getnum(st,4) == 1 ) {
- map_foreachinmap(buildin_killmonster_sub, m, BL_MOB, event ,allflag);
- return 0;
- }
- }
-
- map_freeblock_lock();
- map_foreachinmap(buildin_killmonster_sub_strip, m, BL_MOB, event ,allflag);
- map_freeblock_unlock();
- return 0;
-}
-
-static int buildin_killmonsterall_sub_strip(struct block_list *bl,va_list ap)
-{ //Strips the event from the mob if it's killed the old method.
- struct mob_data *md;
-
- md = BL_CAST(BL_MOB, bl);
- if (md->npc_event[0])
- md->npc_event[0] = 0;
-
- status_kill(bl);
- return 0;
-}
-static int buildin_killmonsterall_sub(struct block_list *bl,va_list ap)
-{
- status_kill(bl);
- return 0;
-}
-BUILDIN_FUNC(killmonsterall)
-{
- const char *mapname;
- int16 m;
- mapname=script_getstr(st,2);
-
- if( (m = map_mapname2mapid(mapname))<0 )
- return 0;
-
- if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 )
- return 0;
-
- if( script_hasdata(st,3) ) {
- if ( script_getnum(st,3) == 1 ) {
- map_foreachinmap(buildin_killmonsterall_sub,m,BL_MOB);
- return 0;
- }
- }
-
- map_foreachinmap(buildin_killmonsterall_sub_strip,m,BL_MOB);
- return 0;
-}
-
-/*==========================================
- * Creates a clone of a player.
- * clone map, x, y, event, char_id, master_id, mode, flag, duration
- *------------------------------------------*/
-BUILDIN_FUNC(clone)
-{
- TBL_PC *sd, *msd=NULL;
- int char_id,master_id=0,x,y, mode = 0, flag = 0, m;
- unsigned int duration = 0;
- const char *map,*event="";
-
- map=script_getstr(st,2);
- x=script_getnum(st,3);
- y=script_getnum(st,4);
- event=script_getstr(st,5);
- char_id=script_getnum(st,6);
-
- if( script_hasdata(st,7) )
- master_id=script_getnum(st,7);
-
- if( script_hasdata(st,8) )
- mode=script_getnum(st,8);
-
- if( script_hasdata(st,9) )
- flag=script_getnum(st,9);
-
- if( script_hasdata(st,10) )
- duration=script_getnum(st,10);
-
- check_event(st, event);
-
- m = map_mapname2mapid(map);
- if (m < 0) return 0;
-
- sd = map_charid2sd(char_id);
-
- if (master_id) {
- msd = map_charid2sd(master_id);
- if (msd)
- master_id = msd->bl.id;
- else
- master_id = 0;
- }
- if (sd) //Return ID of newly crafted clone.
- script_pushint(st,mob_clone_spawn(sd, m, x, y, event, master_id, mode, flag, 1000*duration));
- else //Failed to create clone.
- script_pushint(st,0);
-
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(doevent)
-{
- const char* event = script_getstr(st,2);
- struct map_session_data* sd;
-
- if( ( sd = script_rid2sd(st) ) == NULL )
- {
- return 0;
- }
-
- check_event(st, event);
- npc_event(sd, event, 0);
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(donpcevent)
-{
- const char* event = script_getstr(st,2);
- check_event(st, event);
- if( !npc_event_do(event) ) {
- struct npc_data * nd = map_id2nd(st->oid);
- ShowDebug("NPCEvent '%s' not found! (source: %s)\n",event,nd?nd->name:"Unknown");
- script_pushint(st, 0);
- } else
- script_pushint(st, 1);
- return 0;
-}
-
-/// for Aegis compatibility
-/// basically a specialized 'donpcevent', with the event specified as two arguments instead of one
-BUILDIN_FUNC(cmdothernpc) // Added by RoVeRT
-{
- const char* npc = script_getstr(st,2);
- const char* command = script_getstr(st,3);
- char event[EVENT_NAME_LENGTH];
- snprintf(event, sizeof(event), "%s::OnCommand%s", npc, command);
- check_event(st, event);
- npc_event_do(event);
- return 0;
-}
-
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(addtimer)
-{
- int tick = script_getnum(st,2);
- const char* event = script_getstr(st, 3);
- TBL_PC* sd;
-
- check_event(st, event);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- pc_addeventtimer(sd,tick,event);
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(deltimer)
-{
- const char *event;
- TBL_PC* sd;
-
- event=script_getstr(st, 2);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- check_event(st, event);
- pc_deleventtimer(sd,event);
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(addtimercount)
-{
- const char *event;
- int tick;
- TBL_PC* sd;
-
- event=script_getstr(st, 2);
- tick=script_getnum(st,3);
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- check_event(st, event);
- pc_addeventtimercount(sd,event,tick);
- return 0;
-}
-
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(initnpctimer)
-{
- struct npc_data *nd;
- int flag = 0;
-
- if( script_hasdata(st,3) )
- { //Two arguments: NPC name and attach flag.
- nd = npc_name2id(script_getstr(st, 2));
- flag = script_getnum(st,3);
- }
- else if( script_hasdata(st,2) )
- { //Check if argument is numeric (flag) or string (npc name)
- struct script_data *data;
- data = script_getdata(st,2);
- get_val(st,data);
- if( data_isstring(data) ) //NPC name
- nd = npc_name2id(conv_str(st, data));
- else if( data_isint(data) ) //Flag
- {
- nd = (struct npc_data *)map_id2bl(st->oid);
- flag = conv_num(st,data);
- }
- else
- {
- ShowError("initnpctimer: invalid argument type #1 (needs be int or string)).\n");
- return 1;
- }
- }
- else
- nd = (struct npc_data *)map_id2bl(st->oid);
-
- if( !nd )
- return 0;
- if( flag ) //Attach
- {
- TBL_PC* sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
- nd->u.scr.rid = sd->bl.id;
- }
-
- nd->u.scr.timertick = 0;
- npc_settimerevent_tick(nd,0);
- npc_timerevent_start(nd, st->rid);
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(startnpctimer)
-{
- struct npc_data *nd;
- int flag = 0;
-
- if( script_hasdata(st,3) )
- { //Two arguments: NPC name and attach flag.
- nd = npc_name2id(script_getstr(st, 2));
- flag = script_getnum(st,3);
- }
- else if( script_hasdata(st,2) )
- { //Check if argument is numeric (flag) or string (npc name)
- struct script_data *data;
- data = script_getdata(st,2);
- get_val(st,data);
- if( data_isstring(data) ) //NPC name
- nd = npc_name2id(conv_str(st, data));
- else if( data_isint(data) ) //Flag
- {
- nd = (struct npc_data *)map_id2bl(st->oid);
- flag = conv_num(st,data);
- }
- else
- {
- ShowError("initnpctimer: invalid argument type #1 (needs be int or string)).\n");
- return 1;
- }
- }
- else
- nd=(struct npc_data *)map_id2bl(st->oid);
-
- if( !nd )
- return 0;
- if( flag ) //Attach
- {
- TBL_PC* sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
- nd->u.scr.rid = sd->bl.id;
- }
-
- npc_timerevent_start(nd, st->rid);
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(stopnpctimer)
-{
- struct npc_data *nd;
- int flag = 0;
-
- if( script_hasdata(st,3) )
- { //Two arguments: NPC name and attach flag.
- nd = npc_name2id(script_getstr(st, 2));
- flag = script_getnum(st,3);
- }
- else if( script_hasdata(st,2) )
- { //Check if argument is numeric (flag) or string (npc name)
- struct script_data *data;
- data = script_getdata(st,2);
- get_val(st,data);
- if( data_isstring(data) ) //NPC name
- nd = npc_name2id(conv_str(st, data));
- else if( data_isint(data) ) //Flag
- {
- nd = (struct npc_data *)map_id2bl(st->oid);
- flag = conv_num(st,data);
- }
- else
- {
- ShowError("initnpctimer: invalid argument type #1 (needs be int or string)).\n");
- return 1;
- }
- }
- else
- nd=(struct npc_data *)map_id2bl(st->oid);
-
- if( !nd )
- return 0;
- if( flag ) //Detach
- nd->u.scr.rid = 0;
-
- npc_timerevent_stop(nd);
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(getnpctimer)
-{
- struct npc_data *nd;
- TBL_PC *sd;
- int type = script_getnum(st,2);
- int val = 0;
-
- if( script_hasdata(st,3) )
- nd = npc_name2id(script_getstr(st,3));
- else
- nd = (struct npc_data *)map_id2bl(st->oid);
-
- if( !nd || nd->bl.type != BL_NPC )
- {
- script_pushint(st,0);
- ShowError("getnpctimer: Invalid NPC.\n");
- return 1;
- }
-
- switch( type )
- {
- case 0: val = npc_gettimerevent_tick(nd); break;
- case 1:
- if( nd->u.scr.rid )
- {
- sd = map_id2sd(nd->u.scr.rid);
- if( !sd )
- {
- ShowError("buildin_getnpctimer: Attached player not found!\n");
- break;
- }
- val = (sd->npc_timer_id != INVALID_TIMER);
- }
- else
- val = (nd->u.scr.timerid != INVALID_TIMER);
- break;
- case 2: val = nd->u.scr.timeramount; break;
- }
-
- script_pushint(st,val);
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(setnpctimer)
-{
- int tick;
- struct npc_data *nd;
-
- tick = script_getnum(st,2);
- if( script_hasdata(st,3) )
- nd = npc_name2id(script_getstr(st,3));
- else
- nd = (struct npc_data *)map_id2bl(st->oid);
-
- if( !nd || nd->bl.type != BL_NPC )
- {
- script_pushint(st,1);
- ShowError("setnpctimer: Invalid NPC.\n");
- return 1;
- }
-
- npc_settimerevent_tick(nd,tick);
- script_pushint(st,0);
- return 0;
-}
-
-/*==========================================
- * attaches the player rid to the timer [Celest]
- *------------------------------------------*/
-BUILDIN_FUNC(attachnpctimer)
-{
- TBL_PC *sd;
- struct npc_data *nd = (struct npc_data *)map_id2bl(st->oid);
-
- if( !nd || nd->bl.type != BL_NPC )
- {
- script_pushint(st,1);
- ShowError("setnpctimer: Invalid NPC.\n");
- return 1;
- }
-
- if( script_hasdata(st,2) )
- sd = map_nick2sd(script_getstr(st,2));
- else
- sd = script_rid2sd(st);
-
- if( !sd )
- {
- script_pushint(st,1);
- ShowWarning("attachnpctimer: Invalid player.\n");
- return 1;
- }
-
- nd->u.scr.rid = sd->bl.id;
- script_pushint(st,0);
- return 0;
-}
-
-/*==========================================
- * detaches a player rid from the timer [Celest]
- *------------------------------------------*/
-BUILDIN_FUNC(detachnpctimer)
-{
- struct npc_data *nd;
-
- if( script_hasdata(st,2) )
- nd = npc_name2id(script_getstr(st,2));
- else
- nd = (struct npc_data *)map_id2bl(st->oid);
-
- if( !nd || nd->bl.type != BL_NPC )
- {
- script_pushint(st,1);
- ShowError("detachnpctimer: Invalid NPC.\n");
- return 1;
- }
-
- nd->u.scr.rid = 0;
- script_pushint(st,0);
- return 0;
-}
-
-/*==========================================
- * To avoid "player not attached" script errors, this function is provided,
- * it checks if there is a player attached to the current script. [Skotlex]
- * If no, returns 0, if yes, returns the account_id of the attached player.
- *------------------------------------------*/
-BUILDIN_FUNC(playerattached)
-{
- if(st->rid == 0 || map_id2sd(st->rid) == NULL)
- script_pushint(st,0);
- else
- script_pushint(st,st->rid);
- return 0;
-}
-
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(announce)
-{
- const char *mes = script_getstr(st,2);
- int flag = script_getnum(st,3);
- const char *fontColor = script_hasdata(st,4) ? script_getstr(st,4) : NULL;
- int fontType = script_hasdata(st,5) ? script_getnum(st,5) : 0x190; // default fontType (FW_NORMAL)
- int fontSize = script_hasdata(st,6) ? script_getnum(st,6) : 12; // default fontSize
- int fontAlign = script_hasdata(st,7) ? script_getnum(st,7) : 0; // default fontAlign
- int fontY = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontY
-
- if (flag&0x0f) // Broadcast source or broadcast region defined
- {
- send_target target;
- struct block_list *bl = (flag&0x08) ? map_id2bl(st->oid) : (struct block_list *)script_rid2sd(st); // If bc_npc flag is set, use NPC as broadcast source
- if (bl == NULL)
- return 0;
-
- flag &= 0x07;
- target = (flag == 1) ? ALL_SAMEMAP :
- (flag == 2) ? AREA :
- (flag == 3) ? SELF :
- ALL_CLIENT;
- if (fontColor)
- clif_broadcast2(bl, mes, (int)strlen(mes)+1, strtol(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY, target);
- else
- clif_broadcast(bl, mes, (int)strlen(mes)+1, flag&0xf0, target);
- }
- else
- {
- if (fontColor)
- intif_broadcast2(mes, (int)strlen(mes)+1, strtol(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY);
- else
- intif_broadcast(mes, (int)strlen(mes)+1, flag&0xf0);
- }
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-static int buildin_announce_sub(struct block_list *bl, va_list ap)
-{
- char *mes = va_arg(ap, char *);
- int len = va_arg(ap, int);
- int type = va_arg(ap, int);
- char *fontColor = va_arg(ap, char *);
- short fontType = (short)va_arg(ap, int);
- short fontSize = (short)va_arg(ap, int);
- short fontAlign = (short)va_arg(ap, int);
- short fontY = (short)va_arg(ap, int);
- if (fontColor)
- clif_broadcast2(bl, mes, len, strtol(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY, SELF);
- else
- clif_broadcast(bl, mes, len, type, SELF);
- return 0;
-}
-
-BUILDIN_FUNC(mapannounce)
-{
- const char *mapname = script_getstr(st,2);
- const char *mes = script_getstr(st,3);
- int flag = script_getnum(st,4);
- const char *fontColor = script_hasdata(st,5) ? script_getstr(st,5) : NULL;
- int fontType = script_hasdata(st,6) ? script_getnum(st,6) : 0x190; // default fontType (FW_NORMAL)
- int fontSize = script_hasdata(st,7) ? script_getnum(st,7) : 12; // default fontSize
- int fontAlign = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontAlign
- int fontY = script_hasdata(st,9) ? script_getnum(st,9) : 0; // default fontY
- int16 m;
-
- if ((m = map_mapname2mapid(mapname)) < 0)
- return 0;
-
- map_foreachinmap(buildin_announce_sub, m, BL_PC,
- mes, strlen(mes)+1, flag&0xf0, fontColor, fontType, fontSize, fontAlign, fontY);
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(areaannounce)
-{
- const char *mapname = script_getstr(st,2);
- int x0 = script_getnum(st,3);
- int y0 = script_getnum(st,4);
- int x1 = script_getnum(st,5);
- int y1 = script_getnum(st,6);
- const char *mes = script_getstr(st,7);
- int flag = script_getnum(st,8);
- const char *fontColor = script_hasdata(st,9) ? script_getstr(st,9) : NULL;
- int fontType = script_hasdata(st,10) ? script_getnum(st,10) : 0x190; // default fontType (FW_NORMAL)
- int fontSize = script_hasdata(st,11) ? script_getnum(st,11) : 12; // default fontSize
- int fontAlign = script_hasdata(st,12) ? script_getnum(st,12) : 0; // default fontAlign
- int fontY = script_hasdata(st,13) ? script_getnum(st,13) : 0; // default fontY
- int16 m;
-
- if ((m = map_mapname2mapid(mapname)) < 0)
- return 0;
-
- map_foreachinarea(buildin_announce_sub, m, x0, y0, x1, y1, BL_PC,
- mes, strlen(mes)+1, flag&0xf0, fontColor, fontType, fontSize, fontAlign, fontY);
- return 0;
-}
-
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(getusers)
-{
- int flag, val = 0;
- struct map_session_data* sd;
- struct block_list* bl = NULL;
-
- flag = script_getnum(st,2);
-
- switch(flag&0x07)
- {
- case 0:
- if(flag&0x8)
- {// npc
- bl = map_id2bl(st->oid);
- }
- else if((sd = script_rid2sd(st))!=NULL)
- {// pc
- bl = &sd->bl;
- }
-
- if(bl)
- {
- val = map[bl->m].users;
- }
- break;
- case 1:
- val = map_getusers();
- break;
- default:
- ShowWarning("buildin_getusers: Unknown type %d.\n", flag);
- script_pushint(st,0);
- return 1;
- }
-
- script_pushint(st,val);
- return 0;
-}
-/*==========================================
- * Works like @WHO - displays all online users names in window
- *------------------------------------------*/
-BUILDIN_FUNC(getusersname)
-{
- TBL_PC *sd, *pl_sd;
- int /*disp_num=1,*/ group_level = 0;
- struct s_mapiterator* iter;
-
- sd = script_rid2sd(st);
- if (!sd) return 0;
-
- group_level = pc_get_group_level(sd);
- iter = mapit_getallusers();
- for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) )
- {
- if (pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) && pc_get_group_level(pl_sd) > group_level)
- continue; // skip hidden sessions
-
- /* Temporary fix for bugreport:1023.
- * Do not uncomment unless you want thousands of 'next' buttons.
- if((disp_num++)%10==0)
- clif_scriptnext(sd,st->oid);*/
- clif_scriptmes(sd,st->oid,pl_sd->status.name);
- }
- mapit_free(iter);
-
- return 0;
-}
-/*==========================================
- * getmapguildusers("mapname",guild ID) Returns the number guild members present on a map [Reddozen]
- *------------------------------------------*/
-BUILDIN_FUNC(getmapguildusers)
-{
- const char *str;
- int16 m;
- int gid;
- int i=0,c=0;
- struct guild *g = NULL;
- str=script_getstr(st,2);
- gid=script_getnum(st,3);
- if ((m = map_mapname2mapid(str)) < 0) { // map id on this server (m == -1 if not in actual map-server)
- script_pushint(st,-1);
- return 0;
- }
- g = guild_search(gid);
-
- if (g){
- for(i = 0; i < g->max_member; i++)
- {
- if (g->member[i].sd && g->member[i].sd->bl.m == m)
- c++;
- }
- }
-
- script_pushint(st,c);
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(getmapusers)
-{
- const char *str;
- int16 m;
- str=script_getstr(st,2);
- if( (m=map_mapname2mapid(str))< 0){
- script_pushint(st,-1);
- return 0;
- }
- script_pushint(st,map[m].users);
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-static int buildin_getareausers_sub(struct block_list *bl,va_list ap)
-{
- int *users=va_arg(ap,int *);
- (*users)++;
- return 0;
-}
-BUILDIN_FUNC(getareausers)
-{
- const char *str;
- int16 m,x0,y0,x1,y1,users=0; //doubt we can have more then 32k users on
- str=script_getstr(st,2);
- x0=script_getnum(st,3);
- y0=script_getnum(st,4);
- x1=script_getnum(st,5);
- y1=script_getnum(st,6);
- if( (m=map_mapname2mapid(str))< 0){
- script_pushint(st,-1);
- return 0;
- }
- map_foreachinarea(buildin_getareausers_sub,
- m,x0,y0,x1,y1,BL_PC,&users);
- script_pushint(st,users);
- return 0;
-}
-
-/*==========================================
- *------------------------------------------*/
-static int buildin_getareadropitem_sub(struct block_list *bl,va_list ap)
-{
- int item=va_arg(ap,int);
- int *amount=va_arg(ap,int *);
- struct flooritem_data *drop=(struct flooritem_data *)bl;
-
- if(drop->item_data.nameid==item)
- (*amount)+=drop->item_data.amount;
-
- return 0;
-}
-BUILDIN_FUNC(getareadropitem)
-{
- const char *str;
- int16 m,x0,y0,x1,y1;
- int item,amount=0;
- struct script_data *data;
-
- str=script_getstr(st,2);
- x0=script_getnum(st,3);
- y0=script_getnum(st,4);
- x1=script_getnum(st,5);
- y1=script_getnum(st,6);
-
- data=script_getdata(st,7);
- get_val(st,data);
- if( data_isstring(data) ){
- const char *name=conv_str(st,data);
- struct item_data *item_data = itemdb_searchname(name);
- item=UNKNOWN_ITEM_ID;
- if( item_data )
- item=item_data->nameid;
- }else
- item=conv_num(st,data);
-
- if( (m=map_mapname2mapid(str))< 0){
- script_pushint(st,-1);
- return 0;
- }
- map_foreachinarea(buildin_getareadropitem_sub,
- m,x0,y0,x1,y1,BL_ITEM,item,&amount);
- script_pushint(st,amount);
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(enablenpc)
-{
- const char *str;
- str=script_getstr(st,2);
- npc_enable(str,1);
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(disablenpc)
-{
- const char *str;
- str=script_getstr(st,2);
- npc_enable(str,0);
- return 0;
-}
-
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(hideoffnpc)
-{
- const char *str;
- str=script_getstr(st,2);
- npc_enable(str,2);
- return 0;
-}
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(hideonnpc)
-{
- const char *str;
- str=script_getstr(st,2);
- npc_enable(str,4);
- return 0;
-}
-
-/// Starts a status effect on the target unit or on the attached player.
-///
-/// sc_start <effect_id>,<duration>,<val1>{,<unit_id>};
-BUILDIN_FUNC(sc_start)
-{
- struct block_list* bl;
- enum sc_type type;
- int tick;
- int val1;
- int val4 = 0;
-
- type = (sc_type)script_getnum(st,2);
- tick = script_getnum(st,3);
- val1 = script_getnum(st,4);
- if( script_hasdata(st,5) )
- bl = map_id2bl(script_getnum(st,5));
- else
- bl = map_id2bl(st->rid);
-
- if( tick == 0 && val1 > 0 && type > SC_NONE && type < SC_MAX && status_sc2skill(type) != 0 )
- {// When there isn't a duration specified, try to get it from the skill_db
- tick = skill_get_time(status_sc2skill(type), val1);
- }
-
- if( potion_flag == 1 && potion_target )
- { //skill.c set the flags before running the script, this must be a potion-pitched effect.
- bl = map_id2bl(potion_target);
- tick /= 2;// Thrown potions only last half.
- val4 = 1;// Mark that this was a thrown sc_effect
- }
-
- if( bl )
- status_change_start(bl, type, 10000, val1, 0, 0, val4, tick, 2);
-
- return 0;
-}
-
-/// Starts a status effect on the target unit or on the attached player.
-///
-/// sc_start2 <effect_id>,<duration>,<val1>,<percent chance>{,<unit_id>};
-BUILDIN_FUNC(sc_start2)
-{
- struct block_list* bl;
- enum sc_type type;
- int tick;
- int val1;
- int val4 = 0;
- int rate;
-
- type = (sc_type)script_getnum(st,2);
- tick = script_getnum(st,3);
- val1 = script_getnum(st,4);
- rate = script_getnum(st,5);
- if( script_hasdata(st,6) )
- bl = map_id2bl(script_getnum(st,6));
- else
- bl = map_id2bl(st->rid);
-
- if( tick == 0 && val1 > 0 && type > SC_NONE && type < SC_MAX && status_sc2skill(type) != 0 )
- {// When there isn't a duration specified, try to get it from the skill_db
- tick = skill_get_time(status_sc2skill(type), val1);
- }
-
- if( potion_flag == 1 && potion_target )
- { //skill.c set the flags before running the script, this must be a potion-pitched effect.
- bl = map_id2bl(potion_target);
- tick /= 2;// Thrown potions only last half.
- val4 = 1;// Mark that this was a thrown sc_effect
- }
-
- if( bl )
- status_change_start(bl, type, rate, val1, 0, 0, val4, tick, 2);
-
- return 0;
-}
-
-/// Starts a status effect on the target unit or on the attached player.
-///
-/// sc_start4 <effect_id>,<duration>,<val1>,<val2>,<val3>,<val4>{,<unit_id>};
-BUILDIN_FUNC(sc_start4)
-{
- struct block_list* bl;
- enum sc_type type;
- int tick;
- int val1;
- int val2;
- int val3;
- int val4;
-
- type = (sc_type)script_getnum(st,2);
- tick = script_getnum(st,3);
- val1 = script_getnum(st,4);
- val2 = script_getnum(st,5);
- val3 = script_getnum(st,6);
- val4 = script_getnum(st,7);
- if( script_hasdata(st,8) )
- bl = map_id2bl(script_getnum(st,8));
- else
- bl = map_id2bl(st->rid);
-
- if( tick == 0 && val1 > 0 && type > SC_NONE && type < SC_MAX && status_sc2skill(type) != 0 )
- {// When there isn't a duration specified, try to get it from the skill_db
- tick = skill_get_time(status_sc2skill(type), val1);
- }
-
- if( potion_flag == 1 && potion_target )
- { //skill.c set the flags before running the script, this must be a potion-pitched effect.
- bl = map_id2bl(potion_target);
- tick /= 2;// Thrown potions only last half.
- }
-
- if( bl )
- status_change_start(bl, type, 10000, val1, val2, val3, val4, tick, 2);
-
- return 0;
-}
-
-/// Ends one or all status effects on the target unit or on the attached player.
-///
-/// sc_end <effect_id>{,<unit_id>};
-BUILDIN_FUNC(sc_end)
-{
- struct block_list* bl;
- int type;
-
- type = script_getnum(st, 2);
- if (script_hasdata(st, 3))
- bl = map_id2bl(script_getnum(st, 3));
- else
- bl = map_id2bl(st->rid);
-
- if (potion_flag == 1 && potion_target) //##TODO how does this work [FlavioJS]
- bl = map_id2bl(potion_target);
-
- if (!bl)
- return 0;
-
- if (type >= 0 && type < SC_MAX)
- {
- struct status_change *sc = status_get_sc(bl);
- struct status_change_entry *sce = sc ? sc->data[type] : NULL;
-
- if (!sce)
- return 0;
-
-
- switch (type)
- {
- case SC_WEIGHT50:
- case SC_WEIGHT90:
- case SC_NOCHAT:
- case SC_PUSH_CART:
- return 0;
-
- default:
- break;
- }
-
- //This should help status_change_end force disabling the SC in case it has no limit.
- sce->val1 = sce->val2 = sce->val3 = sce->val4 = 0;
- status_change_end(bl, (sc_type)type, INVALID_TIMER);
- }
- else
- status_change_clear(bl, 3); // remove all effects
-
- return 0;
-}
-
-/*==========================================
- * @FIXME atm will return reduced tick, 0 immune, 1 no tick
- *------------------------------------------*/
-BUILDIN_FUNC(getscrate)
-{
- struct block_list *bl;
- int type,rate;
-
- type=script_getnum(st,2);
- rate=script_getnum(st,3);
- if( script_hasdata(st,4) ) //get for the bl assigned
- bl = map_id2bl(script_getnum(st,4));
- else
- bl = map_id2bl(st->rid);
-
- if (bl)
- rate = status_get_sc_def(bl, (sc_type)type, 10000, 10000, 0);
-
- script_pushint(st,rate);
- return 0;
-}
-
-/*==========================================
- * getstatus <type>{, <info>};
- *------------------------------------------*/
-BUILDIN_FUNC(getstatus)
-{
- int id, type;
- struct map_session_data* sd = script_rid2sd(st);
-
- if( sd == NULL )
- {// no player attached
- return 0;
- }
-
- id = script_getnum(st, 2);
- type = script_hasdata(st, 3) ? script_getnum(st, 3) : 0;
-
- if( id <= SC_NONE || id >= SC_MAX )
- {// invalid status type given
- ShowWarning("script.c:getstatus: Invalid status type given (%d).\n", id);
- return 0;
- }
-
- if( sd->sc.count == 0 || !sd->sc.data[id] )
- {// no status is active
- script_pushint(st, 0);
- return 0;
- }
-
- switch( type )
- {
- case 1: script_pushint(st, sd->sc.data[id]->val1); break;
- case 2: script_pushint(st, sd->sc.data[id]->val2); break;
- case 3: script_pushint(st, sd->sc.data[id]->val3); break;
- case 4: script_pushint(st, sd->sc.data[id]->val4); break;
- case 5:
- {
- struct TimerData* timer = (struct TimerData*)get_timer(sd->sc.data[id]->timer);
-
- if( timer )
- {// return the amount of time remaining
- script_pushint(st, timer->tick - gettick());
- }
- }
- break;
- default: script_pushint(st, 1); break;
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(debugmes)
-{
- const char *str;
- str=script_getstr(st,2);
- ShowDebug("script debug : %d %d : %s\n",st->rid,st->oid,str);
- return 0;
-}
-
-/*==========================================
- *------------------------------------------*/
-BUILDIN_FUNC(catchpet)
-{
- int pet_id;
- TBL_PC *sd;
-
- pet_id= script_getnum(st,2);
- sd=script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- pet_catch_process1(sd,pet_id);
- return 0;
-}
-
-/*==========================================
- * [orn]
- *------------------------------------------*/
-BUILDIN_FUNC(homunculus_evolution)
-{
- TBL_PC *sd;
-
- sd=script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if(merc_is_hom_active(sd->hd))
- {
- if (sd->hd->homunculus.intimacy > 91000)
- merc_hom_evolution(sd->hd);
- else
- clif_emotion(&sd->hd->bl, E_SWT);
- }
- return 0;
-}
-
-/*==========================================
- * [Xantara]
- *------------------------------------------*/
-BUILDIN_FUNC(homunculus_mutate)
-{
- int homun_id, m_class, m_id;
- TBL_PC *sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if(script_hasdata(st,2))
- homun_id = script_getnum(st,2);
- else
- homun_id = 6048 + (rnd() % 4);
-
- if(merc_is_hom_active(sd->hd)) {
- m_class = hom_class2mapid(sd->hd->homunculus.class_);
- m_id = hom_class2mapid(homun_id);
-
- if ( m_class != -1 && m_id != -1 && m_class&HOM_EVO && m_id&HOM_S && sd->hd->homunculus.level >= 99 )
- hom_mutate(sd->hd, homun_id);
- else
- clif_emotion(&sd->hd->bl, E_SWT);
- }
- return 0;
-}
-
-// [Zephyrus]
-BUILDIN_FUNC(homunculus_shuffle)
-{
- TBL_PC *sd;
-
- sd=script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if(merc_is_hom_active(sd->hd))
- merc_hom_shuffle(sd->hd);
-
- return 0;
-}
-
-//These two functions bring the eA MAPID_* class functionality to scripts.
-BUILDIN_FUNC(eaclass)
-{
- int class_;
- if( script_hasdata(st,2) )
- class_ = script_getnum(st,2);
- else {
- TBL_PC *sd;
- sd=script_rid2sd(st);
- if (!sd) {
- script_pushint(st,-1);
- return 0;
- }
- class_ = sd->status.class_;
- }
- script_pushint(st,pc_jobid2mapid(class_));
- return 0;
-}
-
-BUILDIN_FUNC(roclass)
-{
- int class_ =script_getnum(st,2);
- int sex;
- if( script_hasdata(st,3) )
- sex = script_getnum(st,3);
- else {
- TBL_PC *sd;
- if (st->rid && (sd=script_rid2sd(st)))
- sex = sd->status.sex;
- else
- sex = 1; //Just use male when not found.
- }
- script_pushint(st,pc_mapid2jobid(class_, sex));
- return 0;
-}
-
-/*==========================================
- * Tells client to open a hatching window, used for pet incubator
- *------------------------------------------*/
-BUILDIN_FUNC(birthpet)
-{
- TBL_PC *sd;
- sd=script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- if( sd->status.pet_id )
- {// do not send egg list, when you already have a pet
- return 0;
- }
-
- clif_sendegg(sd);
- return 0;
-}
-
-/*==========================================
- * Added - AppleGirl For Advanced Classes, (Updated for Cleaner Script Purposes)
- * @type
- * 1 : make like after rebirth
- * 2 : blvl,jlvl=1, skillpoint=0
- * 3 : don't reset skill, blvl=1
- * 4 : jlvl=0
- *------------------------------------------*/
-BUILDIN_FUNC(resetlvl)
-{
- TBL_PC *sd;
-
- int type=script_getnum(st,2);
-
- sd=script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- pc_resetlvl(sd,type);
- return 0;
-}
-/*==========================================
- * Reset a player status point
- *------------------------------------------*/
-BUILDIN_FUNC(resetstatus)
-{
- TBL_PC *sd;
- sd=script_rid2sd(st);
- pc_resetstate(sd);
- return 0;
-}
-
-/*==========================================
- * script command resetskill
- *------------------------------------------*/
-BUILDIN_FUNC(resetskill)
-{
- TBL_PC *sd;
- sd=script_rid2sd(st);
- pc_resetskill(sd,1);
- return 0;
-}
-
-/*==========================================
- * Counts total amount of skill points.
- *------------------------------------------*/
-BUILDIN_FUNC(skillpointcount)
-{
- TBL_PC *sd;
- sd=script_rid2sd(st);
- script_pushint(st,sd->status.skill_point + pc_resetskill(sd,2));
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(changebase)
-{
- TBL_PC *sd=NULL;
- int vclass;
-
- if( script_hasdata(st,3) )
- sd=map_id2sd(script_getnum(st,3));
- else
- sd=script_rid2sd(st);
-
- if(sd == NULL)
- return 0;
-
- vclass = script_getnum(st,2);
- if(vclass == JOB_WEDDING)
- {
- if (!battle_config.wedding_modifydisplay || //Do not show the wedding sprites
- sd->class_&JOBL_BABY //Baby classes screw up when showing wedding sprites. [Skotlex] They don't seem to anymore.
- )
- return 0;
- }
-
- if(!sd->disguise && vclass != sd->vd.class_) {
- status_set_viewdata(&sd->bl, vclass);
- //Updated client view. Base, Weapon and Cloth Colors.
- clif_changelook(&sd->bl,LOOK_BASE,sd->vd.class_);
- clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
- if (sd->vd.cloth_color)
- clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color);
- clif_skillinfoblock(sd);
- }
-
- return 0;
-}
-
-/*==========================================
- * Unequip all item and request for a changesex to char-serv
- *------------------------------------------*/
-BUILDIN_FUNC(changesex)
-{
- int i;
- TBL_PC *sd = NULL;
- sd = script_rid2sd(st);
-
- pc_resetskill(sd,4);
- // to avoid any problem with equipment and invalid sex, equipment is unequiped.
- for( i=0; i<EQI_MAX; i++ )
- if( sd->equip_index[i] >= 0 ) pc_unequipitem(sd, sd->equip_index[i], 3);
- chrif_changesex(sd);
- return 0;
-}
-
-/*==========================================
- * Works like 'announce' but outputs in the common chat window
- *------------------------------------------*/
-BUILDIN_FUNC(globalmes)
-{
- struct block_list *bl = map_id2bl(st->oid);
- struct npc_data *nd = (struct npc_data *)bl;
- const char *name=NULL,*mes;
-
- mes=script_getstr(st,2);
- if(mes==NULL) return 0;
-
- if(script_hasdata(st,3)){ // npc name to display
- name=script_getstr(st,3);
- } else {
- name=nd->name; //use current npc name
- }
-
- npc_globalmessage(name,mes); // broadcast to all players connected
-
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////
-// NPC waiting room (chat room)
-//
-
-/// Creates a waiting room (chat room) for this npc.
-///
-/// waitingroom "<title>",<limit>{,"<event>"{,<trigger>{,<zeny>{,<minlvl>{,<maxlvl>}}}}};
-BUILDIN_FUNC(waitingroom)
-{
- struct npc_data* nd;
- int pub = 1;
- const char* title = script_getstr(st, 2);
- int limit = script_getnum(st, 3);
- const char* ev = script_hasdata(st,4) ? script_getstr(st,4) : "";
- int trigger = script_hasdata(st,5) ? script_getnum(st,5) : limit;
- int zeny = script_hasdata(st,6) ? script_getnum(st,6) : 0;
- int minLvl = script_hasdata(st,7) ? script_getnum(st,7) : 1;
- int maxLvl = script_hasdata(st,8) ? script_getnum(st,8) : MAX_LEVEL;
-
- nd = (struct npc_data *)map_id2bl(st->oid);
- if( nd != NULL )
- chat_createnpcchat(nd, title, limit, pub, trigger, ev, zeny, minLvl, maxLvl);
-
- return 0;
-}
-
-/// Removes the waiting room of the current or target npc.
-///
-/// delwaitingroom "<npc_name>";
-/// delwaitingroom;
-BUILDIN_FUNC(delwaitingroom)
-{
- struct npc_data* nd;
- if( script_hasdata(st,2) )
- nd = npc_name2id(script_getstr(st, 2));
- else
- nd = (struct npc_data *)map_id2bl(st->oid);
- if( nd != NULL )
- chat_deletenpcchat(nd);
- return 0;
-}
-
-/// Kicks all the players from the waiting room of the current or target npc.
-///
-/// kickwaitingroomall "<npc_name>";
-/// kickwaitingroomall;
-BUILDIN_FUNC(waitingroomkickall)
-{
- struct npc_data* nd;
- struct chat_data* cd;
-
- if( script_hasdata(st,2) )
- nd = npc_name2id(script_getstr(st,2));
- else
- nd = (struct npc_data *)map_id2bl(st->oid);
-
- if( nd != NULL && (cd=(struct chat_data *)map_id2bl(nd->chat_id)) != NULL )
- chat_npckickall(cd);
- return 0;
-}
-
-/// Enables the waiting room event of the current or target npc.
-///
-/// enablewaitingroomevent "<npc_name>";
-/// enablewaitingroomevent;
-BUILDIN_FUNC(enablewaitingroomevent)
-{
- struct npc_data* nd;
- struct chat_data* cd;
-
- if( script_hasdata(st,2) )
- nd = npc_name2id(script_getstr(st, 2));
- else
- nd = (struct npc_data *)map_id2bl(st->oid);
-
- if( nd != NULL && (cd=(struct chat_data *)map_id2bl(nd->chat_id)) != NULL )
- chat_enableevent(cd);
- return 0;
-}
-
-/// Disables the waiting room event of the current or target npc.
-///
-/// disablewaitingroomevent "<npc_name>";
-/// disablewaitingroomevent;
-BUILDIN_FUNC(disablewaitingroomevent)
-{
- struct npc_data *nd;
- struct chat_data *cd;
-
- if( script_hasdata(st,2) )
- nd = npc_name2id(script_getstr(st, 2));
- else
- nd = (struct npc_data *)map_id2bl(st->oid);
-
- if( nd != NULL && (cd=(struct chat_data *)map_id2bl(nd->chat_id)) != NULL )
- chat_disableevent(cd);
- return 0;
-}
-
-/// Returns info on the waiting room of the current or target npc.
-/// Returns -1 if the type unknown
-/// <type>=0 : current number of users
-/// <type>=1 : maximum number of users allowed
-/// <type>=2 : the number of users that trigger the event
-/// <type>=3 : if the trigger is disabled
-/// <type>=4 : the title of the waiting room
-/// <type>=5 : the password of the waiting room
-/// <type>=16 : the name of the waiting room event
-/// <type>=32 : if the waiting room is full
-/// <type>=33 : if there are enough users to trigger the event
-///
-/// getwaitingroomstate(<type>,"<npc_name>") -> <info>
-/// getwaitingroomstate(<type>) -> <info>
-BUILDIN_FUNC(getwaitingroomstate)
-{
- struct npc_data *nd;
- struct chat_data *cd;
- int type;
-
- type = script_getnum(st,2);
- if( script_hasdata(st,3) )
- nd = npc_name2id(script_getstr(st, 3));
- else
- nd = (struct npc_data *)map_id2bl(st->oid);
-
- if( nd == NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id)) == NULL )
- {
- script_pushint(st, -1);
- return 0;
- }
-
- switch(type)
- {
- case 0: script_pushint(st, cd->users); break;
- case 1: script_pushint(st, cd->limit); break;
- case 2: script_pushint(st, cd->trigger&0x7f); break;
- case 3: script_pushint(st, ((cd->trigger&0x80)!=0)); break;
- case 4: script_pushstrcopy(st, cd->title); break;
- case 5: script_pushstrcopy(st, cd->pass); break;
- case 16: script_pushstrcopy(st, cd->npc_event);break;
- case 32: script_pushint(st, (cd->users >= cd->limit)); break;
- case 33: script_pushint(st, (cd->users >= cd->trigger)); break;
- default: script_pushint(st, -1); break;
- }
- return 0;
-}
-
-/// Warps the trigger or target amount of players to the target map and position.
-/// Players are automatically removed from the waiting room.
-/// Those waiting the longest will get warped first.
-/// The target map can be "Random" for a random position in the current map,
-/// and "SavePoint" for the savepoint map+position.
-/// The map flag noteleport of the current map is only considered when teleporting to the savepoint.
-///
-/// The id's of the teleported players are put into the array $@warpwaitingpc[]
-/// The total number of teleported players is put into $@warpwaitingpcnum
-///
-/// warpwaitingpc "<map name>",<x>,<y>,<number of players>;
-/// warpwaitingpc "<map name>",<x>,<y>;
-BUILDIN_FUNC(warpwaitingpc)
-{
- int x;
- int y;
- int i;
- int n;
- const char* map_name;
- struct npc_data* nd;
- struct chat_data* cd;
- TBL_PC* sd;
-
- nd = (struct npc_data *)map_id2bl(st->oid);
- if( nd == NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id)) == NULL )
- return 0;
-
- map_name = script_getstr(st,2);
- x = script_getnum(st,3);
- y = script_getnum(st,4);
- n = cd->trigger&0x7f;
-
- if( script_hasdata(st,5) )
- n = script_getnum(st,5);
-
- for( i = 0; i < n && cd->users > 0; i++ )
- {
- sd = cd->usersd[0];
-
- if( strcmp(map_name,"SavePoint") == 0 && map[sd->bl.m].flag.noteleport )
- {// can't teleport on this map
- break;
- }
-
- if( cd->zeny )
- {// fee set
- if( (uint32)sd->status.zeny < cd->zeny )
- {// no zeny to cover set fee
- break;
- }
- pc_payzeny(sd, cd->zeny, LOG_TYPE_NPC, NULL);
- }
-
- mapreg_setreg(reference_uid(add_str("$@warpwaitingpc"), i), sd->bl.id);
-
- if( strcmp(map_name,"Random") == 0 )
- pc_randomwarp(sd,CLR_TELEPORT);
- else if( strcmp(map_name,"SavePoint") == 0 )
- pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT);
- else
- pc_setpos(sd, mapindex_name2id(map_name), x, y, CLR_OUTSIGHT);
- }
- mapreg_setreg(add_str("$@warpwaitingpcnum"), i);
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////
-// ...
-//
-
-/// Detaches a character from a script.
-///
-/// @param st Script state to detach the character from.
-static void script_detach_rid(struct script_state* st)
-{
- if(st->rid)
- {
- script_detach_state(st, false);
- st->rid = 0;
- }
-}
-
-/*==========================================
- * Attach sd char id to script and detach current one if any
- *------------------------------------------*/
-BUILDIN_FUNC(attachrid)
-{
- int rid = script_getnum(st,2);
- struct map_session_data* sd;
-
- if ((sd = map_id2sd(rid))!=NULL) {
- script_detach_rid(st);
-
- st->rid = rid;
- script_attach_state(st);
- script_pushint(st,1);
- } else
- script_pushint(st,0);
- return 0;
-}
-/*==========================================
- * Detach script to rid
- *------------------------------------------*/
-BUILDIN_FUNC(detachrid)
-{
- script_detach_rid(st);
- return 0;
-}
-/*==========================================
- * Chk if account connected, (and charid from account if specified)
- *------------------------------------------*/
-BUILDIN_FUNC(isloggedin)
-{
- TBL_PC* sd = map_id2sd(script_getnum(st,2));
- if (script_hasdata(st,3) && sd &&
- sd->status.char_id != script_getnum(st,3))
- sd = NULL;
- push_val(st->stack,C_INT,sd!=NULL);
- return 0;
-}
-
-
-/*==========================================
- *
- *------------------------------------------*/
-BUILDIN_FUNC(setmapflagnosave)
-{
- int16 m,x,y;
- unsigned short mapindex;
- const char *str,*str2;
-
- str=script_getstr(st,2);
- str2=script_getstr(st,3);
- x=script_getnum(st,4);
- y=script_getnum(st,5);
- m = map_mapname2mapid(str);
- mapindex = mapindex_name2id(str2);
-
- if(m >= 0 && mapindex) {
- map[m].flag.nosave=1;
- map[m].save.map=mapindex;
- map[m].save.x=x;
- map[m].save.y=y;
- }
-
- return 0;
-}
-
-BUILDIN_FUNC(getmapflag)
-{
- int16 m,i;
- const char *str;
-
- str=script_getstr(st,2);
- i=script_getnum(st,3);
-
- m = map_mapname2mapid(str);
- if(m >= 0) {
- switch(i) {
- case MF_NOMEMO: script_pushint(st,map[m].flag.nomemo); break;
- case MF_NOTELEPORT: script_pushint(st,map[m].flag.noteleport); break;
- case MF_NOSAVE: script_pushint(st,map[m].flag.nosave); break;
- case MF_NOBRANCH: script_pushint(st,map[m].flag.nobranch); break;
- case MF_NOPENALTY: script_pushint(st,map[m].flag.noexppenalty); break;
- case MF_NOZENYPENALTY: script_pushint(st,map[m].flag.nozenypenalty); break;
- case MF_PVP: script_pushint(st,map[m].flag.pvp); break;
- case MF_PVP_NOPARTY: script_pushint(st,map[m].flag.pvp_noparty); break;
- case MF_PVP_NOGUILD: script_pushint(st,map[m].flag.pvp_noguild); break;
- case MF_GVG: script_pushint(st,map[m].flag.gvg); break;
- case MF_GVG_NOPARTY: script_pushint(st,map[m].flag.gvg_noparty); break;
- case MF_NOTRADE: script_pushint(st,map[m].flag.notrade); break;
- case MF_NOSKILL: script_pushint(st,map[m].flag.noskill); break;
- case MF_NOWARP: script_pushint(st,map[m].flag.nowarp); break;
- case MF_PARTYLOCK: script_pushint(st,map[m].flag.partylock); break;
- case MF_NOICEWALL: script_pushint(st,map[m].flag.noicewall); break;
- case MF_SNOW: script_pushint(st,map[m].flag.snow); break;
- case MF_FOG: script_pushint(st,map[m].flag.fog); break;
- case MF_SAKURA: script_pushint(st,map[m].flag.sakura); break;
- case MF_LEAVES: script_pushint(st,map[m].flag.leaves); break;
- /**
- * No longer available, keeping here just in case it's back someday. [Ind]
- **/
- //case MF_RAIN: script_pushint(st,map[m].flag.rain); break;
- case MF_NOGO: script_pushint(st,map[m].flag.nogo); break;
- case MF_CLOUDS: script_pushint(st,map[m].flag.clouds); break;
- case MF_CLOUDS2: script_pushint(st,map[m].flag.clouds2); break;
- case MF_FIREWORKS: script_pushint(st,map[m].flag.fireworks); break;
- case MF_GVG_CASTLE: script_pushint(st,map[m].flag.gvg_castle); break;
- case MF_GVG_DUNGEON: script_pushint(st,map[m].flag.gvg_dungeon); break;
- case MF_NIGHTENABLED: script_pushint(st,map[m].flag.nightenabled); break;
- case MF_NOBASEEXP: script_pushint(st,map[m].flag.nobaseexp); break;
- case MF_NOJOBEXP: script_pushint(st,map[m].flag.nojobexp); break;
- case MF_NOMOBLOOT: script_pushint(st,map[m].flag.nomobloot); break;
- case MF_NOMVPLOOT: script_pushint(st,map[m].flag.nomvploot); break;
- case MF_NORETURN: script_pushint(st,map[m].flag.noreturn); break;
- case MF_NOWARPTO: script_pushint(st,map[m].flag.nowarpto); break;
- case MF_NIGHTMAREDROP: script_pushint(st,map[m].flag.pvp_nightmaredrop); break;
- case MF_RESTRICTED: script_pushint(st,map[m].flag.restricted); break;
- case MF_NOCOMMAND: script_pushint(st,map[m].nocommand); break;
- case MF_NODROP: script_pushint(st,map[m].flag.nodrop); break;
- case MF_JEXP: script_pushint(st,map[m].jexp); break;
- case MF_BEXP: script_pushint(st,map[m].bexp); break;
- case MF_NOVENDING: script_pushint(st,map[m].flag.novending); break;
- case MF_LOADEVENT: script_pushint(st,map[m].flag.loadevent); break;
- case MF_NOCHAT: script_pushint(st,map[m].flag.nochat); break;
- case MF_NOEXPPENALTY: script_pushint(st,map[m].flag.noexppenalty ); break;
- case MF_GUILDLOCK: script_pushint(st,map[m].flag.guildlock); break;
- case MF_TOWN: script_pushint(st,map[m].flag.town); break;
- case MF_AUTOTRADE: script_pushint(st,map[m].flag.autotrade); break;
- case MF_ALLOWKS: script_pushint(st,map[m].flag.allowks); break;
- case MF_MONSTER_NOTELEPORT: script_pushint(st,map[m].flag.monster_noteleport); break;
- case MF_PVP_NOCALCRANK: script_pushint(st,map[m].flag.pvp_nocalcrank); break;
- case MF_BATTLEGROUND: script_pushint(st,map[m].flag.battleground); break;
- case MF_RESET: script_pushint(st,map[m].flag.reset); break;
- }
- }
-
- return 0;
-}
-/* pvp timer handling */
-static int script_mapflag_pvp_sub(struct block_list *bl,va_list ap) {
- TBL_PC* sd = (TBL_PC*)bl;
- if (sd->pvp_timer == INVALID_TIMER) {
- sd->pvp_timer = add_timer(gettick() + 200, pc_calc_pvprank_timer, sd->bl.id, 0);
- sd->pvp_rank = 0;
- sd->pvp_lastusers = 0;
- sd->pvp_point = 5;
- sd->pvp_won = 0;
- sd->pvp_lost = 0;
- }
- clif_map_property(sd, MAPPROPERTY_FREEPVPZONE);
- return 0;
-}
-BUILDIN_FUNC(setmapflag)
-{
- int16 m,i;
- const char *str;
- int val=0;
-
- str=script_getstr(st,2);
- i=script_getnum(st,3);
- if(script_hasdata(st,4)){
- val=script_getnum(st,4);
- }
- m = map_mapname2mapid(str);
- if(m >= 0) {
- switch(i) {
- case MF_NOMEMO: map[m].flag.nomemo = 1; break;
- case MF_NOTELEPORT: map[m].flag.noteleport = 1; break;
- case MF_NOSAVE: map[m].flag.nosave = 1; break;
- case MF_NOBRANCH: map[m].flag.nobranch = 1; break;
- case MF_NOPENALTY: map[m].flag.noexppenalty = 1; map[m].flag.nozenypenalty = 1; break;
- case MF_NOZENYPENALTY: map[m].flag.nozenypenalty = 1; break;
- case MF_PVP:
- map[m].flag.pvp = 1;
- if( !battle_config.pk_mode ) {
- map_foreachinmap(script_mapflag_pvp_sub,m,BL_PC);
- }
- break;
- case MF_PVP_NOPARTY: map[m].flag.pvp_noparty = 1; break;
- case MF_PVP_NOGUILD: map[m].flag.pvp_noguild = 1; break;
- case MF_GVG:
- map[m].flag.gvg = 1;
- clif_map_property_mapall(m, MAPPROPERTY_AGITZONE);
- break;
- case MF_GVG_NOPARTY: map[m].flag.gvg_noparty = 1; break;
- case MF_NOTRADE: map[m].flag.notrade = 1; break;
- case MF_NOSKILL: map[m].flag.noskill = 1; break;
- case MF_NOWARP: map[m].flag.nowarp = 1; break;
- case MF_PARTYLOCK: map[m].flag.partylock = 1; break;
- case MF_NOICEWALL: map[m].flag.noicewall = 1; break;
- case MF_SNOW: map[m].flag.snow = 1; break;
- case MF_FOG: map[m].flag.fog = 1; break;
- case MF_SAKURA: map[m].flag.sakura = 1; break;
- case MF_LEAVES: map[m].flag.leaves = 1; break;
- /**
- * No longer available, keeping here just in case it's back someday. [Ind]
- **/
- //case MF_RAIN: map[m].flag.rain = 1; break;
- case MF_NOGO: map[m].flag.nogo = 1; break;
- case MF_CLOUDS: map[m].flag.clouds = 1; break;
- case MF_CLOUDS2: map[m].flag.clouds2 = 1; break;
- case MF_FIREWORKS: map[m].flag.fireworks = 1; break;
- case MF_GVG_CASTLE: map[m].flag.gvg_castle = 1; break;
- case MF_GVG_DUNGEON: map[m].flag.gvg_dungeon = 1; break;
- case MF_NIGHTENABLED: map[m].flag.nightenabled = 1; break;
- case MF_NOBASEEXP: map[m].flag.nobaseexp = 1; break;
- case MF_NOJOBEXP: map[m].flag.nojobexp = 1; break;
- case MF_NOMOBLOOT: map[m].flag.nomobloot = 1; break;
- case MF_NOMVPLOOT: map[m].flag.nomvploot = 1; break;
- case MF_NORETURN: map[m].flag.noreturn = 1; break;
- case MF_NOWARPTO: map[m].flag.nowarpto = 1; break;
- case MF_NIGHTMAREDROP: map[m].flag.pvp_nightmaredrop = 1; break;
- case MF_RESTRICTED:
- map[m].zone |= 1<<(val+1);
- map[m].flag.restricted=1;
- break;
- case MF_NOCOMMAND: map[m].nocommand = (val <= 0) ? 100 : val; break;
- case MF_NODROP: map[m].flag.nodrop = 1; break;
- case MF_JEXP: map[m].jexp = (val <= 0) ? 100 : val; break;
- case MF_BEXP: map[m].bexp = (val <= 0) ? 100 : val; break;
- case MF_NOVENDING: map[m].flag.novending = 1; break;
- case MF_LOADEVENT: map[m].flag.loadevent = 1; break;
- case MF_NOCHAT: map[m].flag.nochat = 1; break;
- case MF_NOEXPPENALTY: map[m].flag.noexppenalty = 1; break;
- case MF_GUILDLOCK: map[m].flag.guildlock = 1; break;
- case MF_TOWN: map[m].flag.town = 1; break;
- case MF_AUTOTRADE: map[m].flag.autotrade = 1; break;
- case MF_ALLOWKS: map[m].flag.allowks = 1; break;
- case MF_MONSTER_NOTELEPORT: map[m].flag.monster_noteleport = 1; break;
- case MF_PVP_NOCALCRANK: map[m].flag.pvp_nocalcrank = 1; break;
- case MF_BATTLEGROUND: map[m].flag.battleground = (val <= 0 || val > 2) ? 1 : val; break;
- case MF_RESET: map[m].flag.reset = 1; break;
- }
- }
-
- return 0;
-}
-
-BUILDIN_FUNC(removemapflag)
-{
- int16 m,i;
- const char *str;
- int val=0;
-
- str=script_getstr(st,2);
- i=script_getnum(st,3);
- if(script_hasdata(st,4)){
- val=script_getnum(st,4);
- }
- m = map_mapname2mapid(str);
- if(m >= 0) {
- switch(i) {
- case MF_NOMEMO: map[m].flag.nomemo = 0; break;
- case MF_NOTELEPORT: map[m].flag.noteleport = 0; break;
- case MF_NOSAVE: map[m].flag.nosave = 0; break;
- case MF_NOBRANCH: map[m].flag.nobranch = 0; break;
- case MF_NOPENALTY: map[m].flag.noexppenalty = 0; map[m].flag.nozenypenalty = 0; break;
- case MF_NOZENYPENALTY: map[m].flag.nozenypenalty = 0; break;
- case MF_PVP:
- map[m].flag.pvp = 0;
- clif_map_property_mapall(m, MAPPROPERTY_NOTHING);
- break;
- case MF_PVP_NOPARTY: map[m].flag.pvp_noparty = 0; break;
- case MF_PVP_NOGUILD: map[m].flag.pvp_noguild = 0; break;
- case MF_GVG:
- map[m].flag.gvg = 0;
- clif_map_property_mapall(m, MAPPROPERTY_NOTHING);
- break;
- case MF_GVG_NOPARTY: map[m].flag.gvg_noparty = 0; break;
- case MF_NOTRADE: map[m].flag.notrade = 0; break;
- case MF_NOSKILL: map[m].flag.noskill = 0; break;
- case MF_NOWARP: map[m].flag.nowarp = 0; break;
- case MF_PARTYLOCK: map[m].flag.partylock = 0; break;
- case MF_NOICEWALL: map[m].flag.noicewall = 0; break;
- case MF_SNOW: map[m].flag.snow = 0; break;
- case MF_FOG: map[m].flag.fog = 0; break;
- case MF_SAKURA: map[m].flag.sakura = 0; break;
- case MF_LEAVES: map[m].flag.leaves = 0; break;
- /**
- * No longer available, keeping here just in case it's back someday. [Ind]
- **/
- //case MF_RAIN: map[m].flag.rain = 0; break;
- case MF_NOGO: map[m].flag.nogo = 0; break;
- case MF_CLOUDS: map[m].flag.clouds = 0; break;
- case MF_CLOUDS2: map[m].flag.clouds2 = 0; break;
- case MF_FIREWORKS: map[m].flag.fireworks = 0; break;
- case MF_GVG_CASTLE: map[m].flag.gvg_castle = 0; break;
- case MF_GVG_DUNGEON: map[m].flag.gvg_dungeon = 0; break;
- case MF_NIGHTENABLED: map[m].flag.nightenabled = 0; break;
- case MF_NOBASEEXP: map[m].flag.nobaseexp = 0; break;
- case MF_NOJOBEXP: map[m].flag.nojobexp = 0; break;
- case MF_NOMOBLOOT: map[m].flag.nomobloot = 0; break;
- case MF_NOMVPLOOT: map[m].flag.nomvploot = 0; break;
- case MF_NORETURN: map[m].flag.noreturn = 0; break;
- case MF_NOWARPTO: map[m].flag.nowarpto = 0; break;
- case MF_NIGHTMAREDROP: map[m].flag.pvp_nightmaredrop = 0; break;
- case MF_RESTRICTED:
- map[m].zone ^= 1<<(val+1);
- if (map[m].zone == 0){
- map[m].flag.restricted=0;
- }
- break;
- case MF_NOCOMMAND: map[m].nocommand = 0; break;
- case MF_NODROP: map[m].flag.nodrop = 0; break;
- case MF_JEXP: map[m].jexp = 0; break;
- case MF_BEXP: map[m].bexp = 0; break;
- case MF_NOVENDING: map[m].flag.novending = 0; break;
- case MF_LOADEVENT: map[m].flag.loadevent = 0; break;
- case MF_NOCHAT: map[m].flag.nochat = 0; break;
- case MF_NOEXPPENALTY: map[m].flag.noexppenalty = 0; break;
- case MF_GUILDLOCK: map[m].flag.guildlock = 0; break;
- case MF_TOWN: map[m].flag.town = 0; break;
- case MF_AUTOTRADE: map[m].flag.autotrade = 0; break;
- case MF_ALLOWKS: map[m].flag.allowks = 0; break;
- case MF_MONSTER_NOTELEPORT: map[m].flag.monster_noteleport = 0; break;
- case MF_PVP_NOCALCRANK: map[m].flag.pvp_nocalcrank = 0; break;
- case MF_BATTLEGROUND: map[m].flag.battleground = 0; break;
- case MF_RESET: map[m].flag.reset = 0; break;
- }
- }
-
- return 0;
-}
-
-BUILDIN_FUNC(pvpon)
-{
- int16 m;
- const char *str;
- TBL_PC* sd = NULL;
- struct s_mapiterator* iter;
-
- str = script_getstr(st,2);
- m = map_mapname2mapid(str);
- if( m < 0 || map[m].flag.pvp )
- return 0; // nothing to do
-
- map[m].flag.pvp = 1;
- clif_map_property_mapall(m, MAPPROPERTY_FREEPVPZONE);
-
- if(battle_config.pk_mode) // disable ranking functions if pk_mode is on [Valaris]
- return 0;
-
- iter = mapit_getallusers();
- for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) )
- {
- if( sd->bl.m != m || sd->pvp_timer != INVALID_TIMER )
- continue; // not applicable
-
- sd->pvp_timer = add_timer(gettick()+200,pc_calc_pvprank_timer,sd->bl.id,0);
- sd->pvp_rank = 0;
- sd->pvp_lastusers = 0;
- sd->pvp_point = 5;
- sd->pvp_won = 0;
- sd->pvp_lost = 0;
- }
- mapit_free(iter);
-
- return 0;
-}
-
-static int buildin_pvpoff_sub(struct block_list *bl,va_list ap)
-{
- TBL_PC* sd = (TBL_PC*)bl;
- clif_pvpset(sd, 0, 0, 2);
- if (sd->pvp_timer != INVALID_TIMER) {
- delete_timer(sd->pvp_timer, pc_calc_pvprank_timer);
- sd->pvp_timer = INVALID_TIMER;
- }
- return 0;
-}
-
-BUILDIN_FUNC(pvpoff)
-{
- int16 m;
- const char *str;
-
- str=script_getstr(st,2);
- m = map_mapname2mapid(str);
- if(m < 0 || !map[m].flag.pvp)
- return 0; //fixed Lupus
-
- map[m].flag.pvp = 0;
- clif_map_property_mapall(m, MAPPROPERTY_NOTHING);
-
- if(battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris]
- return 0;
-
- map_foreachinmap(buildin_pvpoff_sub, m, BL_PC);
- return 0;
-}
-
-BUILDIN_FUNC(gvgon)
-{
- int16 m;
- const char *str;
-
- str=script_getstr(st,2);
- m = map_mapname2mapid(str);
- if(m >= 0 && !map[m].flag.gvg) {
- map[m].flag.gvg = 1;
- clif_map_property_mapall(m, MAPPROPERTY_AGITZONE);
- }
-
- return 0;
-}
-BUILDIN_FUNC(gvgoff)
-{
- int16 m;
- const char *str;
-
- str=script_getstr(st,2);
- m = map_mapname2mapid(str);
- if(m >= 0 && map[m].flag.gvg) {
- map[m].flag.gvg = 0;
- clif_map_property_mapall(m, MAPPROPERTY_NOTHING);
- }
-
- return 0;
-}
-/*==========================================
- * Shows an emoticon on top of the player/npc
- * emotion emotion#, <target: 0 - NPC, 1 - PC>, <NPC/PC name>
- *------------------------------------------*/
-//Optional second parameter added by [Skotlex]
-BUILDIN_FUNC(emotion)
-{
- int type;
- int player=0;
-
- type=script_getnum(st,2);
- if(type < 0 || type > 100)
- return 0;
-
- if( script_hasdata(st,3) )
- player=script_getnum(st,3);
-
- if (player) {
- TBL_PC *sd = NULL;
- if( script_hasdata(st,4) )
- sd = map_nick2sd(script_getstr(st,4));
- else
- sd = script_rid2sd(st);
- if (sd)
- clif_emotion(&sd->bl,type);
- } else
- if( script_hasdata(st,4) )
- {
- TBL_NPC *nd = npc_name2id(script_getstr(st,4));
- if(nd)
- clif_emotion(&nd->bl,type);
- }
- else
- clif_emotion(map_id2bl(st->oid),type);
- return 0;
-}
-
-static int buildin_maprespawnguildid_sub_pc(struct map_session_data* sd, va_list ap)
-{
- int16 m=va_arg(ap,int);
- int g_id=va_arg(ap,int);
- int flag=va_arg(ap,int);
-
- if(!sd || sd->bl.m != m)
- return 0;
- if(
- (sd->status.guild_id == g_id && flag&1) || //Warp out owners
- (sd->status.guild_id != g_id && flag&2) || //Warp out outsiders
- (sd->status.guild_id == 0) // Warp out players not in guild [Valaris]
- )
- pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
- return 1;
-}
-
-static int buildin_maprespawnguildid_sub_mob(struct block_list *bl,va_list ap)
-{
- struct mob_data *md=(struct mob_data *)bl;
-
- if(!md->guardian_data && md->class_ != MOBID_EMPERIUM)
- status_kill(bl);
-
- return 0;
-}
-
-BUILDIN_FUNC(maprespawnguildid)
-{
- const char *mapname=script_getstr(st,2);
- int g_id=script_getnum(st,3);
- int flag=script_getnum(st,4);
-
- int16 m=map_mapname2mapid(mapname);
-
- if(m == -1)
- return 0;
-
- //Catch ALL players (in case some are 'between maps' on execution time)
- map_foreachpc(buildin_maprespawnguildid_sub_pc,m,g_id,flag);
- if (flag&4) //Remove script mobs.
- map_foreachinmap(buildin_maprespawnguildid_sub_mob,m,BL_MOB);
- return 0;
-}
-
-BUILDIN_FUNC(agitstart)
-{
- if(agit_flag==1) return 0; // Agit already Start.
- agit_flag=1;
- guild_agit_start();
- return 0;
-}
-
-BUILDIN_FUNC(agitend)
-{
- if(agit_flag==0) return 0; // Agit already End.
- agit_flag=0;
- guild_agit_end();
- return 0;
-}
-
-BUILDIN_FUNC(agitstart2)
-{
- if(agit2_flag==1) return 0; // Agit2 already Start.
- agit2_flag=1;
- guild_agit2_start();
- return 0;
-}
-
-BUILDIN_FUNC(agitend2)
-{
- if(agit2_flag==0) return 0; // Agit2 already End.
- agit2_flag=0;
- guild_agit2_end();
- return 0;
-}
-
-/*==========================================
- * Returns whether woe is on or off. // choice script
- *------------------------------------------*/
-BUILDIN_FUNC(agitcheck)
-{
- script_pushint(st,agit_flag);
- return 0;
-}
-
-/*==========================================
- * Returns whether woese is on or off. // choice script
- *------------------------------------------*/
-BUILDIN_FUNC(agitcheck2)
-{
- script_pushint(st,agit2_flag);
- return 0;
-}
-
-/// Sets the guild_id of this npc.
-///
-/// flagemblem <guild_id>;
-BUILDIN_FUNC(flagemblem)
-{
- TBL_NPC* nd;
- int g_id = script_getnum(st,2);
-
- if(g_id < 0) return 0;
-
- nd = (TBL_NPC*)map_id2nd(st->oid);
- if( nd == NULL ) {
- ShowError("script:flagemblem: npc %d not found\n", st->oid);
- } else if( nd->subtype != SCRIPT ) {
- ShowError("script:flagemblem: unexpected subtype %d for npc %d '%s'\n", nd->subtype, st->oid, nd->exname);
- } else {
- bool changed = ( nd->u.scr.guild_id != g_id )?true:false;
- nd->u.scr.guild_id = g_id;
- clif_guild_emblem_area(&nd->bl);
- /* guild flag caching */
- if( g_id ) /* adding a id */
- guild_flag_add(nd);
- else if( changed ) /* removing a flag */
- guild_flag_remove(nd);
- }
- return 0;
-}
-
-BUILDIN_FUNC(getcastlename)
-{
- const char* mapname = mapindex_getmapname(script_getstr(st,2),NULL);
- struct guild_castle* gc = guild_mapname2gc(mapname);
- const char* name = (gc) ? gc->castle_name : "";
- script_pushstrcopy(st,name);
- return 0;
-}
-
-BUILDIN_FUNC(getcastledata)
-{
- const char *mapname = mapindex_getmapname(script_getstr(st,2),NULL);
- int index = script_getnum(st,3);
- struct guild_castle *gc = guild_mapname2gc(mapname);
-
- if (gc == NULL) {
- script_pushint(st,0);
- ShowWarning("buildin_setcastledata: guild castle for map '%s' not found\n", mapname);
- return 1;
- }
-
- switch (index) {
- case 1:
- script_pushint(st,gc->guild_id); break;
- case 2:
- script_pushint(st,gc->economy); break;
- case 3:
- script_pushint(st,gc->defense); break;
- case 4:
- script_pushint(st,gc->triggerE); break;
- case 5:
- script_pushint(st,gc->triggerD); break;
- case 6:
- script_pushint(st,gc->nextTime); break;
- case 7:
- script_pushint(st,gc->payTime); break;
- case 8:
- script_pushint(st,gc->createTime); break;
- case 9:
- script_pushint(st,gc->visibleC); break;
- default:
- if (index > 9 && index <= 9+MAX_GUARDIANS) {
- script_pushint(st,gc->guardian[index-10].visible);
- break;
- }
- script_pushint(st,0);
- ShowWarning("buildin_setcastledata: index = '%d' is out of allowed range\n", index);
- return 1;
- }
- return 0;
-}
-
-BUILDIN_FUNC(setcastledata)
-{
- const char *mapname = mapindex_getmapname(script_getstr(st,2),NULL);
- int index = script_getnum(st,3);
- int value = script_getnum(st,4);
- struct guild_castle *gc = guild_mapname2gc(mapname);
-
- if (gc == NULL) {
- ShowWarning("buildin_setcastledata: guild castle for map '%s' not found\n", mapname);
- return 1;
- }
-
- if (index <= 0 || index > 9+MAX_GUARDIANS) {
- ShowWarning("buildin_setcastledata: index = '%d' is out of allowed range\n", index);
- return 1;
- }
-
- guild_castledatasave(gc->castle_id, index, value);
- return 0;
-}
-
-/* =====================================================================
- * ---------------------------------------------------------------------*/
-BUILDIN_FUNC(requestguildinfo)
-{
- int guild_id=script_getnum(st,2);
- const char *event=NULL;
-
- if( script_hasdata(st,3) ){
- event=script_getstr(st,3);
- check_event(st, event);
- }
-
- if(guild_id>0)
- guild_npc_request_info(guild_id,event);
- return 0;
-}
-
-/// Returns the number of cards that have been compounded onto the specified equipped item.
-/// getequipcardcnt(<equipment slot>);
-BUILDIN_FUNC(getequipcardcnt)
-{
- int i=-1,j,num;
- TBL_PC *sd;
- int count;
-
- num=script_getnum(st,2);
- sd=script_rid2sd(st);
- if (num > 0 && num <= ARRAYLENGTH(equip))
- i=pc_checkequip(sd,equip[num-1]);
-
- if (i < 0 || !sd->inventory_data[i]) {
- script_pushint(st,0);
- return 0;
- }
-
- if(itemdb_isspecial(sd->status.inventory[i].card[0]))
- {
- script_pushint(st,0);
- return 0;
- }
-
- count = 0;
- for( j = 0; j < sd->inventory_data[i]->slot; j++ )
- if( sd->status.inventory[i].card[j] && itemdb_type(sd->status.inventory[i].card[j]) == IT_CARD )
- count++;
-
- script_pushint(st,count);
- return 0;
-}
-
-/// Removes all cards from the item found in the specified equipment slot of the invoking character,
-/// and give them to the character. If any cards were removed in this manner, it will also show a success effect.
-/// successremovecards <slot>;
-BUILDIN_FUNC(successremovecards) {
- int i=-1,j,c,cardflag=0;
-
- TBL_PC* sd = script_rid2sd(st);
- int num = script_getnum(st,2);
-
- if (num > 0 && num <= ARRAYLENGTH(equip))
- i=pc_checkequip(sd,equip[num-1]);
-
- if (i < 0 || !sd->inventory_data[i]) {
- return 0;
- }
-
- if(itemdb_isspecial(sd->status.inventory[i].card[0]))
- return 0;
-
- for( c = sd->inventory_data[i]->slot - 1; c >= 0; --c ) {
- if( sd->status.inventory[i].card[c] && itemdb_type(sd->status.inventory[i].card[c]) == IT_CARD ) {// extract this card from the item
- int flag;
- struct item item_tmp;
- memset(&item_tmp,0,sizeof(item_tmp));
- cardflag = 1;
- item_tmp.nameid = sd->status.inventory[i].card[c];
- item_tmp.identify = 1;
-
- if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){ // get back the cart in inventory
- clif_additem(sd,0,0,flag);
- map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
- }
- }
-
- if(cardflag == 1) {//if card was remove remplace item with no card
- int flag;
- struct item item_tmp;
- memset(&item_tmp,0,sizeof(item_tmp));
-
- item_tmp.nameid = sd->status.inventory[i].nameid;
- item_tmp.identify = 1;
- item_tmp.refine = sd->status.inventory[i].refine;
- item_tmp.attribute = sd->status.inventory[i].attribute;
- item_tmp.expire_time = sd->status.inventory[i].expire_time;
-
- for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++)
- item_tmp.card[j]=sd->status.inventory[i].card[j];
-
- pc_delitem(sd,i,1,0,3,LOG_TYPE_SCRIPT);
- if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){ //chk if can be spawn in inventory otherwise put on floor
- clif_additem(sd,0,0,flag);
- map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
-
- clif_misceffect(&sd->bl,3);
- }
- return 0;
-}
-
-/// Removes all cards from the item found in the specified equipment slot of the invoking character.
-/// failedremovecards <slot>, <type>;
-/// <type>=0 : will destroy both the item and the cards.
-/// <type>=1 : will keep the item, but destroy the cards.
-/// <type>=2 : will keep the cards, but destroy the item.
-/// <type>=? : will just display the failure effect.
-BUILDIN_FUNC(failedremovecards) {
- int i=-1,j,c,cardflag=0;
-
- TBL_PC* sd = script_rid2sd(st);
- int num = script_getnum(st,2);
- int typefail = script_getnum(st,3);
-
- if (num > 0 && num <= ARRAYLENGTH(equip))
- i=pc_checkequip(sd,equip[num-1]);
-
- if (i < 0 || !sd->inventory_data[i])
- return 0;
-
- if(itemdb_isspecial(sd->status.inventory[i].card[0]))
- return 0;
-
- for( c = sd->inventory_data[i]->slot - 1; c >= 0; --c ) {
- if( sd->status.inventory[i].card[c] && itemdb_type(sd->status.inventory[i].card[c]) == IT_CARD ) {
- cardflag = 1;
-
- if(typefail == 2) {// add cards to inventory, clear
- int flag;
- struct item item_tmp;
-
- memset(&item_tmp,0,sizeof(item_tmp));
-
- item_tmp.nameid = sd->status.inventory[i].card[c];
- item_tmp.identify = 1;
-
- if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){
- clif_additem(sd,0,0,flag);
- map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
- }
- }
- }
-
- if(cardflag == 1) {
- if(typefail == 0 || typefail == 2){ // destroy the item
- pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT);
- }
- if(typefail == 1){ // destroy the card
- int flag;
- struct item item_tmp;
-
- memset(&item_tmp,0,sizeof(item_tmp));
-
- item_tmp.nameid = sd->status.inventory[i].nameid;
- item_tmp.identify = 1;
- item_tmp.refine = sd->status.inventory[i].refine;
- item_tmp.attribute = sd->status.inventory[i].attribute;
- item_tmp.expire_time = sd->status.inventory[i].expire_time;
-
- for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++)
- item_tmp.card[j]=sd->status.inventory[i].card[j];
-
- pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT);
-
- if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){
- clif_additem(sd,0,0,flag);
- map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
- }
- clif_misceffect(&sd->bl,2);
- }
-
- return 0;
-}
-
-/* ================================================================
- * mapwarp "<from map>","<to map>",<x>,<y>,<type>,<ID for Type>;
- * type: 0=everyone, 1=guild, 2=party; [Reddozen]
- * improved by [Lance]
- * ================================================================*/
-BUILDIN_FUNC(mapwarp) // Added by RoVeRT
-{
- int x,y,m,check_val=0,check_ID=0,i=0;
- struct guild *g = NULL;
- struct party_data *p = NULL;
- const char *str;
- const char *mapname;
- unsigned int index;
- mapname=script_getstr(st,2);
- str=script_getstr(st,3);
- x=script_getnum(st,4);
- y=script_getnum(st,5);
- if(script_hasdata(st,7)){
- check_val=script_getnum(st,6);
- check_ID=script_getnum(st,7);
- }
-
- if((m=map_mapname2mapid(mapname))< 0)
- return 0;
-
- if(!(index=mapindex_name2id(str)))
- return 0;
-
- switch(check_val){
- case 1:
- g = guild_search(check_ID);
- if (g){
- for( i=0; i < g->max_member; i++)
- {
- if(g->member[i].sd && g->member[i].sd->bl.m==m){
- pc_setpos(g->member[i].sd,index,x,y,CLR_TELEPORT);
- }
- }
- }
- break;
- case 2:
- p = party_search(check_ID);
- if(p){
- for(i=0;i<MAX_PARTY; i++){
- if(p->data[i].sd && p->data[i].sd->bl.m == m){
- pc_setpos(p->data[i].sd,index,x,y,CLR_TELEPORT);
- }
- }
- }
- break;
- default:
- map_foreachinmap(buildin_areawarp_sub,m,BL_PC,index,x,y,0,0);
- break;
- }
-
- return 0;
-}
-
-static int buildin_mobcount_sub(struct block_list *bl,va_list ap) // Added by RoVeRT
-{
- char *event=va_arg(ap,char *);
- struct mob_data *md = ((struct mob_data *)bl);
- if( md->status.hp > 0 && (!event || strcmp(event,md->npc_event) == 0) )
- return 1;
- return 0;
-}
-
-BUILDIN_FUNC(mobcount) // Added by RoVeRT
-{
- const char *mapname,*event;
- int16 m;
- mapname=script_getstr(st,2);
- event=script_getstr(st,3);
-
- if( strcmp(event, "all") == 0 )
- event = NULL;
- else
- check_event(st, event);
-
- if( strcmp(mapname, "this") == 0 ) {
- struct map_session_data *sd = script_rid2sd(st);
- if( sd )
- m = sd->bl.m;
- else {
- script_pushint(st,-1);
- return 0;
- }
- }
- else if( (m = map_mapname2mapid(mapname)) < 0 ) {
- script_pushint(st,-1);
- return 0;
- }
-
- if( map[m].flag.src4instance && map[m].instance_id == 0 && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 )
- {
- script_pushint(st,-1);
- return 0;
- }
-
- script_pushint(st,map_foreachinmap(buildin_mobcount_sub, m, BL_MOB, event));
-
- return 0;
-}
-
-BUILDIN_FUNC(marriage)
-{
- const char *partner=script_getstr(st,2);
- TBL_PC *sd=script_rid2sd(st);
- TBL_PC *p_sd=map_nick2sd(partner);
-
- if(sd==NULL || p_sd==NULL || pc_marriage(sd,p_sd) < 0){
- script_pushint(st,0);
- return 0;
- }
- script_pushint(st,1);
- return 0;
-}
-BUILDIN_FUNC(wedding_effect)
-{
- TBL_PC *sd=script_rid2sd(st);
- struct block_list *bl;
-
- if(sd==NULL) {
- bl=map_id2bl(st->oid);
- } else
- bl=&sd->bl;
- clif_wedding_effect(bl);
- return 0;
-}
-BUILDIN_FUNC(divorce)
-{
- TBL_PC *sd=script_rid2sd(st);
- if(sd==NULL || pc_divorce(sd) < 0){
- script_pushint(st,0);
- return 0;
- }
- script_pushint(st,1);
- return 0;
-}
-
-BUILDIN_FUNC(ispartneron)
-{
- TBL_PC *sd=script_rid2sd(st);
-
- if(sd==NULL || !pc_ismarried(sd) ||
- map_charid2sd(sd->status.partner_id) == NULL) {
- script_pushint(st,0);
- return 0;
- }
-
- script_pushint(st,1);
- return 0;
-}
-
-BUILDIN_FUNC(getpartnerid)
-{
- TBL_PC *sd=script_rid2sd(st);
- if (sd == NULL) {
- script_pushint(st,0);
- return 0;
- }
-
- script_pushint(st,sd->status.partner_id);
- return 0;
-}
-
-BUILDIN_FUNC(getchildid)
-{
- TBL_PC *sd=script_rid2sd(st);
- if (sd == NULL) {
- script_pushint(st,0);
- return 0;
- }
-
- script_pushint(st,sd->status.child);
- return 0;
-}
-
-BUILDIN_FUNC(getmotherid)
-{
- TBL_PC *sd=script_rid2sd(st);
- if (sd == NULL) {
- script_pushint(st,0);
- return 0;
- }
-
- script_pushint(st,sd->status.mother);
- return 0;
-}
-
-BUILDIN_FUNC(getfatherid)
-{
- TBL_PC *sd=script_rid2sd(st);
- if (sd == NULL) {
- script_pushint(st,0);
- return 0;
- }
-
- script_pushint(st,sd->status.father);
- return 0;
-}
-
-BUILDIN_FUNC(warppartner)
-{
- int x,y;
- unsigned short mapindex;
- const char *str;
- TBL_PC *sd=script_rid2sd(st);
- TBL_PC *p_sd=NULL;
-
- if(sd==NULL || !pc_ismarried(sd) ||
- (p_sd=map_charid2sd(sd->status.partner_id)) == NULL) {
- script_pushint(st,0);
- return 0;
- }
-
- str=script_getstr(st,2);
- x=script_getnum(st,3);
- y=script_getnum(st,4);
-
- mapindex = mapindex_name2id(str);
- if (mapindex) {
- pc_setpos(p_sd,mapindex,x,y,CLR_OUTSIGHT);
- script_pushint(st,1);
- } else
- script_pushint(st,0);
- return 0;
-}
-
-/*================================================
- * Script for Displaying MOB Information [Valaris]
- *------------------------------------------------*/
-BUILDIN_FUNC(strmobinfo)
-{
-
- int num=script_getnum(st,2);
- int class_=script_getnum(st,3);
-
- if(!mobdb_checkid(class_))
- {
- if (num < 3) //requested a string
- script_pushconststr(st,"");
- else
- script_pushint(st,0);
- return 0;
- }
-
- switch (num) {
- case 1: script_pushstrcopy(st,mob_db(class_)->name); break;
- case 2: script_pushstrcopy(st,mob_db(class_)->jname); break;
- case 3: script_pushint(st,mob_db(class_)->lv); break;
- case 4: script_pushint(st,mob_db(class_)->status.max_hp); break;
- case 5: script_pushint(st,mob_db(class_)->status.max_sp); break;
- case 6: script_pushint(st,mob_db(class_)->base_exp); break;
- case 7: script_pushint(st,mob_db(class_)->job_exp); break;
- default:
- script_pushint(st,0);
- break;
- }
- return 0;
-}
-
-/*==========================================
- * Summon guardians [Valaris]
- * guardian("<map name>",<x>,<y>,"<name to show>",<mob id>{,"<event label>"}{,<guardian index>}) -> <id>
- *------------------------------------------*/
-BUILDIN_FUNC(guardian)
-{
- int class_=0,x=0,y=0,guardian=0;
- const char *str,*map,*evt="";
- struct script_data *data;
- bool has_index = false;
-
- map =script_getstr(st,2);
- x =script_getnum(st,3);
- y =script_getnum(st,4);
- str =script_getstr(st,5);
- class_=script_getnum(st,6);
-
- if( script_hasdata(st,8) )
- {// "<event label>",<guardian index>
- evt=script_getstr(st,7);
- guardian=script_getnum(st,8);
- has_index = true;
- } else if( script_hasdata(st,7) ){
- data=script_getdata(st,7);
- get_val(st,data);
- if( data_isstring(data) )
- {// "<event label>"
- evt=script_getstr(st,7);
- } else if( data_isint(data) )
- {// <guardian index>
- guardian=script_getnum(st,7);
- has_index = true;
- } else {
- ShowError("script:guardian: invalid data type for argument #6 (from 1)\n");
- script_reportdata(data);
- return 1;
- }
- }
-
- check_event(st, evt);
- script_pushint(st, mob_spawn_guardian(map,x,y,str,class_,evt,guardian,has_index));
-
- return 0;
-}
-/*==========================================
- * Invisible Walls [Zephyrus]
- *------------------------------------------*/
-BUILDIN_FUNC(setwall)
-{
- const char *map, *name;
- int x, y, m, size, dir;
- bool shootable;
-
- map = script_getstr(st,2);
- x = script_getnum(st,3);
- y = script_getnum(st,4);
- size = script_getnum(st,5);
- dir = script_getnum(st,6);
- shootable = script_getnum(st,7);
- name = script_getstr(st,8);
-
- if( (m = map_mapname2mapid(map)) < 0 )
- return 0; // Invalid Map
-
- map_iwall_set(m, x, y, size, dir, shootable, name);
- return 0;
-}
-BUILDIN_FUNC(delwall)
-{
- const char *name = script_getstr(st,2);
- map_iwall_remove(name);
-
- return 0;
-}
-
-/// Retrieves various information about the specified guardian.
-///
-/// guardianinfo("<map_name>", <index>, <type>) -> <value>
-/// type: 0 - whether it is deployed or not
-/// 1 - maximum hp
-/// 2 - current hp
-///
-BUILDIN_FUNC(guardianinfo)
-{
- const char* mapname = mapindex_getmapname(script_getstr(st,2),NULL);
- int id = script_getnum(st,3);
- int type = script_getnum(st,4);
-
- struct guild_castle* gc = guild_mapname2gc(mapname);
- struct mob_data* gd;
-
- if( gc == NULL || id < 0 || id >= MAX_GUARDIANS )
- {
- script_pushint(st,-1);
- return 0;
- }
-
- if( type == 0 )
- script_pushint(st, gc->guardian[id].visible);
- else
- if( !gc->guardian[id].visible )
- script_pushint(st,-1);
- else
- if( (gd = map_id2md(gc->guardian[id].id)) == NULL )
- script_pushint(st,-1);
- else
- {
- if ( type == 1 ) script_pushint(st,gd->status.max_hp);
- else if( type == 2 ) script_pushint(st,gd->status.hp);
- else
- script_pushint(st,-1);
- }
-
- return 0;
-}
-
-/*==========================================
- * Get the item name by item_id or null
- *------------------------------------------*/
-BUILDIN_FUNC(getitemname)
-{
- int item_id=0;
- struct item_data *i_data;
- char *item_name;
- struct script_data *data;
-
- data=script_getdata(st,2);
- get_val(st,data);
-
- if( data_isstring(data) ){
- const char *name=conv_str(st,data);
- struct item_data *item_data = itemdb_searchname(name);
- if( item_data )
- item_id=item_data->nameid;
- }else
- item_id=conv_num(st,data);
-
- i_data = itemdb_exists(item_id);
- if (i_data == NULL)
- {
- script_pushconststr(st,"null");
- return 0;
- }
- item_name=(char *)aMalloc(ITEM_NAME_LENGTH*sizeof(char));
-
- memcpy(item_name, i_data->jname, ITEM_NAME_LENGTH);
- script_pushstr(st,item_name);
- return 0;
-}
-/*==========================================
- * Returns number of slots an item has. [Skotlex]
- *------------------------------------------*/
-BUILDIN_FUNC(getitemslots)
-{
- int item_id;
- struct item_data *i_data;
-
- item_id=script_getnum(st,2);
-
- i_data = itemdb_exists(item_id);
-
- if (i_data)
- script_pushint(st,i_data->slot);
- else
- script_pushint(st,-1);
- return 0;
-}
-
-// TODO: add matk here if needed/once we get rid of RENEWAL
-
-/*==========================================
- * Returns some values of an item [Lupus]
- * Price, Weight, etc...
- getiteminfo(itemID,n), where n
- 0 value_buy;
- 1 value_sell;
- 2 type;
- 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc..
- if = 0, then monsters don't drop it at all (rare or a quest item)
- if = -1, then this item is sold in NPC shops only
- 4 sex;
- 5 equip;
- 6 weight;
- 7 atk;
- 8 def;
- 9 range;
- 10 slot;
- 11 look;
- 12 elv;
- 13 wlv;
- 14 view id
- *------------------------------------------*/
-BUILDIN_FUNC(getiteminfo)
-{
- int item_id,n;
- int *item_arr;
- struct item_data *i_data;
-
- item_id = script_getnum(st,2);
- n = script_getnum(st,3);
- i_data = itemdb_exists(item_id);
-
- if (i_data && n>=0 && n<=14) {
- item_arr = (int*)&i_data->value_buy;
- script_pushint(st,item_arr[n]);
- } else
- script_pushint(st,-1);
- return 0;
-}
-
-/*==========================================
- * Set some values of an item [Lupus]
- * Price, Weight, etc...
- setiteminfo(itemID,n,Value), where n
- 0 value_buy;
- 1 value_sell;
- 2 type;
- 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc..
- if = 0, then monsters don't drop it at all (rare or a quest item)
- if = -1, then this item is sold in NPC shops only
- 4 sex;
- 5 equip;
- 6 weight;
- 7 atk;
- 8 def;
- 9 range;
- 10 slot;
- 11 look;
- 12 elv;
- 13 wlv;
- 14 view id
- * Returns Value or -1 if the wrong field's been set
- *------------------------------------------*/
-BUILDIN_FUNC(setiteminfo)
-{
- int item_id,n,value;
- int *item_arr;
- struct item_data *i_data;
-
- item_id = script_getnum(st,2);
- n = script_getnum(st,3);
- value = script_getnum(st,4);
- i_data = itemdb_exists(item_id);
-
- if (i_data && n>=0 && n<=14) {
- item_arr = (int*)&i_data->value_buy;
- item_arr[n] = value;
- script_pushint(st,value);
- } else
- script_pushint(st,-1);
- return 0;
-}
-
-/*==========================================
- * Returns value from equipped item slot n [Lupus]
- getequipcardid(num,slot)
- where
- num = eqip position slot
- slot = 0,1,2,3 (Card Slot N)
-
- This func returns CARD ID, 255,254,-255 (for card 0, if the item is produced)
- it's useful when you want to check item cards or if it's signed
- Useful for such quests as "Sign this refined item with players name" etc
- Hat[0] +4 -> Player's Hat[0] +4
- *------------------------------------------*/
-BUILDIN_FUNC(getequipcardid)
-{
- int i=-1,num,slot;
- TBL_PC *sd;
-
- num=script_getnum(st,2);
- slot=script_getnum(st,3);
- sd=script_rid2sd(st);
- if (num > 0 && num <= ARRAYLENGTH(equip))
- i=pc_checkequip(sd,equip[num-1]);
- if(i >= 0 && slot>=0 && slot<4)
- script_pushint(st,sd->status.inventory[i].card[slot]);
- else
- script_pushint(st,0);
-
- return 0;
-}
-
-/*==========================================
- * petskillbonus [Valaris] //Rewritten by [Skotlex]
- *------------------------------------------*/
-BUILDIN_FUNC(petskillbonus)
-{
- struct pet_data *pd;
-
- TBL_PC *sd=script_rid2sd(st);
-
- if(sd==NULL || sd->pd==NULL)
- return 0;
-
- pd=sd->pd;
- if (pd->bonus)
- { //Clear previous bonus
- if (pd->bonus->timer != INVALID_TIMER)
- delete_timer(pd->bonus->timer, pet_skill_bonus_timer);
- } else //init
- pd->bonus = (struct pet_bonus *) aMalloc(sizeof(struct pet_bonus));
-
- pd->bonus->type=script_getnum(st,2);
- pd->bonus->val=script_getnum(st,3);
- pd->bonus->duration=script_getnum(st,4);
- pd->bonus->delay=script_getnum(st,5);
-
- if (pd->state.skillbonus == 1)
- pd->state.skillbonus=0; // waiting state
-
- // wait for timer to start
- if (battle_config.pet_equip_required && pd->pet.equip == 0)
- pd->bonus->timer = INVALID_TIMER;
- else
- pd->bonus->timer = add_timer(gettick()+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0);
-
- return 0;
-}
-
-/*==========================================
- * pet looting [Valaris] //Rewritten by [Skotlex]
- *------------------------------------------*/
-BUILDIN_FUNC(petloot)
-{
- int max;
- struct pet_data *pd;
- TBL_PC *sd=script_rid2sd(st);
-
- if(sd==NULL || sd->pd==NULL)
- return 0;
-
- max=script_getnum(st,2);
-
- if(max < 1)
- max = 1; //Let'em loot at least 1 item.
- else if (max > MAX_PETLOOT_SIZE)
- max = MAX_PETLOOT_SIZE;
-
- pd = sd->pd;
- if (pd->loot != NULL)
- { //Release whatever was there already and reallocate memory
- pet_lootitem_drop(pd, pd->msd);
- aFree(pd->loot->item);
- }
- else
- pd->loot = (struct pet_loot *)aMalloc(sizeof(struct pet_loot));
-
- pd->loot->item = (struct item *)aCalloc(max,sizeof(struct item));
-
- pd->loot->max=max;
- pd->loot->count = 0;
- pd->loot->weight = 0;
-
- return 0;
-}
-/*==========================================
- * Set arrays with info of all sd inventory :
- * @inventorylist_id, @inventorylist_amount, @inventorylist_equip,
- * @inventorylist_refine, @inventorylist_identify, @inventorylist_attribute,
- * @inventorylist_card(0..3), @inventorylist_expire
- * @inventorylist_count = scalar
- *------------------------------------------*/
-BUILDIN_FUNC(getinventorylist)
-{
- TBL_PC *sd=script_rid2sd(st);
- char card_var[NAME_LENGTH];
-
- int i,j=0,k;
- if(!sd) return 0;
- for(i=0;i<MAX_INVENTORY;i++){
- if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0){
- pc_setreg(sd,reference_uid(add_str("@inventorylist_id"), j),sd->status.inventory[i].nameid);
- pc_setreg(sd,reference_uid(add_str("@inventorylist_amount"), j),sd->status.inventory[i].amount);
- pc_setreg(sd,reference_uid(add_str("@inventorylist_equip"), j),sd->status.inventory[i].equip);
- pc_setreg(sd,reference_uid(add_str("@inventorylist_refine"), j),sd->status.inventory[i].refine);
- pc_setreg(sd,reference_uid(add_str("@inventorylist_identify"), j),sd->status.inventory[i].identify);
- pc_setreg(sd,reference_uid(add_str("@inventorylist_attribute"), j),sd->status.inventory[i].attribute);
- for (k = 0; k < MAX_SLOTS; k++)
- {
- sprintf(card_var, "@inventorylist_card%d",k+1);
- pc_setreg(sd,reference_uid(add_str(card_var), j),sd->status.inventory[i].card[k]);
- }
- pc_setreg(sd,reference_uid(add_str("@inventorylist_expire"), j),sd->status.inventory[i].expire_time);
- j++;
- }
- }
- pc_setreg(sd,add_str("@inventorylist_count"),j);
- return 0;
-}
-
-BUILDIN_FUNC(getskilllist)
-{
- TBL_PC *sd=script_rid2sd(st);
- int i,j=0;
- if(!sd) return 0;
- for(i=0;i<MAX_SKILL;i++){
- if(sd->status.skill[i].id > 0 && sd->status.skill[i].lv > 0){
- pc_setreg(sd,reference_uid(add_str("@skilllist_id"), j),sd->status.skill[i].id);
- pc_setreg(sd,reference_uid(add_str("@skilllist_lv"), j),sd->status.skill[i].lv);
- pc_setreg(sd,reference_uid(add_str("@skilllist_flag"), j),sd->status.skill[i].flag);
- j++;
- }
- }
- pc_setreg(sd,add_str("@skilllist_count"),j);
- return 0;
-}
-
-BUILDIN_FUNC(clearitem)
-{
- TBL_PC *sd=script_rid2sd(st);
- int i;
- if(sd==NULL) return 0;
- for (i=0; i<MAX_INVENTORY; i++) {
- if (sd->status.inventory[i].amount) {
- pc_delitem(sd, i, sd->status.inventory[i].amount, 0, 0, LOG_TYPE_SCRIPT);
- }
- }
- return 0;
-}
-
-/*==========================================
- * Disguise Player (returns Mob/NPC ID if success, 0 on fail)
- *------------------------------------------*/
-BUILDIN_FUNC(disguise)
-{
- int id;
- TBL_PC* sd = script_rid2sd(st);
- if (sd == NULL) return 0;
-
- id = script_getnum(st,2);
-
- if (mobdb_checkid(id) || npcdb_checkid(id)) {
- pc_disguise(sd, id);
- script_pushint(st,id);
- } else
- script_pushint(st,0);
-
- return 0;
-}
-
-/*==========================================
- * Undisguise Player (returns 1 if success, 0 on fail)
- *------------------------------------------*/
-BUILDIN_FUNC(undisguise)
-{
- TBL_PC* sd = script_rid2sd(st);
- if (sd == NULL) return 0;
-
- if (sd->disguise) {
- pc_disguise(sd, 0);
- script_pushint(st,0);
- } else {
- script_pushint(st,1);
- }
- return 0;
-}
-
-/*==========================================
- * Transform a bl to another _class,
- * @type unused
- *------------------------------------------*/
-BUILDIN_FUNC(classchange)
-{
- int _class,type;
- struct block_list *bl=map_id2bl(st->oid);
-
- if(bl==NULL) return 0;
-
- _class=script_getnum(st,2);
- type=script_getnum(st,3);
- clif_class_change(bl,_class,type);
- return 0;
-}
-
-/*==========================================
- * Display an effect
- *------------------------------------------*/
-BUILDIN_FUNC(misceffect)
-{
- int type;
-
- type=script_getnum(st,2);
- if(st->oid && st->oid != fake_nd->bl.id) {
- struct block_list *bl = map_id2bl(st->oid);
- if (bl)
- clif_specialeffect(bl,type,AREA);
- } else{
- TBL_PC *sd=script_rid2sd(st);
- if(sd)
- clif_specialeffect(&sd->bl,type,AREA);
- }
- return 0;
-}
-/*==========================================
- * Play a BGM on a single client [Rikter/Yommy]
- *------------------------------------------*/
-BUILDIN_FUNC(playBGM)
-{
- const char* name;
- struct map_session_data* sd;
-
- if( ( sd = script_rid2sd(st) ) != NULL )
- {
- name = script_getstr(st,2);
-
- clif_playBGM(sd, name);
- }
-
- return 0;
-}
-
-static int playBGM_sub(struct block_list* bl,va_list ap)
-{
- const char* name = va_arg(ap,const char*);
-
- clif_playBGM(BL_CAST(BL_PC, bl), name);
-
- return 0;
-}
-
-static int playBGM_foreachpc_sub(struct map_session_data* sd, va_list args)
-{
- const char* name = va_arg(args, const char*);
-
- clif_playBGM(sd, name);
- return 0;
-}
-
-/*==========================================
- * Play a BGM on multiple client [Rikter/Yommy]
- *------------------------------------------*/
-BUILDIN_FUNC(playBGMall)
-{
- const char* name;
-
- name = script_getstr(st,2);
-
- if( script_hasdata(st,7) )
- {// specified part of map
- const char* map = script_getstr(st,3);
- int x0 = script_getnum(st,4);
- int y0 = script_getnum(st,5);
- int x1 = script_getnum(st,6);
- int y1 = script_getnum(st,7);
-
- map_foreachinarea(playBGM_sub, map_mapname2mapid(map), x0, y0, x1, y1, BL_PC, name);
- }
- else if( script_hasdata(st,3) )
- {// entire map
- const char* map = script_getstr(st,3);
-
- map_foreachinmap(playBGM_sub, map_mapname2mapid(map), BL_PC, name);
- }
- else
- {// entire server
- map_foreachpc(&playBGM_foreachpc_sub, name);
- }
-
- return 0;
-}
-
-/*==========================================
- * Play a .wav sound for sd
- *------------------------------------------*/
-BUILDIN_FUNC(soundeffect)
-{
- TBL_PC* sd = script_rid2sd(st);
- const char* name = script_getstr(st,2);
- int type = script_getnum(st,3);
-
- if(sd)
- {
- clif_soundeffect(sd,&sd->bl,name,type);
- }
- return 0;
-}
-
-int soundeffect_sub(struct block_list* bl,va_list ap)
-{
- char* name = va_arg(ap,char*);
- int type = va_arg(ap,int);
-
- clif_soundeffect((TBL_PC *)bl, bl, name, type);
-
- return 0;
-}
-
-/*==========================================
- * Play a sound effect (.wav) on multiple clients
- * soundeffectall "<filepath>",<type>{,"<map name>"}{,<x0>,<y0>,<x1>,<y1>};
- *------------------------------------------*/
-BUILDIN_FUNC(soundeffectall)
-{
- struct block_list* bl;
- const char* name;
- int type;
-
- bl = (st->rid) ? &(script_rid2sd(st)->bl) : map_id2bl(st->oid);
- if (!bl)
- return 0;
-
- name = script_getstr(st,2);
- type = script_getnum(st,3);
-
- //FIXME: enumerating map squares (map_foreach) is slower than enumerating the list of online players (map_foreachpc?) [ultramage]
-
- if(!script_hasdata(st,4))
- { // area around
- clif_soundeffectall(bl, name, type, AREA);
- }
- else
- if(!script_hasdata(st,5))
- { // entire map
- const char* map = script_getstr(st,4);
- map_foreachinmap(soundeffect_sub, map_mapname2mapid(map), BL_PC, name, type);
- }
- else
- if(script_hasdata(st,8))
- { // specified part of map
- const char* map = script_getstr(st,4);
- int x0 = script_getnum(st,5);
- int y0 = script_getnum(st,6);
- int x1 = script_getnum(st,7);
- int y1 = script_getnum(st,8);
- map_foreachinarea(soundeffect_sub, map_mapname2mapid(map), x0, y0, x1, y1, BL_PC, name, type);
- }
- else
- {
- ShowError("buildin_soundeffectall: insufficient arguments for specific area broadcast.\n");
- }
-
- return 0;
-}
-/*==========================================
- * pet status recovery [Valaris] / Rewritten by [Skotlex]
- *------------------------------------------*/
-BUILDIN_FUNC(petrecovery)
-{
- struct pet_data *pd;
- TBL_PC *sd=script_rid2sd(st);
-
- if(sd==NULL || sd->pd==NULL)
- return 0;
-
- pd=sd->pd;
-
- if (pd->recovery)
- { //Halt previous bonus
- if (pd->recovery->timer != INVALID_TIMER)
- delete_timer(pd->recovery->timer, pet_recovery_timer);
- } else //Init
- pd->recovery = (struct pet_recovery *)aMalloc(sizeof(struct pet_recovery));
-
- pd->recovery->type = (sc_type)script_getnum(st,2);
- pd->recovery->delay = script_getnum(st,3);
- pd->recovery->timer = INVALID_TIMER;
-
- return 0;
-}
-
-/*==========================================
- * pet healing [Valaris] //Rewritten by [Skotlex]
- *------------------------------------------*/
-BUILDIN_FUNC(petheal)
-{
- struct pet_data *pd;
- TBL_PC *sd=script_rid2sd(st);
-
- if(sd==NULL || sd->pd==NULL)
- return 0;
-
- pd=sd->pd;
- if (pd->s_skill)
- { //Clear previous skill
- if (pd->s_skill->timer != INVALID_TIMER)
- {
- if (pd->s_skill->id)
- delete_timer(pd->s_skill->timer, pet_skill_support_timer);
- else
- delete_timer(pd->s_skill->timer, pet_heal_timer);
- }
- } else //init memory
- pd->s_skill = (struct pet_skill_support *) aMalloc(sizeof(struct pet_skill_support));
-
- pd->s_skill->id=0; //This id identifies that it IS petheal rather than pet_skillsupport
- //Use the lv as the amount to heal
- pd->s_skill->lv=script_getnum(st,2);
- pd->s_skill->delay=script_getnum(st,3);
- pd->s_skill->hp=script_getnum(st,4);
- pd->s_skill->sp=script_getnum(st,5);
-
- //Use delay as initial offset to avoid skill/heal exploits
- if (battle_config.pet_equip_required && pd->pet.equip == 0)
- pd->s_skill->timer = INVALID_TIMER;
- else
- pd->s_skill->timer = add_timer(gettick()+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0);
-
- return 0;
-}
-
-/*==========================================
- * pet attack skills [Valaris] //Rewritten by [Skotlex]
- *------------------------------------------*/
-/// petskillattack <skill id>,<level>,<rate>,<bonusrate>
-/// petskillattack "<skill name>",<level>,<rate>,<bonusrate>
-BUILDIN_FUNC(petskillattack)
-{
- struct pet_data *pd;
- TBL_PC *sd=script_rid2sd(st);
-
- if(sd==NULL || sd->pd==NULL)
- return 0;
-
- pd=sd->pd;
- if (pd->a_skill == NULL)
- pd->a_skill = (struct pet_skill_attack *)aMalloc(sizeof(struct pet_skill_attack));
-
- pd->a_skill->id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
- pd->a_skill->lv=script_getnum(st,3);
- pd->a_skill->div_ = 0;
- pd->a_skill->rate=script_getnum(st,4);
- pd->a_skill->bonusrate=script_getnum(st,5);
-
- return 0;
-}
-
-/*==========================================
- * pet attack skills [Valaris]
- *------------------------------------------*/
-/// petskillattack2 <skill id>,<level>,<div>,<rate>,<bonusrate>
-/// petskillattack2 "<skill name>",<level>,<div>,<rate>,<bonusrate>
-BUILDIN_FUNC(petskillattack2)
-{
- struct pet_data *pd;
- TBL_PC *sd=script_rid2sd(st);
-
- if(sd==NULL || sd->pd==NULL)
- return 0;
-
- pd=sd->pd;
- if (pd->a_skill == NULL)
- pd->a_skill = (struct pet_skill_attack *)aMalloc(sizeof(struct pet_skill_attack));
-
- pd->a_skill->id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
- pd->a_skill->lv=script_getnum(st,3);
- pd->a_skill->div_ = script_getnum(st,4);
- pd->a_skill->rate=script_getnum(st,5);
- pd->a_skill->bonusrate=script_getnum(st,6);
-
- return 0;
-}
-
-/*==========================================
- * pet support skills [Skotlex]
- *------------------------------------------*/
-/// petskillsupport <skill id>,<level>,<delay>,<hp>,<sp>
-/// petskillsupport "<skill name>",<level>,<delay>,<hp>,<sp>
-BUILDIN_FUNC(petskillsupport)
-{
- struct pet_data *pd;
- TBL_PC *sd=script_rid2sd(st);
-
- if(sd==NULL || sd->pd==NULL)
- return 0;
-
- pd=sd->pd;
- if (pd->s_skill)
- { //Clear previous skill
- if (pd->s_skill->timer != INVALID_TIMER)
- {
- if (pd->s_skill->id)
- delete_timer(pd->s_skill->timer, pet_skill_support_timer);
- else
- delete_timer(pd->s_skill->timer, pet_heal_timer);
- }
- } else //init memory
- pd->s_skill = (struct pet_skill_support *) aMalloc(sizeof(struct pet_skill_support));
-
- pd->s_skill->id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
- pd->s_skill->lv=script_getnum(st,3);
- pd->s_skill->delay=script_getnum(st,4);
- pd->s_skill->hp=script_getnum(st,5);
- pd->s_skill->sp=script_getnum(st,6);
-
- //Use delay as initial offset to avoid skill/heal exploits
- if (battle_config.pet_equip_required && pd->pet.equip == 0)
- pd->s_skill->timer = INVALID_TIMER;
- else
- pd->s_skill->timer = add_timer(gettick()+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0);
-
- return 0;
-}
-
-/*==========================================
- * Scripted skill effects [Celest]
- *------------------------------------------*/
-/// skilleffect <skill id>,<level>
-/// skilleffect "<skill name>",<level>
-BUILDIN_FUNC(skilleffect)
-{
- TBL_PC *sd;
-
- uint16 skill_id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
- uint16 skill_lv=script_getnum(st,3);
- sd=script_rid2sd(st);
-
- clif_skill_nodamage(&sd->bl,&sd->bl,skill_id,skill_lv,1);
-
- return 0;
-}
-
-/*==========================================
- * NPC skill effects [Valaris]
- *------------------------------------------*/
-/// npcskilleffect <skill id>,<level>,<x>,<y>
-/// npcskilleffect "<skill name>",<level>,<x>,<y>
-BUILDIN_FUNC(npcskilleffect)
-{
- struct block_list *bl= map_id2bl(st->oid);
-
- uint16 skill_id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
- uint16 skill_lv=script_getnum(st,3);
- int x=script_getnum(st,4);
- int y=script_getnum(st,5);
-
- if (bl)
- clif_skill_poseffect(bl,skill_id,skill_lv,x,y,gettick());
-
- return 0;
-}
-
-/*==========================================
- * Special effects [Valaris]
- *------------------------------------------*/
-BUILDIN_FUNC(specialeffect)
-{
- struct block_list *bl=map_id2bl(st->oid);
- int type = script_getnum(st,2);
- enum send_target target = script_hasdata(st,3) ? (send_target)script_getnum(st,3) : AREA;
-
- if(bl==NULL)
- return 0;
-
- if( script_hasdata(st,4) )
- {
- TBL_NPC *nd = npc_name2id(script_getstr(st,4));
- if(nd)
- clif_specialeffect(&nd->bl, type, target);
- }
- else
- {
- if (target == SELF) {
- TBL_PC *sd=script_rid2sd(st);
- if (sd)
- clif_specialeffect_single(bl,type,sd->fd);
- } else {
- clif_specialeffect(bl, type, target);
- }
- }
-
- return 0;
-}
-
-BUILDIN_FUNC(specialeffect2)
-{
- TBL_PC *sd=script_rid2sd(st);
- int type = script_getnum(st,2);
- enum send_target target = script_hasdata(st,3) ? (send_target)script_getnum(st,3) : AREA;
-
- if( script_hasdata(st,4) )
- sd = map_nick2sd(script_getstr(st,4));
-
- if (sd)
- clif_specialeffect(&sd->bl, type, target);
-
- return 0;
-}
-
-/*==========================================
- * Nude [Valaris]
- *------------------------------------------*/
-BUILDIN_FUNC(nude)
-{
- TBL_PC *sd = script_rid2sd(st);
- int i, calcflag = 0;
-
- if( sd == NULL )
- return 0;
-
- for( i = 0 ; i < EQI_MAX; i++ ) {
- if( sd->equip_index[ i ] >= 0 ) {
- if( !calcflag )
- calcflag = 1;
- pc_unequipitem( sd , sd->equip_index[ i ] , 2);
- }
- }
-
- if( calcflag )
- status_calc_pc(sd,0);
-
- return 0;
-}
-
-/*==========================================
- * gmcommand [MouseJstr]
- *------------------------------------------*/
-BUILDIN_FUNC(atcommand)
-{
- TBL_PC dummy_sd;
- TBL_PC* sd;
- int fd;
- const char* cmd;
-
- cmd = script_getstr(st,2);
-
- if (st->rid) {
- sd = script_rid2sd(st);
- fd = sd->fd;
- } else { //Use a dummy character.
- sd = &dummy_sd;
- fd = 0;
-
- memset(&dummy_sd, 0, sizeof(TBL_PC));
- if (st->oid)
- {
- struct block_list* bl = map_id2bl(st->oid);
- memcpy(&dummy_sd.bl, bl, sizeof(struct block_list));
- if (bl->type == BL_NPC)
- safestrncpy(dummy_sd.status.name, ((TBL_NPC*)bl)->name, NAME_LENGTH);
- }
- }
-
- if (!is_atcommand(fd, sd, cmd, 0)) {
- ShowWarning("script: buildin_atcommand: failed to execute command '%s'\n", cmd);
- script_reportsrc(st);
- return 1;
- }
-
- return 0;
-}
-
-/*==========================================
- * Displays a message for the player only (like system messages like "you got an apple" )
- *------------------------------------------*/
-BUILDIN_FUNC(dispbottom)
-{
- TBL_PC *sd=script_rid2sd(st);
- const char *message;
- message=script_getstr(st,2);
- if(sd)
- clif_disp_onlyself(sd,message,(int)strlen(message));
- return 0;
-}
-
-/*==========================================
- * All The Players Full Recovery
- * (HP/SP full restore and resurrect if need)
- *------------------------------------------*/
-BUILDIN_FUNC(recovery)
-{
- TBL_PC* sd;
- struct s_mapiterator* iter;
-
- iter = mapit_getallusers();
- for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) )
- {
- if(pc_isdead(sd))
- status_revive(&sd->bl, 100, 100);
- else
- status_percent_heal(&sd->bl, 100, 100);
- clif_displaymessage(sd->fd,msg_txt(680));
- }
- mapit_free(iter);
- return 0;
-}
-/*==========================================
- * Get your pet info: getpetinfo(n)
- * n -> 0:pet_id 1:pet_class 2:pet_name
- * 3:friendly 4:hungry, 5: rename flag.
- *------------------------------------------*/
-BUILDIN_FUNC(getpetinfo)
-{
- TBL_PC *sd=script_rid2sd(st);
- TBL_PET *pd;
- int type=script_getnum(st,2);
-
- if(!sd || !sd->pd) {
- if (type == 2)
- script_pushconststr(st,"null");
- else
- script_pushint(st,0);
- return 0;
- }
- pd = sd->pd;
- switch(type){
- case 0: script_pushint(st,pd->pet.pet_id); break;
- case 1: script_pushint(st,pd->pet.class_); break;
- case 2: script_pushstrcopy(st,pd->pet.name); break;
- case 3: script_pushint(st,pd->pet.intimate); break;
- case 4: script_pushint(st,pd->pet.hungry); break;
- case 5: script_pushint(st,pd->pet.rename_flag); break;
- default:
- script_pushint(st,0);
- break;
- }
- return 0;
-}
-
-/*==========================================
- * Get your homunculus info: gethominfo(n)
- * n -> 0:hom_id 1:class 2:name
- * 3:friendly 4:hungry, 5: rename flag.
- * 6: level
- *------------------------------------------*/
-BUILDIN_FUNC(gethominfo)
-{
- TBL_PC *sd=script_rid2sd(st);
- TBL_HOM *hd;
- int type=script_getnum(st,2);
-
- hd = sd?sd->hd:NULL;
- if(!merc_is_hom_active(hd))
- {
- if (type == 2)
- script_pushconststr(st,"null");
- else
- script_pushint(st,0);
- return 0;
- }
-
- switch(type){
- case 0: script_pushint(st,hd->homunculus.hom_id); break;
- case 1: script_pushint(st,hd->homunculus.class_); break;
- case 2: script_pushstrcopy(st,hd->homunculus.name); break;
- case 3: script_pushint(st,hd->homunculus.intimacy); break;
- case 4: script_pushint(st,hd->homunculus.hunger); break;
- case 5: script_pushint(st,hd->homunculus.rename_flag); break;
- case 6: script_pushint(st,hd->homunculus.level); break;
- default:
- script_pushint(st,0);
- break;
- }
- return 0;
-}
-
-/// Retrieves information about character's mercenary
-/// getmercinfo <type>[,<char id>];
-BUILDIN_FUNC(getmercinfo)
-{
- int type, char_id;
- struct map_session_data* sd;
- struct mercenary_data* md;
-
- type = script_getnum(st,2);
-
- if( script_hasdata(st,3) )
- {
- char_id = script_getnum(st,3);
-
- if( ( sd = map_charid2sd(char_id) ) == NULL )
- {
- ShowError("buildin_getmercinfo: No such character (char_id=%d).\n", char_id);
- script_pushnil(st);
- return 1;
- }
- }
- else
- {
- if( ( sd = script_rid2sd(st) ) == NULL )
- {
- script_pushnil(st);
- return 0;
- }
- }
-
- md = ( sd->status.mer_id && sd->md ) ? sd->md : NULL;
-
- switch( type )
- {
- case 0: script_pushint(st,md ? md->mercenary.mercenary_id : 0); break;
- case 1: script_pushint(st,md ? md->mercenary.class_ : 0); break;
- case 2:
- if( md )
- script_pushstrcopy(st,md->db->name);
- else
- script_pushconststr(st,"");
- break;
- case 3: script_pushint(st,md ? mercenary_get_faith(md) : 0); break;
- case 4: script_pushint(st,md ? mercenary_get_calls(md) : 0); break;
- case 5: script_pushint(st,md ? md->mercenary.kill_count : 0); break;
- case 6: script_pushint(st,md ? mercenary_get_lifetime(md) : 0); break;
- case 7: script_pushint(st,md ? md->db->lv : 0); break;
- default:
- ShowError("buildin_getmercinfo: Invalid type %d (char_id=%d).\n", type, sd->status.char_id);
- script_pushnil(st);
- return 1;
- }
-
- return 0;
-}
-
-/*==========================================
- * Shows wether your inventory(and equips) contain
- selected card or not.
- checkequipedcard(4001);
- *------------------------------------------*/
-BUILDIN_FUNC(checkequipedcard)
-{
- TBL_PC *sd=script_rid2sd(st);
-
- if(sd){
- int n,i,c=0;
- c=script_getnum(st,2);
-
- for(i=0;i<MAX_INVENTORY;i++){
- if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount && sd->inventory_data[i]){
- if (itemdb_isspecial(sd->status.inventory[i].card[0]))
- continue;
- for(n=0;n<sd->inventory_data[i]->slot;n++){
- if(sd->status.inventory[i].card[n]==c){
- script_pushint(st,1);
- return 0;
- }
- }
- }
- }
- }
- script_pushint(st,0);
- return 0;
-}
-
-BUILDIN_FUNC(jump_zero)
-{
- int sel;
- sel=script_getnum(st,2);
- if(!sel) {
- int pos;
- if( !data_islabel(script_getdata(st,3)) ){
- ShowError("script: jump_zero: not label !\n");
- st->state=END;
- return 1;
- }
-
- pos=script_getnum(st,3);
- st->pos=pos;
- st->state=GOTO;
- }
- return 0;
-}
-
-/*==========================================
- * movenpc [MouseJstr]
- *------------------------------------------*/
-BUILDIN_FUNC(movenpc)
-{
- TBL_NPC *nd = NULL;
- const char *npc;
- int x,y;
-
- npc = script_getstr(st,2);
- x = script_getnum(st,3);
- y = script_getnum(st,4);
-
- if ((nd = npc_name2id(npc)) == NULL)
- return -1;
-
- if (script_hasdata(st,5))
- nd->ud.dir = script_getnum(st,5) % 8;
- npc_movenpc(nd, x, y);
- return 0;
-}
-
-/*==========================================
- * message [MouseJstr]
- *------------------------------------------*/
-BUILDIN_FUNC(message)
-{
- const char *msg,*player;
- TBL_PC *pl_sd = NULL;
-
- player = script_getstr(st,2);
- msg = script_getstr(st,3);
-
- if((pl_sd=map_nick2sd((char *) player)) == NULL)
- return 0;
- clif_displaymessage(pl_sd->fd, msg);
-
- return 0;
-}
-
-/*==========================================
- * npctalk (sends message to surrounding area)
- *------------------------------------------*/
-BUILDIN_FUNC(npctalk)
-{
- const char* str;
- char name[NAME_LENGTH], message[256];
-
- struct npc_data* nd = (struct npc_data *)map_id2bl(st->oid);
- str = script_getstr(st,2);
-
- if(nd)
- {
- safestrncpy(name, nd->name, sizeof(name));
- strtok(name, "#"); // discard extra name identifier if present
- safesnprintf(message, sizeof(message), "%s : %s", name, str);
- clif_message(&nd->bl, message);
- }
-
- return 0;
-}
-
-// change npc walkspeed [Valaris]
-BUILDIN_FUNC(npcspeed)
-{
- struct npc_data* nd;
- int speed;
-
- speed = script_getnum(st,2);
- nd =(struct npc_data *)map_id2bl(st->oid);
-
- if( nd )
- {
- nd->speed = speed;
- nd->ud.state.speed_changed = 1;
- }
-
- return 0;
-}
-// make an npc walk to a position [Valaris]
-BUILDIN_FUNC(npcwalkto)
-{
- struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
- int x=0,y=0;
-
- x=script_getnum(st,2);
- y=script_getnum(st,3);
-
- if(nd) {
- unit_walktoxy(&nd->bl,x,y,0);
- }
-
- return 0;
-}
-// stop an npc's movement [Valaris]
-BUILDIN_FUNC(npcstop)
-{
- struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
-
- if(nd) {
- unit_stop_walking(&nd->bl,1|4);
- }
-
- return 0;
-}
-
-
-/*==========================================
- * getlook char info. getlook(arg)
- *------------------------------------------*/
-BUILDIN_FUNC(getlook)
-{
- int type,val;
- TBL_PC *sd;
- sd=script_rid2sd(st);
-
- type=script_getnum(st,2);
- val=-1;
- switch(type) {
- case LOOK_HAIR: val=sd->status.hair; break; //1
- case LOOK_WEAPON: val=sd->status.weapon; break; //2
- case LOOK_HEAD_BOTTOM: val=sd->status.head_bottom; break; //3
- case LOOK_HEAD_TOP: val=sd->status.head_top; break; //4
- case LOOK_HEAD_MID: val=sd->status.head_mid; break; //5
- case LOOK_HAIR_COLOR: val=sd->status.hair_color; break; //6
- case LOOK_CLOTHES_COLOR: val=sd->status.clothes_color; break; //7
- case LOOK_SHIELD: val=sd->status.shield; break; //8
- case LOOK_SHOES: break; //9
- }
-
- script_pushint(st,val);
- return 0;
-}
-
-/*==========================================
- * get char save point. argument: 0- map name, 1- x, 2- y
- *------------------------------------------*/
-BUILDIN_FUNC(getsavepoint)
-{
- TBL_PC* sd;
- int type;
-
- sd = script_rid2sd(st);
- if (sd == NULL) {
- script_pushint(st,0);
- return 0;
- }
-
- type = script_getnum(st,2);
-
- switch(type) {
- case 0: script_pushstrcopy(st,mapindex_id2name(sd->status.save_point.map)); break;
- case 1: script_pushint(st,sd->status.save_point.x); break;
- case 2: script_pushint(st,sd->status.save_point.y); break;
- default:
- script_pushint(st,0);
- break;
- }
- return 0;
-}
-
-/*==========================================
- * Get position for char/npc/pet/mob objects. Added by Lorky
- *
- * int getMapXY(MapName$,MapX,MapY,type,[CharName$]);
- * where type:
- * MapName$ - String variable for output map name
- * MapX - Integer variable for output coord X
- * MapY - Integer variable for output coord Y
- * type - type of object
- * 0 - Character coord
- * 1 - NPC coord
- * 2 - Pet coord
- * 3 - Mob coord (not released)
- * 4 - Homun coord
- * 5 - Mercenary coord
- * 6 - Elemental coord
- * CharName$ - Name object. If miss or "this" the current object
- *
- * Return:
- * 0 - success
- * -1 - some error, MapName$,MapX,MapY contains unknown value.
- *------------------------------------------*/
-BUILDIN_FUNC(getmapxy)
-{
- struct block_list *bl = NULL;
- TBL_PC *sd=NULL;
-
- int num;
- const char *name;
- char prefix;
-
- int x,y,type;
- char mapname[MAP_NAME_LENGTH];
-
- if( !data_isreference(script_getdata(st,2)) ){
- ShowWarning("script: buildin_getmapxy: not mapname variable\n");
- script_pushint(st,-1);
- return 1;
- }
- if( !data_isreference(script_getdata(st,3)) ){
- ShowWarning("script: buildin_getmapxy: not mapx variable\n");
- script_pushint(st,-1);
- return 1;
- }
- if( !data_isreference(script_getdata(st,4)) ){
- ShowWarning("script: buildin_getmapxy: not mapy variable\n");
- script_pushint(st,-1);
- return 1;
- }
-
- // Possible needly check function parameters on C_STR,C_INT,C_INT
- type=script_getnum(st,5);
-
- switch (type){
- case 0: //Get Character Position
- if( script_hasdata(st,6) )
- sd=map_nick2sd(script_getstr(st,6));
- else
- sd=script_rid2sd(st);
-
- if (sd)
- bl = &sd->bl;
- break;
- case 1: //Get NPC Position
- if( script_hasdata(st,6) )
- {
- struct npc_data *nd;
- nd=npc_name2id(script_getstr(st,6));
- if (nd)
- bl = &nd->bl;
- } else //In case the origin is not an npc?
- bl=map_id2bl(st->oid);
- break;
- case 2: //Get Pet Position
- if(script_hasdata(st,6))
- sd=map_nick2sd(script_getstr(st,6));
- else
- sd=script_rid2sd(st);
-
- if (sd && sd->pd)
- bl = &sd->pd->bl;
- break;
- case 3: //Get Mob Position
- break; //Not supported?
- case 4: //Get Homun Position
- if(script_hasdata(st,6))
- sd=map_nick2sd(script_getstr(st,6));
- else
- sd=script_rid2sd(st);
-
- if (sd && sd->hd)
- bl = &sd->hd->bl;
- break;
- case 5: //Get Mercenary Position
- if(script_hasdata(st,6))
- sd=map_nick2sd(script_getstr(st,6));
- else
- sd=script_rid2sd(st);
-
- if (sd && sd->md)
- bl = &sd->md->bl;
- break;
- case 6: //Get Elemental Position
- if(script_hasdata(st,6))
- sd=map_nick2sd(script_getstr(st,6));
- else
- sd=script_rid2sd(st);
-
- if (sd && sd->ed)
- bl = &sd->ed->bl;
- break;
- default:
- ShowWarning("script: buildin_getmapxy: Invalid type %d\n", type);
- script_pushint(st,-1);
- return 1;
- }
- if (!bl) { //No object found.
- script_pushint(st,-1);
- return 0;
- }
-
- x= bl->x;
- y= bl->y;
- safestrncpy(mapname, map[bl->m].name, MAP_NAME_LENGTH);
-
- //Set MapName$
- num=st->stack->stack_data[st->start+2].u.num;
- name=get_str(num&0x00ffffff);
- prefix=*name;
-
- if(not_server_variable(prefix))
- sd=script_rid2sd(st);
- else
- sd=NULL;
- set_reg(st,sd,num,name,(void*)mapname,script_getref(st,2));
-
- //Set MapX
- num=st->stack->stack_data[st->start+3].u.num;
- name=get_str(num&0x00ffffff);
- prefix=*name;
-
- if(not_server_variable(prefix))
- sd=script_rid2sd(st);
- else
- sd=NULL;
- set_reg(st,sd,num,name,(void*)__64BPRTSIZE(x),script_getref(st,3));
-
- //Set MapY
- num=st->stack->stack_data[st->start+4].u.num;
- name=get_str(num&0x00ffffff);
- prefix=*name;
-
- if(not_server_variable(prefix))
- sd=script_rid2sd(st);
- else
- sd=NULL;
- set_reg(st,sd,num,name,(void*)__64BPRTSIZE(y),script_getref(st,4));
-
- //Return Success value
- script_pushint(st,0);
- return 0;
-}
-
-/*==========================================
- * Allows player to write NPC logs (i.e. Bank NPC, etc) [Lupus]
- *------------------------------------------*/
-BUILDIN_FUNC(logmes)
-{
- const char *str;
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 1;
-
- str = script_getstr(st,2);
- log_npc(sd,str);
- return 0;
-}
-
-BUILDIN_FUNC(summon)
-{
- int _class, timeout=0;
- const char *str,*event="";
- TBL_PC *sd;
- struct mob_data *md;
- int tick = gettick();
-
- sd=script_rid2sd(st);
- if (!sd) return 0;
-
- str =script_getstr(st,2);
- _class=script_getnum(st,3);
- if( script_hasdata(st,4) )
- timeout=script_getnum(st,4);
- if( script_hasdata(st,5) ){
- event=script_getstr(st,5);
- check_event(st, event);
- }
-
- clif_skill_poseffect(&sd->bl,AM_CALLHOMUN,1,sd->bl.x,sd->bl.y,tick);
-
- md = mob_once_spawn_sub(&sd->bl, sd->bl.m, sd->bl.x, sd->bl.y, str, _class, event, SZ_SMALL, AI_NONE);
- if (md) {
- md->master_id=sd->bl.id;
- md->special_state.ai = AI_ATTACK;
- if( md->deletetimer != INVALID_TIMER )
- delete_timer(md->deletetimer, mob_timer_delete);
- md->deletetimer = add_timer(tick+(timeout>0?timeout*1000:60000),mob_timer_delete,md->bl.id,0);
- mob_spawn (md); //Now it is ready for spawning.
- clif_specialeffect(&md->bl,344,AREA);
- sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000);
- }
- return 0;
-}
-
-/*==========================================
- * Checks whether it is daytime/nighttime
- *------------------------------------------*/
-BUILDIN_FUNC(isnight)
-{
- script_pushint(st,(night_flag == 1));
- return 0;
-}
-
-BUILDIN_FUNC(isday)
-{
- script_pushint(st,(night_flag == 0));
- return 0;
-}
-
-/*================================================
- * Check how many items/cards in the list are
- * equipped - used for 2/15's cards patch [celest]
- *------------------------------------------------*/
-BUILDIN_FUNC(isequippedcnt)
-{
- TBL_PC *sd;
- int i, j, k, id = 1;
- int ret = 0;
-
- sd = script_rid2sd(st);
- if (!sd) { //If the player is not attached it is a script error anyway... but better prevent the map server from crashing...
- script_pushint(st,0);
- return 0;
- }
-
- for (i=0; id!=0; i++) {
- FETCH (i+2, id) else id = 0;
- if (id <= 0)
- continue;
-
- for (j=0; j<EQI_MAX; j++) {
- int index;
- index = sd->equip_index[j];
- if(index < 0) continue;
- if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) continue;
- if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) continue;
- if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) continue;
-
- if(!sd->inventory_data[index])
- continue;
-
- if (itemdb_type(id) != IT_CARD) { //No card. Count amount in inventory.
- if (sd->inventory_data[index]->nameid == id)
- ret+= sd->status.inventory[index].amount;
- } else { //Count cards.
- if (itemdb_isspecial(sd->status.inventory[index].card[0]))
- continue; //No cards
- for(k=0; k<sd->inventory_data[index]->slot; k++) {
- if (sd->status.inventory[index].card[k] == id)
- ret++; //[Lupus]
- }
- }
- }
- }
-
- script_pushint(st,ret);
- return 0;
-}
-
-/*================================================
- * Check whether another card has been
- * equipped - used for 2/15's cards patch [celest]
- * -- Items checked cannot be reused in another
- * card set to prevent exploits
- *------------------------------------------------*/
-BUILDIN_FUNC(isequipped)
-{
- TBL_PC *sd;
- int i, j, k, id = 1;
- int index, flag;
- int ret = -1;
- //Original hash to reverse it when full check fails.
- unsigned int setitem_hash = 0, setitem_hash2 = 0;
-
- sd = script_rid2sd(st);
-
- if (!sd) { //If the player is not attached it is a script error anyway... but better prevent the map server from crashing...
- script_pushint(st,0);
- return 0;
- }
-
- setitem_hash = sd->bonus.setitem_hash;
- setitem_hash2 = sd->bonus.setitem_hash2;
- for (i=0; id!=0; i++) {
- FETCH (i+2, id) else id = 0;
- if (id <= 0)
- continue;
- flag = 0;
- for (j=0; j<EQI_MAX; j++) {
- index = sd->equip_index[j];
- if(index < 0) continue;
- if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) continue;
- if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) continue;
- if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) continue;
-
- if(!sd->inventory_data[index])
- continue;
-
- if (itemdb_type(id) != IT_CARD) {
- if (sd->inventory_data[index]->nameid != id)
- continue;
- flag = 1;
- break;
- } else { //Cards
- if (sd->inventory_data[index]->slot == 0 ||
- itemdb_isspecial(sd->status.inventory[index].card[0]))
- continue;
-
- for (k = 0; k < sd->inventory_data[index]->slot; k++)
- { //New hash system which should support up to 4 slots on any equipment. [Skotlex]
- unsigned int hash = 0;
- if (sd->status.inventory[index].card[k] != id)
- continue;
-
- hash = 1<<((j<5?j:j-5)*4 + k);
- // check if card is already used by another set
- if ( ( j < 5 ? sd->bonus.setitem_hash : sd->bonus.setitem_hash2 ) & hash)
- continue;
-
- // We have found a match
- flag = 1;
- // Set hash so this card cannot be used by another
- if (j<5)
- sd->bonus.setitem_hash |= hash;
- else
- sd->bonus.setitem_hash2 |= hash;
- break;
- }
- }
- if (flag) break; //Card found
- }
- if (ret == -1)
- ret = flag;
- else
- ret &= flag;
- if (!ret) break;
- }
- if (!ret) {//When check fails, restore original hash values. [Skotlex]
- sd->bonus.setitem_hash = setitem_hash;
- sd->bonus.setitem_hash2 = setitem_hash2;
- }
- script_pushint(st,ret);
- return 0;
-}
-
-/*================================================
- * Check how many given inserted cards in the CURRENT
- * weapon - used for 2/15's cards patch [Lupus]
- *------------------------------------------------*/
-BUILDIN_FUNC(cardscnt)
-{
- TBL_PC *sd;
- int i, k, id = 1;
- int ret = 0;
- int index;
-
- sd = script_rid2sd(st);
-
- for (i=0; id!=0; i++) {
- FETCH (i+2, id) else id = 0;
- if (id <= 0)
- continue;
-
- index = current_equip_item_index; //we get CURRENT WEAPON inventory index from status.c [Lupus]
- if(index < 0) continue;
-
- if(!sd->inventory_data[index])
- continue;
-
- if(itemdb_type(id) != IT_CARD) {
- if (sd->inventory_data[index]->nameid == id)
- ret+= sd->status.inventory[index].amount;
- } else {
- if (itemdb_isspecial(sd->status.inventory[index].card[0]))
- continue;
- for(k=0; k<sd->inventory_data[index]->slot; k++) {
- if (sd->status.inventory[index].card[k] == id)
- ret++;
- }
- }
- }
- script_pushint(st,ret);
-// script_pushint(st,current_equip_item_index);
- return 0;
-}
-
-/*=======================================================
- * Returns the refined number of the current item, or an
- * item with inventory index specified
- *-------------------------------------------------------*/
-BUILDIN_FUNC(getrefine)
-{
- TBL_PC *sd;
- if ((sd = script_rid2sd(st))!= NULL)
- script_pushint(st,sd->status.inventory[current_equip_item_index].refine);
- else
- script_pushint(st,0);
- return 0;
-}
-
-/*=======================================================
- * Day/Night controls
- *-------------------------------------------------------*/
-BUILDIN_FUNC(night)
-{
- if (night_flag != 1) map_night_timer(night_timer_tid, 0, 0, 1);
- return 0;
-}
-BUILDIN_FUNC(day)
-{
- if (night_flag != 0) map_day_timer(day_timer_tid, 0, 0, 1);
- return 0;
-}
-
-//=======================================================
-// Unequip [Spectre]
-//-------------------------------------------------------
-BUILDIN_FUNC(unequip)
-{
- int i;
- size_t num;
- TBL_PC *sd;
-
- num = script_getnum(st,2);
- sd = script_rid2sd(st);
- if( sd != NULL && num >= 1 && num <= ARRAYLENGTH(equip) )
- {
- i = pc_checkequip(sd,equip[num-1]);
- if (i >= 0)
- pc_unequipitem(sd,i,1|2);
- }
- return 0;
-}
-
-BUILDIN_FUNC(equip)
-{
- int nameid=0,i;
- TBL_PC *sd;
- struct item_data *item_data;
-
- sd = script_rid2sd(st);
-
- nameid=script_getnum(st,2);
- if((item_data = itemdb_exists(nameid)) == NULL)
- {
- ShowError("wrong item ID : equipitem(%i)\n",nameid);
- return 1;
- }
- ARR_FIND( 0, MAX_INVENTORY, i, sd->status.inventory[i].nameid == nameid );
- if( i < MAX_INVENTORY )
- pc_equipitem(sd,i,item_data->equip);
-
- return 0;
-}
-
-BUILDIN_FUNC(autoequip)
-{
- int nameid, flag;
- struct item_data *item_data;
- nameid=script_getnum(st,2);
- flag=script_getnum(st,3);
-
- if( ( item_data = itemdb_exists(nameid) ) == NULL )
- {
- ShowError("buildin_autoequip: Invalid item '%d'.\n", nameid);
- return 1;
- }
-
- if( !itemdb_isequip2(item_data) )
- {
- ShowError("buildin_autoequip: Item '%d' cannot be equipped.\n", nameid);
- return 1;
- }
-
- item_data->flag.autoequip = flag>0?1:0;
- return 0;
-}
-
-BUILDIN_FUNC(setbattleflag)
-{
- const char *flag, *value;
-
- flag = script_getstr(st,2);
- value = script_getstr(st,3); // HACK: Retrieve number as string (auto-converted) for battle_set_value
-
- if (battle_set_value(flag, value) == 0)
- ShowWarning("buildin_setbattleflag: unknown battle_config flag '%s'\n",flag);
- else
- ShowInfo("buildin_setbattleflag: battle_config flag '%s' is now set to '%s'.\n",flag,value);
-
- return 0;
-}
-
-BUILDIN_FUNC(getbattleflag)
-{
- const char *flag;
- flag = script_getstr(st,2);
- script_pushint(st,battle_get_value(flag));
- return 0;
-}
-
-//=======================================================
-// strlen [Valaris]
-//-------------------------------------------------------
-BUILDIN_FUNC(getstrlen)
-{
-
- const char *str = script_getstr(st,2);
- int len = (str) ? (int)strlen(str) : 0;
-
- script_pushint(st,len);
- return 0;
-}
-
-//=======================================================
-// isalpha [Valaris]
-//-------------------------------------------------------
-BUILDIN_FUNC(charisalpha)
-{
- const char *str=script_getstr(st,2);
- int pos=script_getnum(st,3);
-
- int val = ( str && pos >= 0 && (unsigned int)pos < strlen(str) ) ? ISALPHA( str[pos] ) != 0 : 0;
-
- script_pushint(st,val);
- return 0;
-}
-
-//=======================================================
-// charisupper <str>, <index>
-//-------------------------------------------------------
-BUILDIN_FUNC(charisupper)
-{
- const char *str = script_getstr(st,2);
- int pos = script_getnum(st,3);
-
- int val = ( str && pos >= 0 && (unsigned int)pos < strlen(str) ) ? ISUPPER( str[pos] ) : 0;
-
- script_pushint(st,val);
- return 0;
-}
-
-//=======================================================
-// charislower <str>, <index>
-//-------------------------------------------------------
-BUILDIN_FUNC(charislower)
-{
- const char *str = script_getstr(st,2);
- int pos = script_getnum(st,3);
-
- int val = ( str && pos >= 0 && (unsigned int)pos < strlen(str) ) ? ISLOWER( str[pos] ) : 0;
-
- script_pushint(st,val);
- return 0;
-}
-
-//=======================================================
-// charat <str>, <index>
-//-------------------------------------------------------
-BUILDIN_FUNC(charat) {
- const char *str = script_getstr(st,2);
- int pos = script_getnum(st,3);
-
- if( pos >= 0 && (unsigned int)pos < strlen(str) ) {
- char output[2];
- output[0] = str[pos];
- output[1] = '\0';
- script_pushstrcopy(st, output);
- } else
- script_pushconststr(st, "");
- return 0;
-}
-
-//=======================================================
-// setchar <string>, <char>, <index>
-//-------------------------------------------------------
-BUILDIN_FUNC(setchar)
-{
- const char *str = script_getstr(st,2);
- const char *c = script_getstr(st,3);
- int index = script_getnum(st,4);
- char *output = aStrdup(str);
-
- if(index >= 0 && index < strlen(output))
- output[index] = *c;
-
- script_pushstr(st, output);
- return 0;
-}
-
-//=======================================================
-// insertchar <string>, <char>, <index>
-//-------------------------------------------------------
-BUILDIN_FUNC(insertchar)
-{
- const char *str = script_getstr(st,2);
- const char *c = script_getstr(st,3);
- int index = script_getnum(st,4);
- char *output;
- size_t len = strlen(str);
-
- if(index < 0)
- index = 0;
- else if(index > len)
- index = len;
-
- output = (char*)aMalloc(len + 2);
-
- memcpy(output, str, index);
- output[index] = c[0];
- memcpy(&output[index+1], &str[index], len - index);
- output[len+1] = '\0';
-
- script_pushstr(st, output);
- return 0;
-}
-
-//=======================================================
-// delchar <string>, <index>
-//-------------------------------------------------------
-BUILDIN_FUNC(delchar)
-{
- const char *str = script_getstr(st,2);
- int index = script_getnum(st,3);
- char *output;
- size_t len = strlen(str);
-
- if(index < 0 || index > len) {
- //return original
- output = aStrdup(str);
- script_pushstr(st, output);
- return 0;
- }
-
- output = (char*)aMalloc(len);
-
- memcpy(output, str, index);
- memcpy(&output[index], &str[index+1], len - index);
-
- script_pushstr(st, output);
- return 0;
-}
-
-//=======================================================
-// strtoupper <str>
-//-------------------------------------------------------
-BUILDIN_FUNC(strtoupper)
-{
- const char *str = script_getstr(st,2);
- char *output = aStrdup(str);
- char *cursor = output;
-
- while (*cursor != '\0') {
- *cursor = TOUPPER(*cursor);
- cursor++;
- }
-
- script_pushstr(st, output);
- return 0;
-}
-
-//=======================================================
-// strtolower <str>
-//-------------------------------------------------------
-BUILDIN_FUNC(strtolower)
-{
- const char *str = script_getstr(st,2);
- char *output = aStrdup(str);
- char *cursor = output;
-
- while (*cursor != '\0') {
- *cursor = TOLOWER(*cursor);
- cursor++;
- }
-
- script_pushstr(st, output);
- return 0;
-}
-
-//=======================================================
-// substr <str>, <start>, <end>
-//-------------------------------------------------------
-BUILDIN_FUNC(substr)
-{
- const char *str = script_getstr(st,2);
- char *output;
- int start = script_getnum(st,3);
- int end = script_getnum(st,4);
-
- int len = 0;
-
- if(start >= 0 && end < strlen(str) && start <= end) {
- len = end - start + 1;
- output = (char*)aMalloc(len + 1);
- memcpy(output, &str[start], len);
- } else
- output = (char*)aMalloc(1);
-
- output[len] = '\0';
-
- script_pushstr(st, output);
- return 0;
-}
-
-//=======================================================
-// explode <dest_string_array>, <str>, <delimiter>
-// Note: delimiter is limited to 1 char
-//-------------------------------------------------------
-BUILDIN_FUNC(explode)
-{
- struct script_data* data = script_getdata(st, 2);
- const char *str = script_getstr(st,3);
- const char delimiter = script_getstr(st, 4)[0];
- int32 id;
- size_t len = strlen(str);
- int i = 0, j = 0;
- int start;
-
-
- char *temp;
- const char* name;
-
- TBL_PC* sd = NULL;
-
- temp = (char*)aMalloc(len + 1);
-
- if( !data_isreference(data) )
- {
- ShowError("script:explode: not a variable\n");
- script_reportdata(data);
- st->state = END;
- return 1;// not a variable
- }
-
- id = reference_getid(data);
- start = reference_getindex(data);
- name = reference_getname(data);
-
- if( not_array_variable(*name) )
- {
- ShowError("script:explode: illegal scope\n");
- script_reportdata(data);
- st->state = END;
- return 1;// not supported
- }
-
- if( !is_string_variable(name) )
- {
- ShowError("script:explode: not string array\n");
- script_reportdata(data);
- st->state = END;
- return 1;// data type mismatch
- }
-
- if( not_server_variable(*name) )
- {
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached
- }
-
- while(str[i] != '\0') {
- if(str[i] == delimiter && start < SCRIPT_MAX_ARRAYSIZE-1) { //break at delimiter but ignore after reaching last array index
- temp[j] = '\0';
- set_reg(st, sd, reference_uid(id, start++), name, (void*)temp, reference_getref(data));
- j = 0;
- ++i;
- } else {
- temp[j++] = str[i++];
- }
- }
- //set last string
- temp[j] = '\0';
- set_reg(st, sd, reference_uid(id, start), name, (void*)temp, reference_getref(data));
-
- aFree(temp);
- return 0;
-}
-
-//=======================================================
-// implode <string_array>
-// implode <string_array>, <glue>
-//-------------------------------------------------------
-BUILDIN_FUNC(implode)
-{
- struct script_data* data = script_getdata(st, 2);
- const char *glue = NULL, *name, *temp;
- int32 glue_len = 0, array_size, id;
- size_t len = 0;
- int i, k = 0;
-
- TBL_PC* sd = NULL;
-
- char *output;
-
- if( !data_isreference(data) )
- {
- ShowError("script:implode: not a variable\n");
- script_reportdata(data);
- st->state = END;
- return 1;// not a variable
- }
-
- id = reference_getid(data);
- name = reference_getname(data);
-
- if( not_array_variable(*name) )
- {
- ShowError("script:implode: illegal scope\n");
- script_reportdata(data);
- st->state = END;
- return 1;// not supported
- }
-
- if( !is_string_variable(name) )
- {
- ShowError("script:implode: not string array\n");
- script_reportdata(data);
- st->state = END;
- return 1;// data type mismatch
- }
-
- if( not_server_variable(*name) )
- {
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached
- }
-
- //count chars
- array_size = getarraysize(st, id, reference_getindex(data), is_string_variable(name), reference_getref(data)) - 1;
-
- if(array_size == -1) //empty array check (AmsTaff)
- {
- ShowWarning("script:implode: array length = 0\n");
- output = (char*)aMalloc(sizeof(char)*5);
- sprintf(output,"%s","NULL");
- } else {
- for(i = 0; i <= array_size; ++i) {
- temp = (char*) get_val2(st, reference_uid(id, i), reference_getref(data));
- len += strlen(temp);
- script_removetop(st, -1, 0);
- }
-
- //allocate mem
- if( script_hasdata(st,3) ) {
- glue = script_getstr(st,3);
- glue_len = strlen(glue);
- len += glue_len * (array_size);
- }
- output = (char*)aMalloc(len + 1);
-
- //build output
- for(i = 0; i < array_size; ++i) {
- temp = (char*) get_val2(st, reference_uid(id, i), reference_getref(data));
- len = strlen(temp);
- memcpy(&output[k], temp, len);
- k += len;
- if(glue_len != 0) {
- memcpy(&output[k], glue, glue_len);
- k += glue_len;
- }
- script_removetop(st, -1, 0);
- }
- temp = (char*) get_val2(st, reference_uid(id, array_size), reference_getref(data));
- len = strlen(temp);
- memcpy(&output[k], temp, len);
- k += len;
- script_removetop(st, -1, 0);
-
- output[k] = '\0';
- }
-
- script_pushstr(st, output);
- return 0;
-}
-
-//=======================================================
-// sprintf(<format>, ...);
-// Implements C sprintf, except format %n. The resulting string is
-// returned, instead of being saved in variable by reference.
-//-------------------------------------------------------
-BUILDIN_FUNC(sprintf)
-{
- unsigned int len, argc = 0, arg = 0, buf2_len = 0;
- const char* format;
- char* p;
- char* q;
- char* buf = NULL;
- char* buf2 = NULL;
- struct script_data* data;
- StringBuf final_buf;
-
- // Fetch init data
- format = script_getstr(st, 2);
- argc = script_lastdata(st)-2;
- len = strlen(format);
-
- // Skip parsing, where no parsing is required.
- if(len==0){
- script_pushconststr(st,"");
- return 0;
- }
-
- // Pessimistic alloc
- CREATE(buf, char, len+1);
-
- // Need not be parsed, just solve stuff like %%.
- if(argc==0){
- memcpy(buf,format,len+1);
- script_pushstrcopy(st, buf);
- aFree(buf);
- return 0;
- }
-
- safestrncpy(buf, format, len+1);
-
- // Issue sprintf for each parameter
- StringBuf_Init(&final_buf);
- q = buf;
- while((p = strchr(q, '%'))!=NULL){
- if(p!=q){
- len = p-q+1;
- if(buf2_len<len){
- RECREATE(buf2, char, len);
- buf2_len = len;
- }
- safestrncpy(buf2, q, len);
- StringBuf_AppendStr(&final_buf, buf2);
- q = p;
- }
- p = q+1;
- if(*p=='%'){ // %%
- StringBuf_AppendStr(&final_buf, "%");
- q+=2;
- continue;
- }
- if(*p=='n'){ // %n
- ShowWarning("buildin_sprintf: Format %%n not supported! Skipping...\n");
- script_reportsrc(st);
- q+=2;
- continue;
- }
- if(arg>=argc){
- ShowError("buildin_sprintf: Not enough arguments passed!\n");
- if(buf) aFree(buf);
- if(buf2) aFree(buf2);
- StringBuf_Destroy(&final_buf);
- script_pushconststr(st,"");
- return 1;
- }
- if((p = strchr(q+1, '%'))==NULL){
- p = strchr(q, 0); // EOS
- }
- len = p-q+1;
- if(buf2_len<len){
- RECREATE(buf2, char, len);
- buf2_len = len;
- }
- safestrncpy(buf2, q, len);
- q = p;
-
- // Note: This assumes the passed value being the correct
- // type to the current format specifier. If not, the server
- // probably crashes or returns anything else, than expected,
- // but it would behave in normal code the same way so it's
- // the scripter's responsibility.
- data = script_getdata(st, arg+3);
- if(data_isstring(data)){ // String
- StringBuf_Printf(&final_buf, buf2, script_getstr(st, arg+3));
- }else if(data_isint(data)){ // Number
- StringBuf_Printf(&final_buf, buf2, script_getnum(st, arg+3));
- }else if(data_isreference(data)){ // Variable
- char* name = reference_getname(data);
- if(name[strlen(name)-1]=='$'){ // var Str
- StringBuf_Printf(&final_buf, buf2, script_getstr(st, arg+3));
- }else{ // var Int
- StringBuf_Printf(&final_buf, buf2, script_getnum(st, arg+3));
- }
- }else{ // Unsupported type
- ShowError("buildin_sprintf: Unknown argument type!\n");
- if(buf) aFree(buf);
- if(buf2) aFree(buf2);
- StringBuf_Destroy(&final_buf);
- script_pushconststr(st,"");
- return 1;
- }
- arg++;
- }
-
- // Append anything left
- if(*q){
- StringBuf_AppendStr(&final_buf, q);
- }
-
- // Passed more, than needed
- if(arg<argc){
- ShowWarning("buildin_sprintf: Unused arguments passed.\n");
- script_reportsrc(st);
- }
-
- script_pushstrcopy(st, StringBuf_Value(&final_buf));
-
- if(buf) aFree(buf);
- if(buf2) aFree(buf2);
- StringBuf_Destroy(&final_buf);
-
- return 0;
-}
-
-//=======================================================
-// sscanf(<str>, <format>, ...);
-// Implements C sscanf.
-//-------------------------------------------------------
-BUILDIN_FUNC(sscanf){
- unsigned int argc, arg = 0, len;
- struct script_data* data;
- struct map_session_data* sd = NULL;
- const char* str;
- const char* format;
- const char* p;
- const char* q;
- char* buf = NULL;
- char* buf_p;
- char* ref_str = NULL;
- int ref_int;
-
- // Get data
- str = script_getstr(st, 2);
- format = script_getstr(st, 3);
- argc = script_lastdata(st)-3;
-
- len = strlen(format);
- CREATE(buf, char, len*2+1);
-
- // Issue sscanf for each parameter
- *buf = 0;
- q = format;
- while((p = strchr(q, '%'))){
- if(p!=q){
- strncat(buf, q, (size_t)(p-q));
- q = p;
- }
- p = q+1;
- if(*p=='*' || *p=='%'){ // Skip
- strncat(buf, q, 2);
- q+=2;
- continue;
- }
- if(arg>=argc){
- ShowError("buildin_sscanf: Not enough arguments passed!\n");
- script_pushint(st, -1);
- if(buf) aFree(buf);
- if(ref_str) aFree(ref_str);
- return 1;
- }
- if((p = strchr(q+1, '%'))==NULL){
- p = strchr(q, 0); // EOS
- }
- len = p-q;
- strncat(buf, q, len);
- q = p;
-
- // Validate output
- data = script_getdata(st, arg+4);
- if(!data_isreference(data) || !reference_tovariable(data)){
- ShowError("buildin_sscanf: Target argument is not a variable!\n");
- script_pushint(st, -1);
- if(buf) aFree(buf);
- if(ref_str) aFree(ref_str);
- return 1;
- }
- buf_p = reference_getname(data);
- if(not_server_variable(*buf_p) && (sd = script_rid2sd(st))==NULL){
- script_pushint(st, -1);
- if(buf) aFree(buf);
- if(ref_str) aFree(ref_str);
- return 0;
- }
-
- // Save value if any
- if(buf_p[strlen(buf_p)-1]=='$'){ // String
- if(ref_str==NULL){
- CREATE(ref_str, char, strlen(str)+1);
- }
- if(sscanf(str, buf, ref_str)==0){
- break;
- }
- set_reg(st, sd, add_str(buf_p), buf_p, (void *)(ref_str), reference_getref(data));
- }else{ // Number
- if(sscanf(str, buf, &ref_int)==0){
- break;
- }
- set_reg(st, sd, add_str(buf_p), buf_p, (void *)__64BPRTSIZE(ref_int), reference_getref(data));
- }
- arg++;
-
- // Disable used format (%... -> %*...)
- buf_p = strchr(buf, 0);
- memmove(buf_p-len+2, buf_p-len+1, len);
- *(buf_p-len+1) = '*';
- }
-
- script_pushint(st, arg);
- if(buf) aFree(buf);
- if(ref_str) aFree(ref_str);
-
- return 0;
-}
-
-//=======================================================
-// strpos(<haystack>, <needle>)
-// strpos(<haystack>, <needle>, <offset>)
-//
-// Implements PHP style strpos. Adapted from code from
-// http://www.daniweb.com/code/snippet313.html, Dave Sinkula
-//-------------------------------------------------------
-BUILDIN_FUNC(strpos) {
- const char *haystack = script_getstr(st,2);
- const char *needle = script_getstr(st,3);
- int i;
- size_t len;
-
- if( script_hasdata(st,4) )
- i = script_getnum(st,4);
- else
- i = 0;
-
- if (needle[0] == '\0') {
- script_pushint(st, -1);
- return 0;
- }
-
- len = strlen(haystack);
- for ( ; i < len; ++i ) {
- if ( haystack[i] == *needle ) {
- // matched starting char -- loop through remaining chars
- const char *h, *n;
- for ( h = &haystack[i], n = needle; *h && *n; ++h, ++n ) {
- if ( *h != *n ) {
- break;
- }
- }
- if ( !*n ) { // matched all of 'needle' to null termination
- script_pushint(st, i);
- return 0;
- }
- }
- }
- script_pushint(st, -1);
- return 0;
-}
-
-//===============================================================
-// replacestr <input>, <search>, <replace>{, <usecase>{, <count>}}
-//
-// Note: Finds all instances of <search> in <input> and replaces
-// with <replace>. If specified will only replace as many
-// instances as specified in <count>. By default will be case
-// sensitive.
-//---------------------------------------------------------------
-BUILDIN_FUNC(replacestr)
-{
- const char *input = script_getstr(st, 2);
- const char *find = script_getstr(st, 3);
- const char *replace = script_getstr(st, 4);
- size_t inputlen = strlen(input);
- size_t findlen = strlen(find);
- struct StringBuf output;
- bool usecase = true;
-
- int count = 0;
- int numFinds = 0;
- int i = 0, f = 0;
-
- if(findlen == 0) {
- ShowError("script:replacestr: Invalid search length.\n");
- st->state = END;
- return 1;
- }
-
- if(script_hasdata(st, 5)) {
- if( !script_isstring(st,5) )
- usecase = script_getnum(st, 5) != 0;
- else {
- ShowError("script:replacestr: Invalid usecase value. Expected int got string\n");
- st->state = END;
- return 1;
- }
- }
-
- if(script_hasdata(st, 6)) {
- count = script_getnum(st, 6);
- if(count == 0) {
- ShowError("script:replacestr: Invalid count value. Expected int got string\n");
- st->state = END;
- return 1;
- }
- }
-
- StringBuf_Init(&output);
-
- for(; i < inputlen; i++) {
- if(count && count == numFinds) { //found enough, stop looking
- break;
- }
-
- for(f = 0; f <= findlen; f++) {
- if(f == findlen) { //complete match
- numFinds++;
- StringBuf_AppendStr(&output, replace);
-
- i += findlen - 1;
- break;
- } else {
- if(usecase) {
- if((i + f) > inputlen || input[i + f] != find[f]) {
- StringBuf_Printf(&output, "%c", input[i]);
- break;
- }
- } else {
- if(((i + f) > inputlen || input[i + f] != find[f]) && TOUPPER(input[i+f]) != TOUPPER(find[f])) {
- StringBuf_Printf(&output, "%c", input[i]);
- break;
- }
- }
- }
- }
- }
-
- //append excess after enough found
- if(i < inputlen)
- StringBuf_AppendStr(&output, &(input[i]));
-
- script_pushstrcopy(st, StringBuf_Value(&output));
- StringBuf_Destroy(&output);
- return 0;
-}
-
-//========================================================
-// countstr <input>, <search>{, <usecase>}
-//
-// Note: Counts the number of times <search> occurs in
-// <input>. By default will be case sensitive.
-//--------------------------------------------------------
-BUILDIN_FUNC(countstr)
-{
- const char *input = script_getstr(st, 2);
- const char *find = script_getstr(st, 3);
- size_t inputlen = strlen(input);
- size_t findlen = strlen(find);
- bool usecase = true;
-
- int numFinds = 0;
- int i = 0, f = 0;
-
- if(findlen == 0) {
- ShowError("script:countstr: Invalid search length.\n");
- st->state = END;
- return 1;
- }
-
- if(script_hasdata(st, 4)) {
- if( !script_isstring(st,4) )
- usecase = script_getnum(st, 4) != 0;
- else {
- ShowError("script:countstr: Invalid usecase value. Expected int got string\n");
- st->state = END;
- return 1;
- }
- }
-
- for(; i < inputlen; i++) {
- for(f = 0; f <= findlen; f++) {
- if(f == findlen) { //complete match
- numFinds++;
- i += findlen - 1;
- break;
- } else {
- if(usecase) {
- if((i + f) > inputlen || input[i + f] != find[f]) {
- break;
- }
- } else {
- if(((i + f) > inputlen || input[i + f] != find[f]) && TOUPPER(input[i+f]) != TOUPPER(find[f])) {
- break;
- }
- }
- }
- }
- }
- script_pushint(st, numFinds);
- return 0;
-}
-
-
-/// Changes the display name and/or display class of the npc.
-/// Returns 0 is successful, 1 if the npc does not exist.
-///
-/// setnpcdisplay("<npc name>", "<new display name>", <new class id>, <new size>) -> <int>
-/// setnpcdisplay("<npc name>", "<new display name>", <new class id>) -> <int>
-/// setnpcdisplay("<npc name>", "<new display name>") -> <int>
-/// setnpcdisplay("<npc name>", <new class id>) -> <int>
-BUILDIN_FUNC(setnpcdisplay)
-{
- const char* name;
- const char* newname = NULL;
- int class_ = -1, size = -1;
- struct script_data* data;
- struct npc_data* nd;
-
- name = script_getstr(st,2);
- data = script_getdata(st,3);
-
- if( script_hasdata(st,4) )
- class_ = script_getnum(st,4);
- if( script_hasdata(st,5) )
- size = script_getnum(st,5);
-
- get_val(st, data);
- if( data_isstring(data) )
- newname = conv_str(st,data);
- else if( data_isint(data) )
- class_ = conv_num(st,data);
- else
- {
- ShowError("script:setnpcdisplay: expected a string or number\n");
- script_reportdata(data);
- return 1;
- }
-
- nd = npc_name2id(name);
- if( nd == NULL )
- {// not found
- script_pushint(st,1);
- return 0;
- }
-
- // update npc
- if( newname )
- npc_setdisplayname(nd, newname);
-
- if( size != -1 && size != (int)nd->size )
- nd->size = size;
- else
- size = -1;
-
- if( class_ != -1 && nd->class_ != class_ )
- npc_setclass(nd, class_);
- else if( size != -1 )
- { // Required to update the visual size
- clif_clearunit_area(&nd->bl, CLR_OUTSIGHT);
- clif_spawn(&nd->bl);
- }
-
- script_pushint(st,0);
- return 0;
-}
-
-BUILDIN_FUNC(atoi)
-{
- const char *value;
- value = script_getstr(st,2);
- script_pushint(st,atoi(value));
- return 0;
-}
-
-// case-insensitive substring search [lordalfa]
-BUILDIN_FUNC(compare)
-{
- const char *message;
- const char *cmpstring;
- message = script_getstr(st,2);
- cmpstring = script_getstr(st,3);
- script_pushint(st,(stristr(message,cmpstring) != NULL));
- return 0;
-}
-
-// [zBuffer] List of mathematics commands --->
-BUILDIN_FUNC(sqrt)
-{
- double i, a;
- i = script_getnum(st,2);
- a = sqrt(i);
- script_pushint(st,(int)a);
- return 0;
-}
-
-BUILDIN_FUNC(pow)
-{
- double i, a, b;
- a = script_getnum(st,2);
- b = script_getnum(st,3);
- i = pow(a,b);
- script_pushint(st,(int)i);
- return 0;
-}
-
-BUILDIN_FUNC(distance)
-{
- int x0, y0, x1, y1;
-
- x0 = script_getnum(st,2);
- y0 = script_getnum(st,3);
- x1 = script_getnum(st,4);
- y1 = script_getnum(st,5);
-
- script_pushint(st,distance_xy(x0,y0,x1,y1));
- return 0;
-}
-
-// <--- [zBuffer] List of mathematics commands
-
-BUILDIN_FUNC(md5)
-{
- const char *tmpstr;
- char *md5str;
-
- tmpstr = script_getstr(st,2);
- md5str = (char *)aMalloc((32+1)*sizeof(char));
- MD5_String(tmpstr, md5str);
- script_pushstr(st, md5str);
- return 0;
-}
-
-// [zBuffer] List of dynamic var commands --->
-
-BUILDIN_FUNC(setd)
-{
- TBL_PC *sd=NULL;
- char varname[100];
- const char *buffer;
- int elem;
- buffer = script_getstr(st, 2);
-
- if(sscanf(buffer, "%99[^[][%d]", varname, &elem) < 2)
- elem = 0;
-
- if( not_server_variable(*varname) )
- {
- sd = script_rid2sd(st);
- if( sd == NULL )
- {
- ShowError("script:setd: no player attached for player variable '%s'\n", buffer);
- return 0;
- }
- }
-
- if( is_string_variable(varname) ) {
- setd_sub(st, sd, varname, elem, (void *)script_getstr(st, 3), NULL);
- } else {
- setd_sub(st, sd, varname, elem, (void *)__64BPRTSIZE(script_getnum(st, 3)), NULL);
- }
-
- return 0;
-}
-
-int buildin_query_sql_sub(struct script_state* st, Sql* handle)
-{
- int i, j;
- TBL_PC* sd = NULL;
- const char* query;
- struct script_data* data;
- const char* name;
- int max_rows = SCRIPT_MAX_ARRAYSIZE; // maximum number of rows
- int num_vars;
- int num_cols;
-
- // check target variables
- for( i = 3; script_hasdata(st,i); ++i ) {
- data = script_getdata(st, i);
- if( data_isreference(data) ) { // it's a variable
- name = reference_getname(data);
- if( not_server_variable(*name) && sd == NULL ) { // requires a player
- sd = script_rid2sd(st);
- if( sd == NULL ) { // no player attached
- script_reportdata(data);
- st->state = END;
- return 1;
- }
- }
- if( not_array_variable(*name) )
- max_rows = 1;// not an array, limit to one row
- } else {
- ShowError("script:query_sql: not a variable\n");
- script_reportdata(data);
- st->state = END;
- return 1;
- }
- }
- num_vars = i - 3;
-
- // Execute the query
- query = script_getstr(st,2);
-
- if( SQL_ERROR == Sql_QueryStr(handle, query) ) {
- Sql_ShowDebug(handle);
- script_pushint(st, 0);
- return 1;
- }
-
- if( Sql_NumRows(handle) == 0 ) { // No data received
- Sql_FreeResult(handle);
- script_pushint(st, 0);
- return 0;
- }
-
- // Count the number of columns to store
- num_cols = Sql_NumColumns(handle);
- if( num_vars < num_cols ) {
- ShowWarning("script:query_sql: Too many columns, discarding last %u columns.\n", (unsigned int)(num_cols-num_vars));
- script_reportsrc(st);
- } else if( num_vars > num_cols ) {
- ShowWarning("script:query_sql: Too many variables (%u extra).\n", (unsigned int)(num_vars-num_cols));
- script_reportsrc(st);
- }
-
- // Store data
- for( i = 0; i < max_rows && SQL_SUCCESS == Sql_NextRow(handle); ++i ) {
- for( j = 0; j < num_vars; ++j ) {
- char* str = NULL;
-
- if( j < num_cols )
- Sql_GetData(handle, j, &str, NULL);
-
- data = script_getdata(st, j+3);
- name = reference_getname(data);
- if( is_string_variable(name) )
- setd_sub(st, sd, name, i, (void *)(str?str:""), reference_getref(data));
- else
- setd_sub(st, sd, name, i, (void *)__64BPRTSIZE((str?atoi(str):0)), reference_getref(data));
- }
- }
- if( i == max_rows && max_rows < Sql_NumRows(handle) ) {
- ShowWarning("script:query_sql: Only %d/%u rows have been stored.\n", max_rows, (unsigned int)Sql_NumRows(handle));
- script_reportsrc(st);
- }
-
- // Free data
- Sql_FreeResult(handle);
- script_pushint(st, i);
-
- return 0;
-}
-
-BUILDIN_FUNC(query_sql) {
-#ifdef BETA_THREAD_TEST
- if( st->state != RERUNLINE ) {
- queryThread_add(st,false);
-
- st->state = RERUNLINE;/* will continue when the query is finished running. */
- } else
- st->state = RUN;
-
- return 0;
-#else
- return buildin_query_sql_sub(st, mmysql_handle);
-#endif
-}
-
-BUILDIN_FUNC(query_logsql) {
- if( !log_config.sql_logs ) {// logmysql_handle == NULL
- ShowWarning("buildin_query_logsql: SQL logs are disabled, query '%s' will not be executed.\n", script_getstr(st,2));
- script_pushint(st,-1);
- return 1;
- }
-#ifdef BETA_THREAD_TEST
- if( st->state != RERUNLINE ) {
- queryThread_add(st,true);
-
- st->state = RERUNLINE;/* will continue when the query is finished running. */
- } else
- st->state = RUN;
-
- return 0;
-#else
- return buildin_query_sql_sub(st, logmysql_handle);
-#endif
-}
-
-//Allows escaping of a given string.
-BUILDIN_FUNC(escape_sql)
-{
- const char *str;
- char *esc_str;
- size_t len;
-
- str = script_getstr(st,2);
- len = strlen(str);
- esc_str = (char*)aMalloc(len*2+1);
- Sql_EscapeStringLen(mmysql_handle, esc_str, str, len);
- script_pushstr(st, esc_str);
- return 0;
-}
-
-BUILDIN_FUNC(getd)
-{
- char varname[100];
- const char *buffer;
- int elem;
-
- buffer = script_getstr(st, 2);
-
- if(sscanf(buffer, "%[^[][%d]", varname, &elem) < 2)
- elem = 0;
-
- // Push the 'pointer' so it's more flexible [Lance]
- push_val(st->stack, C_NAME, reference_uid(add_str(varname), elem));
-
- return 0;
-}
-
-// <--- [zBuffer] List of dynamic var commands
-// Pet stat [Lance]
-BUILDIN_FUNC(petstat)
-{
- TBL_PC *sd = NULL;
- struct pet_data *pd;
- int flag = script_getnum(st,2);
- sd = script_rid2sd(st);
- if(!sd || !sd->status.pet_id || !sd->pd){
- if(flag == 2)
- script_pushconststr(st, "");
- else
- script_pushint(st,0);
- return 0;
- }
- pd = sd->pd;
- switch(flag){
- case 1: script_pushint(st,(int)pd->pet.class_); break;
- case 2: script_pushstrcopy(st, pd->pet.name); break;
- case 3: script_pushint(st,(int)pd->pet.level); break;
- case 4: script_pushint(st,(int)pd->pet.hungry); break;
- case 5: script_pushint(st,(int)pd->pet.intimate); break;
- default:
- script_pushint(st,0);
- break;
- }
- return 0;
-}
-
-BUILDIN_FUNC(callshop)
-{
- TBL_PC *sd = NULL;
- struct npc_data *nd;
- const char *shopname;
- int flag = 0;
- sd = script_rid2sd(st);
- if (!sd) {
- script_pushint(st,0);
- return 0;
- }
- shopname = script_getstr(st, 2);
- if( script_hasdata(st,3) )
- flag = script_getnum(st,3);
- nd = npc_name2id(shopname);
- if( !nd || nd->bl.type != BL_NPC || (nd->subtype != SHOP && nd->subtype != CASHSHOP) )
- {
- ShowError("buildin_callshop: Shop [%s] not found (or NPC is not shop type)\n", shopname);
- script_pushint(st,0);
- return 1;
- }
-
- if( nd->subtype == SHOP )
- {
- // flag the user as using a valid script call for opening the shop (for floating NPCs)
- sd->state.callshop = 1;
-
- switch( flag )
- {
- case 1: npc_buysellsel(sd,nd->bl.id,0); break; //Buy window
- case 2: npc_buysellsel(sd,nd->bl.id,1); break; //Sell window
- default: clif_npcbuysell(sd,nd->bl.id); break; //Show menu
- }
- }
- else
- clif_cashshop_show(sd, nd);
-
- sd->npc_shopid = nd->bl.id;
- script_pushint(st,1);
- return 0;
-}
-
-BUILDIN_FUNC(npcshopitem)
-{
- const char* npcname = script_getstr(st, 2);
- struct npc_data* nd = npc_name2id(npcname);
- int n, i;
- int amount;
-
- if( !nd || ( nd->subtype != SHOP && nd->subtype != CASHSHOP ) )
- { //Not found.
- script_pushint(st,0);
- return 0;
- }
-
- // get the count of new entries
- amount = (script_lastdata(st)-2)/2;
-
- // generate new shop item list
- RECREATE(nd->u.shop.shop_item, struct npc_item_list, amount);
- for( n = 0, i = 3; n < amount; n++, i+=2 )
- {
- nd->u.shop.shop_item[n].nameid = script_getnum(st,i);
- nd->u.shop.shop_item[n].value = script_getnum(st,i+1);
- }
- nd->u.shop.count = n;
-
- script_pushint(st,1);
- return 0;
-}
-
-BUILDIN_FUNC(npcshopadditem)
-{
- const char* npcname = script_getstr(st,2);
- struct npc_data* nd = npc_name2id(npcname);
- int n, i;
- int amount;
-
- if( !nd || ( nd->subtype != SHOP && nd->subtype != CASHSHOP ) )
- { //Not found.
- script_pushint(st,0);
- return 0;
- }
-
- // get the count of new entries
- amount = (script_lastdata(st)-2)/2;
-
- // append new items to existing shop item list
- RECREATE(nd->u.shop.shop_item, struct npc_item_list, nd->u.shop.count+amount);
- for( n = nd->u.shop.count, i = 3; n < nd->u.shop.count+amount; n++, i+=2 )
- {
- nd->u.shop.shop_item[n].nameid = script_getnum(st,i);
- nd->u.shop.shop_item[n].value = script_getnum(st,i+1);
- }
- nd->u.shop.count = n;
-
- script_pushint(st,1);
- return 0;
-}
-
-BUILDIN_FUNC(npcshopdelitem)
-{
- const char* npcname = script_getstr(st,2);
- struct npc_data* nd = npc_name2id(npcname);
- unsigned int nameid;
- int n, i;
- int amount;
- int size;
-
- if( !nd || ( nd->subtype != SHOP && nd->subtype != CASHSHOP ) )
- { //Not found.
- script_pushint(st,0);
- return 0;
- }
-
- amount = script_lastdata(st)-2;
- size = nd->u.shop.count;
-
- // remove specified items from the shop item list
- for( i = 3; i < 3 + amount; i++ )
- {
- nameid = script_getnum(st,i);
-
- ARR_FIND( 0, size, n, nd->u.shop.shop_item[n].nameid == nameid );
- if( n < size )
- {
- memmove(&nd->u.shop.shop_item[n], &nd->u.shop.shop_item[n+1], sizeof(nd->u.shop.shop_item[0])*(size-n));
- size--;
- }
- }
-
- RECREATE(nd->u.shop.shop_item, struct npc_item_list, size);
- nd->u.shop.count = size;
-
- script_pushint(st,1);
- return 0;
-}
-
-//Sets a script to attach to a shop npc.
-BUILDIN_FUNC(npcshopattach)
-{
- const char* npcname = script_getstr(st,2);
- struct npc_data* nd = npc_name2id(npcname);
- int flag = 1;
-
- if( script_hasdata(st,3) )
- flag = script_getnum(st,3);
-
- if( !nd || nd->subtype != SHOP )
- { //Not found.
- script_pushint(st,0);
- return 0;
- }
-
- if (flag)
- nd->master_nd = ((struct npc_data *)map_id2bl(st->oid));
- else
- nd->master_nd = NULL;
-
- script_pushint(st,1);
- return 0;
-}
-
-/*==========================================
- * Returns some values of an item [Lupus]
- * Price, Weight, etc...
- setitemscript(itemID,"{new item bonus script}",[n]);
- Where n:
- 0 - script
- 1 - Equip script
- 2 - Unequip script
- *------------------------------------------*/
-BUILDIN_FUNC(setitemscript)
-{
- int item_id,n=0;
- const char *script;
- struct item_data *i_data;
- struct script_code **dstscript;
-
- item_id = script_getnum(st,2);
- script = script_getstr(st,3);
- if( script_hasdata(st,4) )
- n=script_getnum(st,4);
- i_data = itemdb_exists(item_id);
-
- if (!i_data || script==NULL || ( script[0] && script[0]!='{' )) {
- script_pushint(st,0);
- return 0;
- }
- switch (n) {
- case 2:
- dstscript = &i_data->unequip_script;
- break;
- case 1:
- dstscript = &i_data->equip_script;
- break;
- default:
- dstscript = &i_data->script;
- break;
- }
- if(*dstscript)
- script_free_code(*dstscript);
-
- *dstscript = script[0] ? parse_script(script, "script_setitemscript", 0, 0) : NULL;
- script_pushint(st,1);
- return 0;
-}
-
-/* Work In Progress [Lupus]
-BUILDIN_FUNC(addmonsterdrop)
-{
- int class_,item_id,chance;
- class_=script_getnum(st,2);
- item_id=script_getnum(st,3);
- chance=script_getnum(st,4);
- if(class_>1000 && item_id>500 && chance>0) {
- script_pushint(st,1);
- } else {
- script_pushint(st,0);
- }
-}
-
-BUILDIN_FUNC(delmonsterdrop)
-{
- int class_,item_id;
- class_=script_getnum(st,2);
- item_id=script_getnum(st,3);
- if(class_>1000 && item_id>500) {
- script_pushint(st,1);
- } else {
- script_pushint(st,0);
- }
-}
-*/
-
-/*==========================================
- * Returns some values of a monster [Lupus]
- * Name, Level, race, size, etc...
- getmonsterinfo(monsterID,queryIndex);
- *------------------------------------------*/
-BUILDIN_FUNC(getmonsterinfo)
-{
- struct mob_db *mob;
- int mob_id;
-
- mob_id = script_getnum(st,2);
- if (!mobdb_checkid(mob_id)) {
- ShowError("buildin_getmonsterinfo: Wrong Monster ID: %i\n", mob_id);
- if ( !script_getnum(st,3) ) //requested a string
- script_pushconststr(st,"null");
- else
- script_pushint(st,-1);
- return -1;
- }
- mob = mob_db(mob_id);
- switch ( script_getnum(st,3) ) {
- case 0: script_pushstrcopy(st,mob->jname); break;
- case 1: script_pushint(st,mob->lv); break;
- case 2: script_pushint(st,mob->status.max_hp); break;
- case 3: script_pushint(st,mob->base_exp); break;
- case 4: script_pushint(st,mob->job_exp); break;
- case 5: script_pushint(st,mob->status.rhw.atk); break;
- case 6: script_pushint(st,mob->status.rhw.atk2); break;
- case 7: script_pushint(st,mob->status.def); break;
- case 8: script_pushint(st,mob->status.mdef); break;
- case 9: script_pushint(st,mob->status.str); break;
- case 10: script_pushint(st,mob->status.agi); break;
- case 11: script_pushint(st,mob->status.vit); break;
- case 12: script_pushint(st,mob->status.int_); break;
- case 13: script_pushint(st,mob->status.dex); break;
- case 14: script_pushint(st,mob->status.luk); break;
- case 15: script_pushint(st,mob->status.rhw.range); break;
- case 16: script_pushint(st,mob->range2); break;
- case 17: script_pushint(st,mob->range3); break;
- case 18: script_pushint(st,mob->status.size); break;
- case 19: script_pushint(st,mob->status.race); break;
- case 20: script_pushint(st,mob->status.def_ele); break;
- case 21: script_pushint(st,mob->status.mode); break;
- case 22: script_pushint(st,mob->mexp); break;
- default: script_pushint(st,-1); //wrong Index
- }
- return 0;
-}
-
-BUILDIN_FUNC(checkvending) // check vending [Nab4]
-{
- TBL_PC *sd = NULL;
-
- if(script_hasdata(st,2))
- sd = map_nick2sd(script_getstr(st,2));
- else
- sd = script_rid2sd(st);
-
- if(sd)
- script_pushint(st, sd->state.autotrade ? 2 : sd->state.vending);
- else
- script_pushint(st,0);
-
- return 0;
-}
-
-
-BUILDIN_FUNC(checkchatting) // check chatting [Marka]
-{
- TBL_PC *sd = NULL;
-
- if(script_hasdata(st,2))
- sd = map_nick2sd(script_getstr(st,2));
- else
- sd = script_rid2sd(st);
-
- if(sd)
- script_pushint(st,(sd->chatID != 0));
- else
- script_pushint(st,0);
-
- return 0;
-}
-
-BUILDIN_FUNC(searchitem)
-{
- struct script_data* data = script_getdata(st, 2);
- const char *itemname = script_getstr(st,3);
- struct item_data *items[MAX_SEARCH];
- int count;
-
- char* name;
- int32 start;
- int32 id;
- int32 i;
- TBL_PC* sd = NULL;
-
- if ((items[0] = itemdb_exists(atoi(itemname))))
- count = 1;
- else {
- count = itemdb_searchname_array(items, ARRAYLENGTH(items), itemname);
- if (count > MAX_SEARCH) count = MAX_SEARCH;
- }
-
- if (!count) {
- script_pushint(st, 0);
- return 0;
- }
-
- if( !data_isreference(data) )
- {
- ShowError("script:searchitem: not a variable\n");
- script_reportdata(data);
- st->state = END;
- return 1;// not a variable
- }
-
- id = reference_getid(data);
- start = reference_getindex(data);
- name = reference_getname(data);
- if( not_array_variable(*name) )
- {
- ShowError("script:searchitem: illegal scope\n");
- script_reportdata(data);
- st->state = END;
- return 1;// not supported
- }
-
- if( not_server_variable(*name) )
- {
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;// no player attached
- }
-
- if( is_string_variable(name) )
- {// string array
- ShowError("script:searchitem: not an integer array reference\n");
- script_reportdata(data);
- st->state = END;
- return 1;// not supported
- }
-
- for( i = 0; i < count; ++start, ++i )
- {// Set array
- void* v = (void*)__64BPRTSIZE((int)items[i]->nameid);
- set_reg(st, sd, reference_uid(id, start), name, v, reference_getref(data));
- }
-
- script_pushint(st, count);
- return 0;
-}
-
-int axtoi(const char *hexStg)
-{
- int n = 0; // position in string
- int16 m = 0; // position in digit[] to shift
- int count; // loop index
- int intValue = 0; // integer value of hex string
- int digit[11]; // hold values to convert
- while (n < 10) {
- if (hexStg[n]=='\0')
- break;
- if (hexStg[n] > 0x29 && hexStg[n] < 0x40 ) //if 0 to 9
- digit[n] = hexStg[n] & 0x0f; //convert to int
- else if (hexStg[n] >='a' && hexStg[n] <= 'f') //if a to f
- digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
- else if (hexStg[n] >='A' && hexStg[n] <= 'F') //if A to F
- digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
- else break;
- n++;
- }
- count = n;
- m = n - 1;
- n = 0;
- while(n < count) {
- // digit[n] is value of hex digit at position n
- // (m << 2) is the number of positions to shift
- // OR the bits into return value
- intValue = intValue | (digit[n] << (m << 2));
- m--; // adjust the position to set
- n++; // next digit to process
- }
- return (intValue);
-}
-
-// [Lance] Hex string to integer converter
-BUILDIN_FUNC(axtoi)
-{
- const char *hex = script_getstr(st,2);
- script_pushint(st,axtoi(hex));
- return 0;
-}
-
-// [zBuffer] List of player cont commands --->
-BUILDIN_FUNC(rid2name)
-{
- struct block_list *bl = NULL;
- int rid = script_getnum(st,2);
- if((bl = map_id2bl(rid)))
- {
- switch(bl->type) {
- case BL_MOB: script_pushstrcopy(st,((TBL_MOB*)bl)->name); break;
- case BL_PC: script_pushstrcopy(st,((TBL_PC*)bl)->status.name); break;
- case BL_NPC: script_pushstrcopy(st,((TBL_NPC*)bl)->exname); break;
- case BL_PET: script_pushstrcopy(st,((TBL_PET*)bl)->pet.name); break;
- case BL_HOM: script_pushstrcopy(st,((TBL_HOM*)bl)->homunculus.name); break;
- case BL_MER: script_pushstrcopy(st,((TBL_MER*)bl)->db->name); break;
- default:
- ShowError("buildin_rid2name: BL type unknown.\n");
- script_pushconststr(st,"");
- break;
- }
- } else {
- ShowError("buildin_rid2name: invalid RID\n");
- script_pushconststr(st,"(null)");
- }
- return 0;
-}
-
-BUILDIN_FUNC(pcblockmove)
-{
- int id, flag;
- TBL_PC *sd = NULL;
-
- id = script_getnum(st,2);
- flag = script_getnum(st,3);
-
- if(id)
- sd = map_id2sd(id);
- else
- sd = script_rid2sd(st);
-
- if(sd)
- sd->state.blockedmove = flag > 0;
-
- return 0;
-}
-
-BUILDIN_FUNC(pcfollow)
-{
- int id, targetid;
- TBL_PC *sd = NULL;
-
-
- id = script_getnum(st,2);
- targetid = script_getnum(st,3);
-
- if(id)
- sd = map_id2sd(id);
- else
- sd = script_rid2sd(st);
-
- if(sd)
- pc_follow(sd, targetid);
-
- return 0;
-}
-
-BUILDIN_FUNC(pcstopfollow)
-{
- int id;
- TBL_PC *sd = NULL;
-
-
- id = script_getnum(st,2);
-
- if(id)
- sd = map_id2sd(id);
- else
- sd = script_rid2sd(st);
-
- if(sd)
- pc_stop_following(sd);
-
- return 0;
-}
-// <--- [zBuffer] List of player cont commands
-// [zBuffer] List of mob control commands --->
-//## TODO always return if the request/whatever was successfull [FlavioJS]
-
-/// Makes the unit walk to target position or map
-/// Returns if it was successfull
-///
-/// unitwalk(<unit_id>,<x>,<y>) -> <bool>
-/// unitwalk(<unit_id>,<map_id>) -> <bool>
-BUILDIN_FUNC(unitwalk)
-{
- struct block_list* bl;
-
- bl = map_id2bl(script_getnum(st,2));
- if( bl == NULL )
- {
- script_pushint(st, 0);
- }
- else if( script_hasdata(st,4) )
- {
- int x = script_getnum(st,3);
- int y = script_getnum(st,4);
- script_pushint(st, unit_walktoxy(bl,x,y,0));// We'll use harder calculations.
- }
- else
- {
- int map_id = script_getnum(st,3);
- script_pushint(st, unit_walktobl(bl,map_id2bl(map_id),65025,1));
- }
-
- return 0;
-}
-
-/// Kills the unit
-///
-/// unitkill <unit_id>;
-BUILDIN_FUNC(unitkill)
-{
- struct block_list* bl = map_id2bl(script_getnum(st,2));
- if( bl != NULL )
- status_kill(bl);
-
- return 0;
-}
-
-/// Warps the unit to the target position in the target map
-/// Returns if it was successfull
-///
-/// unitwarp(<unit_id>,"<map name>",<x>,<y>) -> <bool>
-BUILDIN_FUNC(unitwarp)
-{
- int unit_id;
- int map;
- short x;
- short y;
- struct block_list* bl;
- const char *mapname;
-
- unit_id = script_getnum(st,2);
- mapname = script_getstr(st, 3);
- x = (short)script_getnum(st,4);
- y = (short)script_getnum(st,5);
-
- if (!unit_id) //Warp the script's runner
- bl = map_id2bl(st->rid);
- else
- bl = map_id2bl(unit_id);
-
- if( strcmp(mapname,"this") == 0 )
- map = bl?bl->m:-1;
- else
- map = map_mapname2mapid(mapname);
-
- if( map >= 0 && bl != NULL )
- script_pushint(st, unit_warp(bl,map,x,y,CLR_OUTSIGHT));
- else
- script_pushint(st, 0);
-
- return 0;
-}
-
-/// Makes the unit attack the target.
-/// If the unit is a player and <action type> is not 0, it does a continuous
-/// attack instead of a single attack.
-/// Returns if the request was successfull.
-///
-/// unitattack(<unit_id>,"<target name>"{,<action type>}) -> <bool>
-/// unitattack(<unit_id>,<target_id>{,<action type>}) -> <bool>
-BUILDIN_FUNC(unitattack)
-{
- struct block_list* unit_bl;
- struct block_list* target_bl = NULL;
- struct script_data* data;
- int actiontype = 0;
-
- // get unit
- unit_bl = map_id2bl(script_getnum(st,2));
- if( unit_bl == NULL ) {
- script_pushint(st, 0);
- return 0;
- }
-
- data = script_getdata(st, 3);
- get_val(st, data);
- if( data_isstring(data) )
- {
- TBL_PC* sd = map_nick2sd(conv_str(st, data));
- if( sd != NULL )
- target_bl = &sd->bl;
- } else
- target_bl = map_id2bl(conv_num(st, data));
- // request the attack
- if( target_bl == NULL )
- {
- script_pushint(st, 0);
- return 0;
- }
-
- // get actiontype
- if( script_hasdata(st,4) )
- actiontype = script_getnum(st,4);
-
- switch( unit_bl->type )
- {
- case BL_PC:
- clif_parse_ActionRequest_sub(((TBL_PC *)unit_bl), actiontype > 0 ? 0x07 : 0x00, target_bl->id, gettick());
- script_pushint(st, 1);
- return 0;
- case BL_MOB:
- ((TBL_MOB *)unit_bl)->target_id = target_bl->id;
- break;
- case BL_PET:
- ((TBL_PET *)unit_bl)->target_id = target_bl->id;
- break;
- default:
- ShowError("script:unitattack: unsupported source unit type %d\n", unit_bl->type);
- script_pushint(st, 0);
- return 1;
- }
- script_pushint(st, unit_walktobl(unit_bl, target_bl, 65025, 2));
- return 0;
-}
-
-/// Makes the unit stop attacking and moving
-///
-/// unitstop <unit_id>;
-BUILDIN_FUNC(unitstop)
-{
- int unit_id;
- struct block_list* bl;
-
- unit_id = script_getnum(st,2);
-
- bl = map_id2bl(unit_id);
- if( bl != NULL )
- {
- unit_stop_attack(bl);
- unit_stop_walking(bl,4);
- if( bl->type == BL_MOB )
- ((TBL_MOB*)bl)->target_id = 0;
- }
-
- return 0;
-}
-
-/// Makes the unit say the message
-///
-/// unittalk <unit_id>,"<message>";
-BUILDIN_FUNC(unittalk)
-{
- int unit_id;
- const char* message;
- struct block_list* bl;
-
- unit_id = script_getnum(st,2);
- message = script_getstr(st, 3);
-
- bl = map_id2bl(unit_id);
- if( bl != NULL )
- {
- struct StringBuf sbuf;
- StringBuf_Init(&sbuf);
- StringBuf_Printf(&sbuf, "%s : %s", status_get_name(bl), message);
- clif_message(bl, StringBuf_Value(&sbuf));
- if( bl->type == BL_PC )
- clif_displaymessage(((TBL_PC*)bl)->fd, StringBuf_Value(&sbuf));
- StringBuf_Destroy(&sbuf);
- }
-
- return 0;
-}
-
-/// Makes the unit do an emotion
-///
-/// unitemote <unit_id>,<emotion>;
-///
-/// @see e_* in const.txt
-BUILDIN_FUNC(unitemote)
-{
- int unit_id;
- int emotion;
- struct block_list* bl;
-
- unit_id = script_getnum(st,2);
- emotion = script_getnum(st,3);
- bl = map_id2bl(unit_id);
- if( bl != NULL )
- clif_emotion(bl, emotion);
-
- return 0;
-}
-
-/// Makes the unit cast the skill on the target or self if no target is specified
-///
-/// unitskilluseid <unit_id>,<skill_id>,<skill_lv>{,<target_id>};
-/// unitskilluseid <unit_id>,"<skill name>",<skill_lv>{,<target_id>};
-BUILDIN_FUNC(unitskilluseid)
-{
- int unit_id;
- uint16 skill_id;
- uint16 skill_lv;
- int target_id;
- struct block_list* bl;
-
- unit_id = script_getnum(st,2);
- skill_id = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) );
- skill_lv = script_getnum(st,4);
- target_id = ( script_hasdata(st,5) ? script_getnum(st,5) : unit_id );
-
- bl = map_id2bl(unit_id);
- if( bl != NULL )
- unit_skilluse_id(bl, target_id, skill_id, skill_lv);
-
- return 0;
-}
-
-/// Makes the unit cast the skill on the target position.
-///
-/// unitskillusepos <unit_id>,<skill_id>,<skill_lv>,<target_x>,<target_y>;
-/// unitskillusepos <unit_id>,"<skill name>",<skill_lv>,<target_x>,<target_y>;
-BUILDIN_FUNC(unitskillusepos)
-{
- int unit_id;
- uint16 skill_id;
- uint16 skill_lv;
- int skill_x;
- int skill_y;
- struct block_list* bl;
-
- unit_id = script_getnum(st,2);
- skill_id = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) );
- skill_lv = script_getnum(st,4);
- skill_x = script_getnum(st,5);
- skill_y = script_getnum(st,6);
-
- bl = map_id2bl(unit_id);
- if( bl != NULL )
- unit_skilluse_pos(bl, skill_x, skill_y, skill_id, skill_lv);
-
- return 0;
-}
-
-// <--- [zBuffer] List of mob control commands
-
-/// Pauses the execution of the script, detaching the player
-///
-/// sleep <mili seconds>;
-BUILDIN_FUNC(sleep)
-{
- int ticks;
-
- ticks = script_getnum(st,2);
-
- // detach the player
- script_detach_rid(st);
-
- if( ticks <= 0 )
- {// do nothing
- }
- else if( st->sleep.tick == 0 )
- {// sleep for the target amount of time
- st->state = RERUNLINE;
- st->sleep.tick = ticks;
- }
- else
- {// sleep time is over
- st->state = RUN;
- st->sleep.tick = 0;
- }
- return 0;
-}
-
-/// Pauses the execution of the script, keeping the player attached
-/// Returns if a player is still attached
-///
-/// sleep2(<mili secconds>) -> <bool>
-BUILDIN_FUNC(sleep2)
-{
- int ticks;
-
- ticks = script_getnum(st,2);
-
- if( ticks <= 0 )
- {// do nothing
- script_pushint(st, (map_id2sd(st->rid)!=NULL));
- }
- else if( !st->sleep.tick )
- {// sleep for the target amount of time
- st->state = RERUNLINE;
- st->sleep.tick = ticks;
- }
- else
- {// sleep time is over
- st->state = RUN;
- st->sleep.tick = 0;
- script_pushint(st, (map_id2sd(st->rid)!=NULL));
- }
- return 0;
-}
-
-/// Awakes all the sleep timers of the target npc
-///
-/// awake "<npc name>";
-BUILDIN_FUNC(awake)
-{
- struct npc_data* nd;
- struct linkdb_node *node = (struct linkdb_node *)sleep_db;
-
- nd = npc_name2id(script_getstr(st, 2));
- if( nd == NULL ) {
- ShowError("awake: NPC \"%s\" not found\n", script_getstr(st, 2));
- return 1;
- }
-
- while( node )
- {
- if( (int)__64BPRTSIZE(node->key) == nd->bl.id )
- {// sleep timer for the npc
- struct script_state* tst = (struct script_state*)node->data;
- TBL_PC* sd = map_id2sd(tst->rid);
-
- if( tst->sleep.timer == INVALID_TIMER )
- {// already awake ???
- node = node->next;
- continue;
- }
- if( (sd && sd->status.char_id != tst->sleep.charid) || (tst->rid && !sd))
- {// char not online anymore / another char of the same account is online - Cancel execution
- tst->state = END;
- tst->rid = 0;
- }
-
- delete_timer(tst->sleep.timer, run_script_timer);
- node = script_erase_sleepdb(node);
- tst->sleep.timer = INVALID_TIMER;
- if(tst->state != RERUNLINE)
- tst->sleep.tick = 0;
- run_script_main(tst);
- }
- else
- {
- node = node->next;
- }
- }
- return 0;
-}
-
-/// Returns a reference to a variable of the target NPC.
-/// Returns 0 if an error occurs.
-///
-/// getvariableofnpc(<variable>, "<npc name>") -> <reference>
-BUILDIN_FUNC(getvariableofnpc)
-{
- struct script_data* data;
- const char* name;
- struct npc_data* nd;
-
- data = script_getdata(st,2);
- if( !data_isreference(data) )
- {// Not a reference (aka varaible name)
- ShowError("script:getvariableofnpc: not a variable\n");
- script_reportdata(data);
- script_pushnil(st);
- st->state = END;
- return 1;
- }
-
- name = reference_getname(data);
- if( *name != '.' || name[1] == '@' )
- {// not a npc variable
- ShowError("script:getvariableofnpc: invalid scope (not npc variable)\n");
- script_reportdata(data);
- script_pushnil(st);
- st->state = END;
- return 1;
- }
-
- nd = npc_name2id(script_getstr(st,3));
- if( nd == NULL || nd->subtype != SCRIPT || nd->u.scr.script == NULL )
- {// NPC not found or has no script
- ShowError("script:getvariableofnpc: can't find npc %s\n", script_getstr(st,3));
- script_pushnil(st);
- st->state = END;
- return 1;
- }
-
- push_val2(st->stack, C_NAME, reference_getuid(data), &nd->u.scr.script->script_vars );
- return 0;
-}
-
-/// Opens a warp portal.
-/// Has no "portal opening" effect/sound, it opens the portal immediately.
-///
-/// warpportal <source x>,<source y>,"<target map>",<target x>,<target y>;
-///
-/// @author blackhole89
-BUILDIN_FUNC(warpportal)
-{
- int spx;
- int spy;
- unsigned short mapindex;
- int tpx;
- int tpy;
- struct skill_unit_group* group;
- struct block_list* bl;
-
- bl = map_id2bl(st->oid);
- if( bl == NULL )
- {
- ShowError("script:warpportal: npc is needed\n");
- return 1;
- }
-
- spx = script_getnum(st,2);
- spy = script_getnum(st,3);
- mapindex = mapindex_name2id(script_getstr(st, 4));
- tpx = script_getnum(st,5);
- tpy = script_getnum(st,6);
-
- if( mapindex == 0 )
- return 0;// map not found
-
- group = skill_unitsetting(bl, AL_WARP, 4, spx, spy, 0);
- if( group == NULL )
- return 0;// failed
- group->val2 = (tpx<<16) | tpy;
- group->val3 = mapindex;
-
- return 0;
-}
-
-BUILDIN_FUNC(openmail)
-{
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- mail_openmail(sd);
-
- return 0;
-}
-
-BUILDIN_FUNC(openauction)
-{
- TBL_PC* sd;
-
- sd = script_rid2sd(st);
- if( sd == NULL )
- return 0;
-
- clif_Auction_openwindow(sd);
-
- return 0;
-}
-
-/// Retrieves the value of the specified flag of the specified cell.
-///
-/// checkcell("<map name>",<x>,<y>,<type>) -> <bool>
-///
-/// @see cell_chk* constants in const.txt for the types
-BUILDIN_FUNC(checkcell)
-{
- int16 m = map_mapname2mapid(script_getstr(st,2));
- int16 x = script_getnum(st,3);
- int16 y = script_getnum(st,4);
- cell_chk type = (cell_chk)script_getnum(st,5);
-
- script_pushint(st, map_getcell(m, x, y, type));
-
- return 0;
-}
-
-/// Modifies flags of cells in the specified area.
-///
-/// setcell "<map name>",<x1>,<y1>,<x2>,<y2>,<type>,<flag>;
-///
-/// @see cell_* constants in const.txt for the types
-BUILDIN_FUNC(setcell)
-{
- int16 m = map_mapname2mapid(script_getstr(st,2));
- int16 x1 = script_getnum(st,3);
- int16 y1 = script_getnum(st,4);
- int16 x2 = script_getnum(st,5);
- int16 y2 = script_getnum(st,6);
- cell_t type = (cell_t)script_getnum(st,7);
- bool flag = (bool)script_getnum(st,8);
-
- int x,y;
-
- if( x1 > x2 ) swap(x1,x2);
- if( y1 > y2 ) swap(y1,y2);
-
- for( y = y1; y <= y2; ++y )
- for( x = x1; x <= x2; ++x )
- map_setcell(m, x, y, type, flag);
-
- return 0;
-}
-
-/*==========================================
- * Mercenary Commands
- *------------------------------------------*/
-BUILDIN_FUNC(mercenary_create)
-{
- struct map_session_data *sd;
- int class_, contract_time;
-
- if( (sd = script_rid2sd(st)) == NULL || sd->md || sd->status.mer_id != 0 )
- return 0;
-
- class_ = script_getnum(st,2);
-
- if( !merc_class(class_) )
- return 0;
-
- contract_time = script_getnum(st,3);
- merc_create(sd, class_, contract_time);
- return 0;
-}
-
-BUILDIN_FUNC(mercenary_heal)
-{
- struct map_session_data *sd = script_rid2sd(st);
- int hp, sp;
-
- if( sd == NULL || sd->md == NULL )
- return 0;
- hp = script_getnum(st,2);
- sp = script_getnum(st,3);
-
- status_heal(&sd->md->bl, hp, sp, 0);
- return 0;
-}
-
-BUILDIN_FUNC(mercenary_sc_start)
-{
- struct map_session_data *sd = script_rid2sd(st);
- enum sc_type type;
- int tick, val1;
-
- if( sd == NULL || sd->md == NULL )
- return 0;
-
- type = (sc_type)script_getnum(st,2);
- tick = script_getnum(st,3);
- val1 = script_getnum(st,4);
-
- status_change_start(&sd->md->bl, type, 10000, val1, 0, 0, 0, tick, 2);
- return 0;
-}
-
-BUILDIN_FUNC(mercenary_get_calls)
-{
- struct map_session_data *sd = script_rid2sd(st);
- int guild;
-
- if( sd == NULL )
- return 0;
-
- guild = script_getnum(st,2);
- switch( guild )
- {
- case ARCH_MERC_GUILD:
- script_pushint(st,sd->status.arch_calls);
- break;
- case SPEAR_MERC_GUILD:
- script_pushint(st,sd->status.spear_calls);
- break;
- case SWORD_MERC_GUILD:
- script_pushint(st,sd->status.sword_calls);
- break;
- default:
- script_pushint(st,0);
- break;
- }
-
- return 0;
-}
-
-BUILDIN_FUNC(mercenary_set_calls)
-{
- struct map_session_data *sd = script_rid2sd(st);
- int guild, value, *calls;
-
- if( sd == NULL )
- return 0;
-
- guild = script_getnum(st,2);
- value = script_getnum(st,3);
-
- switch( guild )
- {
- case ARCH_MERC_GUILD:
- calls = &sd->status.arch_calls;
- break;
- case SPEAR_MERC_GUILD:
- calls = &sd->status.spear_calls;
- break;
- case SWORD_MERC_GUILD:
- calls = &sd->status.sword_calls;
- break;
- default:
- return 0; // Invalid Guild
- }
-
- *calls += value;
- *calls = cap_value(*calls, 0, INT_MAX);
-
- return 0;
-}
-
-BUILDIN_FUNC(mercenary_get_faith)
-{
- struct map_session_data *sd = script_rid2sd(st);
- int guild;
-
- if( sd == NULL )
- return 0;
-
- guild = script_getnum(st,2);
- switch( guild )
- {
- case ARCH_MERC_GUILD:
- script_pushint(st,sd->status.arch_faith);
- break;
- case SPEAR_MERC_GUILD:
- script_pushint(st,sd->status.spear_faith);
- break;
- case SWORD_MERC_GUILD:
- script_pushint(st,sd->status.sword_faith);
- break;
- default:
- script_pushint(st,0);
- break;
- }
-
- return 0;
-}
-
-BUILDIN_FUNC(mercenary_set_faith)
-{
- struct map_session_data *sd = script_rid2sd(st);
- int guild, value, *calls;
-
- if( sd == NULL )
- return 0;
-
- guild = script_getnum(st,2);
- value = script_getnum(st,3);
-
- switch( guild )
- {
- case ARCH_MERC_GUILD:
- calls = &sd->status.arch_faith;
- break;
- case SPEAR_MERC_GUILD:
- calls = &sd->status.spear_faith;
- break;
- case SWORD_MERC_GUILD:
- calls = &sd->status.sword_faith;
- break;
- default:
- return 0; // Invalid Guild
- }
-
- *calls += value;
- *calls = cap_value(*calls, 0, INT_MAX);
- if( mercenary_get_guild(sd->md) == guild )
- clif_mercenary_updatestatus(sd,SP_MERCFAITH);
-
- return 0;
-}
-
-/*------------------------------------------
- * Book Reading
- *------------------------------------------*/
-BUILDIN_FUNC(readbook)
-{
- struct map_session_data *sd;
- int book_id, page;
-
- if( (sd = script_rid2sd(st)) == NULL )
- return 0;
-
- book_id = script_getnum(st,2);
- page = script_getnum(st,3);
-
- clif_readbook(sd->fd, book_id, page);
- return 0;
-}
-
-/******************
-Questlog script commands
-*******************/
-
-BUILDIN_FUNC(setquest)
-{
- struct map_session_data *sd = script_rid2sd(st);
- nullpo_ret(sd);
-
- quest_add(sd, script_getnum(st, 2));
- return 0;
-}
-
-BUILDIN_FUNC(erasequest)
-{
- struct map_session_data *sd = script_rid2sd(st);
- nullpo_ret(sd);
-
- quest_delete(sd, script_getnum(st, 2));
- return 0;
-}
-
-BUILDIN_FUNC(completequest)
-{
- struct map_session_data *sd = script_rid2sd(st);
- nullpo_ret(sd);
-
- quest_update_status(sd, script_getnum(st, 2), Q_COMPLETE);
- return 0;
-}
-
-BUILDIN_FUNC(changequest)
-{
- struct map_session_data *sd = script_rid2sd(st);
- nullpo_ret(sd);
-
- quest_change(sd, script_getnum(st, 2),script_getnum(st, 3));
- return 0;
-}
-
-BUILDIN_FUNC(checkquest)
-{
- struct map_session_data *sd = script_rid2sd(st);
- quest_check_type type = HAVEQUEST;
-
- nullpo_ret(sd);
-
- if( script_hasdata(st, 3) )
- type = (quest_check_type)script_getnum(st, 3);
-
- script_pushint(st, quest_check(sd, script_getnum(st, 2), type));
-
- return 0;
-}
-
-BUILDIN_FUNC(showevent)
-{
- TBL_PC *sd = script_rid2sd(st);
- struct npc_data *nd = map_id2nd(st->oid);
- int state, color;
-
- if( sd == NULL || nd == NULL )
- return 0;
- state = script_getnum(st, 2);
- color = script_getnum(st, 3);
-
- if( color < 0 || color > 3 )
- color = 0; // set default color
-
- clif_quest_show_event(sd, &nd->bl, state, color);
- return 0;
-}
-
-/*==========================================
- * BattleGround System
- *------------------------------------------*/
-BUILDIN_FUNC(waitingroom2bg)
-{
- struct npc_data *nd;
- struct chat_data *cd;
- const char *map_name, *ev = "", *dev = "";
- int x, y, i, mapindex = 0, bg_id, n;
- struct map_session_data *sd;
-
- if( script_hasdata(st,7) )
- nd = npc_name2id(script_getstr(st,7));
- else
- nd = (struct npc_data *)map_id2bl(st->oid);
-
- if( nd == NULL || (cd = (struct chat_data *)map_id2bl(nd->chat_id)) == NULL )
- {
- script_pushint(st,0);
- return 0;
- }
-
- map_name = script_getstr(st,2);
- if( strcmp(map_name,"-") != 0 )
- {
- mapindex = mapindex_name2id(map_name);
- if( mapindex == 0 )
- { // Invalid Map
- script_pushint(st,0);
- return 0;
- }
- }
-
- x = script_getnum(st,3);
- y = script_getnum(st,4);
- ev = script_getstr(st,5); // Logout Event
- dev = script_getstr(st,6); // Die Event
-
- if( (bg_id = bg_create(mapindex, x, y, ev, dev)) == 0 )
- { // Creation failed
- script_pushint(st,0);
- return 0;
- }
-
- n = cd->users;
- for( i = 0; i < n && i < MAX_BG_MEMBERS; i++ )
- {
- if( (sd = cd->usersd[i]) != NULL && bg_team_join(bg_id, sd) )
- mapreg_setreg(reference_uid(add_str("$@arenamembers"), i), sd->bl.id);
- else
- mapreg_setreg(reference_uid(add_str("$@arenamembers"), i), 0);
- }
-
- mapreg_setreg(add_str("$@arenamembersnum"), i);
- script_pushint(st,bg_id);
- return 0;
-}
-
-BUILDIN_FUNC(waitingroom2bg_single)
-{
- const char* map_name;
- struct npc_data *nd;
- struct chat_data *cd;
- struct map_session_data *sd;
- int x, y, mapindex, bg_id;
-
- bg_id = script_getnum(st,2);
- map_name = script_getstr(st,3);
- if( (mapindex = mapindex_name2id(map_name)) == 0 )
- return 0; // Invalid Map
-
- x = script_getnum(st,4);
- y = script_getnum(st,5);
- nd = npc_name2id(script_getstr(st,6));
-
- if( nd == NULL || (cd = (struct chat_data *)map_id2bl(nd->chat_id)) == NULL || cd->users <= 0 )
- return 0;
-
- if( (sd = cd->usersd[0]) == NULL )
- return 0;
-
- if( bg_team_join(bg_id, sd) )
- {
- pc_setpos(sd, mapindex, x, y, CLR_TELEPORT);
- script_pushint(st,1);
- }
- else
- script_pushint(st,0);
-
- return 0;
-}
-
-BUILDIN_FUNC(bg_team_setxy)
-{
- struct battleground_data *bg;
- int bg_id;
-
- bg_id = script_getnum(st,2);
- if( (bg = bg_team_search(bg_id)) == NULL )
- return 0;
-
- bg->x = script_getnum(st,3);
- bg->y = script_getnum(st,4);
- return 0;
-}
-
-BUILDIN_FUNC(bg_warp)
-{
- int x, y, mapindex, bg_id;
- const char* map_name;
-
- bg_id = script_getnum(st,2);
- map_name = script_getstr(st,3);
- if( (mapindex = mapindex_name2id(map_name)) == 0 )
- return 0; // Invalid Map
- x = script_getnum(st,4);
- y = script_getnum(st,5);
- bg_team_warp(bg_id, mapindex, x, y);
- return 0;
-}
-
-BUILDIN_FUNC(bg_monster)
-{
- int class_ = 0, x = 0, y = 0, bg_id = 0;
- const char *str,*map, *evt="";
-
- bg_id = script_getnum(st,2);
- map = script_getstr(st,3);
- x = script_getnum(st,4);
- y = script_getnum(st,5);
- str = script_getstr(st,6);
- class_ = script_getnum(st,7);
- if( script_hasdata(st,8) ) evt = script_getstr(st,8);
- check_event(st, evt);
- script_pushint(st, mob_spawn_bg(map,x,y,str,class_,evt,bg_id));
- return 0;
-}
-
-BUILDIN_FUNC(bg_monster_set_team)
-{
- struct mob_data *md;
- struct block_list *mbl;
- int id = script_getnum(st,2),
- bg_id = script_getnum(st,3);
-
- if( (mbl = map_id2bl(id)) == NULL || mbl->type != BL_MOB )
- return 0;
- md = (TBL_MOB *)mbl;
- md->bg_id = bg_id;
-
- mob_stop_attack(md);
- mob_stop_walking(md, 0);
- md->target_id = md->attacked_id = 0;
- clif_charnameack(0, &md->bl);
-
- return 0;
-}
-
-BUILDIN_FUNC(bg_leave)
-{
- struct map_session_data *sd = script_rid2sd(st);
- if( sd == NULL || !sd->bg_id )
- return 0;
-
- bg_team_leave(sd,0);
- return 0;
-}
-
-BUILDIN_FUNC(bg_destroy)
-{
- int bg_id = script_getnum(st,2);
- bg_team_delete(bg_id);
- return 0;
-}
-
-BUILDIN_FUNC(bg_getareausers)
-{
- const char *str;
- int16 m, x0, y0, x1, y1;
- int bg_id;
- int i = 0, c = 0;
- struct battleground_data *bg = NULL;
- struct map_session_data *sd;
-
- bg_id = script_getnum(st,2);
- str = script_getstr(st,3);
-
- if( (bg = bg_team_search(bg_id)) == NULL || (m = map_mapname2mapid(str)) < 0 )
- {
- script_pushint(st,0);
- return 0;
- }
-
- x0 = script_getnum(st,4);
- y0 = script_getnum(st,5);
- x1 = script_getnum(st,6);
- y1 = script_getnum(st,7);
-
- for( i = 0; i < MAX_BG_MEMBERS; i++ )
- {
- if( (sd = bg->members[i].sd) == NULL )
- continue;
- if( sd->bl.m != m || sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1 )
- continue;
- c++;
- }
-
- script_pushint(st,c);
- return 0;
-}
-
-BUILDIN_FUNC(bg_updatescore)
-{
- const char *str;
- int16 m;
-
- str = script_getstr(st,2);
- if( (m = map_mapname2mapid(str)) < 0 )
- return 0;
-
- map[m].bgscore_lion = script_getnum(st,3);
- map[m].bgscore_eagle = script_getnum(st,4);
-
- clif_bg_updatescore(m);
- return 0;
-}
-
-BUILDIN_FUNC(bg_get_data)
-{
- struct battleground_data *bg;
- int bg_id = script_getnum(st,2),
- type = script_getnum(st,3);
-
- if( (bg = bg_team_search(bg_id)) == NULL )
- {
- script_pushint(st,0);
- return 0;
- }
-
- switch( type )
- {
- case 0: script_pushint(st, bg->count); break;
- default:
- ShowError("script:bg_get_data: unknown data identifier %d\n", type);
- break;
- }
-
- return 0;
-}
-
-/*==========================================
- * Instancing Script Commands
- *------------------------------------------*/
-
-BUILDIN_FUNC(instance_create)
-{
- const char *name;
- int party_id, res;
-
- name = script_getstr(st, 2);
- party_id = script_getnum(st, 3);
-
- res = instance_create(party_id, name);
- if( res == -4 ) // Already exists
- {
- script_pushint(st, -1);
- return 0;
- }
- else if( res < 0 )
- {
- const char *err;
- switch(res)
- {
- case -3: err = "No free instances"; break;
- case -2: err = "Invalid party ID"; break;
- case -1: err = "Invalid type"; break;
- default: err = "Unknown"; break;
- }
- ShowError("buildin_instance_create: %s [%d].\n", err, res);
- script_pushint(st, -2);
- return 0;
- }
-
- script_pushint(st, res);
- return 0;
-}
-
-BUILDIN_FUNC(instance_destroy)
-{
- int instance_id;
- struct map_session_data *sd;
- struct party_data *p;
-
- if( script_hasdata(st, 2) )
- instance_id = script_getnum(st, 2);
- else if( st->instance_id )
- instance_id = st->instance_id;
- else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
- instance_id = p->instance_id;
- else return 0;
-
- if( instance_id <= 0 || instance_id >= MAX_INSTANCE )
- {
- ShowError("buildin_instance_destroy: Trying to destroy invalid instance %d.\n", instance_id);
- return 0;
- }
-
- instance_destroy(instance_id);
- return 0;
-}
-
-BUILDIN_FUNC(instance_attachmap)
-{
- const char *name;
- int16 m;
- int instance_id;
- bool usebasename = false;
-
- name = script_getstr(st,2);
- instance_id = script_getnum(st,3);
- if( script_hasdata(st,4) && script_getnum(st,4) > 0)
- usebasename = true;
-
- if( (m = instance_add_map(name, instance_id, usebasename)) < 0 ) // [Saithis]
- {
- ShowError("buildin_instance_attachmap: instance creation failed (%s): %d\n", name, m);
- script_pushconststr(st, "");
- return 0;
- }
- script_pushconststr(st, map[m].name);
-
- return 0;
-}
-
-BUILDIN_FUNC(instance_detachmap)
-{
- struct map_session_data *sd;
- struct party_data *p;
- const char *str;
- int16 m;
- int instance_id;
-
- str = script_getstr(st, 2);
- if( script_hasdata(st, 3) )
- instance_id = script_getnum(st, 3);
- else if( st->instance_id )
- instance_id = st->instance_id;
- else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
- instance_id = p->instance_id;
- else return 0;
-
- if( (m = map_mapname2mapid(str)) < 0 || (m = instance_map2imap(m,instance_id)) < 0 )
- {
- ShowError("buildin_instance_detachmap: Trying to detach invalid map %s\n", str);
- return 0;
- }
-
- instance_del_map(m);
- return 0;
-}
-
-BUILDIN_FUNC(instance_attach)
-{
- int instance_id;
-
- instance_id = script_getnum(st, 2);
- if( instance_id <= 0 || instance_id >= MAX_INSTANCE )
- return 0;
-
- st->instance_id = instance_id;
- return 0;
-}
-
-BUILDIN_FUNC(instance_id)
-{
- int type, instance_id;
- struct map_session_data *sd;
- struct party_data *p;
-
- if( script_hasdata(st, 2) )
- {
- type = script_getnum(st, 2);
- if( type == 0 )
- instance_id = st->instance_id;
- else if( type == 1 && (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL )
- instance_id = p->instance_id;
- else
- instance_id = 0;
- }
- else
- instance_id = st->instance_id;
-
- script_pushint(st, instance_id);
- return 0;
-}
-
-BUILDIN_FUNC(instance_set_timeout)
-{
- int progress_timeout, idle_timeout;
- int instance_id;
- struct map_session_data *sd;
- struct party_data *p;
-
- progress_timeout = script_getnum(st, 2);
- idle_timeout = script_getnum(st, 3);
-
- if( script_hasdata(st, 4) )
- instance_id = script_getnum(st, 4);
- else if( st->instance_id )
- instance_id = st->instance_id;
- else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
- instance_id = p->instance_id;
- else return 0;
-
- if( instance_id > 0 )
- instance_set_timeout(instance_id, progress_timeout, idle_timeout);
-
- return 0;
-}
-
-BUILDIN_FUNC(instance_init)
-{
- int instance_id = script_getnum(st, 2);
-
- if( instance[instance_id].state != INSTANCE_IDLE )
- {
- ShowError("instance_init: instance already initialized.\n");
- return 0;
- }
-
- instance_init(instance_id);
- return 0;
-}
-
-BUILDIN_FUNC(instance_announce)
-{
- int instance_id = script_getnum(st,2);
- const char *mes = script_getstr(st,3);
- int flag = script_getnum(st,4);
- const char *fontColor = script_hasdata(st,5) ? script_getstr(st,5) : NULL;
- int fontType = script_hasdata(st,6) ? script_getnum(st,6) : 0x190; // default fontType (FW_NORMAL)
- int fontSize = script_hasdata(st,7) ? script_getnum(st,7) : 12; // default fontSize
- int fontAlign = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontAlign
- int fontY = script_hasdata(st,9) ? script_getnum(st,9) : 0; // default fontY
-
- int i;
- struct map_session_data *sd;
- struct party_data *p;
-
- if( instance_id == 0 )
- {
- if( st->instance_id )
- instance_id = st->instance_id;
- else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
- instance_id = p->instance_id;
- else return 0;
- }
-
- if( instance_id <= 0 || instance_id >= MAX_INSTANCE )
- return 0;
-
- for( i = 0; i < instance[instance_id].num_map; i++ )
- map_foreachinmap(buildin_announce_sub, instance[instance_id].map[i], BL_PC,
- mes, strlen(mes)+1, flag&0xf0, fontColor, fontType, fontSize, fontAlign, fontY);
-
- return 0;
-}
-
-BUILDIN_FUNC(instance_npcname)
-{
- const char *str;
- int instance_id = 0;
-
- struct map_session_data *sd;
- struct party_data *p;
- struct npc_data *nd;
-
- str = script_getstr(st, 2);
- if( script_hasdata(st, 3) )
- instance_id = script_getnum(st, 3);
- else if( st->instance_id )
- instance_id = st->instance_id;
- else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
- instance_id = p->instance_id;
-
- if( instance_id && (nd = npc_name2id(str)) != NULL )
- {
- static char npcname[NAME_LENGTH];
- snprintf(npcname, sizeof(npcname), "dup_%d_%d", instance_id, nd->bl.id);
- script_pushconststr(st,npcname);
- }
- else
- {
- ShowError("script:instance_npcname: invalid instance NPC (instance_id: %d, NPC name: \"%s\".)\n", instance_id, str);
- st->state = END;
- return 1;
- }
-
- return 0;
-}
-
-BUILDIN_FUNC(has_instance)
-{
- struct map_session_data *sd;
- struct party_data *p;
- const char *str;
- int16 m;
- int instance_id = 0;
-
- str = script_getstr(st, 2);
- if( script_hasdata(st, 3) )
- instance_id = script_getnum(st, 3);
- else if( st->instance_id )
- instance_id = st->instance_id;
- else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
- instance_id = p->instance_id;
-
- if( !instance_id || (m = map_mapname2mapid(str)) < 0 || (m = instance_map2imap(m, instance_id)) < 0 )
- {
- script_pushconststr(st, "");
- return 0;
- }
-
- script_pushconststr(st, map[m].name);
- return 0;
-}
-
-BUILDIN_FUNC(instance_warpall)
-{
- struct map_session_data *pl_sd;
- int16 m, i;
- int instance_id;
- const char *mapn;
- int x, y;
- unsigned short mapindex;
- struct party_data *p = NULL;
-
- mapn = script_getstr(st,2);
- x = script_getnum(st,3);
- y = script_getnum(st,4);
- if( script_hasdata(st,5) )
- instance_id = script_getnum(st,5);
- else if( st->instance_id )
- instance_id = st->instance_id;
- else if( (pl_sd = script_rid2sd(st)) != NULL && pl_sd->status.party_id && (p = party_search(pl_sd->status.party_id)) != NULL && p->instance_id )
- instance_id = p->instance_id;
- else return 0;
-
- if( (m = map_mapname2mapid(mapn)) < 0 || (map[m].flag.src4instance && (m = instance_mapid2imapid(m, instance_id)) < 0) )
- return 0;
-
- if( !(p = party_search(instance[instance_id].party_id)) )
- return 0;
-
- mapindex = map_id2index(m);
- for( i = 0; i < MAX_PARTY; i++ )
- if( (pl_sd = p->data[i].sd) && map[pl_sd->bl.m].instance_id == st->instance_id ) pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT);
-
- return 0;
-}
-
-/*==========================================
- * instance_check_party [malufett]
- * Values:
- * party_id : Party ID of the invoking character. [Required Parameter]
- * amount : Amount of needed Partymembers for the Instance. [Optional Parameter]
- * min : Minimum Level needed to join the Instance. [Optional Parameter]
- * max : Maxium Level allowed to join the Instance. [Optional Parameter]
- * Example: instance_check_party (getcharid(1){,amount}{,min}{,max});
- * Example 2: instance_check_party (getcharid(1),1,1,99);
- *------------------------------------------*/
-BUILDIN_FUNC(instance_check_party)
-{
- struct map_session_data *pl_sd;
- int amount, min, max, i, party_id, c = 0;
- struct party_data *p = NULL;
-
- amount = script_hasdata(st,3) ? script_getnum(st,3) : 1; // Amount of needed Partymembers for the Instance.
- min = script_hasdata(st,4) ? script_getnum(st,4) : 1; // Minimum Level needed to join the Instance.
- max = script_hasdata(st,5) ? script_getnum(st,5) : MAX_LEVEL; // Maxium Level allowed to join the Instance.
-
- if( min < 1 || min > MAX_LEVEL){
- ShowError("instance_check_party: Invalid min level, %d\n", min);
- return 0;
- }else if( max < 1 || max > MAX_LEVEL){
- ShowError("instance_check_party: Invalid max level, %d\n", max);
- return 0;
- }
-
- if( script_hasdata(st,2) )
- party_id = script_getnum(st,2);
- else return 0;
-
- if( !(p = party_search(party_id)) ){
- script_pushint(st, 0); // Returns false if party does not exist.
- return 0;
- }
-
- for( i = 0; i < MAX_PARTY; i++ )
- if( (pl_sd = p->data[i].sd) )
- if(map_id2bl(pl_sd->bl.id)){
- if(pl_sd->status.base_level < min){
- script_pushint(st, 0);
- return 0;
- }else if(pl_sd->status.base_level > max){
- script_pushint(st, 0);
- return 0;
- }
- c++;
- }
-
- if(c < amount){
- script_pushint(st, 0); // Not enough Members in the Party to join Instance.
- }else
- script_pushint(st, 1);
-
- return 0;
-}
-
-/*==========================================
- * Custom Fonts
- *------------------------------------------*/
-BUILDIN_FUNC(setfont)
-{
- struct map_session_data *sd = script_rid2sd(st);
- int font = script_getnum(st,2);
- if( sd == NULL )
- return 0;
-
- if( sd->user_font != font )
- sd->user_font = font;
- else
- sd->user_font = 0;
-
- clif_font(sd);
- return 0;
-}
-
-static int buildin_mobuseskill_sub(struct block_list *bl,va_list ap)
-{
- TBL_MOB* md = (TBL_MOB*)bl;
- struct block_list *tbl;
- int mobid = va_arg(ap,int);
- uint16 skill_id = va_arg(ap,int);
- uint16 skill_lv = va_arg(ap,int);
- int casttime = va_arg(ap,int);
- int cancel = va_arg(ap,int);
- int emotion = va_arg(ap,int);
- int target = va_arg(ap,int);
-
- if( md->class_ != mobid )
- return 0;
-
- // 0:self, 1:target, 2:master, default:random
- switch( target )
- {
- case 0: tbl = map_id2bl(md->bl.id); break;
- case 1: tbl = map_id2bl(md->target_id); break;
- case 2: tbl = map_id2bl(md->master_id); break;
- default:tbl = battle_getenemy(&md->bl, DEFAULT_ENEMY_TYPE(md),skill_get_range2(&md->bl, skill_id, skill_lv)); break;
- }
-
- if( !tbl )
- return 0;
-
- if( md->ud.skilltimer != INVALID_TIMER ) // Cancel the casting skill.
- unit_skillcastcancel(bl,0);
-
- if( skill_get_casttype(skill_id) == CAST_GROUND )
- unit_skilluse_pos2(&md->bl, tbl->x, tbl->y, skill_id, skill_lv, casttime, cancel);
- else
- unit_skilluse_id2(&md->bl, tbl->id, skill_id, skill_lv, casttime, cancel);
-
- clif_emotion(&md->bl, emotion);
-
- return 0;
-}
-/*==========================================
- * areamobuseskill "Map Name",<x>,<y>,<range>,<Mob ID>,"Skill Name"/<Skill ID>,<Skill Lv>,<Cast Time>,<Cancelable>,<Emotion>,<Target Type>;
- *------------------------------------------*/
-BUILDIN_FUNC(areamobuseskill)
-{
- struct block_list center;
- int16 m;
- int range,mobid,skill_id,skill_lv,casttime,emotion,target,cancel;
-
- if( (m = map_mapname2mapid(script_getstr(st,2))) < 0 )
- {
- ShowError("areamobuseskill: invalid map name.\n");
- return 0;
- }
-
- if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 )
- return 0;
-
- center.m = m;
- center.x = script_getnum(st,3);
- center.y = script_getnum(st,4);
- range = script_getnum(st,5);
- mobid = script_getnum(st,6);
- skill_id = ( script_isstring(st,7) ? skill_name2id(script_getstr(st,7)) : script_getnum(st,7) );
- if( (skill_lv = script_getnum(st,8)) > battle_config.mob_max_skilllvl )
- skill_lv = battle_config.mob_max_skilllvl;
-
- casttime = script_getnum(st,9);
- cancel = script_getnum(st,10);
- emotion = script_getnum(st,11);
- target = script_getnum(st,12);
-
- map_foreachinrange(buildin_mobuseskill_sub, &center, range, BL_MOB, mobid, skill_id, skill_lv, casttime, cancel, emotion, target);
- return 0;
-}
-
-
-BUILDIN_FUNC(progressbar)
-{
- struct map_session_data * sd = script_rid2sd(st);
- const char * color;
- unsigned int second;
-
- if( !st || !sd )
- return 0;
-
- st->state = STOP;
-
- color = script_getstr(st,2);
- second = script_getnum(st,3);
-
- sd->progressbar.npc_id = st->oid;
- sd->progressbar.timeout = gettick() + second*1000;
-
- clif_progressbar(sd, strtol(color, (char **)NULL, 0), second);
- return 0;
-}
-
-BUILDIN_FUNC(pushpc)
-{
- uint8 dir;
- int cells, dx, dy;
- struct map_session_data* sd;
-
- if((sd = script_rid2sd(st))==NULL)
- {
- return 0;
- }
-
- dir = script_getnum(st,2);
- cells = script_getnum(st,3);
-
- if(dir>7)
- {
- ShowWarning("buildin_pushpc: Invalid direction %d specified.\n", dir);
- script_reportsrc(st);
-
- dir%= 8; // trim spin-over
- }
-
- if(!cells)
- {// zero distance
- return 0;
- }
- else if(cells<0)
- {// pushing backwards
- dir = (dir+4)%8; // turn around
- cells = -cells;
- }
-
- dx = dirx[dir];
- dy = diry[dir];
-
- unit_blown(&sd->bl, dx, dy, cells, 0);
- return 0;
-}
-
-
-/// Invokes buying store preparation window
-/// buyingstore <slots>;
-BUILDIN_FUNC(buyingstore)
-{
- struct map_session_data* sd;
-
- if( ( sd = script_rid2sd(st) ) == NULL )
- {
- return 0;
- }
-
- buyingstore_setup(sd, script_getnum(st,2));
- return 0;
-}
-
-
-/// Invokes search store info window
-/// searchstores <uses>,<effect>;
-BUILDIN_FUNC(searchstores)
-{
- unsigned short effect;
- unsigned int uses;
- struct map_session_data* sd;
-
- if( ( sd = script_rid2sd(st) ) == NULL )
- {
- return 0;
- }
-
- uses = script_getnum(st,2);
- effect = script_getnum(st,3);
-
- if( !uses )
- {
- ShowError("buildin_searchstores: Amount of uses cannot be zero.\n");
- return 1;
- }
-
- if( effect > 1 )
- {
- ShowError("buildin_searchstores: Invalid effect id %hu, specified.\n", effect);
- return 1;
- }
-
- searchstore_open(sd, uses, effect);
- return 0;
-}
-/// Displays a number as large digital clock.
-/// showdigit <value>[,<type>];
-BUILDIN_FUNC(showdigit)
-{
- unsigned int type = 0;
- int value;
- struct map_session_data* sd;
-
- if( ( sd = script_rid2sd(st) ) == NULL )
- {
- return 0;
- }
-
- value = script_getnum(st,2);
-
- if( script_hasdata(st,3) )
- {
- type = script_getnum(st,3);
-
- if( type > 3 )
- {
- ShowError("buildin_showdigit: Invalid type %u.\n", type);
- return 1;
- }
- }
-
- clif_showdigit(sd, (unsigned char)type, value);
- return 0;
-}
-/**
- * Rune Knight
- **/
-BUILDIN_FUNC(makerune) {
- TBL_PC* sd;
- if( (sd = script_rid2sd(st)) == NULL )
- return 0;
- clif_skill_produce_mix_list(sd,RK_RUNEMASTERY,24);
- sd->itemid = script_getnum(st,2);
- return 0;
-}
-/**
- * checkdragon() returns 1 if mounting a dragon or 0 otherwise.
- **/
-BUILDIN_FUNC(checkdragon) {
- TBL_PC* sd;
- if( (sd = script_rid2sd(st)) == NULL )
- return 0;
- if( pc_isridingdragon(sd) )
- script_pushint(st,1);
- else
- script_pushint(st,0);
- return 0;
-}
-/**
- * setdragon({optional Color}) returns 1 on success or 0 otherwise
- * - Toggles the dragon on a RK if he can mount;
- * @param Color - when not provided uses the green dragon;
- * - 1 : Green Dragon
- * - 2 : Brown Dragon
- * - 3 : Gray Dragon
- * - 4 : Blue Dragon
- * - 5 : Red Dragon
- **/
-BUILDIN_FUNC(setdragon) {
- TBL_PC* sd;
- int color = script_hasdata(st,2) ? script_getnum(st,2) : 0;
- unsigned int option = OPTION_DRAGON1;
- if( (sd = script_rid2sd(st)) == NULL )
- return 0;
- if( !pc_checkskill(sd,RK_DRAGONTRAINING) || (sd->class_&MAPID_THIRDMASK) != MAPID_RUNE_KNIGHT )
- script_pushint(st,0);//Doesn't have the skill or it's not a Rune Knight
- else if ( pc_isridingdragon(sd) ) {//Is mounted; release
- pc_setoption(sd, sd->sc.option&~OPTION_DRAGON);
- script_pushint(st,1);
- } else {//Not mounted; Mount now.
- if( color ) {
- option = ( color == 1 ? OPTION_DRAGON1 :
- color == 2 ? OPTION_DRAGON2 :
- color == 3 ? OPTION_DRAGON3 :
- color == 4 ? OPTION_DRAGON4 :
- color == 5 ? OPTION_DRAGON5 : 0);
- if( !option ) {
- ShowWarning("script_setdragon: Unknown Color %d used; changing to green (1)\n",color);
- option = OPTION_DRAGON1;
- }
- }
- pc_setoption(sd, sd->sc.option|option);
- script_pushint(st,1);
- }
- return 0;
-}
-
-/**
- * ismounting() returns 1 if mounting a new mount or 0 otherwise
- **/
-BUILDIN_FUNC(ismounting) {
- TBL_PC* sd;
- if( (sd = script_rid2sd(st)) == NULL )
- return 0;
- if( sd->sc.option&OPTION_MOUNTING )
- script_pushint(st,1);
- else
- script_pushint(st,0);
- return 0;
-}
-
-/**
- * setmounting() returns 1 on success or 0 otherwise
- * - Toggles new mounts on a player when he can mount
- * - Will fail if the player is mounting a non-new mount, e.g. dragon, peco, wug, etc.
- * - Will unmount the player is he is already mounting
- **/
-BUILDIN_FUNC(setmounting) {
- TBL_PC* sd;
- if( (sd = script_rid2sd(st)) == NULL )
- return 0;
- if( sd->sc.option&(OPTION_WUGRIDER|OPTION_RIDING|OPTION_DRAGON|OPTION_MADOGEAR) )
- script_pushint(st,0);//can't mount with one of these
- else {
- if( sd->sc.option&OPTION_MOUNTING )
- pc_setoption(sd, sd->sc.option&~OPTION_MOUNTING);//release mount
- else
- pc_setoption(sd, sd->sc.option|OPTION_MOUNTING);//mount
- script_pushint(st,1);//in both cases, return 1.
- }
- return 0;
-}
-/**
- * Retrieves quantity of arguments provided to callfunc/callsub.
- * getargcount() -> amount of arguments received in a function
- **/
-BUILDIN_FUNC(getargcount) {
- struct script_retinfo* ri;
-
- if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp - 1].type != C_RETINFO ) {
- ShowError("script:getargcount: used out of function or callsub label!\n");
- st->state = END;
- return 1;
- }
- ri = st->stack->stack_data[st->stack->defsp - 1].u.ri;
-
- script_pushint(st, ri->nargs);
-
- return 0;
-}
-/**
- * getcharip(<account ID>/<character ID>/<character name>)
- **/
-BUILDIN_FUNC(getcharip)
-{
- struct map_session_data* sd = NULL;
- int id = 0;
-
- /* check if a character name is specified */
- if( script_hasdata(st, 2) )
- {
- if (script_isstring(st, 2))
- sd = map_nick2sd(script_getstr(st, 2));
- else if (script_isint(st, 2) || script_getnum(st, 2))
- {
- id = script_getnum(st, 2);
- sd = (map_id2sd(id) ? map_id2sd(id) : map_charid2sd(id));
- }
- }
- else
- sd = script_rid2sd(st);
-
- /* check for sd and IP */
- if (!sd || !session[sd->fd]->client_addr)
- {
- script_pushconststr(st, "");
- return 0;
- }
-
- /* return the client ip_addr converted for output */
- if (sd && sd->fd && session[sd->fd])
- {
- /* initiliaze */
- const char *ip_addr = NULL;
- uint32 ip;
-
- /* set ip, ip_addr and convert to ip and push str */
- ip = session[sd->fd]->client_addr;
- ip_addr = ip2str(ip, NULL);
- script_pushstrcopy(st, ip_addr);
- }
-
- return 0;
-}
-/**
- * is_function(<function name>) -> 1 if function exists, 0 otherwise
- **/
-BUILDIN_FUNC(is_function) {
- const char* str = script_getstr(st,2);
-
- if( strdb_exists(userfunc_db, str) )
- script_pushint(st,1);
- else
- script_pushint(st,0);
-
- return 0;
-}
-/**
- * get_revision() -> retrieves the current svn revision (if available)
- **/
-BUILDIN_FUNC(get_revision) {
- const char * revision;
-
- if ( (revision = get_svn_revision()) != 0 )
- script_pushint(st,atoi(revision));
- else
- script_pushint(st,-1);//unknown
-
- return 0;
-}
-/**
- * freeloop(<toggle>) -> toggles this script instance's looping-check ability
- **/
-BUILDIN_FUNC(freeloop) {
-
- if( script_getnum(st,2) )
- st->freeloop = 1;
- else
- st->freeloop = 0;
-
- script_pushint(st, st->freeloop);
-
- return 0;
-}
-
-/**
- * @commands (script based)
- **/
-BUILDIN_FUNC(bindatcmd) {
- const char* atcmd;
- const char* eventName;
- int i, level = 0, level2 = 0;
- bool create = false;
-
- atcmd = script_getstr(st,2);
- eventName = script_getstr(st,3);
-
- if( *atcmd == atcommand_symbol || *atcmd == charcommand_symbol )
- atcmd++;
-
- if( script_hasdata(st,4) ) level = script_getnum(st,4);
- if( script_hasdata(st,5) ) level2 = script_getnum(st,5);
-
- if( atcmd_binding_count == 0 ) {
- CREATE(atcmd_binding,struct atcmd_binding_data*,1);
-
- create = true;
- } else {
- ARR_FIND(0, atcmd_binding_count, i, strcmp(atcmd_binding[i]->command,atcmd) == 0);
- if( i < atcmd_binding_count ) {/* update existent entry */
- safestrncpy(atcmd_binding[i]->npc_event, eventName, 50);
- atcmd_binding[i]->level = level;
- atcmd_binding[i]->level2 = level2;
- } else
- create = true;
- }
-
- if( create ) {
- i = atcmd_binding_count;
-
- if( atcmd_binding_count++ != 0 )
- RECREATE(atcmd_binding,struct atcmd_binding_data*,atcmd_binding_count);
-
- CREATE(atcmd_binding[i],struct atcmd_binding_data,1);
-
- safestrncpy(atcmd_binding[i]->command, atcmd, 50);
- safestrncpy(atcmd_binding[i]->npc_event, eventName, 50);
- atcmd_binding[i]->level = level;
- atcmd_binding[i]->level2 = level2;
- }
-
- return 0;
-}
-
-BUILDIN_FUNC(unbindatcmd) {
- const char* atcmd;
- int i = 0;
-
- atcmd = script_getstr(st, 2);
-
- if( *atcmd == atcommand_symbol || *atcmd == charcommand_symbol )
- atcmd++;
-
- if( atcmd_binding_count == 0 ) {
- script_pushint(st, 0);
- return 0;
- }
-
- ARR_FIND(0, atcmd_binding_count, i, strcmp(atcmd_binding[i]->command, atcmd) == 0);
- if( i < atcmd_binding_count ) {
- int cursor = 0;
- aFree(atcmd_binding[i]);
- atcmd_binding[i] = NULL;
- /* compact the list now that we freed a slot somewhere */
- for( i = 0, cursor = 0; i < atcmd_binding_count; i++ ) {
- if( atcmd_binding[i] == NULL )
- continue;
-
- if( cursor != i ) {
- memmove(&atcmd_binding[cursor], &atcmd_binding[i], sizeof(struct atcmd_binding_data*));
- }
-
- cursor++;
- }
-
- if( (atcmd_binding_count = cursor) == 0 )
- aFree(atcmd_binding);
-
- script_pushint(st, 1);
- } else
- script_pushint(st, 0);/* not found */
-
- return 0;
-}
-
-BUILDIN_FUNC(useatcmd)
-{
- TBL_PC dummy_sd;
- TBL_PC* sd;
- int fd;
- const char* cmd;
-
- cmd = script_getstr(st,2);
-
- if( st->rid )
- {
- sd = script_rid2sd(st);
- fd = sd->fd;
- }
- else
- { // Use a dummy character.
- sd = &dummy_sd;
- fd = 0;
-
- memset(&dummy_sd, 0, sizeof(TBL_PC));
- if( st->oid )
- {
- struct block_list* bl = map_id2bl(st->oid);
- memcpy(&dummy_sd.bl, bl, sizeof(struct block_list));
- if( bl->type == BL_NPC )
- safestrncpy(dummy_sd.status.name, ((TBL_NPC*)bl)->name, NAME_LENGTH);
- }
- }
-
- // compatibility with previous implementation (deprecated!)
- if( cmd[0] != atcommand_symbol )
- {
- cmd += strlen(sd->status.name);
- while( *cmd != atcommand_symbol && *cmd != 0 )
- cmd++;
- }
-
- is_atcommand(fd, sd, cmd, 1);
- return 0;
-}
-
-BUILDIN_FUNC(checkre)
-{
- int num;
-
- num=script_getnum(st,2);
- switch(num){
- case 0:
- #ifdef RENEWAL
- script_pushint(st, 1);
- #else
- script_pushint(st, 0);
- #endif
- break;
- case 1:
- #ifdef RENEWAL_CAST
- script_pushint(st, 1);
- #else
- script_pushint(st, 0);
- #endif
- break;
- case 2:
- #ifdef RENEWAL_DROP
- script_pushint(st, 1);
- #else
- script_pushint(st, 0);
- #endif
- break;
- case 3:
- #ifdef RENEWAL_EXP
- script_pushint(st, 1);
- #else
- script_pushint(st, 0);
- #endif
- break;
- case 4:
- #ifdef RENEWAL_LVDMG
- script_pushint(st, 1);
- #else
- script_pushint(st, 0);
- #endif
- break;
- case 5:
- #ifdef RENEWAL_EDP
- script_pushint(st, 1);
- #else
- script_pushint(st, 0);
- #endif
- break;
- case 6:
- #ifdef RENEWAL_ASPD
- script_pushint(st, 1);
- #else
- script_pushint(st, 0);
- #endif
- break;
- default:
- ShowWarning("buildin_checkre: unknown parameter.\n");
- break;
- }
- return 0;
-}
-
-/* getrandgroupitem <group_id>,<quantity> */
-BUILDIN_FUNC(getrandgroupitem) {
- TBL_PC* sd;
- int i, get_count = 0, flag, nameid, group = script_getnum(st, 2), qty = script_getnum(st,3);
- struct item item_tmp;
-
- if( !( sd = script_rid2sd(st) ) )
- return 0;
-
- if( qty <= 0 ) {
- ShowError("getrandgroupitem: qty is <= 0!\n");
- return 1;
- }
- if( (nameid = itemdb_searchrandomid(group)) == UNKNOWN_ITEM_ID ) {
- return 1;/* itemdb_searchrandomid will already scream a error */
- }
-
- memset(&item_tmp,0,sizeof(item_tmp));
-
- item_tmp.nameid = nameid;
- item_tmp.identify = itemdb_isidentified(nameid);
-
- //Check if it's stackable.
- if (!itemdb_isstackable(nameid))
- get_count = 1;
- else
- get_count = qty;
-
- for (i = 0; i < qty; i += get_count) {
- // if not pet egg
- if (!pet_create_egg(sd, nameid)) {
- if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_SCRIPT))) {
- clif_additem(sd, 0, 0, flag);
- if( pc_candrop(sd,&item_tmp) )
- map_addflooritem(&item_tmp,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
- }
- }
-
- return 0;
-}
-
-/* cleanmap <map_name>;
- * cleanarea <map_name>, <x0>, <y0>, <x1>, <y1>; */
-static int atcommand_cleanfloor_sub(struct block_list *bl, va_list ap)
-{
- nullpo_ret(bl);
- map_clearflooritem(bl);
-
- return 0;
-}
-
-BUILDIN_FUNC(cleanmap)
-{
- const char *map;
- int16 m = -1;
- int16 x0 = 0, y0 = 0, x1 = 0, y1 = 0;
-
- map = script_getstr(st, 2);
- m = map_mapname2mapid(map);
- if (!m)
- return 1;
-
- if ((script_lastdata(st) - 2) < 4) {
- map_foreachinmap(atcommand_cleanfloor_sub, m, BL_ITEM);
- } else {
- x0 = script_getnum(st, 3);
- y0 = script_getnum(st, 4);
- x1 = script_getnum(st, 5);
- y1 = script_getnum(st, 6);
- if (x0 > 0 && y0 > 0 && x1 > 0 && y1 > 0) {
- map_foreachinarea(atcommand_cleanfloor_sub, m, x0, y0, x1, y1, BL_ITEM);
- } else {
- ShowError("cleanarea: invalid coordinate defined!\n");
- return 1;
- }
- }
-
- return 0;
-}
-/* Cast a skill on the attached player.
- * npcskill <skill id>, <skill lvl>, <stat point>, <NPC level>;
- * npcskill "<skill name>", <skill lvl>, <stat point>, <NPC level>; */
-BUILDIN_FUNC(npcskill)
-{
- uint16 skill_id;
- unsigned short skill_level;
- unsigned int stat_point;
- unsigned int npc_level;
- struct npc_data *nd;
- struct map_session_data *sd;
-
- skill_id = script_isstring(st, 2) ? skill_name2id(script_getstr(st, 2)) : script_getnum(st, 2);
- skill_level = script_getnum(st, 3);
- stat_point = script_getnum(st, 4);
- npc_level = script_getnum(st, 5);
- sd = script_rid2sd(st);
- nd = (struct npc_data *)map_id2bl(sd->npc_id);
-
- if (stat_point > battle_config.max_third_parameter) {
- ShowError("npcskill: stat point exceeded maximum of %d.\n",battle_config.max_third_parameter );
- return 1;
- }
- if (npc_level > MAX_LEVEL) {
- ShowError("npcskill: level exceeded maximum of %d.\n", MAX_LEVEL);
- return 1;
- }
- if (sd == NULL || nd == NULL) { //ain't possible, but I don't trust people.
- return 1;
- }
-
- nd->level = npc_level;
- nd->stat_point = stat_point;
-
- if (!nd->status.hp) {
- status_calc_npc(nd, true);
- } else {
- status_calc_npc(nd, false);
- }
-
- if (skill_get_inf(skill_id)&INF_GROUND_SKILL) {
- unit_skilluse_pos(&nd->bl, sd->bl.x, sd->bl.y, skill_id, skill_level);
- } else {
- unit_skilluse_id(&nd->bl, sd->bl.id, skill_id, skill_level);
- }
-
- return 0;
-}
-
-// declarations that were supposed to be exported from npc_chat.c
-#ifdef PCRE_SUPPORT
-BUILDIN_FUNC(defpattern);
-BUILDIN_FUNC(activatepset);
-BUILDIN_FUNC(deactivatepset);
-BUILDIN_FUNC(deletepset);
-#endif
-
-/// script command definitions
-/// for an explanation on args, see add_buildin_func
-struct script_function buildin_func[] = {
- // NPC interaction
- BUILDIN_DEF(mes,"s*"),
- BUILDIN_DEF(next,""),
- BUILDIN_DEF(close,""),
- BUILDIN_DEF(close2,""),
- BUILDIN_DEF(menu,"sl*"),
- BUILDIN_DEF(select,"s*"), //for future jA script compatibility
- BUILDIN_DEF(prompt,"s*"),
- //
- BUILDIN_DEF(goto,"l"),
- BUILDIN_DEF(callsub,"l*"),
- BUILDIN_DEF(callfunc,"s*"),
- BUILDIN_DEF(return,"?"),
- BUILDIN_DEF(getarg,"i?"),
- BUILDIN_DEF(jobchange,"i?"),
- BUILDIN_DEF(jobname,"i"),
- BUILDIN_DEF(input,"r??"),
- BUILDIN_DEF(warp,"sii"),
- BUILDIN_DEF(areawarp,"siiiisii??"),
- BUILDIN_DEF(warpchar,"siii"), // [LuzZza]
- BUILDIN_DEF(warpparty,"siii?"), // [Fredzilla] [Paradox924X]
- BUILDIN_DEF(warpguild,"siii"), // [Fredzilla]
- BUILDIN_DEF(setlook,"ii"),
- BUILDIN_DEF(changelook,"ii"), // Simulates but don't Store it
- BUILDIN_DEF(set,"rv"),
- BUILDIN_DEF(setarray,"rv*"),
- BUILDIN_DEF(cleararray,"rvi"),
- BUILDIN_DEF(copyarray,"rri"),
- BUILDIN_DEF(getarraysize,"r"),
- BUILDIN_DEF(deletearray,"r?"),
- BUILDIN_DEF(getelementofarray,"ri"),
- BUILDIN_DEF(getitem,"vi?"),
- BUILDIN_DEF(rentitem,"vi"),
- BUILDIN_DEF(getitem2,"viiiiiiii?"),
- BUILDIN_DEF(getnameditem,"vv"),
- BUILDIN_DEF2(grouprandomitem,"groupranditem","i"),
- BUILDIN_DEF(makeitem,"visii"),
- BUILDIN_DEF(delitem,"vi?"),
- BUILDIN_DEF(delitem2,"viiiiiiii?"),
- BUILDIN_DEF2(enableitemuse,"enable_items",""),
- BUILDIN_DEF2(disableitemuse,"disable_items",""),
- BUILDIN_DEF(cutin,"si"),
- BUILDIN_DEF(viewpoint,"iiiii"),
- BUILDIN_DEF(heal,"ii"),
- BUILDIN_DEF(itemheal,"ii"),
- BUILDIN_DEF(percentheal,"ii"),
- BUILDIN_DEF(rand,"i?"),
- BUILDIN_DEF(countitem,"v"),
- BUILDIN_DEF(countitem2,"viiiiiii"),
- BUILDIN_DEF(checkweight,"vi*"),
- BUILDIN_DEF(checkweight2,"rr"),
- BUILDIN_DEF(readparam,"i?"),
- BUILDIN_DEF(getcharid,"i?"),
- BUILDIN_DEF(getnpcid,"i?"),
- BUILDIN_DEF(getpartyname,"i"),
- BUILDIN_DEF(getpartymember,"i?"),
- BUILDIN_DEF(getpartyleader,"i?"),
- BUILDIN_DEF(getguildname,"i"),
- BUILDIN_DEF(getguildmaster,"i"),
- BUILDIN_DEF(getguildmasterid,"i"),
- BUILDIN_DEF(strcharinfo,"i"),
- BUILDIN_DEF(strnpcinfo,"i"),
- BUILDIN_DEF(getequipid,"i"),
- BUILDIN_DEF(getequipname,"i"),
- BUILDIN_DEF(getbrokenid,"i"), // [Valaris]
- BUILDIN_DEF(repair,"i"), // [Valaris]
- BUILDIN_DEF(repairall,""),
- BUILDIN_DEF(getequipisequiped,"i"),
- BUILDIN_DEF(getequipisenableref,"i"),
- BUILDIN_DEF(getequipisidentify,"i"),
- BUILDIN_DEF(getequiprefinerycnt,"i"),
- BUILDIN_DEF(getequipweaponlv,"i"),
- BUILDIN_DEF(getequippercentrefinery,"i"),
- BUILDIN_DEF(successrefitem,"i"),
- BUILDIN_DEF(failedrefitem,"i"),
- BUILDIN_DEF(downrefitem,"i"),
- BUILDIN_DEF(statusup,"i"),
- BUILDIN_DEF(statusup2,"ii"),
- BUILDIN_DEF(bonus,"iv"),
- BUILDIN_DEF2(bonus,"bonus2","ivi"),
- BUILDIN_DEF2(bonus,"bonus3","ivii"),
- BUILDIN_DEF2(bonus,"bonus4","ivvii"),
- BUILDIN_DEF2(bonus,"bonus5","ivviii"),
- BUILDIN_DEF(autobonus,"sii??"),
- BUILDIN_DEF(autobonus2,"sii??"),
- BUILDIN_DEF(autobonus3,"siiv?"),
- BUILDIN_DEF(skill,"vi?"),
- BUILDIN_DEF(addtoskill,"vi?"), // [Valaris]
- BUILDIN_DEF(guildskill,"vi"),
- BUILDIN_DEF(getskilllv,"v"),
- BUILDIN_DEF(getgdskilllv,"iv"),
- BUILDIN_DEF(basicskillcheck,""),
- BUILDIN_DEF(getgmlevel,""),
- BUILDIN_DEF(getgroupid,""),
- BUILDIN_DEF(end,""),
- BUILDIN_DEF(checkoption,"i"),
- BUILDIN_DEF(setoption,"i?"),
- BUILDIN_DEF(setcart,"?"),
- BUILDIN_DEF(checkcart,""),
- BUILDIN_DEF(setfalcon,"?"),
- BUILDIN_DEF(checkfalcon,""),
- BUILDIN_DEF(setriding,"?"),
- BUILDIN_DEF(checkriding,""),
- BUILDIN_DEF(checkwug,""),
- BUILDIN_DEF(checkmadogear,""),
- BUILDIN_DEF(setmadogear,"?"),
- BUILDIN_DEF2(savepoint,"save","sii"),
- BUILDIN_DEF(savepoint,"sii"),
- BUILDIN_DEF(gettimetick,"i"),
- BUILDIN_DEF(gettime,"i"),
- BUILDIN_DEF(gettimestr,"si"),
- BUILDIN_DEF(openstorage,""),
- BUILDIN_DEF(guildopenstorage,""),
- BUILDIN_DEF(itemskill,"vi"),
- BUILDIN_DEF(produce,"i"),
- BUILDIN_DEF(cooking,"i"),
- BUILDIN_DEF(monster,"siisii???"),
- BUILDIN_DEF(getmobdrops,"i"),
- BUILDIN_DEF(areamonster,"siiiisii???"),
- BUILDIN_DEF(killmonster,"ss?"),
- BUILDIN_DEF(killmonsterall,"s?"),
- BUILDIN_DEF(clone,"siisi????"),
- BUILDIN_DEF(doevent,"s"),
- BUILDIN_DEF(donpcevent,"s"),
- BUILDIN_DEF(cmdothernpc,"ss"),
- BUILDIN_DEF(addtimer,"is"),
- BUILDIN_DEF(deltimer,"s"),
- BUILDIN_DEF(addtimercount,"si"),
- BUILDIN_DEF(initnpctimer,"??"),
- BUILDIN_DEF(stopnpctimer,"??"),
- BUILDIN_DEF(startnpctimer,"??"),
- BUILDIN_DEF(setnpctimer,"i?"),
- BUILDIN_DEF(getnpctimer,"i?"),
- BUILDIN_DEF(attachnpctimer,"?"), // attached the player id to the npc timer [Celest]
- BUILDIN_DEF(detachnpctimer,"?"), // detached the player id from the npc timer [Celest]
- BUILDIN_DEF(playerattached,""), // returns id of the current attached player. [Skotlex]
- BUILDIN_DEF(announce,"si?????"),
- BUILDIN_DEF(mapannounce,"ssi?????"),
- BUILDIN_DEF(areaannounce,"siiiisi?????"),
- BUILDIN_DEF(getusers,"i"),
- BUILDIN_DEF(getmapguildusers,"si"),
- BUILDIN_DEF(getmapusers,"s"),
- BUILDIN_DEF(getareausers,"siiii"),
- BUILDIN_DEF(getareadropitem,"siiiiv"),
- BUILDIN_DEF(enablenpc,"s"),
- BUILDIN_DEF(disablenpc,"s"),
- BUILDIN_DEF(hideoffnpc,"s"),
- BUILDIN_DEF(hideonnpc,"s"),
- BUILDIN_DEF(sc_start,"iii?"),
- BUILDIN_DEF(sc_start2,"iiii?"),
- BUILDIN_DEF(sc_start4,"iiiiii?"),
- BUILDIN_DEF(sc_end,"i?"),
- BUILDIN_DEF(getstatus, "i?"),
- BUILDIN_DEF(getscrate,"ii?"),
- BUILDIN_DEF(debugmes,"s"),
- BUILDIN_DEF2(catchpet,"pet","i"),
- BUILDIN_DEF2(birthpet,"bpet",""),
- BUILDIN_DEF(resetlvl,"i"),
- BUILDIN_DEF(resetstatus,""),
- BUILDIN_DEF(resetskill,""),
- BUILDIN_DEF(skillpointcount,""),
- BUILDIN_DEF(changebase,"i?"),
- BUILDIN_DEF(changesex,""),
- BUILDIN_DEF(waitingroom,"si?????"),
- BUILDIN_DEF(delwaitingroom,"?"),
- BUILDIN_DEF2(waitingroomkickall,"kickwaitingroomall","?"),
- BUILDIN_DEF(enablewaitingroomevent,"?"),
- BUILDIN_DEF(disablewaitingroomevent,"?"),
- BUILDIN_DEF2(enablewaitingroomevent,"enablearena",""), // Added by RoVeRT
- BUILDIN_DEF2(disablewaitingroomevent,"disablearena",""), // Added by RoVeRT
- BUILDIN_DEF(getwaitingroomstate,"i?"),
- BUILDIN_DEF(warpwaitingpc,"sii?"),
- BUILDIN_DEF(attachrid,"i"),
- BUILDIN_DEF(detachrid,""),
- BUILDIN_DEF(isloggedin,"i?"),
- BUILDIN_DEF(setmapflagnosave,"ssii"),
- BUILDIN_DEF(getmapflag,"si"),
- BUILDIN_DEF(setmapflag,"si?"),
- BUILDIN_DEF(removemapflag,"si?"),
- BUILDIN_DEF(pvpon,"s"),
- BUILDIN_DEF(pvpoff,"s"),
- BUILDIN_DEF(gvgon,"s"),
- BUILDIN_DEF(gvgoff,"s"),
- BUILDIN_DEF(emotion,"i??"),
- BUILDIN_DEF(maprespawnguildid,"sii"),
- BUILDIN_DEF(agitstart,""), // <Agit>
- BUILDIN_DEF(agitend,""),
- BUILDIN_DEF(agitcheck,""), // <Agitcheck>
- BUILDIN_DEF(flagemblem,"i"), // Flag Emblem
- BUILDIN_DEF(getcastlename,"s"),
- BUILDIN_DEF(getcastledata,"si"),
- BUILDIN_DEF(setcastledata,"sii"),
- BUILDIN_DEF(requestguildinfo,"i?"),
- BUILDIN_DEF(getequipcardcnt,"i"),
- BUILDIN_DEF(successremovecards,"i"),
- BUILDIN_DEF(failedremovecards,"ii"),
- BUILDIN_DEF(marriage,"s"),
- BUILDIN_DEF2(wedding_effect,"wedding",""),
- BUILDIN_DEF(divorce,""),
- BUILDIN_DEF(ispartneron,""),
- BUILDIN_DEF(getpartnerid,""),
- BUILDIN_DEF(getchildid,""),
- BUILDIN_DEF(getmotherid,""),
- BUILDIN_DEF(getfatherid,""),
- BUILDIN_DEF(warppartner,"sii"),
- BUILDIN_DEF(getitemname,"v"),
- BUILDIN_DEF(getitemslots,"i"),
- BUILDIN_DEF(makepet,"i"),
- BUILDIN_DEF(getexp,"ii"),
- BUILDIN_DEF(getinventorylist,""),
- BUILDIN_DEF(getskilllist,""),
- BUILDIN_DEF(clearitem,""),
- BUILDIN_DEF(classchange,"ii"),
- BUILDIN_DEF(misceffect,"i"),
- BUILDIN_DEF(playBGM,"s"),
- BUILDIN_DEF(playBGMall,"s?????"),
- BUILDIN_DEF(soundeffect,"si"),
- BUILDIN_DEF(soundeffectall,"si?????"), // SoundEffectAll [Codemaster]
- BUILDIN_DEF(strmobinfo,"ii"), // display mob data [Valaris]
- BUILDIN_DEF(guardian,"siisi??"), // summon guardians
- BUILDIN_DEF(guardianinfo,"sii"), // display guardian data [Valaris]
- BUILDIN_DEF(petskillbonus,"iiii"), // [Valaris]
- BUILDIN_DEF(petrecovery,"ii"), // [Valaris]
- BUILDIN_DEF(petloot,"i"), // [Valaris]
- BUILDIN_DEF(petheal,"iiii"), // [Valaris]
- BUILDIN_DEF(petskillattack,"viii"), // [Skotlex]
- BUILDIN_DEF(petskillattack2,"viiii"), // [Valaris]
- BUILDIN_DEF(petskillsupport,"viiii"), // [Skotlex]
- BUILDIN_DEF(skilleffect,"vi"), // skill effect [Celest]
- BUILDIN_DEF(npcskilleffect,"viii"), // npc skill effect [Valaris]
- BUILDIN_DEF(specialeffect,"i??"), // npc skill effect [Valaris]
- BUILDIN_DEF(specialeffect2,"i??"), // skill effect on players[Valaris]
- BUILDIN_DEF(nude,""), // nude command [Valaris]
- BUILDIN_DEF(mapwarp,"ssii??"), // Added by RoVeRT
- BUILDIN_DEF(atcommand,"s"), // [MouseJstr]
- BUILDIN_DEF2(atcommand,"charcommand","s"), // [MouseJstr]
- BUILDIN_DEF(movenpc,"sii?"), // [MouseJstr]
- BUILDIN_DEF(message,"ss"), // [MouseJstr]
- BUILDIN_DEF(npctalk,"s"), // [Valaris]
- BUILDIN_DEF(mobcount,"ss"),
- BUILDIN_DEF(getlook,"i"),
- BUILDIN_DEF(getsavepoint,"i"),
- BUILDIN_DEF(npcspeed,"i"), // [Valaris]
- BUILDIN_DEF(npcwalkto,"ii"), // [Valaris]
- BUILDIN_DEF(npcstop,""), // [Valaris]
- BUILDIN_DEF(getmapxy,"rrri?"), //by Lorky [Lupus]
- BUILDIN_DEF(checkoption1,"i"),
- BUILDIN_DEF(checkoption2,"i"),
- BUILDIN_DEF(guildgetexp,"i"),
- BUILDIN_DEF(guildchangegm,"is"),
- BUILDIN_DEF(logmes,"s"), //this command actls as MES but rints info into LOG file either SQL/TXT [Lupus]
- BUILDIN_DEF(summon,"si??"), // summons a slave monster [Celest]
- BUILDIN_DEF(isnight,""), // check whether it is night time [Celest]
- BUILDIN_DEF(isday,""), // check whether it is day time [Celest]
- BUILDIN_DEF(isequipped,"i*"), // check whether another item/card has been equipped [Celest]
- BUILDIN_DEF(isequippedcnt,"i*"), // check how many items/cards are being equipped [Celest]
- BUILDIN_DEF(cardscnt,"i*"), // check how many items/cards are being equipped in the same arm [Lupus]
- BUILDIN_DEF(getrefine,""), // returns the refined number of the current item, or an item with index specified [celest]
- BUILDIN_DEF(night,""), // sets the server to night time
- BUILDIN_DEF(day,""), // sets the server to day time
-#ifdef PCRE_SUPPORT
- BUILDIN_DEF(defpattern,"iss"), // Define pattern to listen for [MouseJstr]
- BUILDIN_DEF(activatepset,"i"), // Activate a pattern set [MouseJstr]
- BUILDIN_DEF(deactivatepset,"i"), // Deactive a pattern set [MouseJstr]
- BUILDIN_DEF(deletepset,"i"), // Delete a pattern set [MouseJstr]
-#endif
- BUILDIN_DEF(dispbottom,"s"), //added from jA [Lupus]
- BUILDIN_DEF(getusersname,""),
- BUILDIN_DEF(recovery,""),
- BUILDIN_DEF(getpetinfo,"i"),
- BUILDIN_DEF(gethominfo,"i"),
- BUILDIN_DEF(getmercinfo,"i?"),
- BUILDIN_DEF(checkequipedcard,"i"),
- BUILDIN_DEF(jump_zero,"il"), //for future jA script compatibility
- BUILDIN_DEF(globalmes,"s?"), //end jA addition
- BUILDIN_DEF(unequip,"i"), // unequip command [Spectre]
- BUILDIN_DEF(getstrlen,"s"), //strlen [Valaris]
- BUILDIN_DEF(charisalpha,"si"), //isalpha [Valaris]
- BUILDIN_DEF(charat,"si"),
- BUILDIN_DEF(setchar,"ssi"),
- BUILDIN_DEF(insertchar,"ssi"),
- BUILDIN_DEF(delchar,"si"),
- BUILDIN_DEF(strtoupper,"s"),
- BUILDIN_DEF(strtolower,"s"),
- BUILDIN_DEF(charisupper, "si"),
- BUILDIN_DEF(charislower, "si"),
- BUILDIN_DEF(substr,"sii"),
- BUILDIN_DEF(explode, "rss"),
- BUILDIN_DEF(implode, "r?"),
- BUILDIN_DEF(sprintf,"s*"), // [Mirei]
- BUILDIN_DEF(sscanf,"ss*"), // [Mirei]
- BUILDIN_DEF(strpos,"ss?"),
- BUILDIN_DEF(replacestr,"sss??"),
- BUILDIN_DEF(countstr,"ss?"),
- BUILDIN_DEF(setnpcdisplay,"sv??"),
- BUILDIN_DEF(compare,"ss"), // Lordalfa - To bring strstr to scripting Engine.
- BUILDIN_DEF(getiteminfo,"ii"), //[Lupus] returns Items Buy / sell Price, etc info
- BUILDIN_DEF(setiteminfo,"iii"), //[Lupus] set Items Buy / sell Price, etc info
- BUILDIN_DEF(getequipcardid,"ii"), //[Lupus] returns CARD ID or other info from CARD slot N of equipped item
- // [zBuffer] List of mathematics commands --->
- BUILDIN_DEF(sqrt,"i"),
- BUILDIN_DEF(pow,"ii"),
- BUILDIN_DEF(distance,"iiii"),
- // <--- [zBuffer] List of mathematics commands
- BUILDIN_DEF(md5,"s"),
- // [zBuffer] List of dynamic var commands --->
- BUILDIN_DEF(getd,"s"),
- BUILDIN_DEF(setd,"sv"),
- // <--- [zBuffer] List of dynamic var commands
- BUILDIN_DEF(petstat,"i"),
- BUILDIN_DEF(callshop,"s?"), // [Skotlex]
- BUILDIN_DEF(npcshopitem,"sii*"), // [Lance]
- BUILDIN_DEF(npcshopadditem,"sii*"),
- BUILDIN_DEF(npcshopdelitem,"si*"),
- BUILDIN_DEF(npcshopattach,"s?"),
- BUILDIN_DEF(equip,"i"),
- BUILDIN_DEF(autoequip,"ii"),
- BUILDIN_DEF(setbattleflag,"si"),
- BUILDIN_DEF(getbattleflag,"s"),
- BUILDIN_DEF(setitemscript,"is?"), //Set NEW item bonus script. Lupus
- BUILDIN_DEF(disguise,"i"), //disguise player. Lupus
- BUILDIN_DEF(undisguise,""), //undisguise player. Lupus
- BUILDIN_DEF(getmonsterinfo,"ii"), //Lupus
- BUILDIN_DEF(axtoi,"s"),
- BUILDIN_DEF(query_sql,"s*"),
- BUILDIN_DEF(query_logsql,"s*"),
- BUILDIN_DEF(escape_sql,"v"),
- BUILDIN_DEF(atoi,"s"),
- // [zBuffer] List of player cont commands --->
- BUILDIN_DEF(rid2name,"i"),
- BUILDIN_DEF(pcfollow,"ii"),
- BUILDIN_DEF(pcstopfollow,"i"),
- BUILDIN_DEF(pcblockmove,"ii"),
- // <--- [zBuffer] List of player cont commands
- // [zBuffer] List of mob control commands --->
- BUILDIN_DEF(unitwalk,"ii?"),
- BUILDIN_DEF(unitkill,"i"),
- BUILDIN_DEF(unitwarp,"isii"),
- BUILDIN_DEF(unitattack,"iv?"),
- BUILDIN_DEF(unitstop,"i"),
- BUILDIN_DEF(unittalk,"is"),
- BUILDIN_DEF(unitemote,"ii"),
- BUILDIN_DEF(unitskilluseid,"ivi?"), // originally by Qamera [Celest]
- BUILDIN_DEF(unitskillusepos,"iviii"), // [Celest]
-// <--- [zBuffer] List of mob control commands
- BUILDIN_DEF(sleep,"i"),
- BUILDIN_DEF(sleep2,"i"),
- BUILDIN_DEF(awake,"s"),
- BUILDIN_DEF(getvariableofnpc,"rs"),
- BUILDIN_DEF(warpportal,"iisii"),
- BUILDIN_DEF2(homunculus_evolution,"homevolution",""), //[orn]
- BUILDIN_DEF2(homunculus_mutate,"hommutate","?"),
- BUILDIN_DEF2(homunculus_shuffle,"homshuffle",""), //[Zephyrus]
- BUILDIN_DEF(eaclass,"?"), //[Skotlex]
- BUILDIN_DEF(roclass,"i?"), //[Skotlex]
- BUILDIN_DEF(checkvending,"?"),
- BUILDIN_DEF(checkchatting,"?"),
- BUILDIN_DEF(openmail,""),
- BUILDIN_DEF(openauction,""),
- BUILDIN_DEF(checkcell,"siii"),
- BUILDIN_DEF(setcell,"siiiiii"),
- BUILDIN_DEF(setwall,"siiiiis"),
- BUILDIN_DEF(delwall,"s"),
- BUILDIN_DEF(searchitem,"rs"),
- BUILDIN_DEF(mercenary_create,"ii"),
- BUILDIN_DEF(mercenary_heal,"ii"),
- BUILDIN_DEF(mercenary_sc_start,"iii"),
- BUILDIN_DEF(mercenary_get_calls,"i"),
- BUILDIN_DEF(mercenary_get_faith,"i"),
- BUILDIN_DEF(mercenary_set_calls,"ii"),
- BUILDIN_DEF(mercenary_set_faith,"ii"),
- BUILDIN_DEF(readbook,"ii"),
- BUILDIN_DEF(setfont,"i"),
- BUILDIN_DEF(areamobuseskill,"siiiiviiiii"),
- BUILDIN_DEF(progressbar,"si"),
- BUILDIN_DEF(pushpc,"ii"),
- BUILDIN_DEF(buyingstore,"i"),
- BUILDIN_DEF(searchstores,"ii"),
- BUILDIN_DEF(showdigit,"i?"),
- // WoE SE
- BUILDIN_DEF(agitstart2,""),
- BUILDIN_DEF(agitend2,""),
- BUILDIN_DEF(agitcheck2,""),
- // BattleGround
- BUILDIN_DEF(waitingroom2bg,"siiss?"),
- BUILDIN_DEF(waitingroom2bg_single,"isiis"),
- BUILDIN_DEF(bg_team_setxy,"iii"),
- BUILDIN_DEF(bg_warp,"isii"),
- BUILDIN_DEF(bg_monster,"isiisi?"),
- BUILDIN_DEF(bg_monster_set_team,"ii"),
- BUILDIN_DEF(bg_leave,""),
- BUILDIN_DEF(bg_destroy,"i"),
- BUILDIN_DEF(areapercentheal,"siiiiii"),
- BUILDIN_DEF(bg_get_data,"ii"),
- BUILDIN_DEF(bg_getareausers,"isiiii"),
- BUILDIN_DEF(bg_updatescore,"sii"),
-
- // Instancing
- BUILDIN_DEF(instance_create,"si"),
- BUILDIN_DEF(instance_destroy,"?"),
- BUILDIN_DEF(instance_attachmap,"si?"),
- BUILDIN_DEF(instance_detachmap,"s?"),
- BUILDIN_DEF(instance_attach,"i"),
- BUILDIN_DEF(instance_id,"?"),
- BUILDIN_DEF(instance_set_timeout,"ii?"),
- BUILDIN_DEF(instance_init,"i"),
- BUILDIN_DEF(instance_announce,"isi?????"),
- BUILDIN_DEF(instance_npcname,"s?"),
- BUILDIN_DEF(has_instance,"s?"),
- BUILDIN_DEF(instance_warpall,"sii?"),
- BUILDIN_DEF(instance_check_party,"i???"),
- /**
- * 3rd-related
- **/
- BUILDIN_DEF(makerune,"i"),
- BUILDIN_DEF(checkdragon,""),//[Ind]
- BUILDIN_DEF(setdragon,"?"),//[Ind]
- BUILDIN_DEF(ismounting,""),//[Ind]
- BUILDIN_DEF(setmounting,""),//[Ind]
- BUILDIN_DEF(checkre,"i"),
- /**
- * rAthena and beyond!
- **/
- BUILDIN_DEF(getargcount,""),
- BUILDIN_DEF(getcharip,"?"),
- BUILDIN_DEF(is_function,"s"),
- BUILDIN_DEF(get_revision,""),
- BUILDIN_DEF(freeloop,"i"),
- BUILDIN_DEF(getrandgroupitem,"ii"),
- BUILDIN_DEF(cleanmap,"s"),
- BUILDIN_DEF2(cleanmap,"cleanarea","siiii"),
- BUILDIN_DEF(npcskill,"viii"),
- /**
- * @commands (script based)
- **/
- BUILDIN_DEF(bindatcmd, "ss??"),
- BUILDIN_DEF(unbindatcmd, "s"),
- BUILDIN_DEF(useatcmd, "s"),
-
- //Quest Log System [Inkfish]
- BUILDIN_DEF(setquest, "i"),
- BUILDIN_DEF(erasequest, "i"),
- BUILDIN_DEF(completequest, "i"),
- BUILDIN_DEF(checkquest, "i?"),
- BUILDIN_DEF(changequest, "ii"),
- BUILDIN_DEF(showevent, "ii"),
- {NULL,NULL,NULL},
-};
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+//#define DEBUG_DISP
+//#define DEBUG_DISASM
+//#define DEBUG_RUN
+//#define DEBUG_HASH
+//#define DEBUG_DUMP_STACK
+
+#include "../common/cbasetypes.h"
+#include "../common/malloc.h"
+#include "../common/md5calc.h"
+#include "../common/nullpo.h"
+#include "../common/random.h"
+#include "../common/showmsg.h"
+#include "../common/socket.h" // usage: getcharip
+#include "../common/strlib.h"
+#include "../common/timer.h"
+#include "../common/utils.h"
+
+#include "map.h"
+#include "path.h"
+#include "clif.h"
+#include "chrif.h"
+#include "itemdb.h"
+#include "pc.h"
+#include "status.h"
+#include "storage.h"
+#include "mob.h"
+#include "npc.h"
+#include "pet.h"
+#include "mapreg.h"
+#include "homunculus.h"
+#include "instance.h"
+#include "mercenary.h"
+#include "intif.h"
+#include "skill.h"
+#include "status.h"
+#include "chat.h"
+#include "battle.h"
+#include "battleground.h"
+#include "party.h"
+#include "guild.h"
+#include "atcommand.h"
+#include "log.h"
+#include "unit.h"
+#include "pet.h"
+#include "mail.h"
+#include "script.h"
+#include "quest.h"
+#include "elemental.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#ifndef WIN32
+ #include <sys/time.h>
+#endif
+#include <time.h>
+#include <setjmp.h>
+#include <errno.h>
+
+#ifdef BETA_THREAD_TEST
+ #include "../common/atomic.h"
+ #include "../common/spinlock.h"
+ #include "../common/thread.h"
+ #include "../common/mutex.h"
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+//## TODO possible enhancements: [FlavioJS]
+// - 'callfunc' supporting labels in the current npc "::LabelName"
+// - 'callfunc' supporting labels in other npcs "NpcName::LabelName"
+// - 'function FuncName;' function declarations reverting to global functions
+// if local label isn't found
+// - join callfunc and callsub's functionality
+// - remove dynamic allocation in add_word()
+// - remove GETVALUE / SETVALUE
+// - clean up the set_reg / set_val / setd_sub mess
+// - detect invalid label references at parse-time
+
+//
+// struct script_state* st;
+//
+
+/// Returns the script_data at the target index
+#define script_getdata(st,i) ( &((st)->stack->stack_data[(st)->start + (i)]) )
+/// Returns if the stack contains data at the target index
+#define script_hasdata(st,i) ( (st)->end > (st)->start + (i) )
+/// Returns the index of the last data in the stack
+#define script_lastdata(st) ( (st)->end - (st)->start - 1 )
+/// Pushes an int into the stack
+#define script_pushint(st,val) push_val((st)->stack, C_INT, (val))
+/// Pushes a string into the stack (script engine frees it automatically)
+#define script_pushstr(st,val) push_str((st)->stack, C_STR, (val))
+/// Pushes a copy of a string into the stack
+#define script_pushstrcopy(st,val) push_str((st)->stack, C_STR, aStrdup(val))
+/// Pushes a constant string into the stack (must never change or be freed)
+#define script_pushconststr(st,val) push_str((st)->stack, C_CONSTSTR, (val))
+/// Pushes a nil into the stack
+#define script_pushnil(st) push_val((st)->stack, C_NOP, 0)
+/// Pushes a copy of the data in the target index
+#define script_pushcopy(st,i) push_copy((st)->stack, (st)->start + (i))
+
+#define script_isstring(st,i) data_isstring(script_getdata(st,i))
+#define script_isint(st,i) data_isint(script_getdata(st,i))
+
+#define script_getnum(st,val) conv_num(st, script_getdata(st,val))
+#define script_getstr(st,val) conv_str(st, script_getdata(st,val))
+#define script_getref(st,val) ( script_getdata(st,val)->ref )
+
+// Note: "top" functions/defines use indexes relative to the top of the stack
+// -1 is the index of the data at the top
+
+/// Returns the script_data at the target index relative to the top of the stack
+#define script_getdatatop(st,i) ( &((st)->stack->stack_data[(st)->stack->sp + (i)]) )
+/// Pushes a copy of the data in the target index relative to the top of the stack
+#define script_pushcopytop(st,i) push_copy((st)->stack, (st)->stack->sp + (i))
+/// Removes the range of values [start,end[ relative to the top of the stack
+#define script_removetop(st,start,end) ( pop_stack((st), ((st)->stack->sp + (start)), (st)->stack->sp + (end)) )
+
+//
+// struct script_data* data;
+//
+
+/// Returns if the script data is a string
+#define data_isstring(data) ( (data)->type == C_STR || (data)->type == C_CONSTSTR )
+/// Returns if the script data is an int
+#define data_isint(data) ( (data)->type == C_INT )
+/// Returns if the script data is a reference
+#define data_isreference(data) ( (data)->type == C_NAME )
+/// Returns if the script data is a label
+#define data_islabel(data) ( (data)->type == C_POS )
+/// Returns if the script data is an internal script function label
+#define data_isfunclabel(data) ( (data)->type == C_USERFUNC_POS )
+
+/// Returns if this is a reference to a constant
+#define reference_toconstant(data) ( str_data[reference_getid(data)].type == C_INT )
+/// Returns if this a reference to a param
+#define reference_toparam(data) ( str_data[reference_getid(data)].type == C_PARAM )
+/// Returns if this a reference to a variable
+//##TODO confirm it's C_NAME [FlavioJS]
+#define reference_tovariable(data) ( str_data[reference_getid(data)].type == C_NAME )
+/// Returns the unique id of the reference (id and index)
+#define reference_getuid(data) ( (data)->u.num )
+/// Returns the id of the reference
+#define reference_getid(data) ( (int32)(reference_getuid(data) & 0x00ffffff) )
+/// Returns the array index of the reference
+#define reference_getindex(data) ( (int32)(((uint32)(reference_getuid(data) & 0xff000000)) >> 24) )
+/// Returns the name of the reference
+#define reference_getname(data) ( str_buf + str_data[reference_getid(data)].str )
+/// Returns the linked list of uid-value pairs of the reference (can be NULL)
+#define reference_getref(data) ( (data)->ref )
+/// Returns the value of the constant
+#define reference_getconstant(data) ( str_data[reference_getid(data)].val )
+/// Returns the type of param
+#define reference_getparamtype(data) ( str_data[reference_getid(data)].val )
+
+/// Composes the uid of a reference from the id and the index
+#define reference_uid(id,idx) ( (int32)((((uint32)(id)) & 0x00ffffff) | (((uint32)(idx)) << 24)) )
+
+#define not_server_variable(prefix) ( (prefix) != '$' && (prefix) != '.' && (prefix) != '\'')
+#define not_array_variable(prefix) ( (prefix) != '$' && (prefix) != '@' && (prefix) != '.' && (prefix) != '\'' )
+#define is_string_variable(name) ( (name)[strlen(name) - 1] == '$' )
+
+#define FETCH(n, t) \
+ if( script_hasdata(st,n) ) \
+ (t)=script_getnum(st,n);
+
+/// Maximum amount of elements in script arrays
+#define SCRIPT_MAX_ARRAYSIZE 128
+
+#define SCRIPT_BLOCK_SIZE 512
+enum { LABEL_NEXTLINE=1,LABEL_START };
+
+/// temporary buffer for passing around compiled bytecode
+/// @see add_scriptb, set_label, parse_script
+static unsigned char* script_buf = NULL;
+static int script_pos = 0, script_size = 0;
+
+static inline int GETVALUE(const unsigned char* buf, int i)
+{
+ return (int)MakeDWord(MakeWord(buf[i], buf[i+1]), MakeWord(buf[i+2], 0));
+}
+static inline void SETVALUE(unsigned char* buf, int i, int n)
+{
+ buf[i] = GetByte(n, 0);
+ buf[i+1] = GetByte(n, 1);
+ buf[i+2] = GetByte(n, 2);
+}
+
+// String buffer structures.
+// str_data stores string information
+static struct str_data_struct {
+ enum c_op type;
+ int str;
+ int backpatch;
+ int label;
+ int (*func)(struct script_state *st);
+ int val;
+ int next;
+} *str_data = NULL;
+static int str_data_size = 0; // size of the data
+static int str_num = LABEL_START; // next id to be assigned
+
+// str_buf holds the strings themselves
+static char *str_buf;
+static int str_size = 0; // size of the buffer
+static int str_pos = 0; // next position to be assigned
+
+
+// Using a prime number for SCRIPT_HASH_SIZE should give better distributions
+#define SCRIPT_HASH_SIZE 1021
+int str_hash[SCRIPT_HASH_SIZE];
+// Specifies which string hashing method to use
+//#define SCRIPT_HASH_DJB2
+//#define SCRIPT_HASH_SDBM
+#define SCRIPT_HASH_ELF
+
+static DBMap* scriptlabel_db=NULL; // const char* label_name -> int script_pos
+static DBMap* userfunc_db=NULL; // const char* func_name -> struct script_code*
+static int parse_options=0;
+DBMap* script_get_label_db(void){ return scriptlabel_db; }
+DBMap* script_get_userfunc_db(void){ return userfunc_db; }
+
+// important buildin function references for usage in scripts
+static int buildin_set_ref = 0;
+static int buildin_callsub_ref = 0;
+static int buildin_callfunc_ref = 0;
+static int buildin_getelementofarray_ref = 0;
+
+// Caches compiled autoscript item code.
+// Note: This is not cleared when reloading itemdb.
+static DBMap* autobonus_db=NULL; // char* script -> char* bytecode
+
+struct Script_Config script_config = {
+ 1, // warn_func_mismatch_argtypes
+ 1, 65535, 2048, //warn_func_mismatch_paramnum/check_cmdcount/check_gotocount
+ 0, INT_MAX, // input_min_value/input_max_value
+ "OnPCDieEvent", //die_event_name
+ "OnPCKillEvent", //kill_pc_event_name
+ "OnNPCKillEvent", //kill_mob_event_name
+ "OnPCLoginEvent", //login_event_name
+ "OnPCLogoutEvent", //logout_event_name
+ "OnPCLoadMapEvent", //loadmap_event_name
+ "OnPCBaseLvUpEvent", //baselvup_event_name
+ "OnPCJobLvUpEvent", //joblvup_event_name
+ "OnTouch_", //ontouch_name (runs on first visible char to enter area, picks another char if the first char leaves)
+ "OnTouch", //ontouch2_name (run whenever a char walks into the OnTouch area)
+};
+
+static jmp_buf error_jump;
+static char* error_msg;
+static const char* error_pos;
+static int error_report; // if the error should produce output
+
+// for advanced scripting support ( nested if, switch, while, for, do-while, function, etc )
+// [Eoe / jA 1080, 1081, 1094, 1164]
+enum curly_type {
+ TYPE_NULL = 0,
+ TYPE_IF,
+ TYPE_SWITCH,
+ TYPE_WHILE,
+ TYPE_FOR,
+ TYPE_DO,
+ TYPE_USERFUNC,
+ TYPE_ARGLIST // function argument list
+};
+
+enum e_arglist
+{
+ ARGLIST_UNDEFINED = 0,
+ ARGLIST_NO_PAREN = 1,
+ ARGLIST_PAREN = 2,
+};
+
+static struct {
+ struct {
+ enum curly_type type;
+ int index;
+ int count;
+ int flag;
+ struct linkdb_node *case_label;
+ } curly[256]; // Information right parenthesis
+ int curly_count; // The number of right brackets
+ int index; // Number of the syntax used in the script
+} syntax;
+
+const char* parse_curly_close(const char* p);
+const char* parse_syntax_close(const char* p);
+const char* parse_syntax_close_sub(const char* p,int* flag);
+const char* parse_syntax(const char* p);
+static int parse_syntax_for_flag = 0;
+
+extern int current_equip_item_index; //for New CARDS Scripts. It contains Inventory Index of the EQUIP_SCRIPT caller item. [Lupus]
+int potion_flag=0; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex]
+int potion_hp=0, potion_per_hp=0, potion_sp=0, potion_per_sp=0;
+int potion_target=0;
+
+
+c_op get_com(unsigned char *script,int *pos);
+int get_num(unsigned char *script,int *pos);
+
+typedef struct script_function {
+ int (*func)(struct script_state *st);
+ const char *name;
+ const char *arg;
+} script_function;
+
+extern script_function buildin_func[];
+
+static struct linkdb_node* sleep_db;// int oid -> struct script_state*
+
+#ifdef BETA_THREAD_TEST
+/**
+ * MySQL Query Slave
+ **/
+static SPIN_LOCK queryThreadLock;
+static rAthread queryThread = NULL;
+static ramutex queryThreadMutex = NULL;
+static racond queryThreadCond = NULL;
+static volatile int32 queryThreadTerminate = 0;
+
+struct queryThreadEntry {
+ bool ok;
+ bool type; /* main db or log db? */
+ struct script_state *st;
+};
+
+/* Ladies and Gentleman the Manager! */
+struct {
+ struct queryThreadEntry **entry;/* array of structs */
+ int count;
+ int timer;/* used to receive processed entries */
+} queryThreadData;
+#endif
+
+/*==========================================
+ * (Only those needed) local declaration prototype
+ *------------------------------------------*/
+const char* parse_subexpr(const char* p,int limit);
+int run_func(struct script_state *st);
+
+enum {
+ MF_NOMEMO, //0
+ MF_NOTELEPORT,
+ MF_NOSAVE,
+ MF_NOBRANCH,
+ MF_NOPENALTY,
+ MF_NOZENYPENALTY,
+ MF_PVP,
+ MF_PVP_NOPARTY,
+ MF_PVP_NOGUILD,
+ MF_GVG,
+ MF_GVG_NOPARTY, //10
+ MF_NOTRADE,
+ MF_NOSKILL,
+ MF_NOWARP,
+ MF_PARTYLOCK,
+ MF_NOICEWALL,
+ MF_SNOW,
+ MF_FOG,
+ MF_SAKURA,
+ MF_LEAVES,
+ /**
+ * No longer available, keeping here just in case it's back someday. [Ind]
+ **/
+ //MF_RAIN, //20
+ // 21 free
+ MF_NOGO = 22,
+ MF_CLOUDS,
+ MF_CLOUDS2,
+ MF_FIREWORKS,
+ MF_GVG_CASTLE,
+ MF_GVG_DUNGEON,
+ MF_NIGHTENABLED,
+ MF_NOBASEEXP,
+ MF_NOJOBEXP, //30
+ MF_NOMOBLOOT,
+ MF_NOMVPLOOT,
+ MF_NORETURN,
+ MF_NOWARPTO,
+ MF_NIGHTMAREDROP,
+ MF_RESTRICTED,
+ MF_NOCOMMAND,
+ MF_NODROP,
+ MF_JEXP,
+ MF_BEXP, //40
+ MF_NOVENDING,
+ MF_LOADEVENT,
+ MF_NOCHAT,
+ MF_NOEXPPENALTY,
+ MF_GUILDLOCK,
+ MF_TOWN,
+ MF_AUTOTRADE,
+ MF_ALLOWKS,
+ MF_MONSTER_NOTELEPORT,
+ MF_PVP_NOCALCRANK, //50
+ MF_BATTLEGROUND,
+ MF_RESET
+};
+
+const char* script_op2name(int op)
+{
+#define RETURN_OP_NAME(type) case type: return #type
+ switch( op )
+ {
+ RETURN_OP_NAME(C_NOP);
+ RETURN_OP_NAME(C_POS);
+ RETURN_OP_NAME(C_INT);
+ RETURN_OP_NAME(C_PARAM);
+ RETURN_OP_NAME(C_FUNC);
+ RETURN_OP_NAME(C_STR);
+ RETURN_OP_NAME(C_CONSTSTR);
+ RETURN_OP_NAME(C_ARG);
+ RETURN_OP_NAME(C_NAME);
+ RETURN_OP_NAME(C_EOL);
+ RETURN_OP_NAME(C_RETINFO);
+ RETURN_OP_NAME(C_USERFUNC);
+ RETURN_OP_NAME(C_USERFUNC_POS);
+
+ // operators
+ RETURN_OP_NAME(C_OP3);
+ RETURN_OP_NAME(C_LOR);
+ RETURN_OP_NAME(C_LAND);
+ RETURN_OP_NAME(C_LE);
+ RETURN_OP_NAME(C_LT);
+ RETURN_OP_NAME(C_GE);
+ RETURN_OP_NAME(C_GT);
+ RETURN_OP_NAME(C_EQ);
+ RETURN_OP_NAME(C_NE);
+ RETURN_OP_NAME(C_XOR);
+ RETURN_OP_NAME(C_OR);
+ RETURN_OP_NAME(C_AND);
+ RETURN_OP_NAME(C_ADD);
+ RETURN_OP_NAME(C_SUB);
+ RETURN_OP_NAME(C_MUL);
+ RETURN_OP_NAME(C_DIV);
+ RETURN_OP_NAME(C_MOD);
+ RETURN_OP_NAME(C_NEG);
+ RETURN_OP_NAME(C_LNOT);
+ RETURN_OP_NAME(C_NOT);
+ RETURN_OP_NAME(C_R_SHIFT);
+ RETURN_OP_NAME(C_L_SHIFT);
+
+ default:
+ ShowDebug("script_op2name: unexpected op=%d\n", op);
+ return "???";
+ }
+#undef RETURN_OP_NAME
+}
+
+#ifdef DEBUG_DUMP_STACK
+static void script_dump_stack(struct script_state* st)
+{
+ int i;
+ ShowMessage("\tstart = %d\n", st->start);
+ ShowMessage("\tend = %d\n", st->end);
+ ShowMessage("\tdefsp = %d\n", st->stack->defsp);
+ ShowMessage("\tsp = %d\n", st->stack->sp);
+ for( i = 0; i < st->stack->sp; ++i )
+ {
+ struct script_data* data = &st->stack->stack_data[i];
+ ShowMessage("\t[%d] %s", i, script_op2name(data->type));
+ switch( data->type )
+ {
+ case C_INT:
+ case C_POS:
+ ShowMessage(" %d\n", data->u.num);
+ break;
+
+ case C_STR:
+ case C_CONSTSTR:
+ ShowMessage(" \"%s\"\n", data->u.str);
+ break;
+
+ case C_NAME:
+ ShowMessage(" \"%s\" (id=%d ref=%p subtype=%s)\n", reference_getname(data), data->u.num, data->ref, script_op2name(str_data[data->u.num].type));
+ break;
+
+ case C_RETINFO:
+ {
+ struct script_retinfo* ri = data->u.ri;
+ ShowMessage(" %p {var_function=%p, script=%p, pos=%d, nargs=%d, defsp=%d}\n", ri, ri->var_function, ri->script, ri->pos, ri->nargs, ri->defsp);
+ }
+ break;
+ default:
+ ShowMessage("\n");
+ break;
+ }
+ }
+}
+#endif
+
+/// Reports on the console the src of a script error.
+static void script_reportsrc(struct script_state *st)
+{
+ struct block_list* bl;
+
+ if( st->oid == 0 )
+ return; //Can't report source.
+
+ bl = map_id2bl(st->oid);
+ if( bl == NULL )
+ return;
+
+ switch( bl->type )
+ {
+ case BL_NPC:
+ if( bl->m >= 0 )
+ ShowDebug("Source (NPC): %s at %s (%d,%d)\n", ((struct npc_data *)bl)->name, map[bl->m].name, bl->x, bl->y);
+ else
+ ShowDebug("Source (NPC): %s (invisible/not on a map)\n", ((struct npc_data *)bl)->name);
+ break;
+ default:
+ if( bl->m >= 0 )
+ ShowDebug("Source (Non-NPC type %d): name %s at %s (%d,%d)\n", bl->type, status_get_name(bl), map[bl->m].name, bl->x, bl->y);
+ else
+ ShowDebug("Source (Non-NPC type %d): name %s (invisible/not on a map)\n", bl->type, status_get_name(bl));
+ break;
+ }
+}
+
+/// Reports on the console information about the script data.
+static void script_reportdata(struct script_data* data)
+{
+ if( data == NULL )
+ return;
+ switch( data->type )
+ {
+ case C_NOP:// no value
+ ShowDebug("Data: nothing (nil)\n");
+ break;
+ case C_INT:// number
+ ShowDebug("Data: number value=%d\n", data->u.num);
+ break;
+ case C_STR:
+ case C_CONSTSTR:// string
+ if( data->u.str )
+ {
+ ShowDebug("Data: string value=\"%s\"\n", data->u.str);
+ }
+ else
+ {
+ ShowDebug("Data: string value=NULL\n");
+ }
+ break;
+ case C_NAME:// reference
+ if( reference_tovariable(data) )
+ {// variable
+ const char* name = reference_getname(data);
+ if( not_array_variable(*name) )
+ ShowDebug("Data: variable name='%s'\n", name);
+ else
+ ShowDebug("Data: variable name='%s' index=%d\n", name, reference_getindex(data));
+ }
+ else if( reference_toconstant(data) )
+ {// constant
+ ShowDebug("Data: constant name='%s' value=%d\n", reference_getname(data), reference_getconstant(data));
+ }
+ else if( reference_toparam(data) )
+ {// param
+ ShowDebug("Data: param name='%s' type=%d\n", reference_getname(data), reference_getparamtype(data));
+ }
+ else
+ {// ???
+ ShowDebug("Data: reference name='%s' type=%s\n", reference_getname(data), script_op2name(data->type));
+ ShowDebug("Please report this!!! - str_data.type=%s\n", script_op2name(str_data[reference_getid(data)].type));
+ }
+ break;
+ case C_POS:// label
+ ShowDebug("Data: label pos=%d\n", data->u.num);
+ break;
+ default:
+ ShowDebug("Data: %s\n", script_op2name(data->type));
+ break;
+ }
+}
+
+
+/// Reports on the console information about the current built-in function.
+static void script_reportfunc(struct script_state* st)
+{
+ int i, params, id;
+ struct script_data* data;
+
+ if( !script_hasdata(st,0) )
+ {// no stack
+ return;
+ }
+
+ data = script_getdata(st,0);
+
+ if( !data_isreference(data) || str_data[reference_getid(data)].type != C_FUNC )
+ {// script currently not executing a built-in function or corrupt stack
+ return;
+ }
+
+ id = reference_getid(data);
+ params = script_lastdata(st)-1;
+
+ if( params > 0 )
+ {
+ ShowDebug("Function: %s (%d parameter%s):\n", get_str(id), params, ( params == 1 ) ? "" : "s");
+
+ for( i = 2; i <= script_lastdata(st); i++ )
+ {
+ script_reportdata(script_getdata(st,i));
+ }
+ }
+ else
+ {
+ ShowDebug("Function: %s (no parameters)\n", get_str(id));
+ }
+}
+
+
+/*==========================================
+ * Output error message
+ *------------------------------------------*/
+static void disp_error_message2(const char *mes,const char *pos,int report)
+{
+ error_msg = aStrdup(mes);
+ error_pos = pos;
+ error_report = report;
+ longjmp( error_jump, 1 );
+}
+#define disp_error_message(mes,pos) disp_error_message2(mes,pos,1)
+
+/// Checks event parameter validity
+static void check_event(struct script_state *st, const char *evt)
+{
+ if( evt && evt[0] && !stristr(evt, "::On") )
+ {
+ ShowWarning("NPC event parameter deprecated! Please use 'NPCNAME::OnEVENT' instead of '%s'.\n", evt);
+ script_reportsrc(st);
+ }
+}
+
+/*==========================================
+ * Hashes the input string
+ *------------------------------------------*/
+static unsigned int calc_hash(const char* p)
+{
+ unsigned int h;
+
+#if defined(SCRIPT_HASH_DJB2)
+ h = 5381;
+ while( *p ) // hash*33 + c
+ h = ( h << 5 ) + h + ((unsigned char)TOLOWER(*p++));
+#elif defined(SCRIPT_HASH_SDBM)
+ h = 0;
+ while( *p ) // hash*65599 + c
+ h = ( h << 6 ) + ( h << 16 ) - h + ((unsigned char)TOLOWER(*p++));
+#elif defined(SCRIPT_HASH_ELF) // UNIX ELF hash
+ h = 0;
+ while( *p ){
+ unsigned int g;
+ h = ( h << 4 ) + ((unsigned char)TOLOWER(*p++));
+ g = h & 0xF0000000;
+ if( g )
+ {
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ }
+#else // athena hash
+ h = 0;
+ while( *p )
+ h = ( h << 1 ) + ( h >> 3 ) + ( h >> 5 ) + ( h >> 8 ) + (unsigned char)TOLOWER(*p++);
+#endif
+
+ return h % SCRIPT_HASH_SIZE;
+}
+
+
+/*==========================================
+ * str_data manipulation functions
+ *------------------------------------------*/
+
+/// Looks up string using the provided id.
+const char* get_str(int id)
+{
+ Assert( id >= LABEL_START && id < str_size );
+ return str_buf+str_data[id].str;
+}
+
+/// Returns the uid of the string, or -1.
+static int search_str(const char* p)
+{
+ int i;
+
+ for( i = str_hash[calc_hash(p)]; i != 0; i = str_data[i].next )
+ if( strcasecmp(get_str(i),p) == 0 )
+ return i;
+
+ return -1;
+}
+
+/// Stores a copy of the string and returns its id.
+/// If an identical string is already present, returns its id instead.
+int add_str(const char* p)
+{
+ int i, h;
+ int len;
+
+ h = calc_hash(p);
+
+ if( str_hash[h] == 0 )
+ {// empty bucket, add new node here
+ str_hash[h] = str_num;
+ }
+ else
+ {// scan for end of list, or occurence of identical string
+ for( i = str_hash[h]; ; i = str_data[i].next )
+ {
+ if( strcasecmp(get_str(i),p) == 0 )
+ return i; // string already in list
+ if( str_data[i].next == 0 )
+ break; // reached the end
+ }
+
+ // append node to end of list
+ str_data[i].next = str_num;
+ }
+
+ // grow list if neccessary
+ if( str_num >= str_data_size )
+ {
+ str_data_size += 128;
+ RECREATE(str_data,struct str_data_struct,str_data_size);
+ memset(str_data + (str_data_size - 128), '\0', 128);
+ }
+
+ len=(int)strlen(p);
+
+ // grow string buffer if neccessary
+ while( str_pos+len+1 >= str_size )
+ {
+ str_size += 256;
+ RECREATE(str_buf,char,str_size);
+ memset(str_buf + (str_size - 256), '\0', 256);
+ }
+
+ safestrncpy(str_buf+str_pos, p, len+1);
+ str_data[str_num].type = C_NOP;
+ str_data[str_num].str = str_pos;
+ str_data[str_num].next = 0;
+ str_data[str_num].func = NULL;
+ str_data[str_num].backpatch = -1;
+ str_data[str_num].label = -1;
+ str_pos += len+1;
+
+ return str_num++;
+}
+
+
+/// Appends 1 byte to the script buffer.
+static void add_scriptb(int a)
+{
+ if( script_pos+1 >= script_size )
+ {
+ script_size += SCRIPT_BLOCK_SIZE;
+ RECREATE(script_buf,unsigned char,script_size);
+ }
+ script_buf[script_pos++] = (uint8)(a);
+}
+
+/// Appends a c_op value to the script buffer.
+/// The value is variable-length encoded into 8-bit blocks.
+/// The encoding scheme is ( 01?????? )* 00??????, LSB first.
+/// All blocks but the last hold 7 bits of data, topmost bit is always 1 (carries).
+static void add_scriptc(int a)
+{
+ while( a >= 0x40 )
+ {
+ add_scriptb((a&0x3f)|0x40);
+ a = (a - 0x40) >> 6;
+ }
+
+ add_scriptb(a);
+}
+
+/// Appends an integer value to the script buffer.
+/// The value is variable-length encoded into 8-bit blocks.
+/// The encoding scheme is ( 11?????? )* 10??????, LSB first.
+/// All blocks but the last hold 7 bits of data, topmost bit is always 1 (carries).
+static void add_scripti(int a)
+{
+ while( a >= 0x40 )
+ {
+ add_scriptb((a&0x3f)|0xc0);
+ a = (a - 0x40) >> 6;
+ }
+ add_scriptb(a|0x80);
+}
+
+/// Appends a str_data object (label/function/variable/integer) to the script buffer.
+
+///
+/// @param l The id of the str_data entry
+// Maximum up to 16M
+static void add_scriptl(int l)
+{
+ int backpatch = str_data[l].backpatch;
+
+ switch(str_data[l].type){
+ case C_POS:
+ case C_USERFUNC_POS:
+ add_scriptc(C_POS);
+ add_scriptb(str_data[l].label);
+ add_scriptb(str_data[l].label>>8);
+ add_scriptb(str_data[l].label>>16);
+ break;
+ case C_NOP:
+ case C_USERFUNC:
+ // Embedded data backpatch there is a possibility of label
+ add_scriptc(C_NAME);
+ str_data[l].backpatch = script_pos;
+ add_scriptb(backpatch);
+ add_scriptb(backpatch>>8);
+ add_scriptb(backpatch>>16);
+ break;
+ case C_INT:
+ add_scripti(abs(str_data[l].val));
+ if( str_data[l].val < 0 ) //Notice that this is negative, from jA (Rayce)
+ add_scriptc(C_NEG);
+ break;
+ default: // assume C_NAME
+ add_scriptc(C_NAME);
+ add_scriptb(l);
+ add_scriptb(l>>8);
+ add_scriptb(l>>16);
+ break;
+ }
+}
+
+/*==========================================
+ * Resolve the label
+ *------------------------------------------*/
+void set_label(int l,int pos, const char* script_pos)
+{
+ int i,next;
+
+ if(str_data[l].type==C_INT || str_data[l].type==C_PARAM || str_data[l].type==C_FUNC)
+ { //Prevent overwriting constants values, parameters and built-in functions [Skotlex]
+ disp_error_message("set_label: invalid label name",script_pos);
+ return;
+ }
+ if(str_data[l].label!=-1){
+ disp_error_message("set_label: dup label ",script_pos);
+ return;
+ }
+ str_data[l].type=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS);
+ str_data[l].label=pos;
+ for(i=str_data[l].backpatch;i>=0 && i!=0x00ffffff;){
+ next=GETVALUE(script_buf,i);
+ script_buf[i-1]=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS);
+ SETVALUE(script_buf,i,pos);
+ i=next;
+ }
+}
+
+/// Skips spaces and/or comments.
+const char* skip_space(const char* p)
+{
+ if( p == NULL )
+ return NULL;
+ for(;;)
+ {
+ while( ISSPACE(*p) )
+ ++p;
+ if( *p == '/' && p[1] == '/' )
+ {// line comment
+ while(*p && *p!='\n')
+ ++p;
+ }
+ else if( *p == '/' && p[1] == '*' )
+ {// block comment
+ p += 2;
+ for(;;)
+ {
+ if( *p == '\0' )
+ return p;//disp_error_message("script:skip_space: end of file while parsing block comment. expected "CL_BOLD"*/"CL_NORM, p);
+ if( *p == '*' && p[1] == '/' )
+ {// end of block comment
+ p += 2;
+ break;
+ }
+ ++p;
+ }
+ }
+ else
+ break;
+ }
+ return p;
+}
+
+/// Skips a word.
+/// A word consists of undercores and/or alfanumeric characters,
+/// and valid variable prefixes/postfixes.
+static
+const char* skip_word(const char* p)
+{
+ // prefix
+ switch( *p )
+ {
+ case '@':// temporary char variable
+ ++p; break;
+ case '#':// account variable
+ p += ( p[1] == '#' ? 2 : 1 ); break;
+ case '\'':// instance variable
+ ++p; break;
+ case '.':// npc variable
+ p += ( p[1] == '@' ? 2 : 1 ); break;
+ case '$':// global variable
+ p += ( p[1] == '@' ? 2 : 1 ); break;
+ }
+
+ while( ISALNUM(*p) || *p == '_' )
+ ++p;
+
+ // postfix
+ if( *p == '$' )// string
+ p++;
+
+ return p;
+}
+
+/// Adds a word to str_data.
+/// @see skip_word
+/// @see add_str
+static
+int add_word(const char* p)
+{
+ char* word;
+ int len;
+ int i;
+
+ // Check for a word
+ len = skip_word(p) - p;
+ if( len == 0 )
+ disp_error_message("script:add_word: invalid word. A word consists of undercores and/or alfanumeric characters, and valid variable prefixes/postfixes.", p);
+
+ // Duplicate the word
+ word = (char*)aMalloc(len+1);
+ memcpy(word, p, len);
+ word[len] = 0;
+
+ // add the word
+ i = add_str(word);
+ aFree(word);
+ return i;
+}
+
+/// Parses a function call.
+/// The argument list can have parenthesis or not.
+/// The number of arguments is checked.
+static
+const char* parse_callfunc(const char* p, int require_paren, int is_custom)
+{
+ const char* p2;
+ const char* arg=NULL;
+ int func;
+
+ func = add_word(p);
+ if( str_data[func].type == C_FUNC ){
+ // buildin function
+ add_scriptl(func);
+ add_scriptc(C_ARG);
+ arg = buildin_func[str_data[func].val].arg;
+ } else if( str_data[func].type == C_USERFUNC || str_data[func].type == C_USERFUNC_POS ){
+ // script defined function
+ add_scriptl(buildin_callsub_ref);
+ add_scriptc(C_ARG);
+ add_scriptl(func);
+ arg = buildin_func[str_data[buildin_callsub_ref].val].arg;
+ if( *arg == 0 )
+ disp_error_message("parse_callfunc: callsub has no arguments, please review it's definition",p);
+ if( *arg != '*' )
+ ++arg; // count func as argument
+ } else {
+#ifdef SCRIPT_CALLFUNC_CHECK
+ const char* name = get_str(func);
+ if( !is_custom && strdb_get(userfunc_db, name) == NULL ) {
+#endif
+ disp_error_message("parse_line: expect command, missing function name or calling undeclared function",p);
+#ifdef SCRIPT_CALLFUNC_CHECK
+ } else {;
+ add_scriptl(buildin_callfunc_ref);
+ add_scriptc(C_ARG);
+ add_scriptc(C_STR);
+ while( *name ) add_scriptb(*name ++);
+ add_scriptb(0);
+ arg = buildin_func[str_data[buildin_callfunc_ref].val].arg;
+ if( *arg != '*' ) ++ arg;
+ }
+#endif
+ }
+
+ p = skip_word(p);
+ p = skip_space(p);
+ syntax.curly[syntax.curly_count].type = TYPE_ARGLIST;
+ syntax.curly[syntax.curly_count].count = 0;
+ if( *p == ';' )
+ {// <func name> ';'
+ syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN;
+ } else if( *p == '(' && *(p2=skip_space(p+1)) == ')' )
+ {// <func name> '(' ')'
+ syntax.curly[syntax.curly_count].flag = ARGLIST_PAREN;
+ p = p2;
+ /*
+ } else if( 0 && require_paren && *p != '(' )
+ {// <func name>
+ syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN;
+ */
+ } else
+ {// <func name> <arg list>
+ if( require_paren ){
+ if( *p != '(' )
+ disp_error_message("need '('",p);
+ ++p; // skip '('
+ syntax.curly[syntax.curly_count].flag = ARGLIST_PAREN;
+ } else if( *p == '(' ){
+ syntax.curly[syntax.curly_count].flag = ARGLIST_UNDEFINED;
+ } else {
+ syntax.curly[syntax.curly_count].flag = ARGLIST_NO_PAREN;
+ }
+ ++syntax.curly_count;
+ while( *arg ) {
+ p2=parse_subexpr(p,-1);
+ if( p == p2 )
+ break; // not an argument
+ if( *arg != '*' )
+ ++arg; // next argument
+
+ p=skip_space(p2);
+ if( *arg == 0 || *p != ',' )
+ break; // no more arguments
+ ++p; // skip comma
+ }
+ --syntax.curly_count;
+ }
+ if( *arg && *arg != '?' && *arg != '*' )
+ disp_error_message2("parse_callfunc: not enough arguments, expected ','", p, script_config.warn_func_mismatch_paramnum);
+ if( syntax.curly[syntax.curly_count].type != TYPE_ARGLIST )
+ disp_error_message("parse_callfunc: DEBUG last curly is not an argument list",p);
+ if( syntax.curly[syntax.curly_count].flag == ARGLIST_PAREN ){
+ if( *p != ')' )
+ disp_error_message("parse_callfunc: expected ')' to close argument list",p);
+ ++p;
+ }
+ add_scriptc(C_FUNC);
+ return p;
+}
+
+/// Processes end of logical script line.
+/// @param first When true, only fix up scheduling data is initialized
+/// @param p Script position for error reporting in set_label
+static void parse_nextline(bool first, const char* p)
+{
+ if( !first )
+ {
+ add_scriptc(C_EOL); // mark end of line for stack cleanup
+ set_label(LABEL_NEXTLINE, script_pos, p); // fix up '-' labels
+ }
+
+ // initialize data for new '-' label fix up scheduling
+ str_data[LABEL_NEXTLINE].type = C_NOP;
+ str_data[LABEL_NEXTLINE].backpatch = -1;
+ str_data[LABEL_NEXTLINE].label = -1;
+}
+
+/// Parse a variable assignment using the direct equals operator
+/// @param p script position where the function should run from
+/// @return NULL if not a variable assignment, the new position otherwise
+const char* parse_variable(const char* p) {
+ int i, j, word;
+ c_op type = C_NOP;
+ const char *p2 = NULL;
+ const char *var = p;
+
+ // skip the variable where applicable
+ p = skip_word(p);
+ p = skip_space(p);
+
+ if( p == NULL ) {// end of the line or invalid buffer
+ return NULL;
+ }
+
+ if( *p == '[' ) {// array variable so process the array as appropriate
+ for( p2 = p, i = 0, j = 1; p; ++ i ) {
+ if( *p ++ == ']' && --(j) == 0 ) break;
+ if( *p == '[' ) ++ j;
+ }
+
+ if( !(p = skip_space(p)) ) {// end of line or invalid characters remaining
+ disp_error_message("Missing right expression or closing bracket for variable.", p);
+ }
+ }
+
+ if( type == C_NOP &&
+ !( ( p[0] == '=' && p[1] != '=' && (type = C_EQ) ) // =
+ || ( p[0] == '+' && p[1] == '=' && (type = C_ADD) ) // +=
+ || ( p[0] == '-' && p[1] == '=' && (type = C_SUB) ) // -=
+ || ( p[0] == '^' && p[1] == '=' && (type = C_XOR) ) // ^=
+ || ( p[0] == '|' && p[1] == '=' && (type = C_OR ) ) // |=
+ || ( p[0] == '&' && p[1] == '=' && (type = C_AND) ) // &=
+ || ( p[0] == '*' && p[1] == '=' && (type = C_MUL) ) // *=
+ || ( p[0] == '/' && p[1] == '=' && (type = C_DIV) ) // /=
+ || ( p[0] == '%' && p[1] == '=' && (type = C_MOD) ) // %=
+ || ( p[0] == '~' && p[1] == '=' && (type = C_NOT) ) // ~=
+ || ( p[0] == '+' && p[1] == '+' && (type = C_ADD_PP) ) // ++
+ || ( p[0] == '-' && p[1] == '-' && (type = C_SUB_PP) ) // --
+ || ( p[0] == '<' && p[1] == '<' && p[2] == '=' && (type = C_L_SHIFT) ) // <<=
+ || ( p[0] == '>' && p[1] == '>' && p[2] == '=' && (type = C_R_SHIFT) ) // >>=
+ ) )
+ {// failed to find a matching operator combination so invalid
+ return NULL;
+ }
+
+ switch( type ) {
+ case C_EQ: {// incremental modifier
+ p = skip_space( &p[1] );
+ }
+ break;
+
+ case C_L_SHIFT:
+ case C_R_SHIFT: {// left or right shift modifier
+ p = skip_space( &p[3] );
+ }
+ break;
+
+ default: {// normal incremental command
+ p = skip_space( &p[2] );
+ }
+ }
+
+ if( p == NULL ) {// end of line or invalid buffer
+ return NULL;
+ }
+
+ // push the set function onto the stack
+ add_scriptl(buildin_set_ref);
+ add_scriptc(C_ARG);
+
+ // always append parenthesis to avoid errors
+ syntax.curly[syntax.curly_count].type = TYPE_ARGLIST;
+ syntax.curly[syntax.curly_count].count = 0;
+ syntax.curly[syntax.curly_count].flag = ARGLIST_PAREN;
+
+ // increment the total curly count for the position in the script
+ ++ syntax.curly_count;
+
+ // parse the variable currently being modified
+ word = add_word(var);
+
+ if( str_data[word].type == C_FUNC || str_data[word].type == C_USERFUNC || str_data[word].type == C_USERFUNC_POS )
+ {// cannot assign a variable which exists as a function or label
+ disp_error_message("Cannot modify a variable which has the same name as a function or label.", p);
+ }
+
+ if( p2 ) {// process the variable index
+ const char* p3 = NULL;
+
+ // push the getelementofarray method into the stack
+ add_scriptl(buildin_getelementofarray_ref);
+ add_scriptc(C_ARG);
+ add_scriptl(word);
+
+ // process the sub-expression for this assignment
+ p3 = parse_subexpr(p2 + 1, 1);
+ p3 = skip_space(p3);
+
+ if( *p3 != ']' ) {// closing parenthesis is required for this script
+ disp_error_message("Missing closing ']' parenthesis for the variable assignment.", p3);
+ }
+
+ // push the closing function stack operator onto the stack
+ add_scriptc(C_FUNC);
+ p3 ++;
+ } else {// simply push the variable or value onto the stack
+ add_scriptl(word);
+ }
+
+ if( type != C_EQ )
+ add_scriptc(C_REF);
+
+ if( type == C_ADD_PP || type == C_SUB_PP ) {// incremental operator for the method
+ add_scripti(1);
+ add_scriptc(type == C_ADD_PP ? C_ADD : C_SUB);
+ } else {// process the value as an expression
+ p = parse_subexpr(p, -1);
+
+ if( type != C_EQ )
+ {// push the type of modifier onto the stack
+ add_scriptc(type);
+ }
+ }
+
+ // decrement the curly count for the position within the script
+ -- syntax.curly_count;
+
+ // close the script by appending the function operator
+ add_scriptc(C_FUNC);
+
+ // push the buffer from the method
+ return p;
+}
+
+/*==========================================
+ * Analysis section
+ *------------------------------------------*/
+const char* parse_simpleexpr(const char *p)
+{
+ int i;
+ p=skip_space(p);
+
+ if(*p==';' || *p==',')
+ disp_error_message("parse_simpleexpr: unexpected expr end",p);
+ if(*p=='('){
+ if( (i=syntax.curly_count-1) >= 0 && syntax.curly[i].type == TYPE_ARGLIST )
+ ++syntax.curly[i].count;
+ p=parse_subexpr(p+1,-1);
+ p=skip_space(p);
+ if( (i=syntax.curly_count-1) >= 0 && syntax.curly[i].type == TYPE_ARGLIST &&
+ syntax.curly[i].flag == ARGLIST_UNDEFINED && --syntax.curly[i].count == 0
+ ){
+ if( *p == ',' ){
+ syntax.curly[i].flag = ARGLIST_PAREN;
+ return p;
+ } else
+ syntax.curly[i].flag = ARGLIST_NO_PAREN;
+ }
+ if( *p != ')' )
+ disp_error_message("parse_simpleexpr: unmatch ')'",p);
+ ++p;
+ } else if(ISDIGIT(*p) || ((*p=='-' || *p=='+') && ISDIGIT(p[1]))){
+ char *np;
+ while(*p == '0' && ISDIGIT(p[1])) p++;
+ i=strtoul(p,&np,0);
+ add_scripti(i);
+ p=np;
+ } else if(*p=='"'){
+ add_scriptc(C_STR);
+ p++;
+ while( *p && *p != '"' ){
+ if( (unsigned char)p[-1] <= 0x7e && *p == '\\' )
+ {
+ char buf[8];
+ size_t len = skip_escaped_c(p) - p;
+ size_t n = sv_unescape_c(buf, p, len);
+ if( n != 1 )
+ ShowDebug("parse_simpleexpr: unexpected length %d after unescape (\"%.*s\" -> %.*s)\n", (int)n, (int)len, p, (int)n, buf);
+ p += len;
+ add_scriptb(*buf);
+ continue;
+ }
+ else if( *p == '\n' )
+ disp_error_message("parse_simpleexpr: unexpected newline @ string",p);
+ add_scriptb(*p++);
+ }
+ if(!*p)
+ disp_error_message("parse_simpleexpr: unexpected eof @ string",p);
+ add_scriptb(0);
+ p++; //'"'
+ } else {
+ int l;
+ const char* pv;
+
+ // label , register , function etc
+ if(skip_word(p)==p)
+ disp_error_message("parse_simpleexpr: unexpected character",p);
+
+ l=add_word(p);
+ if( str_data[l].type == C_FUNC || str_data[l].type == C_USERFUNC || str_data[l].type == C_USERFUNC_POS)
+ return parse_callfunc(p,1,0);
+#ifdef SCRIPT_CALLFUNC_CHECK
+ else {
+ const char* name = get_str(l);
+ if( strdb_get(userfunc_db,name) != NULL ) {
+ return parse_callfunc(p,1,1);
+ }
+ }
+#endif
+
+ if( (pv = parse_variable(p)) )
+ {// successfully processed a variable assignment
+ return pv;
+ }
+
+ p=skip_word(p);
+ if( *p == '[' ){
+ // array(name[i] => getelementofarray(name,i) )
+ add_scriptl(buildin_getelementofarray_ref);
+ add_scriptc(C_ARG);
+ add_scriptl(l);
+
+ p=parse_subexpr(p+1,-1);
+ p=skip_space(p);
+ if( *p != ']' )
+ disp_error_message("parse_simpleexpr: unmatch ']'",p);
+ ++p;
+ add_scriptc(C_FUNC);
+ }else
+ add_scriptl(l);
+
+ }
+
+ return p;
+}
+
+/*==========================================
+ * Analysis of the expression
+ *------------------------------------------*/
+const char* parse_subexpr(const char* p,int limit)
+{
+ int op,opl,len;
+ const char* tmpp;
+
+ p=skip_space(p);
+
+ if( *p == '-' ){
+ tmpp = skip_space(p+1);
+ if( *tmpp == ';' || *tmpp == ',' ){
+ add_scriptl(LABEL_NEXTLINE);
+ p++;
+ return p;
+ }
+ }
+
+ if((op=C_NEG,*p=='-') || (op=C_LNOT,*p=='!') || (op=C_NOT,*p=='~')){
+ p=parse_subexpr(p+1,10);
+ add_scriptc(op);
+ } else
+ p=parse_simpleexpr(p);
+ p=skip_space(p);
+ while((
+ (op=C_OP3,opl=0,len=1,*p=='?') ||
+ (op=C_ADD,opl=8,len=1,*p=='+') ||
+ (op=C_SUB,opl=8,len=1,*p=='-') ||
+ (op=C_MUL,opl=9,len=1,*p=='*') ||
+ (op=C_DIV,opl=9,len=1,*p=='/') ||
+ (op=C_MOD,opl=9,len=1,*p=='%') ||
+ (op=C_LAND,opl=2,len=2,*p=='&' && p[1]=='&') ||
+ (op=C_AND,opl=6,len=1,*p=='&') ||
+ (op=C_LOR,opl=1,len=2,*p=='|' && p[1]=='|') ||
+ (op=C_OR,opl=5,len=1,*p=='|') ||
+ (op=C_XOR,opl=4,len=1,*p=='^') ||
+ (op=C_EQ,opl=3,len=2,*p=='=' && p[1]=='=') ||
+ (op=C_NE,opl=3,len=2,*p=='!' && p[1]=='=') ||
+ (op=C_R_SHIFT,opl=7,len=2,*p=='>' && p[1]=='>') ||
+ (op=C_GE,opl=3,len=2,*p=='>' && p[1]=='=') ||
+ (op=C_GT,opl=3,len=1,*p=='>') ||
+ (op=C_L_SHIFT,opl=7,len=2,*p=='<' && p[1]=='<') ||
+ (op=C_LE,opl=3,len=2,*p=='<' && p[1]=='=') ||
+ (op=C_LT,opl=3,len=1,*p=='<')) && opl>limit){
+ p+=len;
+ if(op == C_OP3) {
+ p=parse_subexpr(p,-1);
+ p=skip_space(p);
+ if( *(p++) != ':')
+ disp_error_message("parse_subexpr: need ':'", p-1);
+ p=parse_subexpr(p,-1);
+ } else {
+ p=parse_subexpr(p,opl);
+ }
+ add_scriptc(op);
+ p=skip_space(p);
+ }
+
+ return p; /* return first untreated operator */
+}
+
+/*==========================================
+ * Evaluation of the expression
+ *------------------------------------------*/
+const char* parse_expr(const char *p)
+{
+ switch(*p){
+ case ')': case ';': case ':': case '[': case ']':
+ case '}':
+ disp_error_message("parse_expr: unexpected char",p);
+ }
+ p=parse_subexpr(p,-1);
+ return p;
+}
+
+/*==========================================
+ * Analysis of the line
+ *------------------------------------------*/
+const char* parse_line(const char* p)
+{
+ const char* p2;
+
+ p=skip_space(p);
+ if(*p==';') {
+ //Close decision for if(); for(); while();
+ p = parse_syntax_close(p + 1);
+ return p;
+ }
+ if(*p==')' && parse_syntax_for_flag)
+ return p+1;
+
+ p = skip_space(p);
+ if(p[0] == '{') {
+ syntax.curly[syntax.curly_count].type = TYPE_NULL;
+ syntax.curly[syntax.curly_count].count = -1;
+ syntax.curly[syntax.curly_count].index = -1;
+ syntax.curly_count++;
+ return p + 1;
+ } else if(p[0] == '}') {
+ return parse_curly_close(p);
+ }
+
+ // Syntax-related processing
+ p2 = parse_syntax(p);
+ if(p2 != NULL)
+ return p2;
+
+ // attempt to process a variable assignment
+ p2 = parse_variable(p);
+
+ if( p2 != NULL )
+ {// variable assignment processed so leave the method
+ return parse_syntax_close(p2 + 1);
+ }
+
+ p = parse_callfunc(p,0,0);
+ p = skip_space(p);
+
+ if(parse_syntax_for_flag) {
+ if( *p != ')' )
+ disp_error_message("parse_line: need ')'",p);
+ } else {
+ if( *p != ';' )
+ disp_error_message("parse_line: need ';'",p);
+ }
+
+ //Binding decision for if(), for(), while()
+ p = parse_syntax_close(p+1);
+
+ return p;
+}
+
+// { ... } Closing process
+const char* parse_curly_close(const char* p)
+{
+ if(syntax.curly_count <= 0) {
+ disp_error_message("parse_curly_close: unexpected string",p);
+ return p + 1;
+ } else if(syntax.curly[syntax.curly_count-1].type == TYPE_NULL) {
+ syntax.curly_count--;
+ //Close decision if, for , while
+ p = parse_syntax_close(p + 1);
+ return p;
+ } else if(syntax.curly[syntax.curly_count-1].type == TYPE_SWITCH) {
+ //Closing switch()
+ int pos = syntax.curly_count-1;
+ char label[256];
+ int l;
+ // Remove temporary variables
+ sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Go to the end pointer unconditionally
+ sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // You are here labeled
+ sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ set_label(l,script_pos, p);
+
+ if(syntax.curly[pos].flag) {
+ //Exists default
+ sprintf(label,"goto __SW%x_DEF;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+ }
+
+ // Label end
+ sprintf(label,"__SW%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos, p);
+ linkdb_final(&syntax.curly[pos].case_label); // free the list of case label
+ syntax.curly_count--;
+ //Closing decision if, for , while
+ p = parse_syntax_close(p + 1);
+ return p;
+ } else {
+ disp_error_message("parse_curly_close: unexpected string",p);
+ return p + 1;
+ }
+}
+
+// Syntax-related processing
+// break, case, continue, default, do, for, function,
+// if, switch, while ? will handle this internally.
+const char* parse_syntax(const char* p)
+{
+ const char *p2 = skip_word(p);
+
+ switch(*p) {
+ case 'B':
+ case 'b':
+ if(p2 - p == 5 && !strncasecmp(p,"break",5)) {
+ // break Processing
+ char label[256];
+ int pos = syntax.curly_count - 1;
+ while(pos >= 0) {
+ if(syntax.curly[pos].type == TYPE_DO) {
+ sprintf(label,"goto __DO%x_FIN;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_FOR) {
+ sprintf(label,"goto __FR%x_FIN;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_WHILE) {
+ sprintf(label,"goto __WL%x_FIN;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_SWITCH) {
+ sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index);
+ break;
+ }
+ pos--;
+ }
+ if(pos < 0) {
+ disp_error_message("parse_syntax: unexpected 'break'",p);
+ } else {
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+ }
+ p = skip_space(p2);
+ if(*p != ';')
+ disp_error_message("parse_syntax: need ';'",p);
+ // Closing decision if, for , while
+ p = parse_syntax_close(p + 1);
+ return p;
+ }
+ break;
+ case 'c':
+ case 'C':
+ if(p2 - p == 4 && !strncasecmp(p,"case",4)) {
+ //Processing case
+ int pos = syntax.curly_count-1;
+ if(pos < 0 || syntax.curly[pos].type != TYPE_SWITCH) {
+ disp_error_message("parse_syntax: unexpected 'case' ",p);
+ return p+1;
+ } else {
+ char label[256];
+ int l,v;
+ char *np;
+ if(syntax.curly[pos].count != 1) {
+ //Jump for FALLTHRU
+ sprintf(label,"goto __SW%x_%xJ;",syntax.curly[pos].index,syntax.curly[pos].count);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // You are here labeled
+ sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ set_label(l,script_pos, p);
+ }
+ //Decision statement switch
+ p = skip_space(p2);
+ if(p == p2) {
+ disp_error_message("parse_syntax: expect space ' '",p);
+ }
+ // check whether case label is integer or not
+ v = strtol(p,&np,0);
+ if(np == p) { //Check for constants
+ p2 = skip_word(p);
+ v = p2-p; // length of word at p2
+ memcpy(label,p,v);
+ label[v]='\0';
+ if( !script_get_constant(label, &v) )
+ disp_error_message("parse_syntax: 'case' label not integer",p);
+ p = skip_word(p);
+ } else { //Numeric value
+ if((*p == '-' || *p == '+') && ISDIGIT(p[1])) // pre-skip because '-' can not skip_word
+ p++;
+ p = skip_word(p);
+ if(np != p)
+ disp_error_message("parse_syntax: 'case' label not integer",np);
+ }
+ p = skip_space(p);
+ if(*p != ':')
+ disp_error_message("parse_syntax: expect ':'",p);
+ sprintf(label,"if(%d != $@__SW%x_VAL) goto __SW%x_%x;",
+ v,syntax.curly[pos].index,syntax.curly[pos].index,syntax.curly[pos].count+1);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ // Bad I do not parse twice
+ p2 = parse_line(label);
+ parse_line(p2);
+ syntax.curly_count--;
+ if(syntax.curly[pos].count != 1) {
+ // Label after the completion of FALLTHRU
+ sprintf(label,"__SW%x_%xJ",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ }
+ // check duplication of case label [Rayce]
+ if(linkdb_search(&syntax.curly[pos].case_label, (void*)__64BPRTSIZE(v)) != NULL)
+ disp_error_message("parse_syntax: dup 'case'",p);
+ linkdb_insert(&syntax.curly[pos].case_label, (void*)__64BPRTSIZE(v), (void*)1);
+
+ sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+
+ parse_line(label);
+ syntax.curly_count--;
+ syntax.curly[pos].count++;
+ }
+ return p + 1;
+ } else if(p2 - p == 8 && !strncasecmp(p,"continue",8)) {
+ // Processing continue
+ char label[256];
+ int pos = syntax.curly_count - 1;
+ while(pos >= 0) {
+ if(syntax.curly[pos].type == TYPE_DO) {
+ sprintf(label,"goto __DO%x_NXT;",syntax.curly[pos].index);
+ syntax.curly[pos].flag = 1; //Flag put the link for continue
+ break;
+ } else if(syntax.curly[pos].type == TYPE_FOR) {
+ sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index);
+ break;
+ } else if(syntax.curly[pos].type == TYPE_WHILE) {
+ sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index);
+ break;
+ }
+ pos--;
+ }
+ if(pos < 0) {
+ disp_error_message("parse_syntax: unexpected 'continue'",p);
+ } else {
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+ }
+ p = skip_space(p2);
+ if(*p != ';')
+ disp_error_message("parse_syntax: need ';'",p);
+ //Closing decision if, for , while
+ p = parse_syntax_close(p + 1);
+ return p;
+ }
+ break;
+ case 'd':
+ case 'D':
+ if(p2 - p == 7 && !strncasecmp(p,"default",7)) {
+ // Switch - default processing
+ int pos = syntax.curly_count-1;
+ if(pos < 0 || syntax.curly[pos].type != TYPE_SWITCH) {
+ disp_error_message("parse_syntax: unexpected 'default'",p);
+ } else if(syntax.curly[pos].flag) {
+ disp_error_message("parse_syntax: dup 'default'",p);
+ } else {
+ char label[256];
+ int l;
+ // Put the label location
+ p = skip_space(p2);
+ if(*p != ':') {
+ disp_error_message("parse_syntax: need ':'",p);
+ }
+ sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+
+ // Skip to the next link w/o condition
+ sprintf(label,"goto __SW%x_%x;",syntax.curly[pos].index,syntax.curly[pos].count+1);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // The default label
+ sprintf(label,"__SW%x_DEF",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+
+ syntax.curly[syntax.curly_count - 1].flag = 1;
+ syntax.curly[pos].count++;
+ }
+ return p + 1;
+ } else if(p2 - p == 2 && !strncasecmp(p,"do",2)) {
+ int l;
+ char label[256];
+ p=skip_space(p2);
+
+ syntax.curly[syntax.curly_count].type = TYPE_DO;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ // Label of the (do) form here
+ sprintf(label,"__DO%x_BGN",syntax.curly[syntax.curly_count].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ syntax.curly_count++;
+ return p;
+ }
+ break;
+ case 'f':
+ case 'F':
+ if(p2 - p == 3 && !strncasecmp(p,"for",3)) {
+ int l;
+ char label[256];
+ int pos = syntax.curly_count;
+ syntax.curly[syntax.curly_count].type = TYPE_FOR;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ syntax.curly_count++;
+
+ p=skip_space(p2);
+
+ if(*p != '(')
+ disp_error_message("parse_syntax: need '('",p);
+ p++;
+
+ // Execute the initialization statement
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ p=parse_line(p);
+ syntax.curly_count--;
+
+ // Form the start of label decision
+ sprintf(label,"__FR%x_J",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+
+ p=skip_space(p);
+ if(*p == ';') {
+ // For (; Because the pattern of always true ;)
+ ;
+ } else {
+ // Skip to the end point if the condition is false
+ sprintf(label,"__FR%x_FIN",syntax.curly[pos].index);
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ }
+ if(*p != ';')
+ disp_error_message("parse_syntax: need ';'",p);
+ p++;
+
+ // Skip to the beginning of the loop
+ sprintf(label,"goto __FR%x_BGN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Labels to form the next loop
+ sprintf(label,"__FR%x_NXT",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+
+ // Process the next time you enter the loop
+ // A ')' last for; flag to be treated as'
+ parse_syntax_for_flag = 1;
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ p=parse_line(p);
+ syntax.curly_count--;
+ parse_syntax_for_flag = 0;
+
+ // Skip to the determination process conditions
+ sprintf(label,"goto __FR%x_J;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Loop start labeling
+ sprintf(label,"__FR%x_BGN",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ return p;
+ }
+ else if( p2 - p == 8 && strncasecmp(p,"function",8) == 0 )
+ {// internal script function
+ const char *func_name;
+
+ func_name = skip_space(p2);
+ p = skip_word(func_name);
+ if( p == func_name )
+ disp_error_message("parse_syntax:function: function name is missing or invalid", p);
+ p2 = skip_space(p);
+ if( *p2 == ';' )
+ {// function <name> ;
+ // function declaration - just register the name
+ int l;
+ l = add_word(func_name);
+ if( str_data[l].type == C_NOP )// register only, if the name was not used by something else
+ str_data[l].type = C_USERFUNC;
+ else if( str_data[l].type == C_USERFUNC )
+ ; // already registered
+ else
+ disp_error_message("parse_syntax:function: function name is invalid", func_name);
+
+ // Close condition of if, for, while
+ p = parse_syntax_close(p2 + 1);
+ return p;
+ }
+ else if(*p2 == '{')
+ {// function <name> <line/block of code>
+ char label[256];
+ int l;
+
+ syntax.curly[syntax.curly_count].type = TYPE_USERFUNC;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ ++syntax.curly_count;
+
+ // Jump over the function code
+ sprintf(label, "goto __FN%x_FIN;", syntax.curly[syntax.curly_count-1].index);
+ syntax.curly[syntax.curly_count].type = TYPE_NULL;
+ ++syntax.curly_count;
+ parse_line(label);
+ --syntax.curly_count;
+
+ // Set the position of the function (label)
+ l=add_word(func_name);
+ if( str_data[l].type == C_NOP || str_data[l].type == C_USERFUNC )// register only, if the name was not used by something else
+ {
+ str_data[l].type = C_USERFUNC;
+ set_label(l, script_pos, p);
+ if( parse_options&SCRIPT_USE_LABEL_DB )
+ strdb_iput(scriptlabel_db, get_str(l), script_pos);
+ }
+ else
+ disp_error_message("parse_syntax:function: function name is invalid", func_name);
+
+ return skip_space(p);
+ }
+ else
+ {
+ disp_error_message("expect ';' or '{' at function syntax",p);
+ }
+ }
+ break;
+ case 'i':
+ case 'I':
+ if(p2 - p == 2 && !strncasecmp(p,"if",2)) {
+ // If process
+ char label[256];
+ p=skip_space(p2);
+ if(*p != '(') { //Prevent if this {} non-c syntax. from Rayce (jA)
+ disp_error_message("need '('",p);
+ }
+ syntax.curly[syntax.curly_count].type = TYPE_IF;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ sprintf(label,"__IF%x_%x",syntax.curly[syntax.curly_count].index,syntax.curly[syntax.curly_count].count);
+ syntax.curly_count++;
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ return p;
+ }
+ break;
+ case 's':
+ case 'S':
+ if(p2 - p == 6 && !strncasecmp(p,"switch",6)) {
+ // Processing of switch ()
+ char label[256];
+ p=skip_space(p2);
+ if(*p != '(') {
+ disp_error_message("need '('",p);
+ }
+ syntax.curly[syntax.curly_count].type = TYPE_SWITCH;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ sprintf(label,"$@__SW%x_VAL",syntax.curly[syntax.curly_count].index);
+ syntax.curly_count++;
+ add_scriptl(add_str("set"));
+ add_scriptc(C_ARG);
+ add_scriptl(add_str(label));
+ p=parse_expr(p);
+ p=skip_space(p);
+ if(*p != '{') {
+ disp_error_message("parse_syntax: need '{'",p);
+ }
+ add_scriptc(C_FUNC);
+ return p + 1;
+ }
+ break;
+ case 'w':
+ case 'W':
+ if(p2 - p == 5 && !strncasecmp(p,"while",5)) {
+ int l;
+ char label[256];
+ p=skip_space(p2);
+ if(*p != '(') {
+ disp_error_message("need '('",p);
+ }
+ syntax.curly[syntax.curly_count].type = TYPE_WHILE;
+ syntax.curly[syntax.curly_count].count = 1;
+ syntax.curly[syntax.curly_count].index = syntax.index++;
+ syntax.curly[syntax.curly_count].flag = 0;
+ // Form the start of label decision
+ sprintf(label,"__WL%x_NXT",syntax.curly[syntax.curly_count].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+
+ // Skip to the end point if the condition is false
+ sprintf(label,"__WL%x_FIN",syntax.curly[syntax.curly_count].index);
+ syntax.curly_count++;
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ return p;
+ }
+ break;
+ }
+ return NULL;
+}
+
+const char* parse_syntax_close(const char *p) {
+ // If (...) for (...) hoge (); as to make sure closed closed once again
+ int flag;
+
+ do {
+ p = parse_syntax_close_sub(p,&flag);
+ } while(flag);
+ return p;
+}
+
+// Close judgment if, for, while, of do
+// flag == 1 : closed
+// flag == 0 : not closed
+const char* parse_syntax_close_sub(const char* p,int* flag)
+{
+ char label[256];
+ int pos = syntax.curly_count - 1;
+ int l;
+ *flag = 1;
+
+ if(syntax.curly_count <= 0) {
+ *flag = 0;
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_IF) {
+ const char *bp = p;
+ const char *p2;
+
+ // if-block and else-block end is a new line
+ parse_nextline(false, p);
+
+ // Skip to the last location if
+ sprintf(label,"goto __IF%x_FIN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Put the label of the location
+ sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+
+ syntax.curly[pos].count++;
+ p = skip_space(p);
+ p2 = skip_word(p);
+ if(!syntax.curly[pos].flag && p2 - p == 4 && !strncasecmp(p,"else",4)) {
+ // else or else - if
+ p = skip_space(p2);
+ p2 = skip_word(p);
+ if(p2 - p == 2 && !strncasecmp(p,"if",2)) {
+ // else - if
+ p=skip_space(p2);
+ if(*p != '(') {
+ disp_error_message("need '('",p);
+ }
+ sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count);
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+ *flag = 0;
+ return p;
+ } else {
+ // else
+ if(!syntax.curly[pos].flag) {
+ syntax.curly[pos].flag = 1;
+ *flag = 0;
+ return p;
+ }
+ }
+ }
+ // Close if
+ syntax.curly_count--;
+ // Put the label of the final location
+ sprintf(label,"__IF%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ if(syntax.curly[pos].flag == 1) {
+ // Because the position of the pointer is the same if not else for this
+ return bp;
+ }
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_DO) {
+ int l;
+ char label[256];
+ const char *p2;
+
+ if(syntax.curly[pos].flag) {
+ // (Come here continue) to form the label here
+ sprintf(label,"__DO%x_NXT",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ }
+
+ // Skip to the end point if the condition is false
+ p = skip_space(p);
+ p2 = skip_word(p);
+ if(p2 - p != 5 || strncasecmp(p,"while",5))
+ disp_error_message("parse_syntax: need 'while'",p);
+
+ p = skip_space(p2);
+ if(*p != '(') {
+ disp_error_message("need '('",p);
+ }
+
+ // do-block end is a new line
+ parse_nextline(false, p);
+
+ sprintf(label,"__DO%x_FIN",syntax.curly[pos].index);
+ add_scriptl(add_str("jump_zero"));
+ add_scriptc(C_ARG);
+ p=parse_expr(p);
+ p=skip_space(p);
+ add_scriptl(add_str(label));
+ add_scriptc(C_FUNC);
+
+ // Skip to the starting point
+ sprintf(label,"goto __DO%x_BGN;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Form label of the end point conditions
+ sprintf(label,"__DO%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ p = skip_space(p);
+ if(*p != ';') {
+ disp_error_message("parse_syntax: need ';'",p);
+ return p+1;
+ }
+ p++;
+ syntax.curly_count--;
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_FOR) {
+ // for-block end is a new line
+ parse_nextline(false, p);
+
+ // Skip to the next loop
+ sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // End for labeling
+ sprintf(label,"__FR%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ syntax.curly_count--;
+ return p;
+ } else if(syntax.curly[pos].type == TYPE_WHILE) {
+ // while-block end is a new line
+ parse_nextline(false, p);
+
+ // Skip to the decision while
+ sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index);
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // End while labeling
+ sprintf(label,"__WL%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ syntax.curly_count--;
+ return p;
+ } else if(syntax.curly[syntax.curly_count-1].type == TYPE_USERFUNC) {
+ int pos = syntax.curly_count-1;
+ char label[256];
+ int l;
+ // Back
+ sprintf(label,"return;");
+ syntax.curly[syntax.curly_count++].type = TYPE_NULL;
+ parse_line(label);
+ syntax.curly_count--;
+
+ // Put the label of the location
+ sprintf(label,"__FN%x_FIN",syntax.curly[pos].index);
+ l=add_str(label);
+ set_label(l,script_pos,p);
+ syntax.curly_count--;
+ return p;
+ } else {
+ *flag = 0;
+ return p;
+ }
+}
+
+/*==========================================
+ * Added built-in functions
+ *------------------------------------------*/
+static void add_buildin_func(void)
+{
+ int i,n;
+ const char* p;
+ for( i = 0; buildin_func[i].func; i++ )
+ {
+ // arg must follow the pattern: (v|s|i|r|l)*\?*\*?
+ // 'v' - value (either string or int or reference)
+ // 's' - string
+ // 'i' - int
+ // 'r' - reference (of a variable)
+ // 'l' - label
+ // '?' - one optional parameter
+ // '*' - unknown number of optional parameters
+ p = buildin_func[i].arg;
+ while( *p == 'v' || *p == 's' || *p == 'i' || *p == 'r' || *p == 'l' ) ++p;
+ while( *p == '?' ) ++p;
+ if( *p == '*' ) ++p;
+ if( *p != 0){
+ ShowWarning("add_buildin_func: ignoring function \"%s\" with invalid arg \"%s\".\n", buildin_func[i].name, buildin_func[i].arg);
+ } else if( *skip_word(buildin_func[i].name) != 0 ){
+ ShowWarning("add_buildin_func: ignoring function with invalid name \"%s\" (must be a word).\n", buildin_func[i].name);
+ } else {
+ n = add_str(buildin_func[i].name);
+ str_data[n].type = C_FUNC;
+ str_data[n].val = i;
+ str_data[n].func = buildin_func[i].func;
+
+ if (!strcmp(buildin_func[i].name, "set")) buildin_set_ref = n;
+ else if (!strcmp(buildin_func[i].name, "callsub")) buildin_callsub_ref = n;
+ else if (!strcmp(buildin_func[i].name, "callfunc")) buildin_callfunc_ref = n;
+ else if( !strcmp(buildin_func[i].name, "getelementofarray") ) buildin_getelementofarray_ref = n;
+ }
+ }
+}
+
+/// Retrieves the value of a constant.
+bool script_get_constant(const char* name, int* value)
+{
+ int n = search_str(name);
+
+ if( n == -1 || str_data[n].type != C_INT )
+ {// not found or not a constant
+ return false;
+ }
+ value[0] = str_data[n].val;
+
+ return true;
+}
+
+/// Creates new constant or parameter with given value.
+void script_set_constant(const char* name, int value, bool isparameter)
+{
+ int n = add_str(name);
+
+ if( str_data[n].type == C_NOP )
+ {// new
+ str_data[n].type = isparameter ? C_PARAM : C_INT;
+ str_data[n].val = value;
+ }
+ else if( str_data[n].type == C_PARAM || str_data[n].type == C_INT )
+ {// existing parameter or constant
+ ShowError("script_set_constant: Attempted to overwrite existing %s '%s' (old value=%d, new value=%d).\n", ( str_data[n].type == C_PARAM ) ? "parameter" : "constant", name, str_data[n].val, value);
+ }
+ else
+ {// existing name
+ ShowError("script_set_constant: Invalid name for %s '%s' (already defined as %s).\n", isparameter ? "parameter" : "constant", name, script_op2name(str_data[n].type));
+ }
+}
+
+/*==========================================
+ * Reading constant databases
+ * const.txt
+ *------------------------------------------*/
+static void read_constdb(void)
+{
+ FILE *fp;
+ char line[1024],name[1024],val[1024];
+ int type;
+
+ sprintf(line, "%s/const.txt", db_path);
+ fp=fopen(line, "r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", line);
+ return ;
+ }
+ while(fgets(line, sizeof(line), fp))
+ {
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ type=0;
+ if(sscanf(line,"%[A-Za-z0-9_],%[-0-9xXA-Fa-f],%d",name,val,&type)>=2 ||
+ sscanf(line,"%[A-Za-z0-9_] %[-0-9xXA-Fa-f] %d",name,val,&type)>=2){
+ script_set_constant(name, (int)strtol(val, NULL, 0), (bool)type);
+ }
+ }
+ fclose(fp);
+}
+
+/*==========================================
+ * Display emplacement line of script
+ *------------------------------------------*/
+static const char* script_print_line(StringBuf* buf, const char* p, const char* mark, int line)
+{
+ int i;
+ if( p == NULL || !p[0] ) return NULL;
+ if( line < 0 )
+ StringBuf_Printf(buf, "*% 5d : ", -line);
+ else
+ StringBuf_Printf(buf, " % 5d : ", line);
+ for(i=0;p[i] && p[i] != '\n';i++){
+ if(p + i != mark)
+ StringBuf_Printf(buf, "%c", p[i]);
+ else
+ StringBuf_Printf(buf, "\'%c\'", p[i]);
+ }
+ StringBuf_AppendStr(buf, "\n");
+ return p+i+(p[i] == '\n' ? 1 : 0);
+}
+
+void script_error(const char* src, const char* file, int start_line, const char* error_msg, const char* error_pos)
+{
+ // Find the line where the error occurred
+ int j;
+ int line = start_line;
+ const char *p;
+ const char *linestart[5] = { NULL, NULL, NULL, NULL, NULL };
+ StringBuf buf;
+
+ for(p=src;p && *p;line++){
+ const char *lineend=strchr(p,'\n');
+ if(lineend==NULL || error_pos<lineend){
+ break;
+ }
+ for( j = 0; j < 4; j++ ) {
+ linestart[j] = linestart[j+1];
+ }
+ linestart[4] = p;
+ p=lineend+1;
+ }
+
+ StringBuf_Init(&buf);
+ StringBuf_AppendStr(&buf, "\a\n");
+ StringBuf_Printf(&buf, "script error on %s line %d\n", file, line);
+ StringBuf_Printf(&buf, " %s\n", error_msg);
+ for(j = 0; j < 5; j++ ) {
+ script_print_line(&buf, linestart[j], NULL, line + j - 5);
+ }
+ p = script_print_line(&buf, p, error_pos, -line);
+ for(j = 0; j < 5; j++) {
+ p = script_print_line(&buf, p, NULL, line + j + 1 );
+ }
+ ShowError("%s", StringBuf_Value(&buf));
+ StringBuf_Destroy(&buf);
+}
+
+/*==========================================
+ * Analysis of the script
+ *------------------------------------------*/
+struct script_code* parse_script(const char *src,const char *file,int line,int options)
+{
+ const char *p,*tmpp;
+ int i;
+ struct script_code* code = NULL;
+ static int first=1;
+ char end;
+ bool unresolved_names = false;
+
+ if( src == NULL )
+ return NULL;// empty script
+
+ memset(&syntax,0,sizeof(syntax));
+ if(first){
+ add_buildin_func();
+ read_constdb();
+ first=0;
+ }
+
+ script_buf=(unsigned char *)aMalloc(SCRIPT_BLOCK_SIZE*sizeof(unsigned char));
+ script_pos=0;
+ script_size=SCRIPT_BLOCK_SIZE;
+ parse_nextline(true, NULL);
+
+ // who called parse_script is responsible for clearing the database after using it, but just in case... lets clear it here
+ if( options&SCRIPT_USE_LABEL_DB )
+ db_clear(scriptlabel_db);
+ parse_options = options;
+
+ if( setjmp( error_jump ) != 0 ) {
+ //Restore program state when script has problems. [from jA]
+ int i;
+ const int size = ARRAYLENGTH(syntax.curly);
+ if( error_report )
+ script_error(src,file,line,error_msg,error_pos);
+ aFree( error_msg );
+ aFree( script_buf );
+ script_pos = 0;
+ script_size = 0;
+ script_buf = NULL;
+ for(i=LABEL_START;i<str_num;i++)
+ if(str_data[i].type == C_NOP) str_data[i].type = C_NAME;
+ for(i=0; i<size; i++)
+ linkdb_final(&syntax.curly[i].case_label);
+ return NULL;
+ }
+
+ parse_syntax_for_flag=0;
+ p=src;
+ p=skip_space(p);
+ if( options&SCRIPT_IGNORE_EXTERNAL_BRACKETS )
+ {// does not require brackets around the script
+ if( *p == '\0' && !(options&SCRIPT_RETURN_EMPTY_SCRIPT) )
+ {// empty script and can return NULL
+ aFree( script_buf );
+ script_pos = 0;
+ script_size = 0;
+ script_buf = NULL;
+ return NULL;
+ }
+ end = '\0';
+ }
+ else
+ {// requires brackets around the script
+ if( *p != '{' )
+ disp_error_message("not found '{'",p);
+ p = skip_space(p+1);
+ if( *p == '}' && !(options&SCRIPT_RETURN_EMPTY_SCRIPT) )
+ {// empty script and can return NULL
+ aFree( script_buf );
+ script_pos = 0;
+ script_size = 0;
+ script_buf = NULL;
+ return NULL;
+ }
+ end = '}';
+ }
+
+ // clear references of labels, variables and internal functions
+ for(i=LABEL_START;i<str_num;i++){
+ if(
+ str_data[i].type==C_POS || str_data[i].type==C_NAME ||
+ str_data[i].type==C_USERFUNC || str_data[i].type == C_USERFUNC_POS
+ ){
+ str_data[i].type=C_NOP;
+ str_data[i].backpatch=-1;
+ str_data[i].label=-1;
+ }
+ }
+
+ while( syntax.curly_count != 0 || *p != end )
+ {
+ if( *p == '\0' )
+ disp_error_message("unexpected end of script",p);
+ // Special handling only label
+ tmpp=skip_space(skip_word(p));
+ if(*tmpp==':' && !(!strncasecmp(p,"default:",8) && p + 7 == tmpp)){
+ i=add_word(p);
+ set_label(i,script_pos,p);
+ if( parse_options&SCRIPT_USE_LABEL_DB )
+ strdb_iput(scriptlabel_db, get_str(i), script_pos);
+ p=tmpp+1;
+ p=skip_space(p);
+ continue;
+ }
+
+ // All other lumped
+ p=parse_line(p);
+ p=skip_space(p);
+
+ parse_nextline(false, p);
+ }
+
+ add_scriptc(C_NOP);
+
+ // trim code to size
+ script_size = script_pos;
+ RECREATE(script_buf,unsigned char,script_pos);
+
+ // default unknown references to variables
+ for(i=LABEL_START;i<str_num;i++){
+ if(str_data[i].type==C_NOP){
+ int j,next;
+ str_data[i].type=C_NAME;
+ str_data[i].label=i;
+ for(j=str_data[i].backpatch;j>=0 && j!=0x00ffffff;){
+ next=GETVALUE(script_buf,j);
+ SETVALUE(script_buf,j,i);
+ j=next;
+ }
+ }
+ else if( str_data[i].type == C_USERFUNC )
+ {// 'function name;' without follow-up code
+ ShowError("parse_script: function '%s' declared but not defined.\n", str_buf+str_data[i].str);
+ unresolved_names = true;
+ }
+ }
+
+ if( unresolved_names )
+ {
+ disp_error_message("parse_script: unresolved function references", p);
+ }
+
+#ifdef DEBUG_DISP
+ for(i=0;i<script_pos;i++){
+ if((i&15)==0) ShowMessage("%04x : ",i);
+ ShowMessage("%02x ",script_buf[i]);
+ if((i&15)==15) ShowMessage("\n");
+ }
+ ShowMessage("\n");
+#endif
+#ifdef DEBUG_DISASM
+ {
+ int i = 0,j;
+ while(i < script_pos) {
+ c_op op = get_com(script_buf,&i);
+
+ ShowMessage("%06x %s", i, script_op2name(op));
+ j = i;
+ switch(op) {
+ case C_INT:
+ ShowMessage(" %d", get_num(script_buf,&i));
+ break;
+ case C_POS:
+ ShowMessage(" 0x%06x", *(int*)(script_buf+i)&0xffffff);
+ i += 3;
+ break;
+ case C_NAME:
+ j = (*(int*)(script_buf+i)&0xffffff);
+ ShowMessage(" %s", ( j == 0xffffff ) ? "?? unknown ??" : get_str(j));
+ i += 3;
+ break;
+ case C_STR:
+ j = strlen(script_buf + i);
+ ShowMessage(" %s", script_buf + i);
+ i += j+1;
+ break;
+ }
+ ShowMessage(CL_CLL"\n");
+ }
+ }
+#endif
+
+ CREATE(code,struct script_code,1);
+ code->script_buf = script_buf;
+ code->script_size = script_size;
+ code->script_vars = idb_alloc(DB_OPT_RELEASE_DATA);
+ return code;
+}
+
+/// Returns the player attached to this script, identified by the rid.
+/// If there is no player attached, the script is terminated.
+TBL_PC *script_rid2sd(struct script_state *st)
+{
+ TBL_PC *sd=map_id2sd(st->rid);
+ if(!sd){
+ ShowError("script_rid2sd: fatal error ! player not attached!\n");
+ script_reportfunc(st);
+ script_reportsrc(st);
+ st->state = END;
+ }
+ return sd;
+}
+
+/// Dereferences a variable/constant, replacing it with a copy of the value.
+///
+/// @param st Script state
+/// @param data Variable/constant
+void get_val(struct script_state* st, struct script_data* data)
+{
+ const char* name;
+ char prefix;
+ char postfix;
+ TBL_PC* sd = NULL;
+
+ if( !data_isreference(data) )
+ return;// not a variable/constant
+
+ name = reference_getname(data);
+ prefix = name[0];
+ postfix = name[strlen(name) - 1];
+
+ //##TODO use reference_tovariable(data) when it's confirmed that it works [FlavioJS]
+ if( !reference_toconstant(data) && not_server_variable(prefix) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ {// needs player attached
+ if( postfix == '$' )
+ {// string variable
+ ShowWarning("script:get_val: cannot access player variable '%s', defaulting to \"\"\n", name);
+ data->type = C_CONSTSTR;
+ data->u.str = "";
+ }
+ else
+ {// integer variable
+ ShowWarning("script:get_val: cannot access player variable '%s', defaulting to 0\n", name);
+ data->type = C_INT;
+ data->u.num = 0;
+ }
+ return;
+ }
+ }
+
+ if( postfix == '$' )
+ {// string variable
+
+ switch( prefix )
+ {
+ case '@':
+ data->u.str = pc_readregstr(sd, data->u.num);
+ break;
+ case '$':
+ data->u.str = mapreg_readregstr(data->u.num);
+ break;
+ case '#':
+ if( name[1] == '#' )
+ data->u.str = pc_readaccountreg2str(sd, name);// global
+ else
+ data->u.str = pc_readaccountregstr(sd, name);// local
+ break;
+ case '.':
+ {
+ struct DBMap* n =
+ data->ref ? *data->ref:
+ name[1] == '@' ? st->stack->var_function:// instance/scope variable
+ st->script->script_vars;// npc variable
+ if( n )
+ data->u.str = (char*)idb_get(n,reference_getuid(data));
+ else
+ data->u.str = NULL;
+ }
+ break;
+ case '\'':
+ if (st->instance_id) {
+ data->u.str = (char*)idb_get(instance[st->instance_id].vars,reference_getuid(data));
+ } else {
+ ShowWarning("script:get_val: cannot access instance variable '%s', defaulting to \"\"\n", name);
+ data->u.str = NULL;
+ }
+ break;
+ default:
+ data->u.str = pc_readglobalreg_str(sd, name);
+ break;
+ }
+
+ if( data->u.str == NULL || data->u.str[0] == '\0' )
+ {// empty string
+ data->type = C_CONSTSTR;
+ data->u.str = "";
+ }
+ else
+ {// duplicate string
+ data->type = C_STR;
+ data->u.str = aStrdup(data->u.str);
+ }
+
+ }
+ else
+ {// integer variable
+
+ data->type = C_INT;
+
+ if( reference_toconstant(data) )
+ {
+ data->u.num = reference_getconstant(data);
+ }
+ else if( reference_toparam(data) )
+ {
+ data->u.num = pc_readparam(sd, reference_getparamtype(data));
+ }
+ else
+ switch( prefix )
+ {
+ case '@':
+ data->u.num = pc_readreg(sd, data->u.num);
+ break;
+ case '$':
+ data->u.num = mapreg_readreg(data->u.num);
+ break;
+ case '#':
+ if( name[1] == '#' )
+ data->u.num = pc_readaccountreg2(sd, name);// global
+ else
+ data->u.num = pc_readaccountreg(sd, name);// local
+ break;
+ case '.':
+ {
+ struct DBMap* n =
+ data->ref ? *data->ref:
+ name[1] == '@' ? st->stack->var_function:// instance/scope variable
+ st->script->script_vars;// npc variable
+ if( n )
+ data->u.num = (int)idb_iget(n,reference_getuid(data));
+ else
+ data->u.num = 0;
+ }
+ break;
+ case '\'':
+ if( st->instance_id )
+ data->u.num = (int)idb_iget(instance[st->instance_id].vars,reference_getuid(data));
+ else {
+ ShowWarning("script:get_val: cannot access instance variable '%s', defaulting to 0\n", name);
+ data->u.num = 0;
+ }
+ break;
+ default:
+ data->u.num = pc_readglobalreg(sd, name);
+ break;
+ }
+
+ }
+
+ return;
+}
+
+struct script_data* push_val2(struct script_stack* stack, enum c_op type, int val, struct DBMap** ref);
+
+/// Retrieves the value of a reference identified by uid (variable, constant, param)
+/// The value is left in the top of the stack and needs to be removed manually.
+void* get_val2(struct script_state* st, int uid, struct DBMap** ref)
+{
+ struct script_data* data;
+ push_val2(st->stack, C_NAME, uid, ref);
+ data = script_getdatatop(st, -1);
+ get_val(st, data);
+ return (data->type == C_INT ? (void*)__64BPRTSIZE(data->u.num) : (void*)__64BPRTSIZE(data->u.str));
+}
+
+/*==========================================
+ * Stores the value of a script variable
+ * Return value is 0 on fail, 1 on success.
+ *------------------------------------------*/
+static int set_reg(struct script_state* st, TBL_PC* sd, int num, const char* name, const void* value, struct DBMap** ref)
+{
+ char prefix = name[0];
+
+ if( is_string_variable(name) )
+ {// string variable
+ const char* str = (const char*)value;
+ switch (prefix) {
+ case '@':
+ return pc_setregstr(sd, num, str);
+ case '$':
+ return mapreg_setregstr(num, str);
+ case '#':
+ return (name[1] == '#') ?
+ pc_setaccountreg2str(sd, name, str) :
+ pc_setaccountregstr(sd, name, str);
+ case '.':
+ {
+ struct DBMap* n;
+ n = (ref) ? *ref : (name[1] == '@') ? st->stack->var_function : st->script->script_vars;
+ if( n ) {
+ idb_remove(n, num);
+ if (str[0]) idb_put(n, num, aStrdup(str));
+ }
+ }
+ return 1;
+ case '\'':
+ if( st->instance_id ) {
+ idb_remove(instance[st->instance_id].vars, num);
+ if( str[0] ) idb_put(instance[st->instance_id].vars, num, aStrdup(str));
+ }
+ return 1;
+ default:
+ return pc_setglobalreg_str(sd, name, str);
+ }
+ }
+ else
+ {// integer variable
+ int val = (int)__64BPRTSIZE(value);
+ if(str_data[num&0x00ffffff].type == C_PARAM)
+ {
+ if( pc_setparam(sd, str_data[num&0x00ffffff].val, val) == 0 )
+ {
+ if( st != NULL )
+ {
+ ShowError("script:set_reg: failed to set param '%s' to %d.\n", name, val);
+ script_reportsrc(st);
+ st->state = END;
+ }
+ return 0;
+ }
+ return 1;
+ }
+
+ switch (prefix) {
+ case '@':
+ return pc_setreg(sd, num, val);
+ case '$':
+ return mapreg_setreg(num, val);
+ case '#':
+ return (name[1] == '#') ?
+ pc_setaccountreg2(sd, name, val) :
+ pc_setaccountreg(sd, name, val);
+ case '.':
+ {
+ struct DBMap* n;
+ n = (ref) ? *ref : (name[1] == '@') ? st->stack->var_function : st->script->script_vars;
+ if( n ) {
+ idb_remove(n, num);
+ if( val != 0 )
+ idb_iput(n, num, val);
+ }
+ }
+ return 1;
+ case '\'':
+ if( st->instance_id ) {
+ idb_remove(instance[st->instance_id].vars, num);
+ if( val != 0 )
+ idb_iput(instance[st->instance_id].vars, num, val);
+ }
+ return 1;
+ default:
+ return pc_setglobalreg(sd, name, val);
+ }
+ }
+}
+
+int set_var(TBL_PC* sd, char* name, void* val)
+{
+ return set_reg(NULL, sd, reference_uid(add_str(name),0), name, val, NULL);
+}
+
+void setd_sub(struct script_state *st, TBL_PC *sd, const char *varname, int elem, void *value, struct DBMap **ref)
+{
+ set_reg(st, sd, reference_uid(add_str(varname),elem), varname, value, ref);
+}
+
+/// Converts the data to a string
+const char* conv_str(struct script_state* st, struct script_data* data)
+{
+ char* p;
+
+ get_val(st, data);
+ if( data_isstring(data) )
+ {// nothing to convert
+ }
+ else if( data_isint(data) )
+ {// int -> string
+ CREATE(p, char, ITEM_NAME_LENGTH);
+ snprintf(p, ITEM_NAME_LENGTH, "%d", data->u.num);
+ p[ITEM_NAME_LENGTH-1] = '\0';
+ data->type = C_STR;
+ data->u.str = p;
+ }
+ else if( data_isreference(data) )
+ {// reference -> string
+ //##TODO when does this happen (check get_val) [FlavioJS]
+ data->type = C_CONSTSTR;
+ data->u.str = reference_getname(data);
+ }
+ else
+ {// unsupported data type
+ ShowError("script:conv_str: cannot convert to string, defaulting to \"\"\n");
+ script_reportdata(data);
+ script_reportsrc(st);
+ data->type = C_CONSTSTR;
+ data->u.str = "";
+ }
+ return data->u.str;
+}
+
+/// Converts the data to an int
+int conv_num(struct script_state* st, struct script_data* data)
+{
+ char* p;
+ long num;
+
+ get_val(st, data);
+ if( data_isint(data) )
+ {// nothing to convert
+ }
+ else if( data_isstring(data) )
+ {// string -> int
+ // the result does not overflow or underflow, it is capped instead
+ // ex: 999999999999 is capped to INT_MAX (2147483647)
+ p = data->u.str;
+ errno = 0;
+ num = strtol(data->u.str, NULL, 10);// change radix to 0 to support octal numbers "o377" and hex numbers "0xFF"
+ if( errno == ERANGE
+#if LONG_MAX > INT_MAX
+ || num < INT_MIN || num > INT_MAX
+#endif
+ )
+ {
+ if( num <= INT_MIN )
+ {
+ num = INT_MIN;
+ ShowError("script:conv_num: underflow detected, capping to %ld\n", num);
+ }
+ else//if( num >= INT_MAX )
+ {
+ num = INT_MAX;
+ ShowError("script:conv_num: overflow detected, capping to %ld\n", num);
+ }
+ script_reportdata(data);
+ script_reportsrc(st);
+ }
+ if( data->type == C_STR )
+ aFree(p);
+ data->type = C_INT;
+ data->u.num = (int)num;
+ }
+#if 0
+ // FIXME this function is being used to retrieve the position of labels and
+ // probably other stuff [FlavioJS]
+ else
+ {// unsupported data type
+ ShowError("script:conv_num: cannot convert to number, defaulting to 0\n");
+ script_reportdata(data);
+ script_reportsrc(st);
+ data->type = C_INT;
+ data->u.num = 0;
+ }
+#endif
+ return data->u.num;
+}
+
+//
+// Stack operations
+//
+
+/// Increases the size of the stack
+void stack_expand(struct script_stack* stack)
+{
+ stack->sp_max += 64;
+ stack->stack_data = (struct script_data*)aRealloc(stack->stack_data,
+ stack->sp_max * sizeof(stack->stack_data[0]) );
+ memset(stack->stack_data + (stack->sp_max - 64), 0,
+ 64 * sizeof(stack->stack_data[0]) );
+}
+
+/// Pushes a value into the stack
+#define push_val(stack,type,val) push_val2(stack, type, val, NULL)
+
+/// Pushes a value into the stack (with reference)
+struct script_data* push_val2(struct script_stack* stack, enum c_op type, int val, struct DBMap** ref)
+{
+ if( stack->sp >= stack->sp_max )
+ stack_expand(stack);
+ stack->stack_data[stack->sp].type = type;
+ stack->stack_data[stack->sp].u.num = val;
+ stack->stack_data[stack->sp].ref = ref;
+ stack->sp++;
+ return &stack->stack_data[stack->sp-1];
+}
+
+/// Pushes a string into the stack
+struct script_data* push_str(struct script_stack* stack, enum c_op type, char* str)
+{
+ if( stack->sp >= stack->sp_max )
+ stack_expand(stack);
+ stack->stack_data[stack->sp].type = type;
+ stack->stack_data[stack->sp].u.str = str;
+ stack->stack_data[stack->sp].ref = NULL;
+ stack->sp++;
+ return &stack->stack_data[stack->sp-1];
+}
+
+/// Pushes a retinfo into the stack
+struct script_data* push_retinfo(struct script_stack* stack, struct script_retinfo* ri, DBMap **ref)
+{
+ if( stack->sp >= stack->sp_max )
+ stack_expand(stack);
+ stack->stack_data[stack->sp].type = C_RETINFO;
+ stack->stack_data[stack->sp].u.ri = ri;
+ stack->stack_data[stack->sp].ref = ref;
+ stack->sp++;
+ return &stack->stack_data[stack->sp-1];
+}
+
+/// Pushes a copy of the target position into the stack
+struct script_data* push_copy(struct script_stack* stack, int pos)
+{
+ switch( stack->stack_data[pos].type )
+ {
+ case C_CONSTSTR:
+ return push_str(stack, C_CONSTSTR, stack->stack_data[pos].u.str);
+ break;
+ case C_STR:
+ return push_str(stack, C_STR, aStrdup(stack->stack_data[pos].u.str));
+ break;
+ case C_RETINFO:
+ ShowFatalError("script:push_copy: can't create copies of C_RETINFO. Exiting...\n");
+ exit(1);
+ break;
+ default:
+ return push_val2(
+ stack,stack->stack_data[pos].type,
+ stack->stack_data[pos].u.num,
+ stack->stack_data[pos].ref
+ );
+ break;
+ }
+}
+
+/// Removes the values in indexes [start,end[ from the stack.
+/// Adjusts all stack pointers.
+void pop_stack(struct script_state* st, int start, int end)
+{
+ struct script_stack* stack = st->stack;
+ struct script_data* data;
+ int i;
+
+ if( start < 0 )
+ start = 0;
+ if( end > stack->sp )
+ end = stack->sp;
+ if( start >= end )
+ return;// nothing to pop
+
+ // free stack elements
+ for( i = start; i < end; i++ )
+ {
+ data = &stack->stack_data[i];
+ if( data->type == C_STR )
+ aFree(data->u.str);
+ if( data->type == C_RETINFO )
+ {
+ struct script_retinfo* ri = data->u.ri;
+ if( ri->var_function )
+ script_free_vars(ri->var_function);
+ if( data->ref )
+ aFree(data->ref);
+ aFree(ri);
+ }
+ data->type = C_NOP;
+ }
+ // move the rest of the elements
+ if( stack->sp > end )
+ {
+ memmove(&stack->stack_data[start], &stack->stack_data[end], sizeof(stack->stack_data[0])*(stack->sp - end));
+ for( i = start + stack->sp - end; i < stack->sp; ++i )
+ stack->stack_data[i].type = C_NOP;
+ }
+ // adjust stack pointers
+ if( st->start > end ) st->start -= end - start;
+ else if( st->start > start ) st->start = start;
+ if( st->end > end ) st->end -= end - start;
+ else if( st->end > start ) st->end = start;
+ if( stack->defsp > end ) stack->defsp -= end - start;
+ else if( stack->defsp > start ) stack->defsp = start;
+ stack->sp -= end - start;
+}
+
+///
+///
+///
+
+/*==========================================
+ * Release script dependent variable, dependent variable of function
+ *------------------------------------------*/
+void script_free_vars(struct DBMap* storage)
+{
+ if( storage )
+ {// destroy the storage construct containing the variables
+ db_destroy(storage);
+ }
+}
+
+void script_free_code(struct script_code* code)
+{
+ script_free_vars( code->script_vars );
+ aFree( code->script_buf );
+ aFree( code );
+}
+
+/// Creates a new script state.
+///
+/// @param script Script code
+/// @param pos Position in the code
+/// @param rid Who is running the script (attached player)
+/// @param oid Where the code is being run (npc 'object')
+/// @return Script state
+struct script_state* script_alloc_state(struct script_code* script, int pos, int rid, int oid)
+{
+ struct script_state* st;
+ CREATE(st, struct script_state, 1);
+ st->stack = (struct script_stack*)aMalloc(sizeof(struct script_stack));
+ st->stack->sp = 0;
+ st->stack->sp_max = 64;
+ CREATE(st->stack->stack_data, struct script_data, st->stack->sp_max);
+ st->stack->defsp = st->stack->sp;
+ st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA);
+ st->state = RUN;
+ st->script = script;
+ //st->scriptroot = script;
+ st->pos = pos;
+ st->rid = rid;
+ st->oid = oid;
+ st->sleep.timer = INVALID_TIMER;
+ return st;
+}
+
+/// Frees a script state.
+///
+/// @param st Script state
+void script_free_state(struct script_state* st)
+{
+ if(st->bk_st)
+ {// backup was not restored
+ ShowDebug("script_free_state: Previous script state lost (rid=%d, oid=%d, state=%d, bk_npcid=%d).\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid);
+ }
+ if( st->sleep.timer != INVALID_TIMER )
+ delete_timer(st->sleep.timer, run_script_timer);
+ script_free_vars(st->stack->var_function);
+ pop_stack(st, 0, st->stack->sp);
+ aFree(st->stack->stack_data);
+ aFree(st->stack);
+ st->stack = NULL;
+ st->pos = -1;
+ aFree(st);
+}
+
+//
+// Main execution unit
+//
+/*==========================================
+ * Read command
+ *------------------------------------------*/
+c_op get_com(unsigned char *script,int *pos)
+{
+ int i = 0, j = 0;
+
+ if(script[*pos]>=0x80){
+ return C_INT;
+ }
+ while(script[*pos]>=0x40){
+ i=script[(*pos)++]<<j;
+ j+=6;
+ }
+ return (c_op)(i+(script[(*pos)++]<<j));
+}
+
+/*==========================================
+ * Income figures
+ *------------------------------------------*/
+int get_num(unsigned char *script,int *pos)
+{
+ int i,j;
+ i=0; j=0;
+ while(script[*pos]>=0xc0){
+ i+=(script[(*pos)++]&0x7f)<<j;
+ j+=6;
+ }
+ return i+((script[(*pos)++]&0x7f)<<j);
+}
+
+/*==========================================
+ * Remove the value from the stack
+ *------------------------------------------*/
+int pop_val(struct script_state* st)
+{
+ if(st->stack->sp<=0)
+ return 0;
+ st->stack->sp--;
+ get_val(st,&(st->stack->stack_data[st->stack->sp]));
+ if(st->stack->stack_data[st->stack->sp].type==C_INT)
+ return st->stack->stack_data[st->stack->sp].u.num;
+ return 0;
+}
+
+/// Ternary operators
+/// test ? if_true : if_false
+void op_3(struct script_state* st, int op)
+{
+ struct script_data* data;
+ int flag = 0;
+
+ data = script_getdatatop(st, -3);
+ get_val(st, data);
+
+ if( data_isstring(data) )
+ flag = data->u.str[0];// "" -> false
+ else if( data_isint(data) )
+ flag = data->u.num;// 0 -> false
+ else
+ {
+ ShowError("script:op_3: invalid data for the ternary operator test\n");
+ script_reportdata(data);
+ script_reportsrc(st);
+ script_removetop(st, -3, 0);
+ script_pushnil(st);
+ return;
+ }
+ if( flag )
+ script_pushcopytop(st, -2);
+ else
+ script_pushcopytop(st, -1);
+ script_removetop(st, -4, -1);
+}
+
+/// Binary string operators
+/// s1 EQ s2 -> i
+/// s1 NE s2 -> i
+/// s1 GT s2 -> i
+/// s1 GE s2 -> i
+/// s1 LT s2 -> i
+/// s1 LE s2 -> i
+/// s1 ADD s2 -> s
+void op_2str(struct script_state* st, int op, const char* s1, const char* s2)
+{
+ int a = 0;
+
+ switch(op){
+ case C_EQ: a = (strcmp(s1,s2) == 0); break;
+ case C_NE: a = (strcmp(s1,s2) != 0); break;
+ case C_GT: a = (strcmp(s1,s2) > 0); break;
+ case C_GE: a = (strcmp(s1,s2) >= 0); break;
+ case C_LT: a = (strcmp(s1,s2) < 0); break;
+ case C_LE: a = (strcmp(s1,s2) <= 0); break;
+ case C_ADD:
+ {
+ char* buf = (char *)aMalloc((strlen(s1)+strlen(s2)+1)*sizeof(char));
+ strcpy(buf, s1);
+ strcat(buf, s2);
+ script_pushstr(st, buf);
+ return;
+ }
+ default:
+ ShowError("script:op2_str: unexpected string operator %s\n", script_op2name(op));
+ script_reportsrc(st);
+ script_pushnil(st);
+ st->state = END;
+ return;
+ }
+
+ script_pushint(st,a);
+}
+
+/// Binary number operators
+/// i OP i -> i
+void op_2num(struct script_state* st, int op, int i1, int i2)
+{
+ int ret;
+ double ret_double;
+
+ switch( op )
+ {
+ case C_AND: ret = i1 & i2; break;
+ case C_OR: ret = i1 | i2; break;
+ case C_XOR: ret = i1 ^ i2; break;
+ case C_LAND: ret = (i1 && i2); break;
+ case C_LOR: ret = (i1 || i2); break;
+ case C_EQ: ret = (i1 == i2); break;
+ case C_NE: ret = (i1 != i2); break;
+ case C_GT: ret = (i1 > i2); break;
+ case C_GE: ret = (i1 >= i2); break;
+ case C_LT: ret = (i1 < i2); break;
+ case C_LE: ret = (i1 <= i2); break;
+ case C_R_SHIFT: ret = i1>>i2; break;
+ case C_L_SHIFT: ret = i1<<i2; break;
+ case C_DIV:
+ case C_MOD:
+ if( i2 == 0 )
+ {
+ ShowError("script:op_2num: division by zero detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2);
+ script_reportsrc(st);
+ script_pushnil(st);
+ st->state = END;
+ return;
+ }
+ else if( op == C_DIV )
+ ret = i1 / i2;
+ else//if( op == C_MOD )
+ ret = i1 % i2;
+ break;
+ default:
+ switch( op )
+ {// operators that can overflow/underflow
+ case C_ADD: ret = i1 + i2; ret_double = (double)i1 + (double)i2; break;
+ case C_SUB: ret = i1 - i2; ret_double = (double)i1 - (double)i2; break;
+ case C_MUL: ret = i1 * i2; ret_double = (double)i1 * (double)i2; break;
+ default:
+ ShowError("script:op_2num: unexpected number operator %s i1=%d i2=%d\n", script_op2name(op), i1, i2);
+ script_reportsrc(st);
+ script_pushnil(st);
+ return;
+ }
+ if( ret_double < (double)INT_MIN )
+ {
+ ShowWarning("script:op_2num: underflow detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2);
+ script_reportsrc(st);
+ ret = INT_MIN;
+ }
+ else if( ret_double > (double)INT_MAX )
+ {
+ ShowWarning("script:op_2num: overflow detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2);
+ script_reportsrc(st);
+ ret = INT_MAX;
+ }
+ }
+ script_pushint(st, ret);
+}
+
+/// Binary operators
+void op_2(struct script_state *st, int op)
+{
+ struct script_data* left, leftref;
+ struct script_data* right;
+
+ leftref.type = C_NOP;
+
+ left = script_getdatatop(st, -2);
+ right = script_getdatatop(st, -1);
+
+ if (st->op2ref) {
+ if (data_isreference(left)) {
+ leftref = *left;
+ }
+
+ st->op2ref = 0;
+ }
+
+ get_val(st, left);
+ get_val(st, right);
+
+ // automatic conversions
+ switch( op )
+ {
+ case C_ADD:
+ if( data_isint(left) && data_isstring(right) )
+ {// convert int-string to string-string
+ conv_str(st, left);
+ }
+ else if( data_isstring(left) && data_isint(right) )
+ {// convert string-int to string-string
+ conv_str(st, right);
+ }
+ break;
+ }
+
+ if( data_isstring(left) && data_isstring(right) )
+ {// ss => op_2str
+ op_2str(st, op, left->u.str, right->u.str);
+ script_removetop(st, leftref.type == C_NOP ? -3 : -2, -1);// pop the two values before the top one
+
+ if (leftref.type != C_NOP)
+ {
+ aFree(left->u.str);
+ *left = leftref;
+ }
+ }
+ else if( data_isint(left) && data_isint(right) )
+ {// ii => op_2num
+ int i1 = left->u.num;
+ int i2 = right->u.num;
+
+ script_removetop(st, leftref.type == C_NOP ? -2 : -1, 0);
+ op_2num(st, op, i1, i2);
+
+ if (leftref.type != C_NOP)
+ *left = leftref;
+ }
+ else
+ {// invalid argument
+ ShowError("script:op_2: invalid data for operator %s\n", script_op2name(op));
+ script_reportdata(left);
+ script_reportdata(right);
+ script_reportsrc(st);
+ script_removetop(st, -2, 0);
+ script_pushnil(st);
+ st->state = END;
+ }
+}
+
+/// Unary operators
+/// NEG i -> i
+/// NOT i -> i
+/// LNOT i -> i
+void op_1(struct script_state* st, int op)
+{
+ struct script_data* data;
+ int i1;
+
+ data = script_getdatatop(st, -1);
+ get_val(st, data);
+
+ if( !data_isint(data) )
+ {// not a number
+ ShowError("script:op_1: argument is not a number (op=%s)\n", script_op2name(op));
+ script_reportdata(data);
+ script_reportsrc(st);
+ script_pushnil(st);
+ st->state = END;
+ return;
+ }
+
+ i1 = data->u.num;
+ script_removetop(st, -1, 0);
+ switch( op )
+ {
+ case C_NEG: i1 = -i1; break;
+ case C_NOT: i1 = ~i1; break;
+ case C_LNOT: i1 = !i1; break;
+ default:
+ ShowError("script:op_1: unexpected operator %s i1=%d\n", script_op2name(op), i1);
+ script_reportsrc(st);
+ script_pushnil(st);
+ st->state = END;
+ return;
+ }
+ script_pushint(st, i1);
+}
+
+
+/// Checks the type of all arguments passed to a built-in function.
+///
+/// @param st Script state whose stack arguments should be inspected.
+/// @param func Built-in function for which the arguments are intended.
+static void script_check_buildin_argtype(struct script_state* st, int func)
+{
+ char type;
+ int idx, invalid = 0;
+ script_function* sf = &buildin_func[str_data[func].val];
+
+ for( idx = 2; script_hasdata(st, idx); idx++ )
+ {
+ struct script_data* data = script_getdata(st, idx);
+
+ type = sf->arg[idx-2];
+
+ if( type == '?' || type == '*' )
+ {// optional argument or unknown number of optional parameters ( no types are after this )
+ break;
+ }
+ else if( type == 0 )
+ {// more arguments than necessary ( should not happen, as it is checked before )
+ ShowWarning("Found more arguments than necessary. unexpected arg type %s\n",script_op2name(data->type));
+ invalid++;
+ break;
+ }
+ else
+ {
+ const char* name = NULL;
+
+ if( data_isreference(data) )
+ {// get name for variables to determine the type they refer to
+ name = reference_getname(data);
+ }
+
+ switch( type )
+ {
+ case 'v':
+ if( !data_isstring(data) && !data_isint(data) && !data_isreference(data) )
+ {// variant
+ ShowWarning("Unexpected type for argument %d. Expected string, number or variable.\n", idx-1);
+ script_reportdata(data);
+ invalid++;
+ }
+ break;
+ case 's':
+ if( !data_isstring(data) && !( data_isreference(data) && is_string_variable(name) ) )
+ {// string
+ ShowWarning("Unexpected type for argument %d. Expected string.\n", idx-1);
+ script_reportdata(data);
+ invalid++;
+ }
+ break;
+ case 'i':
+ if( !data_isint(data) && !( data_isreference(data) && ( reference_toparam(data) || reference_toconstant(data) || !is_string_variable(name) ) ) )
+ {// int ( params and constants are always int )
+ ShowWarning("Unexpected type for argument %d. Expected number.\n", idx-1);
+ script_reportdata(data);
+ invalid++;
+ }
+ break;
+ case 'r':
+ if( !data_isreference(data) )
+ {// variables
+ ShowWarning("Unexpected type for argument %d. Expected variable, got %s.\n", idx-1,script_op2name(data->type));
+ script_reportdata(data);
+ invalid++;
+ }
+ break;
+ case 'l':
+ if( !data_islabel(data) && !data_isfunclabel(data) )
+ {// label
+ ShowWarning("Unexpected type for argument %d. Expected label, got %s\n", idx-1,script_op2name(data->type));
+ script_reportdata(data);
+ invalid++;
+ }
+ break;
+ }
+ }
+ }
+
+ if(invalid)
+ {
+ ShowDebug("Function: %s\n", get_str(func));
+ script_reportsrc(st);
+ }
+}
+
+
+/// Executes a buildin command.
+/// Stack: C_NAME(<command>) C_ARG <arg0> <arg1> ... <argN>
+int run_func(struct script_state *st)
+{
+ struct script_data* data;
+ int i,start_sp,end_sp,func;
+
+ end_sp = st->stack->sp;// position after the last argument
+ for( i = end_sp-1; i > 0 ; --i )
+ if( st->stack->stack_data[i].type == C_ARG )
+ break;
+ if( i == 0 )
+ {
+ ShowError("script:run_func: C_ARG not found. please report this!!!\n");
+ st->state = END;
+ script_reportsrc(st);
+ return 1;
+ }
+ start_sp = i-1;// C_NAME of the command
+ st->start = start_sp;
+ st->end = end_sp;
+
+ data = &st->stack->stack_data[st->start];
+ if( data->type == C_NAME && str_data[data->u.num].type == C_FUNC )
+ func = data->u.num;
+ else
+ {
+ ShowError("script:run_func: not a buildin command.\n");
+ script_reportdata(data);
+ script_reportsrc(st);
+ st->state = END;
+ return 1;
+ }
+
+ if( script_config.warn_func_mismatch_argtypes )
+ {
+ script_check_buildin_argtype(st, func);
+ }
+
+ if(str_data[func].func){
+ if (str_data[func].func(st)) //Report error
+ script_reportsrc(st);
+ } else {
+ ShowError("script:run_func: '%s' (id=%d type=%s) has no C function. please report this!!!\n", get_str(func), func, script_op2name(str_data[func].type));
+ script_reportsrc(st);
+ st->state = END;
+ }
+
+ // Stack's datum are used when re-running functions [Eoe]
+ if( st->state == RERUNLINE )
+ return 0;
+
+ pop_stack(st, st->start, st->end);
+ if( st->state == RETFUNC )
+ {// return from a user-defined function
+ struct script_retinfo* ri;
+ int olddefsp = st->stack->defsp;
+ int nargs;
+
+ pop_stack(st, st->stack->defsp, st->start);// pop distractions from the stack
+ if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp-1].type != C_RETINFO )
+ {
+ ShowWarning("script:run_func: return without callfunc or callsub!\n");
+ script_reportsrc(st);
+ st->state = END;
+ return 1;
+ }
+ script_free_vars( st->stack->var_function );
+
+ ri = st->stack->stack_data[st->stack->defsp-1].u.ri;
+ nargs = ri->nargs;
+ st->pos = ri->pos;
+ st->script = ri->script;
+ st->stack->var_function = ri->var_function;
+ st->stack->defsp = ri->defsp;
+ memset(ri, 0, sizeof(struct script_retinfo));
+
+ pop_stack(st, olddefsp-nargs-1, olddefsp);// pop arguments and retinfo
+
+ st->state = GOTO;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * script execution
+ *------------------------------------------*/
+void run_script(struct script_code *rootscript,int pos,int rid,int oid)
+{
+ struct script_state *st;
+
+ if( rootscript == NULL || pos < 0 )
+ return;
+
+ // TODO In jAthena, this function can take over the pending script in the player. [FlavioJS]
+ // It is unclear how that can be triggered, so it needs the be traced/checked in more detail.
+ // NOTE At the time of this change, this function wasn't capable of taking over the script state because st->scriptroot was never set.
+ st = script_alloc_state(rootscript, pos, rid, oid);
+ run_script_main(st);
+}
+
+void script_stop_sleeptimers(int id)
+{
+ struct script_state* st;
+ for(;;)
+ {
+ st = (struct script_state*)linkdb_erase(&sleep_db,(void*)__64BPRTSIZE(id));
+ if( st == NULL )
+ break; // no more sleep timers
+ script_free_state(st);
+ }
+}
+
+/*==========================================
+ * Delete the specified node from sleep_db
+ *------------------------------------------*/
+struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n)
+{
+ struct linkdb_node *retnode;
+
+ if( n == NULL)
+ return NULL;
+ if( n->prev == NULL )
+ sleep_db = n->next;
+ else
+ n->prev->next = n->next;
+ if( n->next )
+ n->next->prev = n->prev;
+ retnode = n->next;
+ aFree( n );
+ return retnode; // The following; return retnode
+}
+
+/*==========================================
+ * Timer function for sleep
+ *------------------------------------------*/
+int run_script_timer(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct script_state *st = (struct script_state *)data;
+ struct linkdb_node *node = (struct linkdb_node *)sleep_db;
+ TBL_PC *sd = map_id2sd(st->rid);
+
+ if((sd && sd->status.char_id != id) || (st->rid && !sd))
+ { //Character mismatch. Cancel execution.
+ st->rid = 0;
+ st->state = END;
+ }
+ while( node && st->sleep.timer != INVALID_TIMER ) {
+ if( (int)__64BPRTSIZE(node->key) == st->oid && ((struct script_state *)node->data)->sleep.timer == st->sleep.timer ) {
+ script_erase_sleepdb(node);
+ st->sleep.timer = INVALID_TIMER;
+ break;
+ }
+ node = node->next;
+ }
+ if(st->state != RERUNLINE)
+ st->sleep.tick = 0;
+ run_script_main(st);
+ return 0;
+}
+
+/// Detaches script state from possibly attached character and restores it's previous script if any.
+///
+/// @param st Script state to detach.
+/// @param dequeue_event Whether to schedule any queued events, when there was no previous script.
+static void script_detach_state(struct script_state* st, bool dequeue_event)
+{
+ struct map_session_data* sd;
+
+ if(st->rid && (sd = map_id2sd(st->rid))!=NULL)
+ {
+ sd->st = st->bk_st;
+ sd->npc_id = st->bk_npcid;
+ /**
+ * For the Secure NPC Timeout option (check config/Secure.h) [RR]
+ **/
+ #if SECURE_NPCTIMEOUT
+ /**
+ * We're done with this NPC session, so we cancel the timer (if existent) and move on
+ **/
+ if( sd->npc_idle_timer != INVALID_TIMER ) {
+ delete_timer(sd->npc_idle_timer,npc_rr_secure_timeout_timer);
+ sd->npc_idle_timer = INVALID_TIMER;
+ }
+ #endif
+ if(st->bk_st)
+ {
+ //Remove tag for removal.
+ st->bk_st = NULL;
+ st->bk_npcid = 0;
+ }
+ else if(dequeue_event)
+ {
+ npc_event_dequeue(sd);
+ }
+ }
+ else if(st->bk_st)
+ {// rid was set to 0, before detaching the script state
+ ShowError("script_detach_state: Found previous script state without attached player (rid=%d, oid=%d, state=%d, bk_npcid=%d)\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid);
+ script_reportsrc(st->bk_st);
+
+ script_free_state(st->bk_st);
+ st->bk_st = NULL;
+ }
+}
+
+/// Attaches script state to possibly attached character and backups it's previous script, if any.
+///
+/// @param st Script state to attach.
+static void script_attach_state(struct script_state* st)
+{
+ struct map_session_data* sd;
+
+ if(st->rid && (sd = map_id2sd(st->rid))!=NULL)
+ {
+ if(st!=sd->st)
+ {
+ if(st->bk_st)
+ {// there is already a backup
+ ShowDebug("script_free_state: Previous script state lost (rid=%d, oid=%d, state=%d, bk_npcid=%d).\n", st->bk_st->rid, st->bk_st->oid, st->bk_st->state, st->bk_npcid);
+ }
+ st->bk_st = sd->st;
+ st->bk_npcid = sd->npc_id;
+ }
+ sd->st = st;
+ sd->npc_id = st->oid;
+/**
+ * For the Secure NPC Timeout option (check config/Secure.h) [RR]
+ **/
+#if SECURE_NPCTIMEOUT
+ if( sd->npc_idle_timer == INVALID_TIMER )
+ sd->npc_idle_timer = add_timer(gettick() + (SECURE_NPCTIMEOUT_INTERVAL*1000),npc_rr_secure_timeout_timer,sd->bl.id,0);
+ sd->npc_idle_tick = gettick();
+#endif
+ }
+}
+
+/*==========================================
+ * The main part of the script execution
+ *------------------------------------------*/
+void run_script_main(struct script_state *st)
+{
+ int cmdcount = script_config.check_cmdcount;
+ int gotocount = script_config.check_gotocount;
+ TBL_PC *sd;
+ struct script_stack *stack=st->stack;
+ struct npc_data *nd;
+
+ script_attach_state(st);
+
+ nd = map_id2nd(st->oid);
+ if( nd && map[nd->bl.m].instance_id > 0 )
+ st->instance_id = map[nd->bl.m].instance_id;
+
+ if(st->state == RERUNLINE) {
+ run_func(st);
+ if(st->state == GOTO)
+ st->state = RUN;
+ } else if(st->state != END)
+ st->state = RUN;
+
+ while(st->state == RUN)
+ {
+ enum c_op c = get_com(st->script->script_buf,&st->pos);
+ switch(c){
+ case C_EOL:
+ if( stack->defsp > stack->sp )
+ ShowError("script:run_script_main: unexpected stack position (defsp=%d sp=%d). please report this!!!\n", stack->defsp, stack->sp);
+ else
+ pop_stack(st, stack->defsp, stack->sp);// pop unused stack data. (unused return value)
+ break;
+ case C_INT:
+ push_val(stack,C_INT,get_num(st->script->script_buf,&st->pos));
+ break;
+ case C_POS:
+ case C_NAME:
+ push_val(stack,c,GETVALUE(st->script->script_buf,st->pos));
+ st->pos+=3;
+ break;
+ case C_ARG:
+ push_val(stack,c,0);
+ break;
+ case C_STR:
+ push_str(stack,C_CONSTSTR,(char*)(st->script->script_buf+st->pos));
+ while(st->script->script_buf[st->pos++]);
+ break;
+ case C_FUNC:
+ run_func(st);
+ if(st->state==GOTO){
+ st->state = RUN;
+ if( !st->freeloop && gotocount>0 && (--gotocount)<=0 ){
+ ShowError("run_script: infinity loop !\n");
+ script_reportsrc(st);
+ st->state=END;
+ }
+ }
+ break;
+
+ case C_REF:
+ st->op2ref = 1;
+ break;
+
+ case C_NEG:
+ case C_NOT:
+ case C_LNOT:
+ op_1(st ,c);
+ break;
+
+ case C_ADD:
+ case C_SUB:
+ case C_MUL:
+ case C_DIV:
+ case C_MOD:
+ case C_EQ:
+ case C_NE:
+ case C_GT:
+ case C_GE:
+ case C_LT:
+ case C_LE:
+ case C_AND:
+ case C_OR:
+ case C_XOR:
+ case C_LAND:
+ case C_LOR:
+ case C_R_SHIFT:
+ case C_L_SHIFT:
+ op_2(st, c);
+ break;
+
+ case C_OP3:
+ op_3(st, c);
+ break;
+
+ case C_NOP:
+ st->state=END;
+ break;
+
+ default:
+ ShowError("unknown command : %d @ %d\n",c,st->pos);
+ st->state=END;
+ break;
+ }
+ if( !st->freeloop && cmdcount>0 && (--cmdcount)<=0 ){
+ ShowError("run_script: infinity loop !\n");
+ script_reportsrc(st);
+ st->state=END;
+ }
+ }
+
+ if(st->sleep.tick > 0) {
+ //Restore previous script
+ script_detach_state(st, false);
+ //Delay execution
+ sd = map_id2sd(st->rid); // Get sd since script might have attached someone while running. [Inkfish]
+ st->sleep.charid = sd?sd->status.char_id:0;
+ st->sleep.timer = add_timer(gettick()+st->sleep.tick,
+ run_script_timer, st->sleep.charid, (intptr_t)st);
+ linkdb_insert(&sleep_db, (void*)__64BPRTSIZE(st->oid), st);
+ }
+ else if(st->state != END && st->rid){
+ //Resume later (st is already attached to player).
+ if(st->bk_st) {
+ ShowWarning("Unable to restore stack! Double continuation!\n");
+ //Report BOTH scripts to see if that can help somehow.
+ ShowDebug("Previous script (lost):\n");
+ script_reportsrc(st->bk_st);
+ ShowDebug("Current script:\n");
+ script_reportsrc(st);
+
+ script_free_state(st->bk_st);
+ st->bk_st = NULL;
+ }
+ } else {
+ //Dispose of script.
+ if ((sd = map_id2sd(st->rid))!=NULL)
+ { //Restore previous stack and save char.
+ if(sd->state.using_fake_npc){
+ clif_clearunit_single(sd->npc_id, CLR_OUTSIGHT, sd->fd);
+ sd->state.using_fake_npc = 0;
+ }
+ //Restore previous script if any.
+ script_detach_state(st, true);
+ if (sd->state.reg_dirty&2)
+ intif_saveregistry(sd,2);
+ if (sd->state.reg_dirty&1)
+ intif_saveregistry(sd,1);
+ }
+ script_free_state(st);
+ st = NULL;
+ }
+}
+
+int script_config_read(char *cfgName)
+{
+ int i;
+ char line[1024],w1[1024],w2[1024];
+ FILE *fp;
+
+
+ fp=fopen(cfgName,"r");
+ if(fp==NULL){
+ ShowError("File not found: %s\n", cfgName);
+ return 1;
+ }
+ while(fgets(line, sizeof(line), fp))
+ {
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2);
+ if(i!=2)
+ continue;
+
+ if(strcmpi(w1,"warn_func_mismatch_paramnum")==0) {
+ script_config.warn_func_mismatch_paramnum = config_switch(w2);
+ }
+ else if(strcmpi(w1,"check_cmdcount")==0) {
+ script_config.check_cmdcount = config_switch(w2);
+ }
+ else if(strcmpi(w1,"check_gotocount")==0) {
+ script_config.check_gotocount = config_switch(w2);
+ }
+ else if(strcmpi(w1,"input_min_value")==0) {
+ script_config.input_min_value = config_switch(w2);
+ }
+ else if(strcmpi(w1,"input_max_value")==0) {
+ script_config.input_max_value = config_switch(w2);
+ }
+ else if(strcmpi(w1,"warn_func_mismatch_argtypes")==0) {
+ script_config.warn_func_mismatch_argtypes = config_switch(w2);
+ }
+ else if(strcmpi(w1,"import")==0){
+ script_config_read(w2);
+ }
+ else {
+ ShowWarning("Unknown setting '%s' in file %s\n", w1, cfgName);
+ }
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+/**
+ * @see DBApply
+ */
+static int db_script_free_code_sub(DBKey key, DBData *data, va_list ap)
+{
+ struct script_code *code = db_data2ptr(data);
+ if (code)
+ script_free_code(code);
+ return 0;
+}
+
+void script_run_autobonus(const char *autobonus, int id, int pos)
+{
+ struct script_code *script = (struct script_code *)strdb_get(autobonus_db, autobonus);
+
+ if( script )
+ {
+ current_equip_item_index = pos;
+ run_script(script,0,id,0);
+ }
+}
+
+void script_add_autobonus(const char *autobonus)
+{
+ if( strdb_get(autobonus_db, autobonus) == NULL )
+ {
+ struct script_code *script = parse_script(autobonus, "autobonus", 0, 0);
+
+ if( script )
+ strdb_put(autobonus_db, autobonus, script);
+ }
+}
+
+
+/// resets a temporary character array variable to given value
+void script_cleararray_pc(struct map_session_data* sd, const char* varname, void* value)
+{
+ int key;
+ uint8 idx;
+
+ if( not_array_variable(varname[0]) || !not_server_variable(varname[0]) )
+ {
+ ShowError("script_cleararray_pc: Variable '%s' has invalid scope (char_id=%d).\n", varname, sd->status.char_id);
+ return;
+ }
+
+ key = add_str(varname);
+
+ if( is_string_variable(varname) )
+ {
+ for( idx = 0; idx < SCRIPT_MAX_ARRAYSIZE; idx++ )
+ {
+ pc_setregstr(sd, reference_uid(key, idx), (const char*)value);
+ }
+ }
+ else
+ {
+ for( idx = 0; idx < SCRIPT_MAX_ARRAYSIZE; idx++ )
+ {
+ pc_setreg(sd, reference_uid(key, idx), (int)__64BPRTSIZE(value));
+ }
+ }
+}
+
+
+/// sets a temporary character array variable element idx to given value
+/// @param refcache Pointer to an int variable, which keeps a copy of the reference to varname and must be initialized to 0. Can be NULL if only one element is set.
+void script_setarray_pc(struct map_session_data* sd, const char* varname, uint8 idx, void* value, int* refcache)
+{
+ int key;
+
+ if( not_array_variable(varname[0]) || !not_server_variable(varname[0]) )
+ {
+ ShowError("script_setarray_pc: Variable '%s' has invalid scope (char_id=%d).\n", varname, sd->status.char_id);
+ return;
+ }
+
+ if( idx >= SCRIPT_MAX_ARRAYSIZE )
+ {
+ ShowError("script_setarray_pc: Variable '%s' has invalid index '%d' (char_id=%d).\n", varname, (int)idx, sd->status.char_id);
+ return;
+ }
+
+ key = ( refcache && refcache[0] ) ? refcache[0] : add_str(varname);
+
+ if( is_string_variable(varname) )
+ {
+ pc_setregstr(sd, reference_uid(key, idx), (const char*)value);
+ }
+ else
+ {
+ pc_setreg(sd, reference_uid(key, idx), (int)__64BPRTSIZE(value));
+ }
+
+ if( refcache )
+ {// save to avoid repeated add_str calls
+ refcache[0] = key;
+ }
+}
+#ifdef BETA_THREAD_TEST
+int buildin_query_sql_sub(struct script_state* st, Sql* handle);
+
+/* used to receive items the queryThread has already processed */
+int queryThread_timer(int tid, unsigned int tick, int id, intptr_t data) {
+ int i, cursor = 0;
+ bool allOk = true;
+
+ EnterSpinLock(&queryThreadLock);
+
+ for( i = 0; i < queryThreadData.count; i++ ) {
+ struct queryThreadEntry *entry = queryThreadData.entry[i];
+
+ if( !entry->ok ) {
+ allOk = false;
+ continue;
+ }
+
+ run_script_main(entry->st);
+
+ entry->st = NULL;/* empty entries */
+ aFree(entry);
+ queryThreadData.entry[i] = NULL;
+ }
+
+
+ if( allOk ) {
+ /* cancel the repeating timer -- it'll re-create itself when necessary, dont need to remain looping */
+ delete_timer(queryThreadData.timer, queryThread_timer);
+ queryThreadData.timer = INVALID_TIMER;
+ }
+
+ /* now lets clear the mess. */
+ for( i = 0; i < queryThreadData.count; i++ ) {
+ struct queryThreadEntry *entry = queryThreadData.entry[i];
+ if( entry == NULL )
+ continue;/* entry on hold */
+
+ /* move */
+ memmove(&queryThreadData.entry[cursor], &queryThreadData.entry[i], sizeof(struct queryThreadEntry*));
+
+ cursor++;
+ }
+
+ queryThreadData.count = cursor;
+
+ LeaveSpinLock(&queryThreadLock);
+
+ return 0;
+}
+
+void queryThread_add(struct script_state *st, bool type) {
+ int idx = 0;
+ struct queryThreadEntry* entry = NULL;
+
+ EnterSpinLock(&queryThreadLock);
+
+ if( queryThreadData.count++ != 0 )
+ RECREATE(queryThreadData.entry, struct queryThreadEntry* , queryThreadData.count);
+
+ idx = queryThreadData.count-1;
+
+ CREATE(queryThreadData.entry[idx],struct queryThreadEntry,1);
+
+ entry = queryThreadData.entry[idx];
+
+ entry->st = st;
+ entry->ok = false;
+ entry->type = type;
+ if( queryThreadData.timer == INVALID_TIMER ) { /* start the receiver timer */
+ queryThreadData.timer = add_timer_interval(gettick() + 100, queryThread_timer, 0, 0, 100);
+ }
+
+ LeaveSpinLock(&queryThreadLock);
+
+ /* unlock the queryThread */
+ racond_signal(queryThreadCond);
+}
+/* adds a new log to the queue */
+void queryThread_log(char * entry, int length) {
+ int idx = logThreadData.count;
+
+ EnterSpinLock(&queryThreadLock);
+
+ if( logThreadData.count++ != 0 )
+ RECREATE(logThreadData.entry, char* , logThreadData.count);
+
+ CREATE(logThreadData.entry[idx], char, length + 1 );
+ safestrncpy(logThreadData.entry[idx], entry, length + 1 );
+
+ LeaveSpinLock(&queryThreadLock);
+
+ /* unlock the queryThread */
+ racond_signal(queryThreadCond);
+}
+
+/* queryThread_main */
+static void *queryThread_main(void *x) {
+ Sql *queryThread_handle = Sql_Malloc();
+ int i;
+
+ if ( SQL_ERROR == Sql_Connect(queryThread_handle, map_server_id, map_server_pw, map_server_ip, map_server_port, map_server_db) )
+ exit(EXIT_FAILURE);
+
+ if( strlen(default_codepage) > 0 )
+ if ( SQL_ERROR == Sql_SetEncoding(queryThread_handle, default_codepage) )
+ Sql_ShowDebug(queryThread_handle);
+
+ if( log_config.sql_logs ) {
+ logmysql_handle = Sql_Malloc();
+
+ if ( SQL_ERROR == Sql_Connect(logmysql_handle, log_db_id, log_db_pw, log_db_ip, log_db_port, log_db_db) )
+ exit(EXIT_FAILURE);
+
+ if( strlen(default_codepage) > 0 )
+ if ( SQL_ERROR == Sql_SetEncoding(logmysql_handle, default_codepage) )
+ Sql_ShowDebug(logmysql_handle);
+ }
+
+ while( 1 ) {
+
+ if(queryThreadTerminate > 0)
+ break;
+
+ EnterSpinLock(&queryThreadLock);
+
+ /* mess with queryThreadData within the lock */
+ for( i = 0; i < queryThreadData.count; i++ ) {
+ struct queryThreadEntry *entry = queryThreadData.entry[i];
+
+ if( entry->ok )
+ continue;
+ else if ( !entry->st || !entry->st->stack ) {
+ entry->ok = true;/* dispose */
+ continue;
+ }
+
+ buildin_query_sql_sub(entry->st, entry->type ? logmysql_handle : queryThread_handle);
+
+ entry->ok = true;/* we're done with this */
+ }
+
+ /* also check for any logs in need to be sent */
+ if( log_config.sql_logs ) {
+ for( i = 0; i < logThreadData.count; i++ ) {
+ if( SQL_ERROR == Sql_Query(logmysql_handle, logThreadData.entry[i]) )
+ Sql_ShowDebug(logmysql_handle);
+ aFree(logThreadData.entry[i]);
+ }
+ logThreadData.count = 0;
+ }
+
+ LeaveSpinLock(&queryThreadLock);
+
+ ramutex_lock( queryThreadMutex );
+ racond_wait( queryThreadCond, queryThreadMutex, -1 );
+ ramutex_unlock( queryThreadMutex );
+
+ }
+
+ Sql_Free(queryThread_handle);
+
+ if( log_config.sql_logs ) {
+ Sql_Free(logmysql_handle);
+ }
+
+ return NULL;
+}
+#endif
+/*==========================================
+ * Destructor
+ *------------------------------------------*/
+int do_final_script() {
+ int i;
+#ifdef DEBUG_HASH
+ if (battle_config.etc_log)
+ {
+ FILE *fp = fopen("hash_dump.txt","wt");
+ if(fp) {
+ int count[SCRIPT_HASH_SIZE];
+ int count2[SCRIPT_HASH_SIZE]; // number of buckets with a certain number of items
+ int n=0;
+ int min=INT_MAX,max=0,zero=0;
+ double mean=0.0f;
+ double median=0.0f;
+
+ ShowNotice("Dumping script str hash information to hash_dump.txt\n");
+ memset(count, 0, sizeof(count));
+ fprintf(fp,"num : hash : data_name\n");
+ fprintf(fp,"---------------------------------------------------------------\n");
+ for(i=LABEL_START; i<str_num; i++) {
+ unsigned int h = calc_hash(get_str(i));
+ fprintf(fp,"%04d : %4u : %s\n",i,h, get_str(i));
+ ++count[h];
+ }
+ fprintf(fp,"--------------------\n\n");
+ memset(count2, 0, sizeof(count2));
+ for(i=0; i<SCRIPT_HASH_SIZE; i++) {
+ fprintf(fp," hash %3d = %d\n",i,count[i]);
+ if(min > count[i])
+ min = count[i]; // minimun count of collision
+ if(max < count[i])
+ max = count[i]; // maximun count of collision
+ if(count[i] == 0)
+ zero++;
+ ++count2[count[i]];
+ }
+ fprintf(fp,"\n--------------------\n items : buckets\n--------------------\n");
+ for( i=min; i <= max; ++i ){
+ fprintf(fp," %5d : %7d\n",i,count2[i]);
+ mean += 1.0f*i*count2[i]/SCRIPT_HASH_SIZE; // Note: this will always result in <nr labels>/<nr buckets>
+ }
+ for( i=min; i <= max; ++i ){
+ n += count2[i];
+ if( n*2 >= SCRIPT_HASH_SIZE )
+ {
+ if( SCRIPT_HASH_SIZE%2 == 0 && SCRIPT_HASH_SIZE/2 == n )
+ median = (i+i+1)/2.0f;
+ else
+ median = i;
+ break;
+ }
+ }
+ fprintf(fp,"--------------------\n min = %d, max = %d, zero = %d\n mean = %lf, median = %lf\n",min,max,zero,mean,median);
+ fclose(fp);
+ }
+ }
+#endif
+
+ mapreg_final();
+
+ db_destroy(scriptlabel_db);
+ userfunc_db->destroy(userfunc_db, db_script_free_code_sub);
+ autobonus_db->destroy(autobonus_db, db_script_free_code_sub);
+ if(sleep_db) {
+ struct linkdb_node *n = (struct linkdb_node *)sleep_db;
+ while(n) {
+ struct script_state *st = (struct script_state *)n->data;
+ script_free_state(st);
+ n = n->next;
+ }
+ linkdb_final(&sleep_db);
+ }
+
+ if (str_data)
+ aFree(str_data);
+ if (str_buf)
+ aFree(str_buf);
+
+ for( i = 0; i < atcmd_binding_count; i++ ) {
+ aFree(atcmd_binding[i]);
+ }
+
+ if( atcmd_binding_count != 0 )
+ aFree(atcmd_binding);
+#ifdef BETA_THREAD_TEST
+ /* QueryThread */
+ InterlockedIncrement(&queryThreadTerminate);
+ racond_signal(queryThreadCond);
+ rathread_wait(queryThread, NULL);
+
+ // Destroy cond var and mutex.
+ racond_destroy( queryThreadCond );
+ ramutex_destroy( queryThreadMutex );
+
+ /* Clear missing vars */
+ for( i = 0; i < queryThreadData.count; i++ ) {
+ aFree(queryThreadData.entry[i]);
+ }
+
+ aFree(queryThreadData.entry);
+
+ for( i = 0; i < logThreadData.count; i++ ) {
+ aFree(logThreadData.entry[i]);
+ }
+
+ aFree(logThreadData.entry);
+#endif
+
+ return 0;
+}
+/*==========================================
+ * Initialization
+ *------------------------------------------*/
+int do_init_script() {
+ userfunc_db=strdb_alloc(DB_OPT_DUP_KEY,0);
+ scriptlabel_db=strdb_alloc(DB_OPT_DUP_KEY,50);
+ autobonus_db = strdb_alloc(DB_OPT_DUP_KEY,0);
+
+ mapreg_init();
+#ifdef BETA_THREAD_TEST
+ CREATE(queryThreadData.entry, struct queryThreadEntry*, 1);
+ queryThreadData.count = 0;
+ CREATE(logThreadData.entry, char *, 1);
+ logThreadData.count = 0;
+ /* QueryThread Start */
+
+ InitializeSpinLock(&queryThreadLock);
+
+ queryThreadData.timer = INVALID_TIMER;
+ queryThreadTerminate = 0;
+ queryThreadMutex = ramutex_create();
+ queryThreadCond = racond_create();
+
+ queryThread = rathread_create(queryThread_main, NULL);
+
+ if(queryThread == NULL){
+ ShowFatalError("do_init_script: cannot spawn Query Thread.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ add_timer_func_list(queryThread_timer, "queryThread_timer");
+#endif
+ return 0;
+}
+
+int script_reload() {
+ int i;
+
+#ifdef BETA_THREAD_TEST
+ /* we're reloading so any queries undergoing should be...exterminated. */
+ EnterSpinLock(&queryThreadLock);
+
+ for( i = 0; i < queryThreadData.count; i++ ) {
+ aFree(queryThreadData.entry[i]);
+ }
+ queryThreadData.count = 0;
+
+ if( queryThreadData.timer != INVALID_TIMER ) {
+ delete_timer(queryThreadData.timer, queryThread_timer);
+ queryThreadData.timer = INVALID_TIMER;
+ }
+
+ LeaveSpinLock(&queryThreadLock);
+#endif
+
+
+ userfunc_db->clear(userfunc_db, db_script_free_code_sub);
+ db_clear(scriptlabel_db);
+
+ // @commands (script based)
+ // Clear bindings
+ for( i = 0; i < atcmd_binding_count; i++ ) {
+ aFree(atcmd_binding[i]);
+ }
+
+ if( atcmd_binding_count != 0 )
+ aFree(atcmd_binding);
+
+ atcmd_binding_count = 0;
+
+ if(sleep_db) {
+ struct linkdb_node *n = (struct linkdb_node *)sleep_db;
+ while(n) {
+ struct script_state *st = (struct script_state *)n->data;
+ script_free_state(st);
+ n = n->next;
+ }
+ linkdb_final(&sleep_db);
+ }
+ mapreg_reload();
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// buildin functions
+//
+
+#define BUILDIN_DEF(x,args) { buildin_ ## x , #x , args }
+#define BUILDIN_DEF2(x,x2,args) { buildin_ ## x , x2 , args }
+#define BUILDIN_FUNC(x) int buildin_ ## x (struct script_state* st)
+
+/////////////////////////////////////////////////////////////////////
+// NPC interaction
+//
+
+/// Appends a message to the npc dialog.
+/// If a dialog doesn't exist yet, one is created.
+///
+/// mes "<message>";
+BUILDIN_FUNC(mes)
+{
+ TBL_PC* sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if( !script_hasdata(st, 3) )
+ {// only a single line detected in the script
+ clif_scriptmes(sd, st->oid, script_getstr(st, 2));
+ }
+ else
+ {// parse multiple lines as they exist
+ int i;
+
+ for( i = 2; script_hasdata(st, i); i++ )
+ {
+ // send the message to the client
+ clif_scriptmes(sd, st->oid, script_getstr(st, i));
+ }
+ }
+
+ return 0;
+}
+
+/// Displays the button 'next' in the npc dialog.
+/// The dialog text is cleared and the script continues when the button is pressed.
+///
+/// next;
+BUILDIN_FUNC(next)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ st->state = STOP;
+ clif_scriptnext(sd, st->oid);
+ return 0;
+}
+
+/// Ends the script and displays the button 'close' on the npc dialog.
+/// The dialog is closed when the button is pressed.
+///
+/// close;
+BUILDIN_FUNC(close)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ st->state = END;
+ clif_scriptclose(sd, st->oid);
+ return 0;
+}
+
+/// Displays the button 'close' on the npc dialog.
+/// The dialog is closed and the script continues when the button is pressed.
+///
+/// close2;
+BUILDIN_FUNC(close2)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ st->state = STOP;
+ clif_scriptclose(sd, st->oid);
+ return 0;
+}
+
+/// Counts the number of valid and total number of options in 'str'
+/// If max_count > 0 the counting stops when that valid option is reached
+/// total is incremented for each option (NULL is supported)
+static int menu_countoptions(const char* str, int max_count, int* total)
+{
+ int count = 0;
+ int bogus_total;
+
+ if( total == NULL )
+ total = &bogus_total;
+ ++(*total);
+
+ // initial empty options
+ while( *str == ':' )
+ {
+ ++str;
+ ++(*total);
+ }
+ // count menu options
+ while( *str != '\0' )
+ {
+ ++count;
+ --max_count;
+ if( max_count == 0 )
+ break;
+ while( *str != ':' && *str != '\0' )
+ ++str;
+ while( *str == ':' )
+ {
+ ++str;
+ ++(*total);
+ }
+ }
+ return count;
+}
+
+/// Displays a menu with options and goes to the target label.
+/// The script is stopped if cancel is pressed.
+/// Options with no text are not displayed in the client.
+///
+/// Options can be grouped together, separated by the character ':' in the text:
+/// ex: menu "A:B:C",L_target;
+/// All these options go to the specified target label.
+///
+/// The index of the selected option is put in the variable @menu.
+/// Indexes start with 1 and are consistent with grouped and empty options.
+/// ex: menu "A::B",-,"",L_Impossible,"C",-;
+/// // displays "A", "B" and "C", corresponding to indexes 1, 3 and 5
+///
+/// NOTE: the client closes the npc dialog when cancel is pressed
+///
+/// menu "<option_text>",<target_label>{,"<option_text>",<target_label>,...};
+BUILDIN_FUNC(menu)
+{
+ int i;
+ const char* text;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ // TODO detect multiple scripts waiting for input at the same time, and what to do when that happens
+ if( sd->state.menu_or_input == 0 )
+ {
+ struct StringBuf buf;
+ struct script_data* data;
+
+ if( script_lastdata(st) % 2 == 0 )
+ {// argument count is not even (1st argument is at index 2)
+ ShowError("script:menu: illegal number of arguments (%d).\n", (script_lastdata(st) - 1));
+ st->state = END;
+ return 1;
+ }
+
+ StringBuf_Init(&buf);
+ sd->npc_menu = 0;
+ for( i = 2; i < script_lastdata(st); i += 2 )
+ {
+ // menu options
+ text = script_getstr(st, i);
+
+ // target label
+ data = script_getdata(st, i+1);
+ if( !data_islabel(data) )
+ {// not a label
+ StringBuf_Destroy(&buf);
+ ShowError("script:menu: argument #%d (from 1) is not a label or label not found.\n", i);
+ script_reportdata(data);
+ st->state = END;
+ return 1;
+ }
+
+ // append option(s)
+ if( text[0] == '\0' )
+ continue;// empty string, ignore
+ if( sd->npc_menu > 0 )
+ StringBuf_AppendStr(&buf, ":");
+ StringBuf_AppendStr(&buf, text);
+ sd->npc_menu += menu_countoptions(text, 0, NULL);
+ }
+ st->state = RERUNLINE;
+ sd->state.menu_or_input = 1;
+
+ /**
+ * menus beyond this length crash the client (see bugreport:6402)
+ **/
+ if( StringBuf_Length(&buf) >= 2047 ) {
+ struct npc_data * nd = map_id2nd(st->oid);
+ char* menu;
+ CREATE(menu, char, 2048);
+ safestrncpy(menu, StringBuf_Value(&buf), 2047);
+ ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StringBuf_Length(&buf));
+ clif_scriptmenu(sd, st->oid, menu);
+ aFree(menu);
+ } else
+ clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf));
+
+ StringBuf_Destroy(&buf);
+
+ if( sd->npc_menu >= 0xff )
+ {// client supports only up to 254 entries; 0 is not used and 255 is reserved for cancel; excess entries are displayed but cause 'uint8' overflow
+ ShowWarning("buildin_menu: Too many options specified (current=%d, max=254).\n", sd->npc_menu);
+ script_reportsrc(st);
+ }
+ }
+ else if( sd->npc_menu == 0xff )
+ {// Cancel was pressed
+ sd->state.menu_or_input = 0;
+ st->state = END;
+ }
+ else
+ {// goto target label
+ int menu = 0;
+
+ sd->state.menu_or_input = 0;
+ if( sd->npc_menu <= 0 )
+ {
+ ShowDebug("script:menu: unexpected selection (%d)\n", sd->npc_menu);
+ st->state = END;
+ return 1;
+ }
+
+ // get target label
+ for( i = 2; i < script_lastdata(st); i += 2 )
+ {
+ text = script_getstr(st, i);
+ sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu);
+ if( sd->npc_menu <= 0 )
+ break;// entry found
+ }
+ if( sd->npc_menu > 0 )
+ {// Invalid selection
+ ShowDebug("script:menu: selection is out of range (%d pairs are missing?) - please report this\n", sd->npc_menu);
+ st->state = END;
+ return 1;
+ }
+ if( !data_islabel(script_getdata(st, i + 1)) )
+ {// TODO remove this temporary crash-prevention code (fallback for multiple scripts requesting user input)
+ ShowError("script:menu: unexpected data in label argument\n");
+ script_reportdata(script_getdata(st, i + 1));
+ st->state = END;
+ return 1;
+ }
+ pc_setreg(sd, add_str("@menu"), menu);
+ st->pos = script_getnum(st, i + 1);
+ st->state = GOTO;
+ }
+ return 0;
+}
+
+/// Displays a menu with options and returns the selected option.
+/// Behaves like 'menu' without the target labels.
+///
+/// select(<option_text>{,<option_text>,...}) -> <selected_option>
+///
+/// @see menu
+BUILDIN_FUNC(select)
+{
+ int i;
+ const char* text;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if( sd->state.menu_or_input == 0 ) {
+ struct StringBuf buf;
+
+ StringBuf_Init(&buf);
+ sd->npc_menu = 0;
+ for( i = 2; i <= script_lastdata(st); ++i ) {
+ text = script_getstr(st, i);
+
+ if( sd->npc_menu > 0 )
+ StringBuf_AppendStr(&buf, ":");
+
+ StringBuf_AppendStr(&buf, text);
+ sd->npc_menu += menu_countoptions(text, 0, NULL);
+ }
+
+ st->state = RERUNLINE;
+ sd->state.menu_or_input = 1;
+
+ /**
+ * menus beyond this length crash the client (see bugreport:6402)
+ **/
+ if( StringBuf_Length(&buf) >= 2047 ) {
+ struct npc_data * nd = map_id2nd(st->oid);
+ char* menu;
+ CREATE(menu, char, 2048);
+ safestrncpy(menu, StringBuf_Value(&buf), 2047);
+ ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StringBuf_Length(&buf));
+ clif_scriptmenu(sd, st->oid, menu);
+ aFree(menu);
+ } else
+ clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf));
+ StringBuf_Destroy(&buf);
+
+ if( sd->npc_menu >= 0xff ) {
+ ShowWarning("buildin_select: Too many options specified (current=%d, max=254).\n", sd->npc_menu);
+ script_reportsrc(st);
+ }
+ } else if( sd->npc_menu == 0xff ) {// Cancel was pressed
+ sd->state.menu_or_input = 0;
+ st->state = END;
+ } else {// return selected option
+ int menu = 0;
+
+ sd->state.menu_or_input = 0;
+ for( i = 2; i <= script_lastdata(st); ++i ) {
+ text = script_getstr(st, i);
+ sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu);
+ if( sd->npc_menu <= 0 )
+ break;// entry found
+ }
+ pc_setreg(sd, add_str("@menu"), menu);
+ script_pushint(st, menu);
+ st->state = RUN;
+ }
+ return 0;
+}
+
+/// Displays a menu with options and returns the selected option.
+/// Behaves like 'menu' without the target labels, except when cancel is
+/// pressed.
+/// When cancel is pressed, the script continues and 255 is returned.
+///
+/// prompt(<option_text>{,<option_text>,...}) -> <selected_option>
+///
+/// @see menu
+BUILDIN_FUNC(prompt)
+{
+ int i;
+ const char *text;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if( sd->state.menu_or_input == 0 )
+ {
+ struct StringBuf buf;
+
+ StringBuf_Init(&buf);
+ sd->npc_menu = 0;
+ for( i = 2; i <= script_lastdata(st); ++i )
+ {
+ text = script_getstr(st, i);
+ if( sd->npc_menu > 0 )
+ StringBuf_AppendStr(&buf, ":");
+ StringBuf_AppendStr(&buf, text);
+ sd->npc_menu += menu_countoptions(text, 0, NULL);
+ }
+
+ st->state = RERUNLINE;
+ sd->state.menu_or_input = 1;
+
+ /**
+ * menus beyond this length crash the client (see bugreport:6402)
+ **/
+ if( StringBuf_Length(&buf) >= 2047 ) {
+ struct npc_data * nd = map_id2nd(st->oid);
+ char* menu;
+ CREATE(menu, char, 2048);
+ safestrncpy(menu, StringBuf_Value(&buf), 2047);
+ ShowWarning("NPC Menu too long! (source:%s / length:%d)\n",nd?nd->name:"Unknown",StringBuf_Length(&buf));
+ clif_scriptmenu(sd, st->oid, menu);
+ aFree(menu);
+ } else
+ clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf));
+ StringBuf_Destroy(&buf);
+
+ if( sd->npc_menu >= 0xff )
+ {
+ ShowWarning("buildin_prompt: Too many options specified (current=%d, max=254).\n", sd->npc_menu);
+ script_reportsrc(st);
+ }
+ }
+ else if( sd->npc_menu == 0xff )
+ {// Cancel was pressed
+ sd->state.menu_or_input = 0;
+ pc_setreg(sd, add_str("@menu"), 0xff);
+ script_pushint(st, 0xff);
+ st->state = RUN;
+ }
+ else
+ {// return selected option
+ int menu = 0;
+
+ sd->state.menu_or_input = 0;
+ for( i = 2; i <= script_lastdata(st); ++i )
+ {
+ text = script_getstr(st, i);
+ sd->npc_menu -= menu_countoptions(text, sd->npc_menu, &menu);
+ if( sd->npc_menu <= 0 )
+ break;// entry found
+ }
+ pc_setreg(sd, add_str("@menu"), menu);
+ script_pushint(st, menu);
+ st->state = RUN;
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+// ...
+//
+
+/// Jumps to the target script label.
+///
+/// goto <label>;
+BUILDIN_FUNC(goto)
+{
+ if( !data_islabel(script_getdata(st,2)) )
+ {
+ ShowError("script:goto: not a label\n");
+ script_reportdata(script_getdata(st,2));
+ st->state = END;
+ return 1;
+ }
+
+ st->pos = script_getnum(st,2);
+ st->state = GOTO;
+ return 0;
+}
+
+/*==========================================
+ * user-defined function call
+ *------------------------------------------*/
+BUILDIN_FUNC(callfunc)
+{
+ int i, j;
+ struct script_retinfo* ri;
+ struct script_code* scr;
+ const char* str = script_getstr(st,2);
+ DBMap **ref = NULL;
+
+ scr = (struct script_code*)strdb_get(userfunc_db, str);
+ if( !scr )
+ {
+ ShowError("script:callfunc: function not found! [%s]\n", str);
+ st->state = END;
+ return 1;
+ }
+
+ for( i = st->start+3, j = 0; i < st->end; i++, j++ )
+ {
+ struct script_data* data = push_copy(st->stack,i);
+ if( data_isreference(data) && !data->ref )
+ {
+ const char* name = reference_getname(data);
+ if( name[0] == '.' ) {
+ if ( !ref ) {
+ ref = (struct DBMap**)aCalloc(sizeof(struct DBMap*), 1);
+ ref[0] = (name[1] == '@' ? st->stack->var_function : st->script->script_vars);
+ }
+ data->ref = ref;
+ }
+ }
+ }
+
+ CREATE(ri, struct script_retinfo, 1);
+ ri->script = st->script;// script code
+ ri->var_function = st->stack->var_function;// scope variables
+ ri->pos = st->pos;// script location
+ ri->nargs = j;// argument count
+ ri->defsp = st->stack->defsp;// default stack pointer
+ push_retinfo(st->stack, ri, ref);
+
+ st->pos = 0;
+ st->script = scr;
+ st->stack->defsp = st->stack->sp;
+ st->state = GOTO;
+ st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA);
+
+ return 0;
+}
+/*==========================================
+ * subroutine call
+ *------------------------------------------*/
+BUILDIN_FUNC(callsub)
+{
+ int i,j;
+ struct script_retinfo* ri;
+ int pos = script_getnum(st,2);
+ DBMap **ref = NULL;
+
+ if( !data_islabel(script_getdata(st,2)) && !data_isfunclabel(script_getdata(st,2)) )
+ {
+ ShowError("script:callsub: argument is not a label\n");
+ script_reportdata(script_getdata(st,2));
+ st->state = END;
+ return 1;
+ }
+
+ for( i = st->start+3, j = 0; i < st->end; i++, j++ )
+ {
+ struct script_data* data = push_copy(st->stack,i);
+ if( data_isreference(data) && !data->ref )
+ {
+ const char* name = reference_getname(data);
+ if( name[0] == '.' && name[1] == '@' ) {
+ if ( !ref ) {
+ ref = (struct DBMap**)aCalloc(sizeof(struct DBMap*), 1);
+ ref[0] = st->stack->var_function;
+ }
+ data->ref = ref;
+ }
+ }
+ }
+
+ CREATE(ri, struct script_retinfo, 1);
+ ri->script = st->script;// script code
+ ri->var_function = st->stack->var_function;// scope variables
+ ri->pos = st->pos;// script location
+ ri->nargs = j;// argument count
+ ri->defsp = st->stack->defsp;// default stack pointer
+ push_retinfo(st->stack, ri, ref);
+
+ st->pos = pos;
+ st->stack->defsp = st->stack->sp;
+ st->state = GOTO;
+ st->stack->var_function = idb_alloc(DB_OPT_RELEASE_DATA);
+
+ return 0;
+}
+
+/// Retrieves an argument provided to callfunc/callsub.
+/// If the argument doesn't exist
+///
+/// getarg(<index>{,<default_value>}) -> <value>
+BUILDIN_FUNC(getarg)
+{
+ struct script_retinfo* ri;
+ int idx;
+
+ if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp - 1].type != C_RETINFO )
+ {
+ ShowError("script:getarg: no callfunc or callsub!\n");
+ st->state = END;
+ return 1;
+ }
+ ri = st->stack->stack_data[st->stack->defsp - 1].u.ri;
+
+ idx = script_getnum(st,2);
+
+ if( idx >= 0 && idx < ri->nargs )
+ push_copy(st->stack, st->stack->defsp - 1 - ri->nargs + idx);
+ else if( script_hasdata(st,3) )
+ script_pushcopy(st, 3);
+ else
+ {
+ ShowError("script:getarg: index (idx=%d) out of range (nargs=%d) and no default value found\n", idx, ri->nargs);
+ st->state = END;
+ return 1;
+ }
+
+ return 0;
+}
+
+/// Returns from the current function, optionaly returning a value from the functions.
+/// Don't use outside script functions.
+///
+/// return;
+/// return <value>;
+BUILDIN_FUNC(return)
+{
+ if( script_hasdata(st,2) )
+ {// return value
+ struct script_data* data;
+ script_pushcopy(st, 2);
+ data = script_getdatatop(st, -1);
+ if( data_isreference(data) )
+ {
+ const char* name = reference_getname(data);
+ if( name[0] == '.' && name[1] == '@' )
+ {// scope variable
+ if( !data->ref || data->ref == (DBMap**)&st->stack->var_function )
+ get_val(st, data);// current scope, convert to value
+ }
+ else if( name[0] == '.' && !data->ref )
+ {// script variable, link to current script
+ data->ref = &st->script->script_vars;
+ }
+ }
+ }
+ else
+ {// no return value
+ script_pushnil(st);
+ }
+ st->state = RETFUNC;
+ return 0;
+}
+
+/// Returns a random number from 0 to <range>-1.
+/// Or returns a random number from <min> to <max>.
+/// If <min> is greater than <max>, their numbers are switched.
+/// rand(<range>) -> <int>
+/// rand(<min>,<max>) -> <int>
+BUILDIN_FUNC(rand)
+{
+ int range;
+ int min;
+ int max;
+
+ if( script_hasdata(st,3) )
+ {// min,max
+ min = script_getnum(st,2);
+ max = script_getnum(st,3);
+ if( max < min )
+ swap(min, max);
+ range = max - min + 1;
+ }
+ else
+ {// range
+ min = 0;
+ range = script_getnum(st,2);
+ }
+ if( range <= 1 )
+ script_pushint(st, min);
+ else
+ script_pushint(st, rnd()%range + min);
+
+ return 0;
+}
+
+/*==========================================
+ * Warp sd to str,x,y or Random or SavePoint/Save
+ *------------------------------------------*/
+BUILDIN_FUNC(warp)
+{
+ int ret;
+ int x,y;
+ const char* str;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ str = script_getstr(st,2);
+ x = script_getnum(st,3);
+ y = script_getnum(st,4);
+
+ if(strcmp(str,"Random")==0)
+ ret = pc_randomwarp(sd,CLR_TELEPORT);
+ else if(strcmp(str,"SavePoint")==0 || strcmp(str,"Save")==0)
+ ret = pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
+ else
+ ret = pc_setpos(sd,mapindex_name2id(str),x,y,CLR_OUTSIGHT);
+
+ if( ret ) {
+ ShowError("buildin_warp: moving player '%s' to \"%s\",%d,%d failed.\n", sd->status.name, str, x, y);
+ script_reportsrc(st);
+ }
+
+ return 0;
+}
+/*==========================================
+ * Warp a specified area
+ *------------------------------------------*/
+static int buildin_areawarp_sub(struct block_list *bl,va_list ap)
+{
+ int x2,y2,x3,y3;
+ unsigned int index;
+
+ index = va_arg(ap,unsigned int);
+ x2 = va_arg(ap,int);
+ y2 = va_arg(ap,int);
+ x3 = va_arg(ap,int);
+ y3 = va_arg(ap,int);
+
+ if(index == 0)
+ pc_randomwarp((TBL_PC *)bl,CLR_TELEPORT);
+ else if(x3 && y3) {
+ int max, tx, ty, j = 0;
+
+ // choose a suitable max number of attempts
+ if( (max = (y3-y2+1)*(x3-x2+1)*3) > 1000 )
+ max = 1000;
+
+ // find a suitable map cell
+ do {
+ tx = rnd()%(x3-x2+1)+x2;
+ ty = rnd()%(y3-y2+1)+y2;
+ j++;
+ } while( map_getcell(index,tx,ty,CELL_CHKNOPASS) && j < max );
+
+ pc_setpos((TBL_PC *)bl,index,tx,ty,CLR_OUTSIGHT);
+ }
+ else
+ pc_setpos((TBL_PC *)bl,index,x2,y2,CLR_OUTSIGHT);
+ return 0;
+}
+BUILDIN_FUNC(areawarp)
+{
+ int16 m, x0,y0,x1,y1, x2,y2,x3=0,y3=0;
+ unsigned int index;
+ const char *str;
+ const char *mapname;
+
+ mapname = script_getstr(st,2);
+ x0 = script_getnum(st,3);
+ y0 = script_getnum(st,4);
+ x1 = script_getnum(st,5);
+ y1 = script_getnum(st,6);
+ str = script_getstr(st,7);
+ x2 = script_getnum(st,8);
+ y2 = script_getnum(st,9);
+
+ if( script_hasdata(st,10) && script_hasdata(st,11) ) { // Warp area to area
+ if( (x3 = script_getnum(st,10)) < 0 || (y3 = script_getnum(st,11)) < 0 ){
+ x3 = 0;
+ y3 = 0;
+ } else if( x3 && y3 ) {
+ // normalize x3/y3 coordinates
+ if( x3 < x2 ) swap(x3,x2);
+ if( y3 < y2 ) swap(y3,y2);
+ }
+ }
+
+ if( (m = map_mapname2mapid(mapname)) < 0 )
+ return 0;
+
+ if( strcmp(str,"Random") == 0 )
+ index = 0;
+ else if( !(index=mapindex_name2id(str)) )
+ return 0;
+
+ map_foreachinarea(buildin_areawarp_sub, m,x0,y0,x1,y1, BL_PC, index,x2,y2,x3,y3);
+ return 0;
+}
+
+/*==========================================
+ * areapercentheal <map>,<x1>,<y1>,<x2>,<y2>,<hp>,<sp>
+ *------------------------------------------*/
+static int buildin_areapercentheal_sub(struct block_list *bl,va_list ap)
+{
+ int hp, sp;
+ hp = va_arg(ap, int);
+ sp = va_arg(ap, int);
+ pc_percentheal((TBL_PC *)bl,hp,sp);
+ return 0;
+}
+BUILDIN_FUNC(areapercentheal)
+{
+ int hp,sp,m;
+ const char *mapname;
+ int x0,y0,x1,y1;
+
+ mapname=script_getstr(st,2);
+ x0=script_getnum(st,3);
+ y0=script_getnum(st,4);
+ x1=script_getnum(st,5);
+ y1=script_getnum(st,6);
+ hp=script_getnum(st,7);
+ sp=script_getnum(st,8);
+
+ if( (m=map_mapname2mapid(mapname))< 0)
+ return 0;
+
+ map_foreachinarea(buildin_areapercentheal_sub,m,x0,y0,x1,y1,BL_PC,hp,sp);
+ return 0;
+}
+
+/*==========================================
+ * warpchar [LuzZza]
+ * Useful for warp one player from
+ * another player npc-session.
+ * Using: warpchar "mapname",x,y,Char_ID;
+ *------------------------------------------*/
+BUILDIN_FUNC(warpchar)
+{
+ int x,y,a;
+ const char *str;
+ TBL_PC *sd;
+
+ str=script_getstr(st,2);
+ x=script_getnum(st,3);
+ y=script_getnum(st,4);
+ a=script_getnum(st,5);
+
+ sd = map_charid2sd(a);
+ if( sd == NULL )
+ return 0;
+
+ if(strcmp(str, "Random") == 0)
+ pc_randomwarp(sd, CLR_TELEPORT);
+ else
+ if(strcmp(str, "SavePoint") == 0)
+ pc_setpos(sd, sd->status.save_point.map,sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT);
+ else
+ pc_setpos(sd, mapindex_name2id(str), x, y, CLR_TELEPORT);
+
+ return 0;
+}
+/*==========================================
+ * Warpparty - [Fredzilla] [Paradox924X]
+ * Syntax: warpparty "to_mapname",x,y,Party_ID,{"from_mapname"};
+ * If 'from_mapname' is specified, only the party members on that map will be warped
+ *------------------------------------------*/
+BUILDIN_FUNC(warpparty)
+{
+ TBL_PC *sd = NULL;
+ TBL_PC *pl_sd;
+ struct party_data* p;
+ int type;
+ int mapindex;
+ int i;
+
+ const char* str = script_getstr(st,2);
+ int x = script_getnum(st,3);
+ int y = script_getnum(st,4);
+ int p_id = script_getnum(st,5);
+ const char* str2 = NULL;
+ if ( script_hasdata(st,6) )
+ str2 = script_getstr(st,6);
+
+ p = party_search(p_id);
+ if(!p)
+ return 0;
+
+ type = ( strcmp(str,"Random")==0 ) ? 0
+ : ( strcmp(str,"SavePointAll")==0 ) ? 1
+ : ( strcmp(str,"SavePoint")==0 ) ? 2
+ : ( strcmp(str,"Leader")==0 ) ? 3
+ : 4;
+
+ switch (type)
+ {
+ case 3:
+ for(i = 0; i < MAX_PARTY && !p->party.member[i].leader; i++);
+ if (i == MAX_PARTY || !p->data[i].sd) //Leader not found / not online
+ return 0;
+ pl_sd = p->data[i].sd;
+ mapindex = pl_sd->mapindex;
+ x = pl_sd->bl.x;
+ y = pl_sd->bl.y;
+ break;
+ case 4:
+ mapindex = mapindex_name2id(str);
+ break;
+ case 2:
+ //"SavePoint" uses save point of the currently attached player
+ if (( sd = script_rid2sd(st) ) == NULL )
+ return 0;
+ default:
+ mapindex = 0;
+ break;
+ }
+
+ for (i = 0; i < MAX_PARTY; i++)
+ {
+ if( !(pl_sd = p->data[i].sd) || pl_sd->status.party_id != p_id )
+ continue;
+
+ if( str2 && strcmp(str2, map[pl_sd->bl.m].name) != 0 )
+ continue;
+
+ if( pc_isdead(pl_sd) )
+ continue;
+
+ switch( type )
+ {
+ case 0: // Random
+ if(!map[pl_sd->bl.m].flag.nowarp)
+ pc_randomwarp(pl_sd,CLR_TELEPORT);
+ break;
+ case 1: // SavePointAll
+ if(!map[pl_sd->bl.m].flag.noreturn)
+ pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,CLR_TELEPORT);
+ break;
+ case 2: // SavePoint
+ if(!map[pl_sd->bl.m].flag.noreturn)
+ pc_setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
+ break;
+ case 3: // Leader
+ case 4: // m,x,y
+ if(!map[pl_sd->bl.m].flag.noreturn && !map[pl_sd->bl.m].flag.nowarp)
+ pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT);
+ break;
+ }
+ }
+
+ return 0;
+}
+/*==========================================
+ * Warpguild - [Fredzilla]
+ * Syntax: warpguild "mapname",x,y,Guild_ID;
+ *------------------------------------------*/
+BUILDIN_FUNC(warpguild)
+{
+ TBL_PC *sd = NULL;
+ TBL_PC *pl_sd;
+ struct guild* g;
+ struct s_mapiterator* iter;
+ int type;
+
+ const char* str = script_getstr(st,2);
+ int x = script_getnum(st,3);
+ int y = script_getnum(st,4);
+ int gid = script_getnum(st,5);
+
+ g = guild_search(gid);
+ if( g == NULL )
+ return 0;
+
+ type = ( strcmp(str,"Random")==0 ) ? 0
+ : ( strcmp(str,"SavePointAll")==0 ) ? 1
+ : ( strcmp(str,"SavePoint")==0 ) ? 2
+ : 3;
+
+ if( type == 2 && ( sd = script_rid2sd(st) ) == NULL )
+ {// "SavePoint" uses save point of the currently attached player
+ return 0;
+ }
+
+ iter = mapit_getallusers();
+ for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) )
+ {
+ if( pl_sd->status.guild_id != gid )
+ continue;
+
+ switch( type )
+ {
+ case 0: // Random
+ if(!map[pl_sd->bl.m].flag.nowarp)
+ pc_randomwarp(pl_sd,CLR_TELEPORT);
+ break;
+ case 1: // SavePointAll
+ if(!map[pl_sd->bl.m].flag.noreturn)
+ pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,CLR_TELEPORT);
+ break;
+ case 2: // SavePoint
+ if(!map[pl_sd->bl.m].flag.noreturn)
+ pc_setpos(pl_sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
+ break;
+ case 3: // m,x,y
+ if(!map[pl_sd->bl.m].flag.noreturn && !map[pl_sd->bl.m].flag.nowarp)
+ pc_setpos(pl_sd,mapindex_name2id(str),x,y,CLR_TELEPORT);
+ break;
+ }
+ }
+ mapit_free(iter);
+
+ return 0;
+}
+/*==========================================
+ * Force Heal a player (hp and sp)
+ *------------------------------------------*/
+BUILDIN_FUNC(heal)
+{
+ TBL_PC *sd;
+ int hp,sp;
+
+ sd = script_rid2sd(st);
+ if (!sd) return 0;
+
+ hp=script_getnum(st,2);
+ sp=script_getnum(st,3);
+ status_heal(&sd->bl, hp, sp, 1);
+ return 0;
+}
+/*==========================================
+ * Heal a player by item (get vit bonus etc)
+ *------------------------------------------*/
+BUILDIN_FUNC(itemheal)
+{
+ TBL_PC *sd;
+ int hp,sp;
+
+ hp=script_getnum(st,2);
+ sp=script_getnum(st,3);
+
+ if(potion_flag==1) {
+ potion_hp = hp;
+ potion_sp = sp;
+ return 0;
+ }
+
+ sd = script_rid2sd(st);
+ if (!sd) return 0;
+ pc_itemheal(sd,sd->itemid,hp,sp);
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(percentheal)
+{
+ int hp,sp;
+ TBL_PC* sd;
+
+ hp=script_getnum(st,2);
+ sp=script_getnum(st,3);
+
+ if(potion_flag==1) {
+ potion_per_hp = hp;
+ potion_per_sp = sp;
+ return 0;
+ }
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+#ifdef RENEWAL
+ if( sd->sc.data[SC_EXTREMITYFIST2] )
+ sp = 0;
+#endif
+ pc_percentheal(sd,hp,sp);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(jobchange)
+{
+ int job, upper=-1;
+
+ job=script_getnum(st,2);
+ if( script_hasdata(st,3) )
+ upper=script_getnum(st,3);
+
+ if (pcdb_checkid(job))
+ {
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pc_jobchange(sd, job, upper);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(jobname)
+{
+ int class_=script_getnum(st,2);
+ script_pushconststr(st, (char*)job_name(class_));
+ return 0;
+}
+
+/// Get input from the player.
+/// For numeric inputs the value is capped to the range [min,max]. Returns 1 if
+/// the value was higher than 'max', -1 if lower than 'min' and 0 otherwise.
+/// For string inputs it returns 1 if the string was longer than 'max', -1 is
+/// shorter than 'min' and 0 otherwise.
+///
+/// input(<var>{,<min>{,<max>}}) -> <int>
+BUILDIN_FUNC(input)
+{
+ TBL_PC* sd;
+ struct script_data* data;
+ int uid;
+ const char* name;
+ int min;
+ int max;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ data = script_getdata(st,2);
+ if( !data_isreference(data) ){
+ ShowError("script:input: not a variable\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;
+ }
+ uid = reference_getuid(data);
+ name = reference_getname(data);
+ min = (script_hasdata(st,3) ? script_getnum(st,3) : script_config.input_min_value);
+ max = (script_hasdata(st,4) ? script_getnum(st,4) : script_config.input_max_value);
+
+ if( !sd->state.menu_or_input )
+ { // first invocation, display npc input box
+ sd->state.menu_or_input = 1;
+ st->state = RERUNLINE;
+ if( is_string_variable(name) )
+ clif_scriptinputstr(sd,st->oid);
+ else
+ clif_scriptinput(sd,st->oid);
+ }
+ else
+ { // take received text/value and store it in the designated variable
+ sd->state.menu_or_input = 0;
+ if( is_string_variable(name) )
+ {
+ int len = (int)strlen(sd->npc_str);
+ set_reg(st, sd, uid, name, (void*)sd->npc_str, script_getref(st,2));
+ script_pushint(st, (len > max ? 1 : len < min ? -1 : 0));
+ }
+ else
+ {
+ int amount = sd->npc_amount;
+ set_reg(st, sd, uid, name, (void*)__64BPRTSIZE(cap_value(amount,min,max)), script_getref(st,2));
+ script_pushint(st, (amount > max ? 1 : amount < min ? -1 : 0));
+ }
+ st->state = RUN;
+ }
+ return 0;
+}
+
+// declare the copyarray method here for future reference
+BUILDIN_FUNC(copyarray);
+
+/// Sets the value of a variable.
+/// The value is converted to the type of the variable.
+///
+/// set(<variable>,<value>) -> <variable>
+BUILDIN_FUNC(set)
+{
+ TBL_PC* sd = NULL;
+ struct script_data* data;
+ //struct script_data* datavalue;
+ int num;
+ const char* name;
+ char prefix;
+
+ data = script_getdata(st,2);
+ //datavalue = script_getdata(st,3);
+ if( !data_isreference(data) )
+ {
+ ShowError("script:set: not a variable\n");
+ script_reportdata(script_getdata(st,2));
+ st->state = END;
+ return 1;
+ }
+
+ num = reference_getuid(data);
+ name = reference_getname(data);
+ prefix = *name;
+
+ if( not_server_variable(prefix) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ {
+ ShowError("script:set: no player attached for player variable '%s'\n", name);
+ return 0;
+ }
+ }
+
+#if 0
+ if( data_isreference(datavalue) )
+ {// the value being referenced is a variable
+ const char* namevalue = reference_getname(datavalue);
+
+ if( !not_array_variable(*namevalue) )
+ {// array variable being copied into another array variable
+ if( sd == NULL && not_server_variable(*namevalue) && !(sd = script_rid2sd(st)) )
+ {// player must be attached in order to copy a player variable
+ ShowError("script:set: no player attached for player variable '%s'\n", namevalue);
+ return 0;
+ }
+
+ if( is_string_variable(namevalue) != is_string_variable(name) )
+ {// non-matching array value types
+ ShowWarning("script:set: two array variables do not match in type.\n");
+ return 0;
+ }
+
+ // push the maximum number of array values to the stack
+ push_val(st->stack, C_INT, SCRIPT_MAX_ARRAYSIZE);
+
+ // call the copy array method directly
+ return buildin_copyarray(st);
+ }
+ }
+#endif
+
+ if( is_string_variable(name) )
+ set_reg(st,sd,num,name,(void*)script_getstr(st,3),script_getref(st,2));
+ else
+ set_reg(st,sd,num,name,(void*)__64BPRTSIZE(script_getnum(st,3)),script_getref(st,2));
+
+ // return a copy of the variable reference
+ script_pushcopy(st,2);
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+/// Array variables
+///
+
+/// Returns the size of the specified array
+static int32 getarraysize(struct script_state* st, int32 id, int32 idx, int isstring, struct DBMap** ref)
+{
+ int32 ret = idx;
+
+ if( isstring )
+ {
+ for( ; idx < SCRIPT_MAX_ARRAYSIZE; ++idx )
+ {
+ char* str = (char*)get_val2(st, reference_uid(id, idx), ref);
+ if( str && *str )
+ ret = idx + 1;
+ script_removetop(st, -1, 0);
+ }
+ }
+ else
+ {
+ for( ; idx < SCRIPT_MAX_ARRAYSIZE; ++idx )
+ {
+ int32 num = (int32)__64BPRTSIZE(get_val2(st, reference_uid(id, idx), ref));
+ if( num )
+ ret = idx + 1;
+ script_removetop(st, -1, 0);
+ }
+ }
+ return ret;
+}
+
+/// Sets values of an array, from the starting index.
+/// ex: setarray arr[1],1,2,3;
+///
+/// setarray <array variable>,<value1>{,<value2>...};
+BUILDIN_FUNC(setarray)
+{
+ struct script_data* data;
+ const char* name;
+ int32 start;
+ int32 end;
+ int32 id;
+ int32 i;
+ TBL_PC* sd = NULL;
+
+ data = script_getdata(st, 2);
+ if( !data_isreference(data) )
+ {
+ ShowError("script:setarray: not a variable\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not a variable
+ }
+
+ id = reference_getid(data);
+ start = reference_getindex(data);
+ name = reference_getname(data);
+ if( not_array_variable(*name) )
+ {
+ ShowError("script:setarray: illegal scope\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ if( not_server_variable(*name) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached
+ }
+
+ end = start + script_lastdata(st) - 2;
+ if( end > SCRIPT_MAX_ARRAYSIZE )
+ end = SCRIPT_MAX_ARRAYSIZE;
+
+ if( is_string_variable(name) )
+ {// string array
+ for( i = 3; start < end; ++start, ++i )
+ set_reg(st, sd, reference_uid(id, start), name, (void*)script_getstr(st,i), reference_getref(data));
+ }
+ else
+ {// int array
+ for( i = 3; start < end; ++start, ++i )
+ set_reg(st, sd, reference_uid(id, start), name, (void*)__64BPRTSIZE(script_getnum(st,i)), reference_getref(data));
+ }
+ return 0;
+}
+
+/// Sets count values of an array, from the starting index.
+/// ex: cleararray arr[0],0,1;
+///
+/// cleararray <array variable>,<value>,<count>;
+BUILDIN_FUNC(cleararray)
+{
+ struct script_data* data;
+ const char* name;
+ int32 start;
+ int32 end;
+ int32 id;
+ void* v;
+ TBL_PC* sd = NULL;
+
+ data = script_getdata(st, 2);
+ if( !data_isreference(data) )
+ {
+ ShowError("script:cleararray: not a variable\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not a variable
+ }
+
+ id = reference_getid(data);
+ start = reference_getindex(data);
+ name = reference_getname(data);
+ if( not_array_variable(*name) )
+ {
+ ShowError("script:cleararray: illegal scope\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ if( not_server_variable(*name) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached
+ }
+
+ if( is_string_variable(name) )
+ v = (void*)script_getstr(st, 3);
+ else
+ v = (void*)__64BPRTSIZE(script_getnum(st, 3));
+
+ end = start + script_getnum(st, 4);
+ if( end > SCRIPT_MAX_ARRAYSIZE )
+ end = SCRIPT_MAX_ARRAYSIZE;
+
+ for( ; start < end; ++start )
+ set_reg(st, sd, reference_uid(id, start), name, v, script_getref(st,2));
+ return 0;
+}
+
+/// Copies data from one array to another.
+/// ex: copyarray arr[0],arr[2],2;
+///
+/// copyarray <destination array variable>,<source array variable>,<count>;
+BUILDIN_FUNC(copyarray)
+{
+ struct script_data* data1;
+ struct script_data* data2;
+ const char* name1;
+ const char* name2;
+ int32 idx1;
+ int32 idx2;
+ int32 id1;
+ int32 id2;
+ void* v;
+ int32 i;
+ int32 count;
+ TBL_PC* sd = NULL;
+
+ data1 = script_getdata(st, 2);
+ data2 = script_getdata(st, 3);
+ if( !data_isreference(data1) || !data_isreference(data2) )
+ {
+ ShowError("script:copyarray: not a variable\n");
+ script_reportdata(data1);
+ script_reportdata(data2);
+ st->state = END;
+ return 1;// not a variable
+ }
+
+ id1 = reference_getid(data1);
+ id2 = reference_getid(data2);
+ idx1 = reference_getindex(data1);
+ idx2 = reference_getindex(data2);
+ name1 = reference_getname(data1);
+ name2 = reference_getname(data2);
+ if( not_array_variable(*name1) || not_array_variable(*name2) )
+ {
+ ShowError("script:copyarray: illegal scope\n");
+ script_reportdata(data1);
+ script_reportdata(data2);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ if( is_string_variable(name1) != is_string_variable(name2) )
+ {
+ ShowError("script:copyarray: type mismatch\n");
+ script_reportdata(data1);
+ script_reportdata(data2);
+ st->state = END;
+ return 1;// data type mismatch
+ }
+
+ if( not_server_variable(*name1) || not_server_variable(*name2) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached
+ }
+
+ count = script_getnum(st, 4);
+ if( count > SCRIPT_MAX_ARRAYSIZE - idx1 )
+ count = SCRIPT_MAX_ARRAYSIZE - idx1;
+ if( count <= 0 || (id1 == id2 && idx1 == idx2) )
+ return 0;// nothing to copy
+
+ if( id1 == id2 && idx1 > idx2 )
+ {// destination might be overlapping the source - copy in reverse order
+ for( i = count - 1; i >= 0; --i )
+ {
+ v = get_val2(st, reference_uid(id2, idx2 + i), reference_getref(data2));
+ set_reg(st, sd, reference_uid(id1, idx1 + i), name1, v, reference_getref(data1));
+ script_removetop(st, -1, 0);
+ }
+ }
+ else
+ {// normal copy
+ for( i = 0; i < count; ++i )
+ {
+ if( idx2 + i < SCRIPT_MAX_ARRAYSIZE )
+ {
+ v = get_val2(st, reference_uid(id2, idx2 + i), reference_getref(data2));
+ set_reg(st, sd, reference_uid(id1, idx1 + i), name1, v, reference_getref(data1));
+ script_removetop(st, -1, 0);
+ }
+ else// out of range - assume ""/0
+ set_reg(st, sd, reference_uid(id1, idx1 + i), name1, (is_string_variable(name1)?(void*)"":(void*)0), reference_getref(data1));
+ }
+ }
+ return 0;
+}
+
+/// Returns the size of the array.
+/// Assumes that everything before the starting index exists.
+/// ex: getarraysize(arr[3])
+///
+/// getarraysize(<array variable>) -> <int>
+BUILDIN_FUNC(getarraysize)
+{
+ struct script_data* data;
+ const char* name;
+
+ data = script_getdata(st, 2);
+ if( !data_isreference(data) )
+ {
+ ShowError("script:getarraysize: not a variable\n");
+ script_reportdata(data);
+ script_pushnil(st);
+ st->state = END;
+ return 1;// not a variable
+ }
+
+ name = reference_getname(data);
+ if( not_array_variable(*name) )
+ {
+ ShowError("script:getarraysize: illegal scope\n");
+ script_reportdata(data);
+ script_pushnil(st);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ script_pushint(st, getarraysize(st, reference_getid(data), reference_getindex(data), is_string_variable(name), reference_getref(data)));
+ return 0;
+}
+
+/// Deletes count or all the elements in an array, from the starting index.
+/// ex: deletearray arr[4],2;
+///
+/// deletearray <array variable>;
+/// deletearray <array variable>,<count>;
+BUILDIN_FUNC(deletearray)
+{
+ struct script_data* data;
+ const char* name;
+ int start;
+ int end;
+ int id;
+ TBL_PC *sd = NULL;
+
+ data = script_getdata(st, 2);
+ if( !data_isreference(data) )
+ {
+ ShowError("script:deletearray: not a variable\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not a variable
+ }
+
+ id = reference_getid(data);
+ start = reference_getindex(data);
+ name = reference_getname(data);
+ if( not_array_variable(*name) )
+ {
+ ShowError("script:deletearray: illegal scope\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ if( not_server_variable(*name) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached
+ }
+
+ end = SCRIPT_MAX_ARRAYSIZE;
+
+ if( start >= end )
+ return 0;// nothing to free
+
+ if( script_hasdata(st,3) )
+ {
+ int count = script_getnum(st, 3);
+ if( count > end - start )
+ count = end - start;
+ if( count <= 0 )
+ return 0;// nothing to free
+
+ // move rest of the elements backward
+ for( ; start + count < end; ++start )
+ {
+ void* v = get_val2(st, reference_uid(id, start + count), reference_getref(data));
+ set_reg(st, sd, reference_uid(id, start), name, v, reference_getref(data));
+ script_removetop(st, -1, 0);
+ }
+ }
+
+ // clear the rest of the array
+ if( is_string_variable(name) )
+ {
+ for( ; start < end; ++start )
+ set_reg(st, sd, reference_uid(id, start), name, (void *)"", reference_getref(data));
+ }
+ else
+ {
+ for( ; start < end; ++start )
+ set_reg(st, sd, reference_uid(id, start), name, (void*)0, reference_getref(data));
+ }
+ return 0;
+}
+
+/// Returns a reference to the target index of the array variable.
+/// Equivalent to var[index].
+///
+/// getelementofarray(<array variable>,<index>) -> <variable reference>
+BUILDIN_FUNC(getelementofarray)
+{
+ struct script_data* data;
+ const char* name;
+ int32 id;
+ int i;
+
+ data = script_getdata(st, 2);
+ if( !data_isreference(data) )
+ {
+ ShowError("script:getelementofarray: not a variable\n");
+ script_reportdata(data);
+ script_pushnil(st);
+ st->state = END;
+ return 1;// not a variable
+ }
+
+ id = reference_getid(data);
+ name = reference_getname(data);
+ if( not_array_variable(*name) )
+ {
+ ShowError("script:getelementofarray: illegal scope\n");
+ script_reportdata(data);
+ script_pushnil(st);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ i = script_getnum(st, 3);
+ if( i < 0 || i >= SCRIPT_MAX_ARRAYSIZE )
+ {
+ ShowWarning("script:getelementofarray: index out of range (%d)\n", i);
+ script_reportdata(data);
+ script_pushnil(st);
+ st->state = END;
+ return 1;// out of range
+ }
+
+ push_val2(st->stack, C_NAME, reference_uid(id, i), reference_getref(data));
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+/// ...
+///
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(setlook)
+{
+ int type,val;
+ TBL_PC* sd;
+
+ type=script_getnum(st,2);
+ val=script_getnum(st,3);
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pc_changelook(sd,type,val);
+
+ return 0;
+}
+
+BUILDIN_FUNC(changelook)
+{ // As setlook but only client side
+ int type,val;
+ TBL_PC* sd;
+
+ type=script_getnum(st,2);
+ val=script_getnum(st,3);
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ clif_changelook(&sd->bl,type,val);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(cutin)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ clif_cutin(sd,script_getstr(st,2),script_getnum(st,3));
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(viewpoint)
+{
+ int type,x,y,id,color;
+ TBL_PC* sd;
+
+ type=script_getnum(st,2);
+ x=script_getnum(st,3);
+ y=script_getnum(st,4);
+ id=script_getnum(st,5);
+ color=script_getnum(st,6);
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ clif_viewpoint(sd,st->oid,type,x,y,id,color);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(countitem)
+{
+ int nameid, i;
+ int count = 0;
+ struct item_data* id = NULL;
+ struct script_data* data;
+
+ TBL_PC* sd = script_rid2sd(st);
+ if (!sd) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ data = script_getdata(st,2);
+ get_val(st, data); // convert into value in case of a variable
+
+ if( data_isstring(data) )
+ {// item name
+ id = itemdb_searchname(conv_str(st, data));
+ }
+ else
+ {// item id
+ id = itemdb_exists(conv_num(st, data));
+ }
+
+ if( id == NULL )
+ {
+ ShowError("buildin_countitem: Invalid item '%s'.\n", script_getstr(st,2)); // returns string, regardless of what it was
+ script_pushint(st,0);
+ return 1;
+ }
+
+ nameid = id->nameid;
+
+ for(i = 0; i < MAX_INVENTORY; i++)
+ if(sd->status.inventory[i].nameid == nameid)
+ count += sd->status.inventory[i].amount;
+
+ script_pushint(st,count);
+ return 0;
+}
+
+/*==========================================
+ * countitem2(nameID,Identified,Refine,Attribute,Card0,Card1,Card2,Card3) [Lupus]
+ * returns number of items that meet the conditions
+ *------------------------------------------*/
+BUILDIN_FUNC(countitem2)
+{
+ int nameid, iden, ref, attr, c1, c2, c3, c4;
+ int count = 0;
+ int i;
+ struct item_data* id = NULL;
+ struct script_data* data;
+
+ TBL_PC* sd = script_rid2sd(st);
+ if (!sd) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ data = script_getdata(st,2);
+ get_val(st, data); // convert into value in case of a variable
+
+ if( data_isstring(data) )
+ {// item name
+ id = itemdb_searchname(conv_str(st, data));
+ }
+ else
+ {// item id
+ id = itemdb_exists(conv_num(st, data));
+ }
+
+ if( id == NULL )
+ {
+ ShowError("buildin_countitem2: Invalid item '%s'.\n", script_getstr(st,2)); // returns string, regardless of what it was
+ script_pushint(st,0);
+ return 1;
+ }
+
+ nameid = id->nameid;
+ iden = script_getnum(st,3);
+ ref = script_getnum(st,4);
+ attr = script_getnum(st,5);
+ c1 = (short)script_getnum(st,6);
+ c2 = (short)script_getnum(st,7);
+ c3 = (short)script_getnum(st,8);
+ c4 = (short)script_getnum(st,9);
+
+ for(i = 0; i < MAX_INVENTORY; i++)
+ if (sd->status.inventory[i].nameid > 0 && sd->inventory_data[i] != NULL &&
+ sd->status.inventory[i].amount > 0 && sd->status.inventory[i].nameid == nameid &&
+ sd->status.inventory[i].identify == iden && sd->status.inventory[i].refine == ref &&
+ sd->status.inventory[i].attribute == attr && sd->status.inventory[i].card[0] == c1 &&
+ sd->status.inventory[i].card[1] == c2 && sd->status.inventory[i].card[2] == c3 &&
+ sd->status.inventory[i].card[3] == c4
+ )
+ count += sd->status.inventory[i].amount;
+
+ script_pushint(st,count);
+ return 0;
+}
+
+/*==========================================
+ * Check if item with this amount can fit in inventory
+ * Checking : weight, stack amount >32k, slots amount >(MAX_INVENTORY)
+ * Return
+ * 0 : fail
+ * 1 : success (npc side only)
+ *------------------------------------------*/
+BUILDIN_FUNC(checkweight)
+{
+ int nameid, amount, slots, amount2=0;
+ unsigned int weight=0, i, nbargs;
+ struct item_data* id = NULL;
+ struct map_session_data* sd;
+ struct script_data* data;
+
+ if( ( sd = script_rid2sd(st) ) == NULL ){
+ return 0;
+ }
+ nbargs = script_lastdata(st)+1;
+ if(nbargs%2){
+ ShowError("buildin_checkweight: Invalid nb of args should be a multiple of 2.\n");
+ script_pushint(st,0);
+ return 1;
+ }
+ slots = pc_inventoryblank(sd); //nb of empty slot
+
+ for(i=2; i<nbargs; i=i+2){
+ data = script_getdata(st,i);
+ get_val(st, data); // convert into value in case of a variable
+ if( data_isstring(data) ){// item name
+ id = itemdb_searchname(conv_str(st, data));
+ } else {// item id
+ id = itemdb_exists(conv_num(st, data));
+ }
+ if( id == NULL ) {
+ ShowError("buildin_checkweight: Invalid item '%s'.\n", script_getstr(st,i)); // returns string, regardless of what it was
+ script_pushint(st,0);
+ return 1;
+ }
+ nameid = id->nameid;
+
+ amount = script_getnum(st,i+1);
+ if( amount < 1 ) {
+ ShowError("buildin_checkweight: Invalid amount '%d'.\n", amount);
+ script_pushint(st,0);
+ return 1;
+ }
+
+ weight += itemdb_weight(nameid)*amount; //total weight for all chk
+ if( weight + sd->weight > sd->max_weight )
+ {// too heavy
+ script_pushint(st,0);
+ return 0;
+ }
+
+ switch( pc_checkadditem(sd, nameid, amount) )
+ {
+ case ADDITEM_EXIST:
+ // item is already in inventory, but there is still space for the requested amount
+ break;
+ case ADDITEM_NEW:
+ if( itemdb_isstackable(nameid) ) {// stackable
+ amount2++;
+ if( slots < amount2 ) {
+ script_pushint(st,0);
+ return 0;
+ }
+ }
+ else {// non-stackable
+ amount2 += amount;
+ if( slots < amount2){
+ script_pushint(st,0);
+ return 0;
+ }
+ }
+ break;
+ case ADDITEM_OVERAMOUNT:
+ script_pushint(st,0);
+ return 0;
+ }
+ }
+ script_pushint(st,1);
+ return 0;
+}
+
+BUILDIN_FUNC(checkweight2)
+{
+ //variable sub checkweight
+ int32 nameid=-1, amount=-1;
+ int i=0, amount2=0, slots=0, weight=0;
+ short fail=0;
+
+ //variable for array parsing
+ struct script_data* data_it;
+ struct script_data* data_nb;
+ const char* name_it;
+ const char* name_nb;
+ int32 id_it, id_nb;
+ int32 idx_it, idx_nb;
+ int nb_it, nb_nb; //array size
+
+ TBL_PC *sd = script_rid2sd(st);
+ nullpo_retr(1,sd);
+
+ data_it = script_getdata(st, 2);
+ data_nb = script_getdata(st, 3);
+
+ if( !data_isreference(data_it) || !data_isreference(data_nb))
+ {
+ ShowError("script:checkweight2: parameter not a variable\n");
+ script_pushint(st,0);
+ return 1;// not a variable
+ }
+ id_it = reference_getid(data_it);
+ id_nb = reference_getid(data_nb);
+ idx_it = reference_getindex(data_it);
+ idx_nb = reference_getindex(data_nb);
+ name_it = reference_getname(data_it);
+ name_nb = reference_getname(data_nb);
+
+ if( not_array_variable(*name_it) || not_array_variable(*name_nb))
+ {
+ ShowError("script:checkweight2: illegal scope\n");
+ script_pushint(st,0);
+ return 1;// not supported
+ }
+ if(is_string_variable(name_it) || is_string_variable(name_nb)){
+ ShowError("script:checkweight2: illegal type, need int\n");
+ script_pushint(st,0);
+ return 1;// not supported
+ }
+ nb_it = getarraysize(st, id_it, idx_it, 0, reference_getref(data_it));
+ nb_nb = getarraysize(st, id_nb, idx_nb, 0, reference_getref(data_nb));
+ if(nb_it != nb_nb){
+ ShowError("Size mistmatch: nb_it=%d, nb_nb=%d\n",nb_it,nb_nb);
+ fail = 1;
+ }
+
+ slots = pc_inventoryblank(sd);
+ for(i=0; i<nb_it; i++){
+ nameid = (int32)__64BPRTSIZE(get_val2(st,reference_uid(id_it,idx_it+i),reference_getref(data_it)));
+ script_removetop(st, -1, 0);
+ amount = (int32)__64BPRTSIZE(get_val2(st,reference_uid(id_nb,idx_nb+i),reference_getref(data_nb)));
+ script_removetop(st, -1, 0);
+ if(fail) continue; //cpntonie to depop rest
+
+ if(itemdb_exists(nameid) == NULL ){
+ ShowError("buildin_checkweight2: Invalid item '%d'.\n", nameid);
+ fail=1;
+ continue;
+ }
+ if(amount < 0 ){
+ ShowError("buildin_checkweight2: Invalid amount '%d'.\n", amount);
+ fail = 1;
+ continue;
+ }
+ weight += itemdb_weight(nameid)*amount;
+ if( weight + sd->weight > sd->max_weight ){
+ fail = 1;
+ continue;
+ }
+ switch( pc_checkadditem(sd, nameid, amount) ) {
+ case ADDITEM_EXIST:
+ // item is already in inventory, but there is still space for the requested amount
+ break;
+ case ADDITEM_NEW:
+ if( itemdb_isstackable(nameid) ){// stackable
+ amount2++;
+ if( slots < amount2 )
+ fail = 1;
+ }
+ else {// non-stackable
+ amount2 += amount;
+ if( slots < amount2 ){
+ fail = 1;
+ }
+ }
+ break;
+ case ADDITEM_OVERAMOUNT:
+ fail = 1;
+ } //end switch
+ } //end loop DO NOT break it prematurly we need to depop all stack
+
+ fail?script_pushint(st,0):script_pushint(st,1);
+ return 0;
+}
+
+/*==========================================
+ * getitem <item id>,<amount>{,<account ID>};
+ * getitem "<item name>",<amount>{,<account ID>};
+ *------------------------------------------*/
+BUILDIN_FUNC(getitem)
+{
+ int nameid,amount,get_count,i,flag = 0;
+ struct item it;
+ TBL_PC *sd;
+ struct script_data *data;
+
+ data=script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) )
+ {// "<item name>"
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data == NULL ){
+ ShowError("buildin_getitem: Nonexistant item %s requested.\n", name);
+ return 1; //No item created.
+ }
+ nameid=item_data->nameid;
+ } else if( data_isint(data) )
+ {// <item id>
+ nameid=conv_num(st,data);
+ //Violet Box, Blue Box, etc - random item pick
+ if( nameid < 0 ) {
+ nameid = -nameid;
+ flag = 1;
+ }
+ if( nameid <= 0 || !itemdb_exists(nameid) ){
+ ShowError("buildin_getitem: Nonexistant item %d requested.\n", nameid);
+ return 1; //No item created.
+ }
+ } else {
+ ShowError("buildin_getitem: invalid data type for argument #1 (%d).", data->type);
+ return 1;
+ }
+
+ // <amount>
+ if( (amount=script_getnum(st,3)) <= 0)
+ return 0; //return if amount <=0, skip the useles iteration
+
+ memset(&it,0,sizeof(it));
+ it.nameid=nameid;
+ if(!flag)
+ it.identify=1;
+ else
+ it.identify=itemdb_isidentified(nameid);
+
+ if( script_hasdata(st,4) )
+ sd=map_id2sd(script_getnum(st,4)); // <Account ID>
+ else
+ sd=script_rid2sd(st); // Attached player
+
+ if( sd == NULL ) // no target
+ return 0;
+
+ //Check if it's stackable.
+ if (!itemdb_isstackable(nameid))
+ get_count = 1;
+ else
+ get_count = amount;
+
+ for (i = 0; i < amount; i += get_count)
+ {
+ // if not pet egg
+ if (!pet_create_egg(sd, nameid))
+ {
+ if ((flag = pc_additem(sd, &it, get_count, LOG_TYPE_SCRIPT)))
+ {
+ clif_additem(sd, 0, 0, flag);
+ if( pc_candrop(sd,&it) )
+ map_addflooritem(&it,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(getitem2)
+{
+ int nameid,amount,get_count,i,flag = 0;
+ int iden,ref,attr,c1,c2,c3,c4;
+ struct item_data *item_data;
+ struct item item_tmp;
+ TBL_PC *sd;
+ struct script_data *data;
+
+ if( script_hasdata(st,11) )
+ sd=map_id2sd(script_getnum(st,11)); // <Account ID>
+ else
+ sd=script_rid2sd(st); // Attached player
+
+ if( sd == NULL ) // no target
+ return 0;
+
+ data=script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data )
+ nameid=item_data->nameid;
+ else
+ nameid=UNKNOWN_ITEM_ID;
+ }else
+ nameid=conv_num(st,data);
+
+ amount=script_getnum(st,3);
+ iden=script_getnum(st,4);
+ ref=script_getnum(st,5);
+ attr=script_getnum(st,6);
+ c1=(short)script_getnum(st,7);
+ c2=(short)script_getnum(st,8);
+ c3=(short)script_getnum(st,9);
+ c4=(short)script_getnum(st,10);
+
+ if(nameid<0) { // Invalide nameid
+ nameid = -nameid;
+ flag = 1;
+ }
+
+ if(nameid > 0) {
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_data=itemdb_exists(nameid);
+ if (item_data == NULL)
+ return -1;
+ if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR){
+ if(ref > MAX_REFINE) ref = MAX_REFINE;
+ }
+ else if(item_data->type==IT_PETEGG) {
+ iden = 1;
+ ref = 0;
+ }
+ else {
+ iden = 1;
+ ref = attr = 0;
+ }
+
+ item_tmp.nameid=nameid;
+ if(!flag)
+ item_tmp.identify=iden;
+ else if(item_data->type==IT_WEAPON || item_data->type==IT_ARMOR)
+ item_tmp.identify=0;
+ item_tmp.refine=ref;
+ item_tmp.attribute=attr;
+ item_tmp.card[0]=(short)c1;
+ item_tmp.card[1]=(short)c2;
+ item_tmp.card[2]=(short)c3;
+ item_tmp.card[3]=(short)c4;
+
+ //Check if it's stackable.
+ if (!itemdb_isstackable(nameid))
+ get_count = 1;
+ else
+ get_count = amount;
+
+ for (i = 0; i < amount; i += get_count)
+ {
+ // if not pet egg
+ if (!pet_create_egg(sd, nameid))
+ {
+ if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_SCRIPT)))
+ {
+ clif_additem(sd, 0, 0, flag);
+ if( pc_candrop(sd,&item_tmp) )
+ map_addflooritem(&item_tmp,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * rentitem <item id>,<seconds>
+ * rentitem "<item name>",<seconds>
+ *------------------------------------------*/
+BUILDIN_FUNC(rentitem)
+{
+ struct map_session_data *sd;
+ struct script_data *data;
+ struct item it;
+ int seconds;
+ int nameid = 0, flag;
+
+ data = script_getdata(st,2);
+ get_val(st,data);
+
+ if( (sd = script_rid2sd(st)) == NULL )
+ return 0;
+
+ if( data_isstring(data) )
+ {
+ const char *name = conv_str(st,data);
+ struct item_data *itd = itemdb_searchname(name);
+ if( itd == NULL )
+ {
+ ShowError("buildin_rentitem: Nonexistant item %s requested.\n", name);
+ return 1;
+ }
+ nameid = itd->nameid;
+ }
+ else if( data_isint(data) )
+ {
+ nameid = conv_num(st,data);
+ if( nameid <= 0 || !itemdb_exists(nameid) )
+ {
+ ShowError("buildin_rentitem: Nonexistant item %d requested.\n", nameid);
+ return 1;
+ }
+ }
+ else
+ {
+ ShowError("buildin_rentitem: invalid data type for argument #1 (%d).\n", data->type);
+ return 1;
+ }
+
+ seconds = script_getnum(st,3);
+ memset(&it, 0, sizeof(it));
+ it.nameid = nameid;
+ it.identify = 1;
+ it.expire_time = (unsigned int)(time(NULL) + seconds);
+
+ if( (flag = pc_additem(sd, &it, 1, LOG_TYPE_SCRIPT)) )
+ {
+ clif_additem(sd, 0, 0, flag);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * gets an item with someone's name inscribed [Skotlex]
+ * getinscribeditem item_num, character_name
+ * Returned Qty is always 1, only works on equip-able
+ * equipment
+ *------------------------------------------*/
+BUILDIN_FUNC(getnameditem)
+{
+ int nameid;
+ struct item item_tmp;
+ TBL_PC *sd, *tsd;
+ struct script_data *data;
+
+ sd = script_rid2sd(st);
+ if (sd == NULL)
+ { //Player not attached!
+ script_pushint(st,0);
+ return 0;
+ }
+
+ data=script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data == NULL)
+ { //Failed
+ script_pushint(st,0);
+ return 0;
+ }
+ nameid = item_data->nameid;
+ }else
+ nameid = conv_num(st,data);
+
+ if(!itemdb_exists(nameid)/* || itemdb_isstackable(nameid)*/)
+ { //Even though named stackable items "could" be risky, they are required for certain quests.
+ script_pushint(st,0);
+ return 0;
+ }
+
+ data=script_getdata(st,3);
+ get_val(st,data);
+ if( data_isstring(data) ) //Char Name
+ tsd=map_nick2sd(conv_str(st,data));
+ else //Char Id was given
+ tsd=map_charid2sd(conv_num(st,data));
+
+ if( tsd == NULL )
+ { //Failed
+ script_pushint(st,0);
+ return 0;
+ }
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=nameid;
+ item_tmp.amount=1;
+ item_tmp.identify=1;
+ item_tmp.card[0]=CARD0_CREATE; //we don't use 255! because for example SIGNED WEAPON shouldn't get TOP10 BS Fame bonus [Lupus]
+ item_tmp.card[2]=tsd->status.char_id;
+ item_tmp.card[3]=tsd->status.char_id >> 16;
+ if(pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT)) {
+ script_pushint(st,0);
+ return 0; //Failed to add item, we will not drop if they don't fit
+ }
+
+ script_pushint(st,1);
+ return 0;
+}
+
+/*==========================================
+ * gets a random item ID from an item group [Skotlex]
+ * groupranditem group_num
+ *------------------------------------------*/
+BUILDIN_FUNC(grouprandomitem)
+{
+ int group;
+
+ group = script_getnum(st,2);
+ script_pushint(st,itemdb_searchrandomid(group));
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(makeitem)
+{
+ int nameid,amount,flag = 0;
+ int x,y,m;
+ const char *mapname;
+ struct item item_tmp;
+ struct script_data *data;
+
+ data=script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data )
+ nameid=item_data->nameid;
+ else
+ nameid=UNKNOWN_ITEM_ID;
+ }else
+ nameid=conv_num(st,data);
+
+ amount=script_getnum(st,3);
+ mapname =script_getstr(st,4);
+ x =script_getnum(st,5);
+ y =script_getnum(st,6);
+
+ if(strcmp(mapname,"this")==0)
+ {
+ TBL_PC *sd;
+ sd = script_rid2sd(st);
+ if (!sd) return 0; //Failed...
+ m=sd->bl.m;
+ } else
+ m=map_mapname2mapid(mapname);
+
+ if(nameid<0) {
+ nameid = -nameid;
+ flag = 1;
+ }
+
+ if(nameid > 0) {
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid=nameid;
+ if(!flag)
+ item_tmp.identify=1;
+ else
+ item_tmp.identify=itemdb_isidentified(nameid);
+
+ map_addflooritem(&item_tmp,amount,m,x,y,0,0,0,0);
+ }
+
+ return 0;
+}
+
+
+/// Counts / deletes the current item given by idx.
+/// Used by buildin_delitem_search
+/// Relies on all input data being already fully valid.
+static void buildin_delitem_delete(struct map_session_data* sd, int idx, int* amount, bool delete_items)
+{
+ int delamount;
+ struct item* inv = &sd->status.inventory[idx];
+
+ delamount = ( amount[0] < inv->amount ) ? amount[0] : inv->amount;
+
+ if( delete_items )
+ {
+ if( sd->inventory_data[idx]->type == IT_PETEGG && inv->card[0] == CARD0_PET )
+ {// delete associated pet
+ intif_delete_petdata(MakeDWord(inv->card[1], inv->card[2]));
+ }
+ pc_delitem(sd, idx, delamount, 0, 0, LOG_TYPE_SCRIPT);
+ }
+
+ amount[0]-= delamount;
+}
+
+
+/// Searches for item(s) and checks, if there is enough of them.
+/// Used by delitem and delitem2
+/// Relies on all input data being already fully valid.
+/// @param exact_match will also match item attributes and cards, not just name id
+/// @return true when all items could be deleted, false when there were not enough items to delete
+static bool buildin_delitem_search(struct map_session_data* sd, struct item* it, bool exact_match)
+{
+ bool delete_items = false;
+ int i, amount, important;
+ struct item* inv;
+
+ // prefer always non-equipped items
+ it->equip = 0;
+
+ // when searching for nameid only, prefer additionally
+ if( !exact_match )
+ {
+ // non-refined items
+ it->refine = 0;
+ // card-less items
+ memset(it->card, 0, sizeof(it->card));
+ }
+
+ for(;;)
+ {
+ amount = it->amount;
+ important = 0;
+
+ // 1st pass -- less important items / exact match
+ for( i = 0; amount && i < ARRAYLENGTH(sd->status.inventory); i++ )
+ {
+ inv = &sd->status.inventory[i];
+
+ if( !inv->nameid || !sd->inventory_data[i] || inv->nameid != it->nameid )
+ {// wrong/invalid item
+ continue;
+ }
+
+ if( inv->equip != it->equip || inv->refine != it->refine )
+ {// not matching attributes
+ important++;
+ continue;
+ }
+
+ if( exact_match )
+ {
+ if( inv->identify != it->identify || inv->attribute != it->attribute || memcmp(inv->card, it->card, sizeof(inv->card)) )
+ {// not matching exact attributes
+ continue;
+ }
+ }
+ else
+ {
+ if( sd->inventory_data[i]->type == IT_PETEGG )
+ {
+ if( inv->card[0] == CARD0_PET && CheckForCharServer() )
+ {// pet which cannot be deleted
+ continue;
+ }
+ }
+ else if( memcmp(inv->card, it->card, sizeof(inv->card)) )
+ {// named/carded item
+ important++;
+ continue;
+ }
+ }
+
+ // count / delete item
+ buildin_delitem_delete(sd, i, &amount, delete_items);
+ }
+
+ // 2nd pass -- any matching item
+ if( amount == 0 || important == 0 )
+ {// either everything was already consumed or no items were skipped
+ ;
+ }
+ else for( i = 0; amount && i < ARRAYLENGTH(sd->status.inventory); i++ )
+ {
+ inv = &sd->status.inventory[i];
+
+ if( !inv->nameid || !sd->inventory_data[i] || inv->nameid != it->nameid )
+ {// wrong/invalid item
+ continue;
+ }
+
+ if( sd->inventory_data[i]->type == IT_PETEGG && inv->card[0] == CARD0_PET && CheckForCharServer() )
+ {// pet which cannot be deleted
+ continue;
+ }
+
+ if( exact_match )
+ {
+ if( inv->refine != it->refine || inv->identify != it->identify || inv->attribute != it->attribute || memcmp(inv->card, it->card, sizeof(inv->card)) )
+ {// not matching attributes
+ continue;
+ }
+ }
+
+ // count / delete item
+ buildin_delitem_delete(sd, i, &amount, delete_items);
+ }
+
+ if( amount )
+ {// not enough items
+ return false;
+ }
+ else if( delete_items )
+ {// we are done with the work
+ return true;
+ }
+ else
+ {// get rid of the items now
+ delete_items = true;
+ }
+ }
+}
+
+
+/// Deletes items from the target/attached player.
+/// Prioritizes ordinary items.
+///
+/// delitem <item id>,<amount>{,<account id>}
+/// delitem "<item name>",<amount>{,<account id>}
+BUILDIN_FUNC(delitem)
+{
+ TBL_PC *sd;
+ struct item it;
+ struct script_data *data;
+
+ if( script_hasdata(st,4) )
+ {
+ int account_id = script_getnum(st,4);
+ sd = map_id2sd(account_id); // <account id>
+ if( sd == NULL )
+ {
+ ShowError("script:delitem: player not found (AID=%d).\n", account_id);
+ st->state = END;
+ return 1;
+ }
+ }
+ else
+ {
+ sd = script_rid2sd(st);// attached player
+ if( sd == NULL )
+ return 0;
+ }
+
+ data = script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) )
+ {
+ const char* item_name = conv_str(st,data);
+ struct item_data* id = itemdb_searchname(item_name);
+ if( id == NULL )
+ {
+ ShowError("script:delitem: unknown item \"%s\".\n", item_name);
+ st->state = END;
+ return 1;
+ }
+ it.nameid = id->nameid;// "<item name>"
+ }
+ else
+ {
+ it.nameid = conv_num(st,data);// <item id>
+ if( !itemdb_exists( it.nameid ) )
+ {
+ ShowError("script:delitem: unknown item \"%d\".\n", it.nameid);
+ st->state = END;
+ return 1;
+ }
+ }
+
+ it.amount=script_getnum(st,3);
+
+ if( it.amount <= 0 )
+ return 0;// nothing to do
+
+ if( buildin_delitem_search(sd, &it, false) )
+ {// success
+ return 0;
+ }
+
+ ShowError("script:delitem: failed to delete %d items (AID=%d item_id=%d).\n", it.amount, sd->status.account_id, it.nameid);
+ st->state = END;
+ clif_scriptclose(sd, st->oid);
+ return 1;
+}
+
+/// Deletes items from the target/attached player.
+///
+/// delitem2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>}
+/// delitem2 "<Item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>}
+BUILDIN_FUNC(delitem2)
+{
+ TBL_PC *sd;
+ struct item it;
+ struct script_data *data;
+
+ if( script_hasdata(st,11) )
+ {
+ int account_id = script_getnum(st,11);
+ sd = map_id2sd(account_id); // <account id>
+ if( sd == NULL )
+ {
+ ShowError("script:delitem2: player not found (AID=%d).\n", account_id);
+ st->state = END;
+ return 1;
+ }
+ }
+ else
+ {
+ sd = script_rid2sd(st);// attached player
+ if( sd == NULL )
+ return 0;
+ }
+
+ data = script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) )
+ {
+ const char* item_name = conv_str(st,data);
+ struct item_data* id = itemdb_searchname(item_name);
+ if( id == NULL )
+ {
+ ShowError("script:delitem2: unknown item \"%s\".\n", item_name);
+ st->state = END;
+ return 1;
+ }
+ it.nameid = id->nameid;// "<item name>"
+ }
+ else
+ {
+ it.nameid = conv_num(st,data);// <item id>
+ if( !itemdb_exists( it.nameid ) )
+ {
+ ShowError("script:delitem: unknown item \"%d\".\n", it.nameid);
+ st->state = END;
+ return 1;
+ }
+ }
+
+ it.amount=script_getnum(st,3);
+ it.identify=script_getnum(st,4);
+ it.refine=script_getnum(st,5);
+ it.attribute=script_getnum(st,6);
+ it.card[0]=(short)script_getnum(st,7);
+ it.card[1]=(short)script_getnum(st,8);
+ it.card[2]=(short)script_getnum(st,9);
+ it.card[3]=(short)script_getnum(st,10);
+
+ if( it.amount <= 0 )
+ return 0;// nothing to do
+
+ if( buildin_delitem_search(sd, &it, true) )
+ {// success
+ return 0;
+ }
+
+ ShowError("script:delitem2: failed to delete %d items (AID=%d item_id=%d).\n", it.amount, sd->status.account_id, it.nameid);
+ st->state = END;
+ clif_scriptclose(sd, st->oid);
+ return 1;
+}
+
+/*==========================================
+ * Enables/Disables use of items while in an NPC [Skotlex]
+ *------------------------------------------*/
+BUILDIN_FUNC(enableitemuse)
+{
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+ if (sd)
+ sd->npc_item_flag = st->oid;
+ return 0;
+}
+
+BUILDIN_FUNC(disableitemuse)
+{
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+ if (sd)
+ sd->npc_item_flag = 0;
+ return 0;
+}
+
+/*==========================================
+ * return the basic stats of sd
+ * chk pc_readparam for available type
+ *------------------------------------------*/
+BUILDIN_FUNC(readparam)
+{
+ int type;
+ TBL_PC *sd;
+
+ type=script_getnum(st,2);
+ if( script_hasdata(st,3) )
+ sd=map_nick2sd(script_getstr(st,3));
+ else
+ sd=script_rid2sd(st);
+
+ if(sd==NULL){
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ script_pushint(st,pc_readparam(sd,type));
+
+ return 0;
+}
+
+/*==========================================
+ * Return charid identification
+ * return by @num :
+ * 0 : char_id
+ * 1 : party_id
+ * 2 : guild_id
+ * 3 : account_id
+ * 4 : bg_id
+ *------------------------------------------*/
+BUILDIN_FUNC(getcharid)
+{
+ int num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ if( script_hasdata(st,3) )
+ sd=map_nick2sd(script_getstr(st,3));
+ else
+ sd=script_rid2sd(st);
+
+ if(sd==NULL){
+ script_pushint(st,0); //return 0, according docs
+ return 0;
+ }
+
+ switch( num ) {
+ case 0: script_pushint(st,sd->status.char_id); break;
+ case 1: script_pushint(st,sd->status.party_id); break;
+ case 2: script_pushint(st,sd->status.guild_id); break;
+ case 3: script_pushint(st,sd->status.account_id); break;
+ case 4: script_pushint(st,sd->bg_id); break;
+ default:
+ ShowError("buildin_getcharid: invalid parameter (%d).\n", num);
+ script_pushint(st,0);
+ break;
+ }
+
+ return 0;
+}
+/*==========================================
+ * returns the GID of an NPC
+ *------------------------------------------*/
+BUILDIN_FUNC(getnpcid)
+{
+ int num = script_getnum(st,2);
+ struct npc_data* nd = NULL;
+
+ if( script_hasdata(st,3) )
+ {// unique npc name
+ if( ( nd = npc_name2id(script_getstr(st,3)) ) == NULL )
+ {
+ ShowError("buildin_getnpcid: No such NPC '%s'.\n", script_getstr(st,3));
+ script_pushint(st,0);
+ return 1;
+ }
+ }
+
+ switch (num) {
+ case 0:
+ script_pushint(st,nd ? nd->bl.id : st->oid);
+ break;
+ default:
+ ShowError("buildin_getnpcid: invalid parameter (%d).\n", num);
+ script_pushint(st,0);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Return the name of the party_id
+ * null if not found
+ *------------------------------------------*/
+BUILDIN_FUNC(getpartyname)
+{
+ int party_id;
+ struct party_data* p;
+
+ party_id = script_getnum(st,2);
+
+ if( ( p = party_search(party_id) ) != NULL )
+ {
+ script_pushstrcopy(st,p->party.name);
+ }
+ else
+ {
+ script_pushconststr(st,"null");
+ }
+ return 0;
+}
+
+/*==========================================
+ * Get the information of the members of a party by type
+ * @party_id, @type
+ * return by @type :
+ * - : nom des membres
+ * 1 : char_id des membres
+ * 2 : account_id des membres
+ *------------------------------------------*/
+BUILDIN_FUNC(getpartymember)
+{
+ struct party_data *p;
+ int i,j=0,type=0;
+
+ p=party_search(script_getnum(st,2));
+
+ if( script_hasdata(st,3) )
+ type=script_getnum(st,3);
+
+ if(p!=NULL){
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->party.member[i].account_id){
+ switch (type) {
+ case 2:
+ mapreg_setreg(reference_uid(add_str("$@partymemberaid"), j),p->party.member[i].account_id);
+ break;
+ case 1:
+ mapreg_setreg(reference_uid(add_str("$@partymembercid"), j),p->party.member[i].char_id);
+ break;
+ default:
+ mapreg_setregstr(reference_uid(add_str("$@partymembername$"), j),p->party.member[i].name);
+ }
+ j++;
+ }
+ }
+ }
+ mapreg_setreg(add_str("$@partymembercount"),j);
+
+ return 0;
+}
+
+/*==========================================
+ * Retrieves party leader. if flag is specified,
+ * return some of the leader data. Otherwise, return name.
+ *------------------------------------------*/
+BUILDIN_FUNC(getpartyleader)
+{
+ int party_id, type = 0, i=0;
+ struct party_data *p;
+
+ party_id=script_getnum(st,2);
+ if( script_hasdata(st,3) )
+ type=script_getnum(st,3);
+
+ p=party_search(party_id);
+
+ if (p) //Search leader
+ for(i = 0; i < MAX_PARTY && !p->party.member[i].leader; i++);
+
+ if (!p || i == MAX_PARTY) { //leader not found
+ if (type)
+ script_pushint(st,-1);
+ else
+ script_pushconststr(st,"null");
+ return 0;
+ }
+
+ switch (type) {
+ case 1: script_pushint(st,p->party.member[i].account_id); break;
+ case 2: script_pushint(st,p->party.member[i].char_id); break;
+ case 3: script_pushint(st,p->party.member[i].class_); break;
+ case 4: script_pushstrcopy(st,mapindex_id2name(p->party.member[i].map)); break;
+ case 5: script_pushint(st,p->party.member[i].lv); break;
+ default: script_pushstrcopy(st,p->party.member[i].name); break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Return the name of the @guild_id
+ * null if not found
+ *------------------------------------------*/
+BUILDIN_FUNC(getguildname)
+{
+ int guild_id;
+ struct guild* g;
+
+ guild_id = script_getnum(st,2);
+
+ if( ( g = guild_search(guild_id) ) != NULL )
+ {
+ script_pushstrcopy(st,g->name);
+ }
+ else
+ {
+ script_pushconststr(st,"null");
+ }
+ return 0;
+}
+
+/*==========================================
+ * Return the name of the guild master of @guild_id
+ * null if not found
+ *------------------------------------------*/
+BUILDIN_FUNC(getguildmaster)
+{
+ int guild_id;
+ struct guild* g;
+
+ guild_id = script_getnum(st,2);
+
+ if( ( g = guild_search(guild_id) ) != NULL )
+ {
+ script_pushstrcopy(st,g->member[0].name);
+ }
+ else
+ {
+ script_pushconststr(st,"null");
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(getguildmasterid)
+{
+ int guild_id;
+ struct guild* g;
+
+ guild_id = script_getnum(st,2);
+
+ if( ( g = guild_search(guild_id) ) != NULL )
+ {
+ script_pushint(st,g->member[0].char_id);
+ }
+ else
+ {
+ script_pushint(st,0);
+ }
+ return 0;
+}
+
+/*==========================================
+ * Get char string information by type :
+ * Return by @type :
+ * 0 : char_name
+ * 1 : party_name or ""
+ * 2 : guild_name or ""
+ * 3 : map_name
+ * - : ""
+ *------------------------------------------*/
+BUILDIN_FUNC(strcharinfo)
+{
+ TBL_PC *sd;
+ int num;
+ struct guild* g;
+ struct party_data* p;
+
+ sd=script_rid2sd(st);
+ if (!sd) { //Avoid crashing....
+ script_pushconststr(st,"");
+ return 0;
+ }
+ num=script_getnum(st,2);
+ switch(num){
+ case 0:
+ script_pushstrcopy(st,sd->status.name);
+ break;
+ case 1:
+ if( ( p = party_search(sd->status.party_id) ) != NULL )
+ {
+ script_pushstrcopy(st,p->party.name);
+ }
+ else
+ {
+ script_pushconststr(st,"");
+ }
+ break;
+ case 2:
+ if( ( g = guild_search(sd->status.guild_id) ) != NULL )
+ {
+ script_pushstrcopy(st,g->name);
+ }
+ else
+ {
+ script_pushconststr(st,"");
+ }
+ break;
+ case 3:
+ script_pushconststr(st,map[sd->bl.m].name);
+ break;
+ default:
+ ShowWarning("buildin_strcharinfo: unknown parameter.\n");
+ script_pushconststr(st,"");
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Get npc string information by type
+ * return by @type:
+ * 0 : name
+ * 1 : str#
+ * 2 : #str
+ * 3 : ::str
+ * 4 : map name
+ *------------------------------------------*/
+BUILDIN_FUNC(strnpcinfo)
+{
+ TBL_NPC* nd;
+ int num;
+ char *buf,*name=NULL;
+
+ nd = map_id2nd(st->oid);
+ if (!nd) {
+ script_pushconststr(st, "");
+ return 0;
+ }
+
+ num = script_getnum(st,2);
+ switch(num){
+ case 0: // display name
+ name = aStrdup(nd->name);
+ break;
+ case 1: // visible part of display name
+ if((buf = strchr(nd->name,'#')) != NULL)
+ {
+ name = aStrdup(nd->name);
+ name[buf - nd->name] = 0;
+ } else // Return the name, there is no '#' present
+ name = aStrdup(nd->name);
+ break;
+ case 2: // # fragment
+ if((buf = strchr(nd->name,'#')) != NULL)
+ name = aStrdup(buf+1);
+ break;
+ case 3: // unique name
+ name = aStrdup(nd->exname);
+ break;
+ case 4: // map name
+ name = aStrdup(map[nd->bl.m].name);
+ break;
+ }
+
+ if(name)
+ script_pushstr(st, name);
+ else
+ script_pushconststr(st, "");
+
+ return 0;
+}
+
+
+// aegis->athena slot position conversion table
+static unsigned int equip[] = {EQP_HEAD_TOP,EQP_ARMOR,EQP_HAND_L,EQP_HAND_R,EQP_GARMENT,EQP_SHOES,EQP_ACC_L,EQP_ACC_R,EQP_HEAD_MID,EQP_HEAD_LOW};
+
+/*==========================================
+ * GetEquipID(Pos); Pos: 1-10
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipid)
+{
+ int i, num;
+ TBL_PC* sd;
+ struct item_data* item;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ num = script_getnum(st,2) - 1;
+ if( num < 0 || num >= ARRAYLENGTH(equip) )
+ {
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ // get inventory position of item
+ i = pc_checkequip(sd,equip[num]);
+ if( i < 0 )
+ {
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ item = sd->inventory_data[i];
+ if( item != 0 )
+ script_pushint(st,item->nameid);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Get the equipement name at pos
+ * return item jname or ""
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipname)
+{
+ int i, num;
+ TBL_PC* sd;
+ struct item_data* item;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ num = script_getnum(st,2) - 1;
+ if( num < 0 || num >= ARRAYLENGTH(equip) )
+ {
+ script_pushconststr(st,"");
+ return 0;
+ }
+
+ // get inventory position of item
+ i = pc_checkequip(sd,equip[num]);
+ if( i < 0 )
+ {
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ item = sd->inventory_data[i];
+ if( item != 0 )
+ script_pushstrcopy(st,item->jname);
+ else
+ script_pushconststr(st,"");
+
+ return 0;
+}
+
+/*==========================================
+ * getbrokenid [Valaris]
+ *------------------------------------------*/
+BUILDIN_FUNC(getbrokenid)
+{
+ int i,num,id=0,brokencounter=0;
+ TBL_PC *sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ num=script_getnum(st,2);
+ for(i=0; i<MAX_INVENTORY; i++) {
+ if(sd->status.inventory[i].attribute){
+ brokencounter++;
+ if(num==brokencounter){
+ id=sd->status.inventory[i].nameid;
+ break;
+ }
+ }
+ }
+
+ script_pushint(st,id);
+
+ return 0;
+}
+
+/*==========================================
+ * repair [Valaris]
+ *------------------------------------------*/
+BUILDIN_FUNC(repair)
+{
+ int i,num;
+ int repaircounter=0;
+ TBL_PC *sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ num=script_getnum(st,2);
+ for(i=0; i<MAX_INVENTORY; i++) {
+ if(sd->status.inventory[i].attribute){
+ repaircounter++;
+ if(num==repaircounter){
+ sd->status.inventory[i].attribute=0;
+ clif_equiplist(sd);
+ clif_produceeffect(sd, 0, sd->status.inventory[i].nameid);
+ clif_misceffect(&sd->bl, 3);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * repairall
+ *------------------------------------------*/
+BUILDIN_FUNC(repairall)
+{
+ int i, repaircounter = 0;
+ TBL_PC *sd;
+
+ sd = script_rid2sd(st);
+ if(sd == NULL)
+ return 0;
+
+ for(i = 0; i < MAX_INVENTORY; i++)
+ {
+ if(sd->status.inventory[i].nameid && sd->status.inventory[i].attribute)
+ {
+ sd->status.inventory[i].attribute = 0;
+ clif_produceeffect(sd,0,sd->status.inventory[i].nameid);
+ repaircounter++;
+ }
+ }
+
+ if(repaircounter)
+ {
+ clif_misceffect(&sd->bl, 3);
+ clif_equiplist(sd);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Chk if player have something equiped at pos
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipisequiped)
+{
+ int i = -1,num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+
+ if(i >= 0)
+ script_pushint(st,1);
+ else
+ script_pushint(st,0);
+ return 0;
+}
+
+/*==========================================
+ * Chk if the player have something equiped at pos
+ * if so chk if this item ain't marked not refinable or rental
+ * return (npc)
+ * 1 : true
+ * 0 : false
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipisenableref)
+{
+ int i = -1,num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if( num > 0 && num <= ARRAYLENGTH(equip) )
+ i = pc_checkequip(sd,equip[num-1]);
+ if( i >= 0 && sd->inventory_data[i] && !sd->inventory_data[i]->flag.no_refine && !sd->status.inventory[i].expire_time )
+ script_pushint(st,1);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Chk if the item equiped at pos is identify (huh ?)
+ * return (npc)
+ * 1 : true
+ * 0 : false
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipisidentify)
+{
+ int i = -1,num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0)
+ script_pushint(st,sd->status.inventory[i].identify);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Get the item refined value at pos
+ * return (npc)
+ * x : refine amount
+ * 0 : false (not refined)
+ *------------------------------------------*/
+BUILDIN_FUNC(getequiprefinerycnt)
+{
+ int i = -1,num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0)
+ script_pushint(st,sd->status.inventory[i].refine);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Get the weapon level value at pos
+ * (pos should normally only be EQI_HAND_L or EQI_HAND_R)
+ * return (npc)
+ * x : weapon level
+ * 0 : false
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipweaponlv)
+{
+ int i = -1,num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0 && sd->inventory_data[i])
+ script_pushint(st,sd->inventory_data[i]->wlv);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Get the item refine chance (from refine.txt) for item at pos
+ * return (npc)
+ * x : refine chance
+ * 0 : false (max refine level or unequip..)
+ *------------------------------------------*/
+BUILDIN_FUNC(getequippercentrefinery)
+{
+ int i = -1,num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0 && sd->status.inventory[i].nameid && sd->status.inventory[i].refine < MAX_REFINE)
+ script_pushint(st,status_get_refine_chance(itemdb_wlv(sd->status.inventory[i].nameid), (int)sd->status.inventory[i].refine));
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Refine +1 item at pos and log and display refine
+ *------------------------------------------*/
+BUILDIN_FUNC(successrefitem)
+{
+ int i=-1,num,ep;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0) {
+ ep=sd->status.inventory[i].equip;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ log_pick_pc(sd, LOG_TYPE_SCRIPT, -1, &sd->status.inventory[i]);
+
+ sd->status.inventory[i].refine++;
+ pc_unequipitem(sd,i,2); // status calc will happen in pc_equipitem() below
+
+ clif_refine(sd->fd,0,i,sd->status.inventory[i].refine);
+ clif_delitem(sd,i,1,3);
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ log_pick_pc(sd, LOG_TYPE_SCRIPT, 1, &sd->status.inventory[i]);
+
+ clif_additem(sd,i,1,0);
+ pc_equipitem(sd,i,ep);
+ clif_misceffect(&sd->bl,3);
+ if(sd->status.inventory[i].refine == MAX_REFINE &&
+ sd->status.inventory[i].card[0] == CARD0_FORGE &&
+ sd->status.char_id == (int)MakeDWord(sd->status.inventory[i].card[2],sd->status.inventory[i].card[3])
+ ){ // Fame point system [DracoRPG]
+ switch (sd->inventory_data[i]->wlv){
+ case 1:
+ pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point
+ break;
+ case 2:
+ pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point
+ break;
+ case 3:
+ pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Show a failed Refine +1 attempt
+ *------------------------------------------*/
+BUILDIN_FUNC(failedrefitem)
+{
+ int i=-1,num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0) {
+ sd->status.inventory[i].refine = 0;
+ pc_unequipitem(sd,i,3); //recalculate bonus
+ clif_refine(sd->fd,1,i,sd->status.inventory[i].refine); //notify client of failure
+
+ pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT);
+
+ clif_misceffect(&sd->bl,2); // display failure effect
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Downgrades an Equipment Part by -1 . [Masao]
+ *------------------------------------------*/
+BUILDIN_FUNC(downrefitem)
+{
+ int i = -1,num,ep;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i = pc_checkequip(sd,equip[num-1]);
+ if(i >= 0) {
+ ep = sd->status.inventory[i].equip;
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ log_pick_pc(sd, LOG_TYPE_SCRIPT, -1, &sd->status.inventory[i]);
+
+ sd->status.inventory[i].refine++;
+ pc_unequipitem(sd,i,2); // status calc will happen in pc_equipitem() below
+
+ clif_refine(sd->fd,2,i,sd->status.inventory[i].refine = sd->status.inventory[i].refine - 2);
+ clif_delitem(sd,i,1,3);
+
+ //Logs items, got from (N)PC scripts [Lupus]
+ log_pick_pc(sd, LOG_TYPE_SCRIPT, 1, &sd->status.inventory[i]);
+
+ clif_additem(sd,i,1,0);
+ pc_equipitem(sd,i,ep);
+ clif_misceffect(&sd->bl,2);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(statusup)
+{
+ int type;
+ TBL_PC *sd;
+
+ type=script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pc_statusup(sd,type);
+
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(statusup2)
+{
+ int type,val;
+ TBL_PC *sd;
+
+ type=script_getnum(st,2);
+ val=script_getnum(st,3);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pc_statusup2(sd,type,val);
+
+ return 0;
+}
+
+/// See 'doc/item_bonus.txt'
+///
+/// bonus <bonus type>,<val1>;
+/// bonus2 <bonus type>,<val1>,<val2>;
+/// bonus3 <bonus type>,<val1>,<val2>,<val3>;
+/// bonus4 <bonus type>,<val1>,<val2>,<val3>,<val4>;
+/// bonus5 <bonus type>,<val1>,<val2>,<val3>,<val4>,<val5>;
+BUILDIN_FUNC(bonus)
+{
+ int type;
+ int val1;
+ int val2 = 0;
+ int val3 = 0;
+ int val4 = 0;
+ int val5 = 0;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0; // no player attached
+
+ type = script_getnum(st,2);
+ switch( type ) {
+ case SP_AUTOSPELL:
+ case SP_AUTOSPELL_WHENHIT:
+ case SP_AUTOSPELL_ONSKILL:
+ case SP_SKILL_ATK:
+ case SP_SKILL_HEAL:
+ case SP_SKILL_HEAL2:
+ case SP_ADD_SKILL_BLOW:
+ case SP_CASTRATE:
+ case SP_ADDEFF_ONSKILL:
+ case SP_SKILL_USE_SP_RATE:
+ case SP_SKILL_COOLDOWN:
+ case SP_SKILL_FIXEDCAST:
+ case SP_SKILL_VARIABLECAST:
+ case SP_VARCASTRATE:
+ case SP_SKILL_USE_SP:
+ // these bonuses support skill names
+ val1 = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) );
+ break;
+ default:
+ val1 = script_getnum(st,3);
+ break;
+ }
+
+ switch( script_lastdata(st)-2 ) {
+ case 1:
+ pc_bonus(sd, type, val1);
+ break;
+ case 2:
+ val2 = script_getnum(st,4);
+ pc_bonus2(sd, type, val1, val2);
+ break;
+ case 3:
+ val2 = script_getnum(st,4);
+ val3 = script_getnum(st,5);
+ pc_bonus3(sd, type, val1, val2, val3);
+ break;
+ case 4:
+ if( type == SP_AUTOSPELL_ONSKILL && script_isstring(st,4) )
+ val2 = skill_name2id(script_getstr(st,4)); // 2nd value can be skill name
+ else
+ val2 = script_getnum(st,4);
+
+ val3 = script_getnum(st,5);
+ val4 = script_getnum(st,6);
+ pc_bonus4(sd, type, val1, val2, val3, val4);
+ break;
+ case 5:
+ if( type == SP_AUTOSPELL_ONSKILL && script_isstring(st,4) )
+ val2 = skill_name2id(script_getstr(st,4)); // 2nd value can be skill name
+ else
+ val2 = script_getnum(st,4);
+
+ val3 = script_getnum(st,5);
+ val4 = script_getnum(st,6);
+ val5 = script_getnum(st,7);
+ pc_bonus5(sd, type, val1, val2, val3, val4, val5);
+ break;
+ default:
+ ShowDebug("buildin_bonus: unexpected number of arguments (%d)\n", (script_lastdata(st) - 1));
+ break;
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(autobonus)
+{
+ unsigned int dur;
+ short rate;
+ short atk_type = 0;
+ TBL_PC* sd;
+ const char *bonus_script, *other_script = NULL;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0; // no player attached
+
+ if( sd->state.autobonus&sd->status.inventory[current_equip_item_index].equip )
+ return 0;
+
+ rate = script_getnum(st,3);
+ dur = script_getnum(st,4);
+ bonus_script = script_getstr(st,2);
+ if( !rate || !dur || !bonus_script )
+ return 0;
+
+ if( script_hasdata(st,5) )
+ atk_type = script_getnum(st,5);
+ if( script_hasdata(st,6) )
+ other_script = script_getstr(st,6);
+
+ if( pc_addautobonus(sd->autobonus,ARRAYLENGTH(sd->autobonus),
+ bonus_script,rate,dur,atk_type,other_script,sd->status.inventory[current_equip_item_index].equip,false) )
+ {
+ script_add_autobonus(bonus_script);
+ if( other_script )
+ script_add_autobonus(other_script);
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(autobonus2)
+{
+ unsigned int dur;
+ short rate;
+ short atk_type = 0;
+ TBL_PC* sd;
+ const char *bonus_script, *other_script = NULL;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0; // no player attached
+
+ if( sd->state.autobonus&sd->status.inventory[current_equip_item_index].equip )
+ return 0;
+
+ rate = script_getnum(st,3);
+ dur = script_getnum(st,4);
+ bonus_script = script_getstr(st,2);
+ if( !rate || !dur || !bonus_script )
+ return 0;
+
+ if( script_hasdata(st,5) )
+ atk_type = script_getnum(st,5);
+ if( script_hasdata(st,6) )
+ other_script = script_getstr(st,6);
+
+ if( pc_addautobonus(sd->autobonus2,ARRAYLENGTH(sd->autobonus2),
+ bonus_script,rate,dur,atk_type,other_script,sd->status.inventory[current_equip_item_index].equip,false) )
+ {
+ script_add_autobonus(bonus_script);
+ if( other_script )
+ script_add_autobonus(other_script);
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(autobonus3)
+{
+ unsigned int dur;
+ short rate,atk_type;
+ TBL_PC* sd;
+ const char *bonus_script, *other_script = NULL;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0; // no player attached
+
+ if( sd->state.autobonus&sd->status.inventory[current_equip_item_index].equip )
+ return 0;
+
+ rate = script_getnum(st,3);
+ dur = script_getnum(st,4);
+ atk_type = ( script_isstring(st,5) ? skill_name2id(script_getstr(st,5)) : script_getnum(st,5) );
+ bonus_script = script_getstr(st,2);
+ if( !rate || !dur || !atk_type || !bonus_script )
+ return 0;
+
+ if( script_hasdata(st,6) )
+ other_script = script_getstr(st,6);
+
+ if( pc_addautobonus(sd->autobonus3,ARRAYLENGTH(sd->autobonus3),
+ bonus_script,rate,dur,atk_type,other_script,sd->status.inventory[current_equip_item_index].equip,true) )
+ {
+ script_add_autobonus(bonus_script);
+ if( other_script )
+ script_add_autobonus(other_script);
+ }
+
+ return 0;
+}
+
+/// Changes the level of a player skill.
+/// <flag> defaults to 1
+/// <flag>=0 : set the level of the skill
+/// <flag>=1 : set the temporary level of the skill
+/// <flag>=2 : add to the level of the skill
+///
+/// skill <skill id>,<level>,<flag>
+/// skill <skill id>,<level>
+/// skill "<skill name>",<level>,<flag>
+/// skill "<skill name>",<level>
+BUILDIN_FUNC(skill)
+{
+ int id;
+ int level;
+ int flag = 1;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ level = script_getnum(st,3);
+ if( script_hasdata(st,4) )
+ flag = script_getnum(st,4);
+ pc_skill(sd, id, level, flag);
+
+ return 0;
+}
+
+/// Changes the level of a player skill.
+/// like skill, but <flag> defaults to 2
+///
+/// addtoskill <skill id>,<amount>,<flag>
+/// addtoskill <skill id>,<amount>
+/// addtoskill "<skill name>",<amount>,<flag>
+/// addtoskill "<skill name>",<amount>
+///
+/// @see skill
+BUILDIN_FUNC(addtoskill)
+{
+ int id;
+ int level;
+ int flag = 2;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ level = script_getnum(st,3);
+ if( script_hasdata(st,4) )
+ flag = script_getnum(st,4);
+ pc_skill(sd, id, level, flag);
+
+ return 0;
+}
+
+/// Increases the level of a guild skill.
+///
+/// guildskill <skill id>,<amount>;
+/// guildskill "<skill name>",<amount>;
+BUILDIN_FUNC(guildskill)
+{
+ int id;
+ int level;
+ TBL_PC* sd;
+ int i;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ level = script_getnum(st,3);
+ for( i=0; i < level; i++ )
+ guild_skillup(sd, id);
+
+ return 0;
+}
+
+/// Returns the level of the player skill.
+///
+/// getskilllv(<skill id>) -> <level>
+/// getskilllv("<skill name>") -> <level>
+BUILDIN_FUNC(getskilllv)
+{
+ int id;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ script_pushint(st, pc_checkskill(sd,id));
+
+ return 0;
+}
+
+/// Returns the level of the guild skill.
+///
+/// getgdskilllv(<guild id>,<skill id>) -> <level>
+/// getgdskilllv(<guild id>,"<skill name>") -> <level>
+BUILDIN_FUNC(getgdskilllv)
+{
+ int guild_id;
+ uint16 skill_id;
+ struct guild* g;
+
+ guild_id = script_getnum(st,2);
+ skill_id = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) );
+ g = guild_search(guild_id);
+ if( g == NULL )
+ script_pushint(st, -1);
+ else
+ script_pushint(st, guild_checkskill(g,skill_id));
+
+ return 0;
+}
+
+/// Returns the 'basic_skill_check' setting.
+/// This config determines if the server checks the skill level of NV_BASIC
+/// before allowing the basic actions.
+///
+/// basicskillcheck() -> <bool>
+BUILDIN_FUNC(basicskillcheck)
+{
+ script_pushint(st, battle_config.basic_skill_check);
+ return 0;
+}
+
+/// Returns the GM level of the player.
+///
+/// getgmlevel() -> <level>
+BUILDIN_FUNC(getgmlevel)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ script_pushint(st, pc_get_group_level(sd));
+
+ return 0;
+}
+
+/// Returns the group ID of the player.
+///
+/// getgroupid() -> <int>
+BUILDIN_FUNC(getgroupid)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if (sd == NULL)
+ return 1; // no player attached, report source
+ script_pushint(st, pc_get_group_id(sd));
+
+ return 0;
+}
+
+/// Terminates the execution of this script instance.
+///
+/// end
+BUILDIN_FUNC(end)
+{
+ st->state = END;
+ return 0;
+}
+
+/// Checks if the player has that effect state (option).
+///
+/// checkoption(<option>) -> <bool>
+BUILDIN_FUNC(checkoption)
+{
+ int option;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ option = script_getnum(st,2);
+ if( sd->sc.option&option )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Checks if the player is in that body state (opt1).
+///
+/// checkoption1(<opt1>) -> <bool>
+BUILDIN_FUNC(checkoption1)
+{
+ int opt1;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ opt1 = script_getnum(st,2);
+ if( sd->sc.opt1 == opt1 )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Checks if the player has that health state (opt2).
+///
+/// checkoption2(<opt2>) -> <bool>
+BUILDIN_FUNC(checkoption2)
+{
+ int opt2;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ opt2 = script_getnum(st,2);
+ if( sd->sc.opt2&opt2 )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Changes the effect state (option) of the player.
+/// <flag> defaults to 1
+/// <flag>=0 : removes the option
+/// <flag>=other : adds the option
+///
+/// setoption <option>,<flag>;
+/// setoption <option>;
+BUILDIN_FUNC(setoption)
+{
+ int option;
+ int flag = 1;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ option = script_getnum(st,2);
+ if( script_hasdata(st,3) )
+ flag = script_getnum(st,3);
+ else if( !option ){// Request to remove everything.
+ flag = 0;
+ option = OPTION_FALCON|OPTION_RIDING;
+#ifndef NEW_CARTS
+ option |= OPTION_CART;
+#endif
+ }
+ if( flag ){// Add option
+ if( option&OPTION_WEDDING && !battle_config.wedding_modifydisplay )
+ option &= ~OPTION_WEDDING;// Do not show the wedding sprites
+ pc_setoption(sd, sd->sc.option|option);
+ } else// Remove option
+ pc_setoption(sd, sd->sc.option&~option);
+
+ return 0;
+}
+
+/// Returns if the player has a cart.
+///
+/// checkcart() -> <bool>
+///
+/// @author Valaris
+BUILDIN_FUNC(checkcart)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( pc_iscarton(sd) )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Sets the cart of the player.
+/// <type> defaults to 1
+/// <type>=0 : removes the cart
+/// <type>=1 : Normal cart
+/// <type>=2 : Wooden cart
+/// <type>=3 : Covered cart with flowers and ferns
+/// <type>=4 : Wooden cart with a Panda doll on the back
+/// <type>=5 : Normal cart with bigger wheels, a roof and a banner on the back
+///
+/// setcart <type>;
+/// setcart;
+BUILDIN_FUNC(setcart)
+{
+ int type = 1;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( script_hasdata(st,2) )
+ type = script_getnum(st,2);
+ pc_setcart(sd, type);
+
+ return 0;
+}
+
+/// Returns if the player has a falcon.
+///
+/// checkfalcon() -> <bool>
+///
+/// @author Valaris
+BUILDIN_FUNC(checkfalcon)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( pc_isfalcon(sd) )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Sets if the player has a falcon or not.
+/// <flag> defaults to 1
+///
+/// setfalcon <flag>;
+/// setfalcon;
+BUILDIN_FUNC(setfalcon)
+{
+ int flag = 1;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( script_hasdata(st,2) )
+ flag = script_getnum(st,2);
+
+ pc_setfalcon(sd, flag);
+
+ return 0;
+}
+
+/// Returns if the player is riding.
+///
+/// checkriding() -> <bool>
+///
+/// @author Valaris
+BUILDIN_FUNC(checkriding)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( pc_isriding(sd) || pc_isridingwug(sd) || pc_isridingdragon(sd) )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Sets if the player is riding.
+/// <flag> defaults to 1
+///
+/// setriding <flag>;
+/// setriding;
+BUILDIN_FUNC(setriding)
+{
+ int flag = 1;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( script_hasdata(st,2) )
+ flag = script_getnum(st,2);
+ pc_setriding(sd, flag);
+
+ return 0;
+}
+
+/// Returns if the player has a warg.
+///
+/// checkwug() -> <bool>
+///
+BUILDIN_FUNC(checkwug)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( pc_iswug(sd) || pc_isridingwug(sd) )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Returns if the player is wearing MADO Gear.
+///
+/// checkmadogear() -> <bool>
+///
+BUILDIN_FUNC(checkmadogear)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( pc_ismadogear(sd) )
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Sets if the player is riding MADO Gear.
+/// <flag> defaults to 1
+///
+/// setmadogear <flag>;
+/// setmadogear;
+BUILDIN_FUNC(setmadogear)
+{
+ int flag = 1;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ if( script_hasdata(st,2) )
+ flag = script_getnum(st,2);
+ pc_setmadogear(sd, flag);
+
+ return 0;
+}
+
+/// Sets the save point of the player.
+///
+/// save "<map name>",<x>,<y>
+/// savepoint "<map name>",<x>,<y>
+BUILDIN_FUNC(savepoint)
+{
+ int x;
+ int y;
+ short map;
+ const char* str;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached, report source
+
+ str = script_getstr(st, 2);
+ x = script_getnum(st,3);
+ y = script_getnum(st,4);
+ map = mapindex_name2id(str);
+ if( map )
+ pc_setsavepoint(sd, map, x, y);
+
+ return 0;
+}
+
+/*==========================================
+ * GetTimeTick(0: System Tick, 1: Time Second Tick)
+ *------------------------------------------*/
+BUILDIN_FUNC(gettimetick) /* Asgard Version */
+{
+ int type;
+ time_t timer;
+ struct tm *t;
+
+ type=script_getnum(st,2);
+
+ switch(type){
+ case 2:
+ //type 2:(Get the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC
+ // from the system clock.)
+ script_pushint(st,(int)time(NULL));
+ break;
+ case 1:
+ //type 1:(Second Ticks: 0-86399, 00:00:00-23:59:59)
+ time(&timer);
+ t=localtime(&timer);
+ script_pushint(st,((t->tm_hour)*3600+(t->tm_min)*60+t->tm_sec));
+ break;
+ case 0:
+ default:
+ //type 0:(System Ticks)
+ script_pushint(st,gettick());
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * GetTime(Type);
+ * 1: Sec 2: Min 3: Hour
+ * 4: WeekDay 5: MonthDay 6: Month
+ * 7: Year
+ *------------------------------------------*/
+BUILDIN_FUNC(gettime) /* Asgard Version */
+{
+ int type;
+ time_t timer;
+ struct tm *t;
+
+ type=script_getnum(st,2);
+
+ time(&timer);
+ t=localtime(&timer);
+
+ switch(type){
+ case 1://Sec(0~59)
+ script_pushint(st,t->tm_sec);
+ break;
+ case 2://Min(0~59)
+ script_pushint(st,t->tm_min);
+ break;
+ case 3://Hour(0~23)
+ script_pushint(st,t->tm_hour);
+ break;
+ case 4://WeekDay(0~6)
+ script_pushint(st,t->tm_wday);
+ break;
+ case 5://MonthDay(01~31)
+ script_pushint(st,t->tm_mday);
+ break;
+ case 6://Month(01~12)
+ script_pushint(st,t->tm_mon+1);
+ break;
+ case 7://Year(20xx)
+ script_pushint(st,t->tm_year+1900);
+ break;
+ case 8://Year Day(01~366)
+ script_pushint(st,t->tm_yday+1);
+ break;
+ default://(format error)
+ script_pushint(st,-1);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * GetTimeStr("TimeFMT", Length);
+ *------------------------------------------*/
+BUILDIN_FUNC(gettimestr)
+{
+ char *tmpstr;
+ const char *fmtstr;
+ int maxlen;
+ time_t now = time(NULL);
+
+ fmtstr=script_getstr(st,2);
+ maxlen=script_getnum(st,3);
+
+ tmpstr=(char *)aMalloc((maxlen+1)*sizeof(char));
+ strftime(tmpstr,maxlen,fmtstr,localtime(&now));
+ tmpstr[maxlen]='\0';
+
+ script_pushstr(st,tmpstr);
+ return 0;
+}
+
+/*==========================================
+ * Open player storage
+ *------------------------------------------*/
+BUILDIN_FUNC(openstorage)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ storage_storageopen(sd);
+ return 0;
+}
+
+BUILDIN_FUNC(guildopenstorage)
+{
+ TBL_PC* sd;
+ int ret;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ ret = storage_guild_storageopen(sd);
+ script_pushint(st,ret);
+ return 0;
+}
+
+/*==========================================
+ * Make player use a skill trought item usage
+ *------------------------------------------*/
+/// itemskill <skill id>,<level>
+/// itemskill "<skill name>",<level>
+BUILDIN_FUNC(itemskill)
+{
+ int id;
+ int lv;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL || sd->ud.skilltimer != INVALID_TIMER )
+ return 0;
+
+ id = ( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ lv = script_getnum(st,3);
+
+ sd->skillitem=id;
+ sd->skillitemlv=lv;
+ clif_item_skill(sd,id,lv);
+ return 0;
+}
+/*==========================================
+ * Attempt to create an item
+ *------------------------------------------*/
+BUILDIN_FUNC(produce)
+{
+ int trigger;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ trigger=script_getnum(st,2);
+ clif_skill_produce_mix_list(sd, -1, trigger);
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(cooking)
+{
+ int trigger;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ trigger=script_getnum(st,2);
+ clif_cooking_list(sd, trigger, AM_PHARMACY, 1, 1);
+ return 0;
+}
+/*==========================================
+ * Create a pet
+ *------------------------------------------*/
+BUILDIN_FUNC(makepet)
+{
+ TBL_PC* sd;
+ int id,pet_id;
+
+ id=script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pet_id = search_petDB_index(id, PET_CLASS);
+
+ if (pet_id < 0)
+ pet_id = search_petDB_index(id, PET_EGG);
+ if (pet_id >= 0 && sd) {
+ sd->catch_target_class = pet_db[pet_id].class_;
+ intif_create_pet(
+ sd->status.account_id, sd->status.char_id,
+ (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv,
+ (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate,
+ 100, 0, 1, pet_db[pet_id].jname);
+ }
+
+ return 0;
+}
+/*==========================================
+ * Give player exp base,job * quest_exp_rate/100
+ *------------------------------------------*/
+BUILDIN_FUNC(getexp)
+{
+ TBL_PC* sd;
+ int base=0,job=0;
+ double bonus;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ base=script_getnum(st,2);
+ job =script_getnum(st,3);
+ if(base<0 || job<0)
+ return 0;
+
+ // bonus for npc-given exp
+ bonus = battle_config.quest_exp_rate / 100.;
+ base = (int) cap_value(base * bonus, 0, INT_MAX);
+ job = (int) cap_value(job * bonus, 0, INT_MAX);
+
+ pc_gainexp(sd, NULL, base, job, true);
+
+ return 0;
+}
+
+/*==========================================
+ * Gain guild exp [Celest]
+ *------------------------------------------*/
+BUILDIN_FUNC(guildgetexp)
+{
+ TBL_PC* sd;
+ int exp;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ exp = script_getnum(st,2);
+ if(exp < 0)
+ return 0;
+ if(sd && sd->status.guild_id > 0)
+ guild_getexp (sd, exp);
+
+ return 0;
+}
+
+/*==========================================
+ * Changes the guild master of a guild [Skotlex]
+ *------------------------------------------*/
+BUILDIN_FUNC(guildchangegm)
+{
+ TBL_PC *sd;
+ int guild_id;
+ const char *name;
+
+ guild_id = script_getnum(st,2);
+ name = script_getstr(st,3);
+ sd=map_nick2sd(name);
+
+ if (!sd)
+ script_pushint(st,0);
+ else
+ script_pushint(st,guild_gm_change(guild_id, sd));
+
+ return 0;
+}
+
+/*==========================================
+ * Spawn a monster :
+ @mapn,x,y : location
+ @str : monster name
+ @class_ : mob_id
+ @amount : nb to spawn
+ @event : event to attach to mob
+ *------------------------------------------*/
+BUILDIN_FUNC(monster)
+{
+ const char* mapn = script_getstr(st,2);
+ int x = script_getnum(st,3);
+ int y = script_getnum(st,4);
+ const char* str = script_getstr(st,5);
+ int class_ = script_getnum(st,6);
+ int amount = script_getnum(st,7);
+ const char* event = "";
+ unsigned int size = SZ_SMALL;
+ unsigned int ai = AI_NONE;
+
+ struct map_session_data* sd;
+ int16 m;
+
+ if (script_hasdata(st, 8))
+ {
+ event = script_getstr(st, 8);
+ check_event(st, event);
+ }
+
+ if (script_hasdata(st, 9))
+ {
+ size = script_getnum(st, 9);
+ if (size > 3)
+ {
+ ShowWarning("buildin_monster: Attempted to spawn non-existing size %d for monster class %d\n", size, class_);
+ return 1;
+ }
+ }
+
+ if (script_hasdata(st, 10))
+ {
+ ai = script_getnum(st, 10);
+ if (ai > 4)
+ {
+ ShowWarning("buildin_monster: Attempted to spawn non-existing ai %d for monster class %d\n", ai, class_);
+ return 1;
+ }
+ }
+
+ if (class_ >= 0 && !mobdb_checkid(class_))
+ {
+ ShowWarning("buildin_monster: Attempted to spawn non-existing monster class %d\n", class_);
+ return 1;
+ }
+
+ sd = map_id2sd(st->rid);
+
+ if (sd && strcmp(mapn, "this") == 0)
+ m = sd->bl.m;
+ else
+ {
+ m = map_mapname2mapid(mapn);
+ if (map[m].flag.src4instance && st->instance_id)
+ { // Try to redirect to the instance map, not the src map
+ if ((m = instance_mapid2imapid(m, st->instance_id)) < 0)
+ {
+ ShowError("buildin_monster: Trying to spawn monster (%d) on instance map (%s) without instance attached.\n", class_, mapn);
+ return 1;
+ }
+ }
+ }
+
+ mob_once_spawn(sd, m, x, y, str, class_, amount, event, size, ai);
+ return 0;
+}
+/*==========================================
+ * Request List of Monster Drops
+ *------------------------------------------*/
+BUILDIN_FUNC(getmobdrops)
+{
+ int class_ = script_getnum(st,2);
+ int i, j = 0;
+ struct mob_db *mob;
+
+ if( !mobdb_checkid(class_) )
+ {
+ script_pushint(st, 0);
+ return 0;
+ }
+
+ mob = mob_db(class_);
+
+ for( i = 0; i < MAX_MOB_DROP; i++ )
+ {
+ if( mob->dropitem[i].nameid < 1 )
+ continue;
+ if( itemdb_exists(mob->dropitem[i].nameid) == NULL )
+ continue;
+
+ mapreg_setreg(reference_uid(add_str("$@MobDrop_item"), j), mob->dropitem[i].nameid);
+ mapreg_setreg(reference_uid(add_str("$@MobDrop_rate"), j), mob->dropitem[i].p);
+
+ j++;
+ }
+
+ mapreg_setreg(add_str("$@MobDrop_count"), j);
+ script_pushint(st, 1);
+
+ return 0;
+}
+/*==========================================
+ * Same as monster but randomize location in x0,x1,y0,y1 area
+ *------------------------------------------*/
+BUILDIN_FUNC(areamonster)
+{
+ const char* mapn = script_getstr(st,2);
+ int x0 = script_getnum(st,3);
+ int y0 = script_getnum(st,4);
+ int x1 = script_getnum(st,5);
+ int y1 = script_getnum(st,6);
+ const char* str = script_getstr(st,7);
+ int class_ = script_getnum(st,8);
+ int amount = script_getnum(st,9);
+ const char* event = "";
+ unsigned int size = SZ_SMALL;
+ unsigned int ai = AI_NONE;
+
+ struct map_session_data* sd;
+ int16 m;
+
+ if (script_hasdata(st,10))
+ {
+ event = script_getstr(st, 10);
+ check_event(st, event);
+ }
+
+ if (script_hasdata(st, 11))
+ {
+ size = script_getnum(st, 11);
+ if (size > 3)
+ {
+ ShowWarning("buildin_monster: Attempted to spawn non-existing size %d for monster class %d\n", size, class_);
+ return 1;
+ }
+ }
+
+ if (script_hasdata(st, 12))
+ {
+ ai = script_getnum(st, 12);
+ if (ai > 4)
+ {
+ ShowWarning("buildin_monster: Attempted to spawn non-existing ai %d for monster class %d\n", ai, class_);
+ return 1;
+ }
+ }
+
+ sd = map_id2sd(st->rid);
+
+ if (sd && strcmp(mapn, "this") == 0)
+ m = sd->bl.m;
+ else
+ {
+ m = map_mapname2mapid(mapn);
+ if (map[m].flag.src4instance && st->instance_id)
+ { // Try to redirect to the instance map, not the src map
+ if ((m = instance_mapid2imapid(m, st->instance_id)) < 0)
+ {
+ ShowError("buildin_areamonster: Trying to spawn monster (%d) on instance map (%s) without instance attached.\n", class_, mapn);
+ return 1;
+ }
+ }
+ }
+
+ mob_once_spawn_area(sd, m, x0, y0, x1, y1, str, class_, amount, event, size, ai);
+ return 0;
+}
+/*==========================================
+ * KillMonster subcheck, verify if mob to kill ain't got an even to handle, could be force kill by allflag
+ *------------------------------------------*/
+ static int buildin_killmonster_sub_strip(struct block_list *bl,va_list ap)
+{ //same fix but with killmonster instead - stripping events from mobs.
+ TBL_MOB* md = (TBL_MOB*)bl;
+ char *event=va_arg(ap,char *);
+ int allflag=va_arg(ap,int);
+
+ md->state.npc_killmonster = 1;
+
+ if(!allflag){
+ if(strcmp(event,md->npc_event)==0)
+ status_kill(bl);
+ }else{
+ if(!md->spawn)
+ status_kill(bl);
+ }
+ md->state.npc_killmonster = 0;
+ return 0;
+}
+static int buildin_killmonster_sub(struct block_list *bl,va_list ap)
+{
+ TBL_MOB* md = (TBL_MOB*)bl;
+ char *event=va_arg(ap,char *);
+ int allflag=va_arg(ap,int);
+
+ if(!allflag){
+ if(strcmp(event,md->npc_event)==0)
+ status_kill(bl);
+ }else{
+ if(!md->spawn)
+ status_kill(bl);
+ }
+ return 0;
+}
+BUILDIN_FUNC(killmonster)
+{
+ const char *mapname,*event;
+ int16 m,allflag=0;
+ mapname=script_getstr(st,2);
+ event=script_getstr(st,3);
+ if(strcmp(event,"All")==0)
+ allflag = 1;
+ else
+ check_event(st, event);
+
+ if( (m=map_mapname2mapid(mapname))<0 )
+ return 0;
+
+ if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 )
+ return 0;
+
+ if( script_hasdata(st,4) ) {
+ if ( script_getnum(st,4) == 1 ) {
+ map_foreachinmap(buildin_killmonster_sub, m, BL_MOB, event ,allflag);
+ return 0;
+ }
+ }
+
+ map_freeblock_lock();
+ map_foreachinmap(buildin_killmonster_sub_strip, m, BL_MOB, event ,allflag);
+ map_freeblock_unlock();
+ return 0;
+}
+
+static int buildin_killmonsterall_sub_strip(struct block_list *bl,va_list ap)
+{ //Strips the event from the mob if it's killed the old method.
+ struct mob_data *md;
+
+ md = BL_CAST(BL_MOB, bl);
+ if (md->npc_event[0])
+ md->npc_event[0] = 0;
+
+ status_kill(bl);
+ return 0;
+}
+static int buildin_killmonsterall_sub(struct block_list *bl,va_list ap)
+{
+ status_kill(bl);
+ return 0;
+}
+BUILDIN_FUNC(killmonsterall)
+{
+ const char *mapname;
+ int16 m;
+ mapname=script_getstr(st,2);
+
+ if( (m = map_mapname2mapid(mapname))<0 )
+ return 0;
+
+ if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 )
+ return 0;
+
+ if( script_hasdata(st,3) ) {
+ if ( script_getnum(st,3) == 1 ) {
+ map_foreachinmap(buildin_killmonsterall_sub,m,BL_MOB);
+ return 0;
+ }
+ }
+
+ map_foreachinmap(buildin_killmonsterall_sub_strip,m,BL_MOB);
+ return 0;
+}
+
+/*==========================================
+ * Creates a clone of a player.
+ * clone map, x, y, event, char_id, master_id, mode, flag, duration
+ *------------------------------------------*/
+BUILDIN_FUNC(clone)
+{
+ TBL_PC *sd, *msd=NULL;
+ int char_id,master_id=0,x,y, mode = 0, flag = 0, m;
+ unsigned int duration = 0;
+ const char *map,*event="";
+
+ map=script_getstr(st,2);
+ x=script_getnum(st,3);
+ y=script_getnum(st,4);
+ event=script_getstr(st,5);
+ char_id=script_getnum(st,6);
+
+ if( script_hasdata(st,7) )
+ master_id=script_getnum(st,7);
+
+ if( script_hasdata(st,8) )
+ mode=script_getnum(st,8);
+
+ if( script_hasdata(st,9) )
+ flag=script_getnum(st,9);
+
+ if( script_hasdata(st,10) )
+ duration=script_getnum(st,10);
+
+ check_event(st, event);
+
+ m = map_mapname2mapid(map);
+ if (m < 0) return 0;
+
+ sd = map_charid2sd(char_id);
+
+ if (master_id) {
+ msd = map_charid2sd(master_id);
+ if (msd)
+ master_id = msd->bl.id;
+ else
+ master_id = 0;
+ }
+ if (sd) //Return ID of newly crafted clone.
+ script_pushint(st,mob_clone_spawn(sd, m, x, y, event, master_id, mode, flag, 1000*duration));
+ else //Failed to create clone.
+ script_pushint(st,0);
+
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(doevent)
+{
+ const char* event = script_getstr(st,2);
+ struct map_session_data* sd;
+
+ if( ( sd = script_rid2sd(st) ) == NULL )
+ {
+ return 0;
+ }
+
+ check_event(st, event);
+ npc_event(sd, event, 0);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(donpcevent)
+{
+ const char* event = script_getstr(st,2);
+ check_event(st, event);
+ if( !npc_event_do(event) ) {
+ struct npc_data * nd = map_id2nd(st->oid);
+ ShowDebug("NPCEvent '%s' not found! (source: %s)\n",event,nd?nd->name:"Unknown");
+ script_pushint(st, 0);
+ } else
+ script_pushint(st, 1);
+ return 0;
+}
+
+/// for Aegis compatibility
+/// basically a specialized 'donpcevent', with the event specified as two arguments instead of one
+BUILDIN_FUNC(cmdothernpc) // Added by RoVeRT
+{
+ const char* npc = script_getstr(st,2);
+ const char* command = script_getstr(st,3);
+ char event[EVENT_NAME_LENGTH];
+ snprintf(event, sizeof(event), "%s::OnCommand%s", npc, command);
+ check_event(st, event);
+ npc_event_do(event);
+ return 0;
+}
+
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(addtimer)
+{
+ int tick = script_getnum(st,2);
+ const char* event = script_getstr(st, 3);
+ TBL_PC* sd;
+
+ check_event(st, event);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pc_addeventtimer(sd,tick,event);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(deltimer)
+{
+ const char *event;
+ TBL_PC* sd;
+
+ event=script_getstr(st, 2);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ check_event(st, event);
+ pc_deleventtimer(sd,event);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(addtimercount)
+{
+ const char *event;
+ int tick;
+ TBL_PC* sd;
+
+ event=script_getstr(st, 2);
+ tick=script_getnum(st,3);
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ check_event(st, event);
+ pc_addeventtimercount(sd,event,tick);
+ return 0;
+}
+
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(initnpctimer)
+{
+ struct npc_data *nd;
+ int flag = 0;
+
+ if( script_hasdata(st,3) )
+ { //Two arguments: NPC name and attach flag.
+ nd = npc_name2id(script_getstr(st, 2));
+ flag = script_getnum(st,3);
+ }
+ else if( script_hasdata(st,2) )
+ { //Check if argument is numeric (flag) or string (npc name)
+ struct script_data *data;
+ data = script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) ) //NPC name
+ nd = npc_name2id(conv_str(st, data));
+ else if( data_isint(data) ) //Flag
+ {
+ nd = (struct npc_data *)map_id2bl(st->oid);
+ flag = conv_num(st,data);
+ }
+ else
+ {
+ ShowError("initnpctimer: invalid argument type #1 (needs be int or string)).\n");
+ return 1;
+ }
+ }
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( !nd )
+ return 0;
+ if( flag ) //Attach
+ {
+ TBL_PC* sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+ nd->u.scr.rid = sd->bl.id;
+ }
+
+ nd->u.scr.timertick = 0;
+ npc_settimerevent_tick(nd,0);
+ npc_timerevent_start(nd, st->rid);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(startnpctimer)
+{
+ struct npc_data *nd;
+ int flag = 0;
+
+ if( script_hasdata(st,3) )
+ { //Two arguments: NPC name and attach flag.
+ nd = npc_name2id(script_getstr(st, 2));
+ flag = script_getnum(st,3);
+ }
+ else if( script_hasdata(st,2) )
+ { //Check if argument is numeric (flag) or string (npc name)
+ struct script_data *data;
+ data = script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) ) //NPC name
+ nd = npc_name2id(conv_str(st, data));
+ else if( data_isint(data) ) //Flag
+ {
+ nd = (struct npc_data *)map_id2bl(st->oid);
+ flag = conv_num(st,data);
+ }
+ else
+ {
+ ShowError("initnpctimer: invalid argument type #1 (needs be int or string)).\n");
+ return 1;
+ }
+ }
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if( !nd )
+ return 0;
+ if( flag ) //Attach
+ {
+ TBL_PC* sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+ nd->u.scr.rid = sd->bl.id;
+ }
+
+ npc_timerevent_start(nd, st->rid);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(stopnpctimer)
+{
+ struct npc_data *nd;
+ int flag = 0;
+
+ if( script_hasdata(st,3) )
+ { //Two arguments: NPC name and attach flag.
+ nd = npc_name2id(script_getstr(st, 2));
+ flag = script_getnum(st,3);
+ }
+ else if( script_hasdata(st,2) )
+ { //Check if argument is numeric (flag) or string (npc name)
+ struct script_data *data;
+ data = script_getdata(st,2);
+ get_val(st,data);
+ if( data_isstring(data) ) //NPC name
+ nd = npc_name2id(conv_str(st, data));
+ else if( data_isint(data) ) //Flag
+ {
+ nd = (struct npc_data *)map_id2bl(st->oid);
+ flag = conv_num(st,data);
+ }
+ else
+ {
+ ShowError("initnpctimer: invalid argument type #1 (needs be int or string)).\n");
+ return 1;
+ }
+ }
+ else
+ nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if( !nd )
+ return 0;
+ if( flag ) //Detach
+ nd->u.scr.rid = 0;
+
+ npc_timerevent_stop(nd);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(getnpctimer)
+{
+ struct npc_data *nd;
+ TBL_PC *sd;
+ int type = script_getnum(st,2);
+ int val = 0;
+
+ if( script_hasdata(st,3) )
+ nd = npc_name2id(script_getstr(st,3));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( !nd || nd->bl.type != BL_NPC )
+ {
+ script_pushint(st,0);
+ ShowError("getnpctimer: Invalid NPC.\n");
+ return 1;
+ }
+
+ switch( type )
+ {
+ case 0: val = npc_gettimerevent_tick(nd); break;
+ case 1:
+ if( nd->u.scr.rid )
+ {
+ sd = map_id2sd(nd->u.scr.rid);
+ if( !sd )
+ {
+ ShowError("buildin_getnpctimer: Attached player not found!\n");
+ break;
+ }
+ val = (sd->npc_timer_id != INVALID_TIMER);
+ }
+ else
+ val = (nd->u.scr.timerid != INVALID_TIMER);
+ break;
+ case 2: val = nd->u.scr.timeramount; break;
+ }
+
+ script_pushint(st,val);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(setnpctimer)
+{
+ int tick;
+ struct npc_data *nd;
+
+ tick = script_getnum(st,2);
+ if( script_hasdata(st,3) )
+ nd = npc_name2id(script_getstr(st,3));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( !nd || nd->bl.type != BL_NPC )
+ {
+ script_pushint(st,1);
+ ShowError("setnpctimer: Invalid NPC.\n");
+ return 1;
+ }
+
+ npc_settimerevent_tick(nd,tick);
+ script_pushint(st,0);
+ return 0;
+}
+
+/*==========================================
+ * attaches the player rid to the timer [Celest]
+ *------------------------------------------*/
+BUILDIN_FUNC(attachnpctimer)
+{
+ TBL_PC *sd;
+ struct npc_data *nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( !nd || nd->bl.type != BL_NPC )
+ {
+ script_pushint(st,1);
+ ShowError("setnpctimer: Invalid NPC.\n");
+ return 1;
+ }
+
+ if( script_hasdata(st,2) )
+ sd = map_nick2sd(script_getstr(st,2));
+ else
+ sd = script_rid2sd(st);
+
+ if( !sd )
+ {
+ script_pushint(st,1);
+ ShowWarning("attachnpctimer: Invalid player.\n");
+ return 1;
+ }
+
+ nd->u.scr.rid = sd->bl.id;
+ script_pushint(st,0);
+ return 0;
+}
+
+/*==========================================
+ * detaches a player rid from the timer [Celest]
+ *------------------------------------------*/
+BUILDIN_FUNC(detachnpctimer)
+{
+ struct npc_data *nd;
+
+ if( script_hasdata(st,2) )
+ nd = npc_name2id(script_getstr(st,2));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( !nd || nd->bl.type != BL_NPC )
+ {
+ script_pushint(st,1);
+ ShowError("detachnpctimer: Invalid NPC.\n");
+ return 1;
+ }
+
+ nd->u.scr.rid = 0;
+ script_pushint(st,0);
+ return 0;
+}
+
+/*==========================================
+ * To avoid "player not attached" script errors, this function is provided,
+ * it checks if there is a player attached to the current script. [Skotlex]
+ * If no, returns 0, if yes, returns the account_id of the attached player.
+ *------------------------------------------*/
+BUILDIN_FUNC(playerattached)
+{
+ if(st->rid == 0 || map_id2sd(st->rid) == NULL)
+ script_pushint(st,0);
+ else
+ script_pushint(st,st->rid);
+ return 0;
+}
+
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(announce)
+{
+ const char *mes = script_getstr(st,2);
+ int flag = script_getnum(st,3);
+ const char *fontColor = script_hasdata(st,4) ? script_getstr(st,4) : NULL;
+ int fontType = script_hasdata(st,5) ? script_getnum(st,5) : 0x190; // default fontType (FW_NORMAL)
+ int fontSize = script_hasdata(st,6) ? script_getnum(st,6) : 12; // default fontSize
+ int fontAlign = script_hasdata(st,7) ? script_getnum(st,7) : 0; // default fontAlign
+ int fontY = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontY
+
+ if (flag&0x0f) // Broadcast source or broadcast region defined
+ {
+ send_target target;
+ struct block_list *bl = (flag&0x08) ? map_id2bl(st->oid) : (struct block_list *)script_rid2sd(st); // If bc_npc flag is set, use NPC as broadcast source
+ if (bl == NULL)
+ return 0;
+
+ flag &= 0x07;
+ target = (flag == 1) ? ALL_SAMEMAP :
+ (flag == 2) ? AREA :
+ (flag == 3) ? SELF :
+ ALL_CLIENT;
+ if (fontColor)
+ clif_broadcast2(bl, mes, (int)strlen(mes)+1, strtol(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY, target);
+ else
+ clif_broadcast(bl, mes, (int)strlen(mes)+1, flag&0xf0, target);
+ }
+ else
+ {
+ if (fontColor)
+ intif_broadcast2(mes, (int)strlen(mes)+1, strtol(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY);
+ else
+ intif_broadcast(mes, (int)strlen(mes)+1, flag&0xf0);
+ }
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+static int buildin_announce_sub(struct block_list *bl, va_list ap)
+{
+ char *mes = va_arg(ap, char *);
+ int len = va_arg(ap, int);
+ int type = va_arg(ap, int);
+ char *fontColor = va_arg(ap, char *);
+ short fontType = (short)va_arg(ap, int);
+ short fontSize = (short)va_arg(ap, int);
+ short fontAlign = (short)va_arg(ap, int);
+ short fontY = (short)va_arg(ap, int);
+ if (fontColor)
+ clif_broadcast2(bl, mes, len, strtol(fontColor, (char **)NULL, 0), fontType, fontSize, fontAlign, fontY, SELF);
+ else
+ clif_broadcast(bl, mes, len, type, SELF);
+ return 0;
+}
+
+BUILDIN_FUNC(mapannounce)
+{
+ const char *mapname = script_getstr(st,2);
+ const char *mes = script_getstr(st,3);
+ int flag = script_getnum(st,4);
+ const char *fontColor = script_hasdata(st,5) ? script_getstr(st,5) : NULL;
+ int fontType = script_hasdata(st,6) ? script_getnum(st,6) : 0x190; // default fontType (FW_NORMAL)
+ int fontSize = script_hasdata(st,7) ? script_getnum(st,7) : 12; // default fontSize
+ int fontAlign = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontAlign
+ int fontY = script_hasdata(st,9) ? script_getnum(st,9) : 0; // default fontY
+ int16 m;
+
+ if ((m = map_mapname2mapid(mapname)) < 0)
+ return 0;
+
+ map_foreachinmap(buildin_announce_sub, m, BL_PC,
+ mes, strlen(mes)+1, flag&0xf0, fontColor, fontType, fontSize, fontAlign, fontY);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(areaannounce)
+{
+ const char *mapname = script_getstr(st,2);
+ int x0 = script_getnum(st,3);
+ int y0 = script_getnum(st,4);
+ int x1 = script_getnum(st,5);
+ int y1 = script_getnum(st,6);
+ const char *mes = script_getstr(st,7);
+ int flag = script_getnum(st,8);
+ const char *fontColor = script_hasdata(st,9) ? script_getstr(st,9) : NULL;
+ int fontType = script_hasdata(st,10) ? script_getnum(st,10) : 0x190; // default fontType (FW_NORMAL)
+ int fontSize = script_hasdata(st,11) ? script_getnum(st,11) : 12; // default fontSize
+ int fontAlign = script_hasdata(st,12) ? script_getnum(st,12) : 0; // default fontAlign
+ int fontY = script_hasdata(st,13) ? script_getnum(st,13) : 0; // default fontY
+ int16 m;
+
+ if ((m = map_mapname2mapid(mapname)) < 0)
+ return 0;
+
+ map_foreachinarea(buildin_announce_sub, m, x0, y0, x1, y1, BL_PC,
+ mes, strlen(mes)+1, flag&0xf0, fontColor, fontType, fontSize, fontAlign, fontY);
+ return 0;
+}
+
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(getusers)
+{
+ int flag, val = 0;
+ struct map_session_data* sd;
+ struct block_list* bl = NULL;
+
+ flag = script_getnum(st,2);
+
+ switch(flag&0x07)
+ {
+ case 0:
+ if(flag&0x8)
+ {// npc
+ bl = map_id2bl(st->oid);
+ }
+ else if((sd = script_rid2sd(st))!=NULL)
+ {// pc
+ bl = &sd->bl;
+ }
+
+ if(bl)
+ {
+ val = map[bl->m].users;
+ }
+ break;
+ case 1:
+ val = map_getusers();
+ break;
+ default:
+ ShowWarning("buildin_getusers: Unknown type %d.\n", flag);
+ script_pushint(st,0);
+ return 1;
+ }
+
+ script_pushint(st,val);
+ return 0;
+}
+/*==========================================
+ * Works like @WHO - displays all online users names in window
+ *------------------------------------------*/
+BUILDIN_FUNC(getusersname)
+{
+ TBL_PC *sd, *pl_sd;
+ int /*disp_num=1,*/ group_level = 0;
+ struct s_mapiterator* iter;
+
+ sd = script_rid2sd(st);
+ if (!sd) return 0;
+
+ group_level = pc_get_group_level(sd);
+ iter = mapit_getallusers();
+ for( pl_sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); pl_sd = (TBL_PC*)mapit_next(iter) )
+ {
+ if (pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) && pc_get_group_level(pl_sd) > group_level)
+ continue; // skip hidden sessions
+
+ /* Temporary fix for bugreport:1023.
+ * Do not uncomment unless you want thousands of 'next' buttons.
+ if((disp_num++)%10==0)
+ clif_scriptnext(sd,st->oid);*/
+ clif_scriptmes(sd,st->oid,pl_sd->status.name);
+ }
+ mapit_free(iter);
+
+ return 0;
+}
+/*==========================================
+ * getmapguildusers("mapname",guild ID) Returns the number guild members present on a map [Reddozen]
+ *------------------------------------------*/
+BUILDIN_FUNC(getmapguildusers)
+{
+ const char *str;
+ int16 m;
+ int gid;
+ int i=0,c=0;
+ struct guild *g = NULL;
+ str=script_getstr(st,2);
+ gid=script_getnum(st,3);
+ if ((m = map_mapname2mapid(str)) < 0) { // map id on this server (m == -1 if not in actual map-server)
+ script_pushint(st,-1);
+ return 0;
+ }
+ g = guild_search(gid);
+
+ if (g){
+ for(i = 0; i < g->max_member; i++)
+ {
+ if (g->member[i].sd && g->member[i].sd->bl.m == m)
+ c++;
+ }
+ }
+
+ script_pushint(st,c);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(getmapusers)
+{
+ const char *str;
+ int16 m;
+ str=script_getstr(st,2);
+ if( (m=map_mapname2mapid(str))< 0){
+ script_pushint(st,-1);
+ return 0;
+ }
+ script_pushint(st,map[m].users);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+static int buildin_getareausers_sub(struct block_list *bl,va_list ap)
+{
+ int *users=va_arg(ap,int *);
+ (*users)++;
+ return 0;
+}
+BUILDIN_FUNC(getareausers)
+{
+ const char *str;
+ int16 m,x0,y0,x1,y1,users=0; //doubt we can have more then 32k users on
+ str=script_getstr(st,2);
+ x0=script_getnum(st,3);
+ y0=script_getnum(st,4);
+ x1=script_getnum(st,5);
+ y1=script_getnum(st,6);
+ if( (m=map_mapname2mapid(str))< 0){
+ script_pushint(st,-1);
+ return 0;
+ }
+ map_foreachinarea(buildin_getareausers_sub,
+ m,x0,y0,x1,y1,BL_PC,&users);
+ script_pushint(st,users);
+ return 0;
+}
+
+/*==========================================
+ *------------------------------------------*/
+static int buildin_getareadropitem_sub(struct block_list *bl,va_list ap)
+{
+ int item=va_arg(ap,int);
+ int *amount=va_arg(ap,int *);
+ struct flooritem_data *drop=(struct flooritem_data *)bl;
+
+ if(drop->item_data.nameid==item)
+ (*amount)+=drop->item_data.amount;
+
+ return 0;
+}
+BUILDIN_FUNC(getareadropitem)
+{
+ const char *str;
+ int16 m,x0,y0,x1,y1;
+ int item,amount=0;
+ struct script_data *data;
+
+ str=script_getstr(st,2);
+ x0=script_getnum(st,3);
+ y0=script_getnum(st,4);
+ x1=script_getnum(st,5);
+ y1=script_getnum(st,6);
+
+ data=script_getdata(st,7);
+ get_val(st,data);
+ if( data_isstring(data) ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ item=UNKNOWN_ITEM_ID;
+ if( item_data )
+ item=item_data->nameid;
+ }else
+ item=conv_num(st,data);
+
+ if( (m=map_mapname2mapid(str))< 0){
+ script_pushint(st,-1);
+ return 0;
+ }
+ map_foreachinarea(buildin_getareadropitem_sub,
+ m,x0,y0,x1,y1,BL_ITEM,item,&amount);
+ script_pushint(st,amount);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(enablenpc)
+{
+ const char *str;
+ str=script_getstr(st,2);
+ npc_enable(str,1);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(disablenpc)
+{
+ const char *str;
+ str=script_getstr(st,2);
+ npc_enable(str,0);
+ return 0;
+}
+
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(hideoffnpc)
+{
+ const char *str;
+ str=script_getstr(st,2);
+ npc_enable(str,2);
+ return 0;
+}
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(hideonnpc)
+{
+ const char *str;
+ str=script_getstr(st,2);
+ npc_enable(str,4);
+ return 0;
+}
+
+/// Starts a status effect on the target unit or on the attached player.
+///
+/// sc_start <effect_id>,<duration>,<val1>{,<unit_id>};
+BUILDIN_FUNC(sc_start)
+{
+ struct block_list* bl;
+ enum sc_type type;
+ int tick;
+ int val1;
+ int val4 = 0;
+
+ type = (sc_type)script_getnum(st,2);
+ tick = script_getnum(st,3);
+ val1 = script_getnum(st,4);
+ if( script_hasdata(st,5) )
+ bl = map_id2bl(script_getnum(st,5));
+ else
+ bl = map_id2bl(st->rid);
+
+ if( tick == 0 && val1 > 0 && type > SC_NONE && type < SC_MAX && status_sc2skill(type) != 0 )
+ {// When there isn't a duration specified, try to get it from the skill_db
+ tick = skill_get_time(status_sc2skill(type), val1);
+ }
+
+ if( potion_flag == 1 && potion_target )
+ { //skill.c set the flags before running the script, this must be a potion-pitched effect.
+ bl = map_id2bl(potion_target);
+ tick /= 2;// Thrown potions only last half.
+ val4 = 1;// Mark that this was a thrown sc_effect
+ }
+
+ if( bl )
+ status_change_start(bl, type, 10000, val1, 0, 0, val4, tick, 2);
+
+ return 0;
+}
+
+/// Starts a status effect on the target unit or on the attached player.
+///
+/// sc_start2 <effect_id>,<duration>,<val1>,<percent chance>{,<unit_id>};
+BUILDIN_FUNC(sc_start2)
+{
+ struct block_list* bl;
+ enum sc_type type;
+ int tick;
+ int val1;
+ int val4 = 0;
+ int rate;
+
+ type = (sc_type)script_getnum(st,2);
+ tick = script_getnum(st,3);
+ val1 = script_getnum(st,4);
+ rate = script_getnum(st,5);
+ if( script_hasdata(st,6) )
+ bl = map_id2bl(script_getnum(st,6));
+ else
+ bl = map_id2bl(st->rid);
+
+ if( tick == 0 && val1 > 0 && type > SC_NONE && type < SC_MAX && status_sc2skill(type) != 0 )
+ {// When there isn't a duration specified, try to get it from the skill_db
+ tick = skill_get_time(status_sc2skill(type), val1);
+ }
+
+ if( potion_flag == 1 && potion_target )
+ { //skill.c set the flags before running the script, this must be a potion-pitched effect.
+ bl = map_id2bl(potion_target);
+ tick /= 2;// Thrown potions only last half.
+ val4 = 1;// Mark that this was a thrown sc_effect
+ }
+
+ if( bl )
+ status_change_start(bl, type, rate, val1, 0, 0, val4, tick, 2);
+
+ return 0;
+}
+
+/// Starts a status effect on the target unit or on the attached player.
+///
+/// sc_start4 <effect_id>,<duration>,<val1>,<val2>,<val3>,<val4>{,<unit_id>};
+BUILDIN_FUNC(sc_start4)
+{
+ struct block_list* bl;
+ enum sc_type type;
+ int tick;
+ int val1;
+ int val2;
+ int val3;
+ int val4;
+
+ type = (sc_type)script_getnum(st,2);
+ tick = script_getnum(st,3);
+ val1 = script_getnum(st,4);
+ val2 = script_getnum(st,5);
+ val3 = script_getnum(st,6);
+ val4 = script_getnum(st,7);
+ if( script_hasdata(st,8) )
+ bl = map_id2bl(script_getnum(st,8));
+ else
+ bl = map_id2bl(st->rid);
+
+ if( tick == 0 && val1 > 0 && type > SC_NONE && type < SC_MAX && status_sc2skill(type) != 0 )
+ {// When there isn't a duration specified, try to get it from the skill_db
+ tick = skill_get_time(status_sc2skill(type), val1);
+ }
+
+ if( potion_flag == 1 && potion_target )
+ { //skill.c set the flags before running the script, this must be a potion-pitched effect.
+ bl = map_id2bl(potion_target);
+ tick /= 2;// Thrown potions only last half.
+ }
+
+ if( bl )
+ status_change_start(bl, type, 10000, val1, val2, val3, val4, tick, 2);
+
+ return 0;
+}
+
+/// Ends one or all status effects on the target unit or on the attached player.
+///
+/// sc_end <effect_id>{,<unit_id>};
+BUILDIN_FUNC(sc_end)
+{
+ struct block_list* bl;
+ int type;
+
+ type = script_getnum(st, 2);
+ if (script_hasdata(st, 3))
+ bl = map_id2bl(script_getnum(st, 3));
+ else
+ bl = map_id2bl(st->rid);
+
+ if (potion_flag == 1 && potion_target) //##TODO how does this work [FlavioJS]
+ bl = map_id2bl(potion_target);
+
+ if (!bl)
+ return 0;
+
+ if (type >= 0 && type < SC_MAX)
+ {
+ struct status_change *sc = status_get_sc(bl);
+ struct status_change_entry *sce = sc ? sc->data[type] : NULL;
+
+ if (!sce)
+ return 0;
+
+
+ switch (type)
+ {
+ case SC_WEIGHT50:
+ case SC_WEIGHT90:
+ case SC_NOCHAT:
+ case SC_PUSH_CART:
+ return 0;
+
+ default:
+ break;
+ }
+
+ //This should help status_change_end force disabling the SC in case it has no limit.
+ sce->val1 = sce->val2 = sce->val3 = sce->val4 = 0;
+ status_change_end(bl, (sc_type)type, INVALID_TIMER);
+ }
+ else
+ status_change_clear(bl, 3); // remove all effects
+
+ return 0;
+}
+
+/*==========================================
+ * @FIXME atm will return reduced tick, 0 immune, 1 no tick
+ *------------------------------------------*/
+BUILDIN_FUNC(getscrate)
+{
+ struct block_list *bl;
+ int type,rate;
+
+ type=script_getnum(st,2);
+ rate=script_getnum(st,3);
+ if( script_hasdata(st,4) ) //get for the bl assigned
+ bl = map_id2bl(script_getnum(st,4));
+ else
+ bl = map_id2bl(st->rid);
+
+ if (bl)
+ rate = status_get_sc_def(bl, (sc_type)type, 10000, 10000, 0);
+
+ script_pushint(st,rate);
+ return 0;
+}
+
+/*==========================================
+ * getstatus <type>{, <info>};
+ *------------------------------------------*/
+BUILDIN_FUNC(getstatus)
+{
+ int id, type;
+ struct map_session_data* sd = script_rid2sd(st);
+
+ if( sd == NULL )
+ {// no player attached
+ return 0;
+ }
+
+ id = script_getnum(st, 2);
+ type = script_hasdata(st, 3) ? script_getnum(st, 3) : 0;
+
+ if( id <= SC_NONE || id >= SC_MAX )
+ {// invalid status type given
+ ShowWarning("script.c:getstatus: Invalid status type given (%d).\n", id);
+ return 0;
+ }
+
+ if( sd->sc.count == 0 || !sd->sc.data[id] )
+ {// no status is active
+ script_pushint(st, 0);
+ return 0;
+ }
+
+ switch( type )
+ {
+ case 1: script_pushint(st, sd->sc.data[id]->val1); break;
+ case 2: script_pushint(st, sd->sc.data[id]->val2); break;
+ case 3: script_pushint(st, sd->sc.data[id]->val3); break;
+ case 4: script_pushint(st, sd->sc.data[id]->val4); break;
+ case 5:
+ {
+ struct TimerData* timer = (struct TimerData*)get_timer(sd->sc.data[id]->timer);
+
+ if( timer )
+ {// return the amount of time remaining
+ script_pushint(st, timer->tick - gettick());
+ }
+ }
+ break;
+ default: script_pushint(st, 1); break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(debugmes)
+{
+ const char *str;
+ str=script_getstr(st,2);
+ ShowDebug("script debug : %d %d : %s\n",st->rid,st->oid,str);
+ return 0;
+}
+
+/*==========================================
+ *------------------------------------------*/
+BUILDIN_FUNC(catchpet)
+{
+ int pet_id;
+ TBL_PC *sd;
+
+ pet_id= script_getnum(st,2);
+ sd=script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pet_catch_process1(sd,pet_id);
+ return 0;
+}
+
+/*==========================================
+ * [orn]
+ *------------------------------------------*/
+BUILDIN_FUNC(homunculus_evolution)
+{
+ TBL_PC *sd;
+
+ sd=script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if(merc_is_hom_active(sd->hd))
+ {
+ if (sd->hd->homunculus.intimacy > 91000)
+ merc_hom_evolution(sd->hd);
+ else
+ clif_emotion(&sd->hd->bl, E_SWT);
+ }
+ return 0;
+}
+
+/*==========================================
+ * [Xantara]
+ *------------------------------------------*/
+BUILDIN_FUNC(homunculus_mutate)
+{
+ int homun_id, m_class, m_id;
+ TBL_PC *sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if(script_hasdata(st,2))
+ homun_id = script_getnum(st,2);
+ else
+ homun_id = 6048 + (rnd() % 4);
+
+ if(merc_is_hom_active(sd->hd)) {
+ m_class = hom_class2mapid(sd->hd->homunculus.class_);
+ m_id = hom_class2mapid(homun_id);
+
+ if ( m_class != -1 && m_id != -1 && m_class&HOM_EVO && m_id&HOM_S && sd->hd->homunculus.level >= 99 )
+ hom_mutate(sd->hd, homun_id);
+ else
+ clif_emotion(&sd->hd->bl, E_SWT);
+ }
+ return 0;
+}
+
+// [Zephyrus]
+BUILDIN_FUNC(homunculus_shuffle)
+{
+ TBL_PC *sd;
+
+ sd=script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if(merc_is_hom_active(sd->hd))
+ merc_hom_shuffle(sd->hd);
+
+ return 0;
+}
+
+//These two functions bring the eA MAPID_* class functionality to scripts.
+BUILDIN_FUNC(eaclass)
+{
+ int class_;
+ if( script_hasdata(st,2) )
+ class_ = script_getnum(st,2);
+ else {
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+ if (!sd) {
+ script_pushint(st,-1);
+ return 0;
+ }
+ class_ = sd->status.class_;
+ }
+ script_pushint(st,pc_jobid2mapid(class_));
+ return 0;
+}
+
+BUILDIN_FUNC(roclass)
+{
+ int class_ =script_getnum(st,2);
+ int sex;
+ if( script_hasdata(st,3) )
+ sex = script_getnum(st,3);
+ else {
+ TBL_PC *sd;
+ if (st->rid && (sd=script_rid2sd(st)))
+ sex = sd->status.sex;
+ else
+ sex = 1; //Just use male when not found.
+ }
+ script_pushint(st,pc_mapid2jobid(class_, sex));
+ return 0;
+}
+
+/*==========================================
+ * Tells client to open a hatching window, used for pet incubator
+ *------------------------------------------*/
+BUILDIN_FUNC(birthpet)
+{
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ if( sd->status.pet_id )
+ {// do not send egg list, when you already have a pet
+ return 0;
+ }
+
+ clif_sendegg(sd);
+ return 0;
+}
+
+/*==========================================
+ * Added - AppleGirl For Advanced Classes, (Updated for Cleaner Script Purposes)
+ * @type
+ * 1 : make like after rebirth
+ * 2 : blvl,jlvl=1, skillpoint=0
+ * 3 : don't reset skill, blvl=1
+ * 4 : jlvl=0
+ *------------------------------------------*/
+BUILDIN_FUNC(resetlvl)
+{
+ TBL_PC *sd;
+
+ int type=script_getnum(st,2);
+
+ sd=script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ pc_resetlvl(sd,type);
+ return 0;
+}
+/*==========================================
+ * Reset a player status point
+ *------------------------------------------*/
+BUILDIN_FUNC(resetstatus)
+{
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+ pc_resetstate(sd);
+ return 0;
+}
+
+/*==========================================
+ * script command resetskill
+ *------------------------------------------*/
+BUILDIN_FUNC(resetskill)
+{
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+ pc_resetskill(sd,1);
+ return 0;
+}
+
+/*==========================================
+ * Counts total amount of skill points.
+ *------------------------------------------*/
+BUILDIN_FUNC(skillpointcount)
+{
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+ script_pushint(st,sd->status.skill_point + pc_resetskill(sd,2));
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(changebase)
+{
+ TBL_PC *sd=NULL;
+ int vclass;
+
+ if( script_hasdata(st,3) )
+ sd=map_id2sd(script_getnum(st,3));
+ else
+ sd=script_rid2sd(st);
+
+ if(sd == NULL)
+ return 0;
+
+ vclass = script_getnum(st,2);
+ if(vclass == JOB_WEDDING)
+ {
+ if (!battle_config.wedding_modifydisplay || //Do not show the wedding sprites
+ sd->class_&JOBL_BABY //Baby classes screw up when showing wedding sprites. [Skotlex] They don't seem to anymore.
+ )
+ return 0;
+ }
+
+ if(!sd->disguise && vclass != sd->vd.class_) {
+ status_set_viewdata(&sd->bl, vclass);
+ //Updated client view. Base, Weapon and Cloth Colors.
+ clif_changelook(&sd->bl,LOOK_BASE,sd->vd.class_);
+ clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon);
+ if (sd->vd.cloth_color)
+ clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color);
+ clif_skillinfoblock(sd);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Unequip all item and request for a changesex to char-serv
+ *------------------------------------------*/
+BUILDIN_FUNC(changesex)
+{
+ int i;
+ TBL_PC *sd = NULL;
+ sd = script_rid2sd(st);
+
+ pc_resetskill(sd,4);
+ // to avoid any problem with equipment and invalid sex, equipment is unequiped.
+ for( i=0; i<EQI_MAX; i++ )
+ if( sd->equip_index[i] >= 0 ) pc_unequipitem(sd, sd->equip_index[i], 3);
+ chrif_changesex(sd);
+ return 0;
+}
+
+/*==========================================
+ * Works like 'announce' but outputs in the common chat window
+ *------------------------------------------*/
+BUILDIN_FUNC(globalmes)
+{
+ struct block_list *bl = map_id2bl(st->oid);
+ struct npc_data *nd = (struct npc_data *)bl;
+ const char *name=NULL,*mes;
+
+ mes=script_getstr(st,2);
+ if(mes==NULL) return 0;
+
+ if(script_hasdata(st,3)){ // npc name to display
+ name=script_getstr(st,3);
+ } else {
+ name=nd->name; //use current npc name
+ }
+
+ npc_globalmessage(name,mes); // broadcast to all players connected
+
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+// NPC waiting room (chat room)
+//
+
+/// Creates a waiting room (chat room) for this npc.
+///
+/// waitingroom "<title>",<limit>{,"<event>"{,<trigger>{,<zeny>{,<minlvl>{,<maxlvl>}}}}};
+BUILDIN_FUNC(waitingroom)
+{
+ struct npc_data* nd;
+ int pub = 1;
+ const char* title = script_getstr(st, 2);
+ int limit = script_getnum(st, 3);
+ const char* ev = script_hasdata(st,4) ? script_getstr(st,4) : "";
+ int trigger = script_hasdata(st,5) ? script_getnum(st,5) : limit;
+ int zeny = script_hasdata(st,6) ? script_getnum(st,6) : 0;
+ int minLvl = script_hasdata(st,7) ? script_getnum(st,7) : 1;
+ int maxLvl = script_hasdata(st,8) ? script_getnum(st,8) : MAX_LEVEL;
+
+ nd = (struct npc_data *)map_id2bl(st->oid);
+ if( nd != NULL )
+ chat_createnpcchat(nd, title, limit, pub, trigger, ev, zeny, minLvl, maxLvl);
+
+ return 0;
+}
+
+/// Removes the waiting room of the current or target npc.
+///
+/// delwaitingroom "<npc_name>";
+/// delwaitingroom;
+BUILDIN_FUNC(delwaitingroom)
+{
+ struct npc_data* nd;
+ if( script_hasdata(st,2) )
+ nd = npc_name2id(script_getstr(st, 2));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+ if( nd != NULL )
+ chat_deletenpcchat(nd);
+ return 0;
+}
+
+/// Kicks all the players from the waiting room of the current or target npc.
+///
+/// kickwaitingroomall "<npc_name>";
+/// kickwaitingroomall;
+BUILDIN_FUNC(waitingroomkickall)
+{
+ struct npc_data* nd;
+ struct chat_data* cd;
+
+ if( script_hasdata(st,2) )
+ nd = npc_name2id(script_getstr(st,2));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( nd != NULL && (cd=(struct chat_data *)map_id2bl(nd->chat_id)) != NULL )
+ chat_npckickall(cd);
+ return 0;
+}
+
+/// Enables the waiting room event of the current or target npc.
+///
+/// enablewaitingroomevent "<npc_name>";
+/// enablewaitingroomevent;
+BUILDIN_FUNC(enablewaitingroomevent)
+{
+ struct npc_data* nd;
+ struct chat_data* cd;
+
+ if( script_hasdata(st,2) )
+ nd = npc_name2id(script_getstr(st, 2));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( nd != NULL && (cd=(struct chat_data *)map_id2bl(nd->chat_id)) != NULL )
+ chat_enableevent(cd);
+ return 0;
+}
+
+/// Disables the waiting room event of the current or target npc.
+///
+/// disablewaitingroomevent "<npc_name>";
+/// disablewaitingroomevent;
+BUILDIN_FUNC(disablewaitingroomevent)
+{
+ struct npc_data *nd;
+ struct chat_data *cd;
+
+ if( script_hasdata(st,2) )
+ nd = npc_name2id(script_getstr(st, 2));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( nd != NULL && (cd=(struct chat_data *)map_id2bl(nd->chat_id)) != NULL )
+ chat_disableevent(cd);
+ return 0;
+}
+
+/// Returns info on the waiting room of the current or target npc.
+/// Returns -1 if the type unknown
+/// <type>=0 : current number of users
+/// <type>=1 : maximum number of users allowed
+/// <type>=2 : the number of users that trigger the event
+/// <type>=3 : if the trigger is disabled
+/// <type>=4 : the title of the waiting room
+/// <type>=5 : the password of the waiting room
+/// <type>=16 : the name of the waiting room event
+/// <type>=32 : if the waiting room is full
+/// <type>=33 : if there are enough users to trigger the event
+///
+/// getwaitingroomstate(<type>,"<npc_name>") -> <info>
+/// getwaitingroomstate(<type>) -> <info>
+BUILDIN_FUNC(getwaitingroomstate)
+{
+ struct npc_data *nd;
+ struct chat_data *cd;
+ int type;
+
+ type = script_getnum(st,2);
+ if( script_hasdata(st,3) )
+ nd = npc_name2id(script_getstr(st, 3));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( nd == NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id)) == NULL )
+ {
+ script_pushint(st, -1);
+ return 0;
+ }
+
+ switch(type)
+ {
+ case 0: script_pushint(st, cd->users); break;
+ case 1: script_pushint(st, cd->limit); break;
+ case 2: script_pushint(st, cd->trigger&0x7f); break;
+ case 3: script_pushint(st, ((cd->trigger&0x80)!=0)); break;
+ case 4: script_pushstrcopy(st, cd->title); break;
+ case 5: script_pushstrcopy(st, cd->pass); break;
+ case 16: script_pushstrcopy(st, cd->npc_event);break;
+ case 32: script_pushint(st, (cd->users >= cd->limit)); break;
+ case 33: script_pushint(st, (cd->users >= cd->trigger)); break;
+ default: script_pushint(st, -1); break;
+ }
+ return 0;
+}
+
+/// Warps the trigger or target amount of players to the target map and position.
+/// Players are automatically removed from the waiting room.
+/// Those waiting the longest will get warped first.
+/// The target map can be "Random" for a random position in the current map,
+/// and "SavePoint" for the savepoint map+position.
+/// The map flag noteleport of the current map is only considered when teleporting to the savepoint.
+///
+/// The id's of the teleported players are put into the array $@warpwaitingpc[]
+/// The total number of teleported players is put into $@warpwaitingpcnum
+///
+/// warpwaitingpc "<map name>",<x>,<y>,<number of players>;
+/// warpwaitingpc "<map name>",<x>,<y>;
+BUILDIN_FUNC(warpwaitingpc)
+{
+ int x;
+ int y;
+ int i;
+ int n;
+ const char* map_name;
+ struct npc_data* nd;
+ struct chat_data* cd;
+ TBL_PC* sd;
+
+ nd = (struct npc_data *)map_id2bl(st->oid);
+ if( nd == NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id)) == NULL )
+ return 0;
+
+ map_name = script_getstr(st,2);
+ x = script_getnum(st,3);
+ y = script_getnum(st,4);
+ n = cd->trigger&0x7f;
+
+ if( script_hasdata(st,5) )
+ n = script_getnum(st,5);
+
+ for( i = 0; i < n && cd->users > 0; i++ )
+ {
+ sd = cd->usersd[0];
+
+ if( strcmp(map_name,"SavePoint") == 0 && map[sd->bl.m].flag.noteleport )
+ {// can't teleport on this map
+ break;
+ }
+
+ if( cd->zeny )
+ {// fee set
+ if( (uint32)sd->status.zeny < cd->zeny )
+ {// no zeny to cover set fee
+ break;
+ }
+ pc_payzeny(sd, cd->zeny, LOG_TYPE_NPC, NULL);
+ }
+
+ mapreg_setreg(reference_uid(add_str("$@warpwaitingpc"), i), sd->bl.id);
+
+ if( strcmp(map_name,"Random") == 0 )
+ pc_randomwarp(sd,CLR_TELEPORT);
+ else if( strcmp(map_name,"SavePoint") == 0 )
+ pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_TELEPORT);
+ else
+ pc_setpos(sd, mapindex_name2id(map_name), x, y, CLR_OUTSIGHT);
+ }
+ mapreg_setreg(add_str("$@warpwaitingpcnum"), i);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////
+// ...
+//
+
+/// Detaches a character from a script.
+///
+/// @param st Script state to detach the character from.
+static void script_detach_rid(struct script_state* st)
+{
+ if(st->rid)
+ {
+ script_detach_state(st, false);
+ st->rid = 0;
+ }
+}
+
+/*==========================================
+ * Attach sd char id to script and detach current one if any
+ *------------------------------------------*/
+BUILDIN_FUNC(attachrid)
+{
+ int rid = script_getnum(st,2);
+ struct map_session_data* sd;
+
+ if ((sd = map_id2sd(rid))!=NULL) {
+ script_detach_rid(st);
+
+ st->rid = rid;
+ script_attach_state(st);
+ script_pushint(st,1);
+ } else
+ script_pushint(st,0);
+ return 0;
+}
+/*==========================================
+ * Detach script to rid
+ *------------------------------------------*/
+BUILDIN_FUNC(detachrid)
+{
+ script_detach_rid(st);
+ return 0;
+}
+/*==========================================
+ * Chk if account connected, (and charid from account if specified)
+ *------------------------------------------*/
+BUILDIN_FUNC(isloggedin)
+{
+ TBL_PC* sd = map_id2sd(script_getnum(st,2));
+ if (script_hasdata(st,3) && sd &&
+ sd->status.char_id != script_getnum(st,3))
+ sd = NULL;
+ push_val(st->stack,C_INT,sd!=NULL);
+ return 0;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------*/
+BUILDIN_FUNC(setmapflagnosave)
+{
+ int16 m,x,y;
+ unsigned short mapindex;
+ const char *str,*str2;
+
+ str=script_getstr(st,2);
+ str2=script_getstr(st,3);
+ x=script_getnum(st,4);
+ y=script_getnum(st,5);
+ m = map_mapname2mapid(str);
+ mapindex = mapindex_name2id(str2);
+
+ if(m >= 0 && mapindex) {
+ map[m].flag.nosave=1;
+ map[m].save.map=mapindex;
+ map[m].save.x=x;
+ map[m].save.y=y;
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(getmapflag)
+{
+ int16 m,i;
+ const char *str;
+
+ str=script_getstr(st,2);
+ i=script_getnum(st,3);
+
+ m = map_mapname2mapid(str);
+ if(m >= 0) {
+ switch(i) {
+ case MF_NOMEMO: script_pushint(st,map[m].flag.nomemo); break;
+ case MF_NOTELEPORT: script_pushint(st,map[m].flag.noteleport); break;
+ case MF_NOSAVE: script_pushint(st,map[m].flag.nosave); break;
+ case MF_NOBRANCH: script_pushint(st,map[m].flag.nobranch); break;
+ case MF_NOPENALTY: script_pushint(st,map[m].flag.noexppenalty); break;
+ case MF_NOZENYPENALTY: script_pushint(st,map[m].flag.nozenypenalty); break;
+ case MF_PVP: script_pushint(st,map[m].flag.pvp); break;
+ case MF_PVP_NOPARTY: script_pushint(st,map[m].flag.pvp_noparty); break;
+ case MF_PVP_NOGUILD: script_pushint(st,map[m].flag.pvp_noguild); break;
+ case MF_GVG: script_pushint(st,map[m].flag.gvg); break;
+ case MF_GVG_NOPARTY: script_pushint(st,map[m].flag.gvg_noparty); break;
+ case MF_NOTRADE: script_pushint(st,map[m].flag.notrade); break;
+ case MF_NOSKILL: script_pushint(st,map[m].flag.noskill); break;
+ case MF_NOWARP: script_pushint(st,map[m].flag.nowarp); break;
+ case MF_PARTYLOCK: script_pushint(st,map[m].flag.partylock); break;
+ case MF_NOICEWALL: script_pushint(st,map[m].flag.noicewall); break;
+ case MF_SNOW: script_pushint(st,map[m].flag.snow); break;
+ case MF_FOG: script_pushint(st,map[m].flag.fog); break;
+ case MF_SAKURA: script_pushint(st,map[m].flag.sakura); break;
+ case MF_LEAVES: script_pushint(st,map[m].flag.leaves); break;
+ /**
+ * No longer available, keeping here just in case it's back someday. [Ind]
+ **/
+ //case MF_RAIN: script_pushint(st,map[m].flag.rain); break;
+ case MF_NOGO: script_pushint(st,map[m].flag.nogo); break;
+ case MF_CLOUDS: script_pushint(st,map[m].flag.clouds); break;
+ case MF_CLOUDS2: script_pushint(st,map[m].flag.clouds2); break;
+ case MF_FIREWORKS: script_pushint(st,map[m].flag.fireworks); break;
+ case MF_GVG_CASTLE: script_pushint(st,map[m].flag.gvg_castle); break;
+ case MF_GVG_DUNGEON: script_pushint(st,map[m].flag.gvg_dungeon); break;
+ case MF_NIGHTENABLED: script_pushint(st,map[m].flag.nightenabled); break;
+ case MF_NOBASEEXP: script_pushint(st,map[m].flag.nobaseexp); break;
+ case MF_NOJOBEXP: script_pushint(st,map[m].flag.nojobexp); break;
+ case MF_NOMOBLOOT: script_pushint(st,map[m].flag.nomobloot); break;
+ case MF_NOMVPLOOT: script_pushint(st,map[m].flag.nomvploot); break;
+ case MF_NORETURN: script_pushint(st,map[m].flag.noreturn); break;
+ case MF_NOWARPTO: script_pushint(st,map[m].flag.nowarpto); break;
+ case MF_NIGHTMAREDROP: script_pushint(st,map[m].flag.pvp_nightmaredrop); break;
+ case MF_RESTRICTED: script_pushint(st,map[m].flag.restricted); break;
+ case MF_NOCOMMAND: script_pushint(st,map[m].nocommand); break;
+ case MF_NODROP: script_pushint(st,map[m].flag.nodrop); break;
+ case MF_JEXP: script_pushint(st,map[m].jexp); break;
+ case MF_BEXP: script_pushint(st,map[m].bexp); break;
+ case MF_NOVENDING: script_pushint(st,map[m].flag.novending); break;
+ case MF_LOADEVENT: script_pushint(st,map[m].flag.loadevent); break;
+ case MF_NOCHAT: script_pushint(st,map[m].flag.nochat); break;
+ case MF_NOEXPPENALTY: script_pushint(st,map[m].flag.noexppenalty ); break;
+ case MF_GUILDLOCK: script_pushint(st,map[m].flag.guildlock); break;
+ case MF_TOWN: script_pushint(st,map[m].flag.town); break;
+ case MF_AUTOTRADE: script_pushint(st,map[m].flag.autotrade); break;
+ case MF_ALLOWKS: script_pushint(st,map[m].flag.allowks); break;
+ case MF_MONSTER_NOTELEPORT: script_pushint(st,map[m].flag.monster_noteleport); break;
+ case MF_PVP_NOCALCRANK: script_pushint(st,map[m].flag.pvp_nocalcrank); break;
+ case MF_BATTLEGROUND: script_pushint(st,map[m].flag.battleground); break;
+ case MF_RESET: script_pushint(st,map[m].flag.reset); break;
+ }
+ }
+
+ return 0;
+}
+/* pvp timer handling */
+static int script_mapflag_pvp_sub(struct block_list *bl,va_list ap) {
+ TBL_PC* sd = (TBL_PC*)bl;
+ if (sd->pvp_timer == INVALID_TIMER) {
+ sd->pvp_timer = add_timer(gettick() + 200, pc_calc_pvprank_timer, sd->bl.id, 0);
+ sd->pvp_rank = 0;
+ sd->pvp_lastusers = 0;
+ sd->pvp_point = 5;
+ sd->pvp_won = 0;
+ sd->pvp_lost = 0;
+ }
+ clif_map_property(sd, MAPPROPERTY_FREEPVPZONE);
+ return 0;
+}
+BUILDIN_FUNC(setmapflag)
+{
+ int16 m,i;
+ const char *str;
+ int val=0;
+
+ str=script_getstr(st,2);
+ i=script_getnum(st,3);
+ if(script_hasdata(st,4)){
+ val=script_getnum(st,4);
+ }
+ m = map_mapname2mapid(str);
+ if(m >= 0) {
+ switch(i) {
+ case MF_NOMEMO: map[m].flag.nomemo = 1; break;
+ case MF_NOTELEPORT: map[m].flag.noteleport = 1; break;
+ case MF_NOSAVE: map[m].flag.nosave = 1; break;
+ case MF_NOBRANCH: map[m].flag.nobranch = 1; break;
+ case MF_NOPENALTY: map[m].flag.noexppenalty = 1; map[m].flag.nozenypenalty = 1; break;
+ case MF_NOZENYPENALTY: map[m].flag.nozenypenalty = 1; break;
+ case MF_PVP:
+ map[m].flag.pvp = 1;
+ if( !battle_config.pk_mode ) {
+ map_foreachinmap(script_mapflag_pvp_sub,m,BL_PC);
+ }
+ break;
+ case MF_PVP_NOPARTY: map[m].flag.pvp_noparty = 1; break;
+ case MF_PVP_NOGUILD: map[m].flag.pvp_noguild = 1; break;
+ case MF_GVG:
+ map[m].flag.gvg = 1;
+ clif_map_property_mapall(m, MAPPROPERTY_AGITZONE);
+ break;
+ case MF_GVG_NOPARTY: map[m].flag.gvg_noparty = 1; break;
+ case MF_NOTRADE: map[m].flag.notrade = 1; break;
+ case MF_NOSKILL: map[m].flag.noskill = 1; break;
+ case MF_NOWARP: map[m].flag.nowarp = 1; break;
+ case MF_PARTYLOCK: map[m].flag.partylock = 1; break;
+ case MF_NOICEWALL: map[m].flag.noicewall = 1; break;
+ case MF_SNOW: map[m].flag.snow = 1; break;
+ case MF_FOG: map[m].flag.fog = 1; break;
+ case MF_SAKURA: map[m].flag.sakura = 1; break;
+ case MF_LEAVES: map[m].flag.leaves = 1; break;
+ /**
+ * No longer available, keeping here just in case it's back someday. [Ind]
+ **/
+ //case MF_RAIN: map[m].flag.rain = 1; break;
+ case MF_NOGO: map[m].flag.nogo = 1; break;
+ case MF_CLOUDS: map[m].flag.clouds = 1; break;
+ case MF_CLOUDS2: map[m].flag.clouds2 = 1; break;
+ case MF_FIREWORKS: map[m].flag.fireworks = 1; break;
+ case MF_GVG_CASTLE: map[m].flag.gvg_castle = 1; break;
+ case MF_GVG_DUNGEON: map[m].flag.gvg_dungeon = 1; break;
+ case MF_NIGHTENABLED: map[m].flag.nightenabled = 1; break;
+ case MF_NOBASEEXP: map[m].flag.nobaseexp = 1; break;
+ case MF_NOJOBEXP: map[m].flag.nojobexp = 1; break;
+ case MF_NOMOBLOOT: map[m].flag.nomobloot = 1; break;
+ case MF_NOMVPLOOT: map[m].flag.nomvploot = 1; break;
+ case MF_NORETURN: map[m].flag.noreturn = 1; break;
+ case MF_NOWARPTO: map[m].flag.nowarpto = 1; break;
+ case MF_NIGHTMAREDROP: map[m].flag.pvp_nightmaredrop = 1; break;
+ case MF_RESTRICTED:
+ map[m].zone |= 1<<(val+1);
+ map[m].flag.restricted=1;
+ break;
+ case MF_NOCOMMAND: map[m].nocommand = (val <= 0) ? 100 : val; break;
+ case MF_NODROP: map[m].flag.nodrop = 1; break;
+ case MF_JEXP: map[m].jexp = (val <= 0) ? 100 : val; break;
+ case MF_BEXP: map[m].bexp = (val <= 0) ? 100 : val; break;
+ case MF_NOVENDING: map[m].flag.novending = 1; break;
+ case MF_LOADEVENT: map[m].flag.loadevent = 1; break;
+ case MF_NOCHAT: map[m].flag.nochat = 1; break;
+ case MF_NOEXPPENALTY: map[m].flag.noexppenalty = 1; break;
+ case MF_GUILDLOCK: map[m].flag.guildlock = 1; break;
+ case MF_TOWN: map[m].flag.town = 1; break;
+ case MF_AUTOTRADE: map[m].flag.autotrade = 1; break;
+ case MF_ALLOWKS: map[m].flag.allowks = 1; break;
+ case MF_MONSTER_NOTELEPORT: map[m].flag.monster_noteleport = 1; break;
+ case MF_PVP_NOCALCRANK: map[m].flag.pvp_nocalcrank = 1; break;
+ case MF_BATTLEGROUND: map[m].flag.battleground = (val <= 0 || val > 2) ? 1 : val; break;
+ case MF_RESET: map[m].flag.reset = 1; break;
+ }
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(removemapflag)
+{
+ int16 m,i;
+ const char *str;
+ int val=0;
+
+ str=script_getstr(st,2);
+ i=script_getnum(st,3);
+ if(script_hasdata(st,4)){
+ val=script_getnum(st,4);
+ }
+ m = map_mapname2mapid(str);
+ if(m >= 0) {
+ switch(i) {
+ case MF_NOMEMO: map[m].flag.nomemo = 0; break;
+ case MF_NOTELEPORT: map[m].flag.noteleport = 0; break;
+ case MF_NOSAVE: map[m].flag.nosave = 0; break;
+ case MF_NOBRANCH: map[m].flag.nobranch = 0; break;
+ case MF_NOPENALTY: map[m].flag.noexppenalty = 0; map[m].flag.nozenypenalty = 0; break;
+ case MF_NOZENYPENALTY: map[m].flag.nozenypenalty = 0; break;
+ case MF_PVP:
+ map[m].flag.pvp = 0;
+ clif_map_property_mapall(m, MAPPROPERTY_NOTHING);
+ break;
+ case MF_PVP_NOPARTY: map[m].flag.pvp_noparty = 0; break;
+ case MF_PVP_NOGUILD: map[m].flag.pvp_noguild = 0; break;
+ case MF_GVG:
+ map[m].flag.gvg = 0;
+ clif_map_property_mapall(m, MAPPROPERTY_NOTHING);
+ break;
+ case MF_GVG_NOPARTY: map[m].flag.gvg_noparty = 0; break;
+ case MF_NOTRADE: map[m].flag.notrade = 0; break;
+ case MF_NOSKILL: map[m].flag.noskill = 0; break;
+ case MF_NOWARP: map[m].flag.nowarp = 0; break;
+ case MF_PARTYLOCK: map[m].flag.partylock = 0; break;
+ case MF_NOICEWALL: map[m].flag.noicewall = 0; break;
+ case MF_SNOW: map[m].flag.snow = 0; break;
+ case MF_FOG: map[m].flag.fog = 0; break;
+ case MF_SAKURA: map[m].flag.sakura = 0; break;
+ case MF_LEAVES: map[m].flag.leaves = 0; break;
+ /**
+ * No longer available, keeping here just in case it's back someday. [Ind]
+ **/
+ //case MF_RAIN: map[m].flag.rain = 0; break;
+ case MF_NOGO: map[m].flag.nogo = 0; break;
+ case MF_CLOUDS: map[m].flag.clouds = 0; break;
+ case MF_CLOUDS2: map[m].flag.clouds2 = 0; break;
+ case MF_FIREWORKS: map[m].flag.fireworks = 0; break;
+ case MF_GVG_CASTLE: map[m].flag.gvg_castle = 0; break;
+ case MF_GVG_DUNGEON: map[m].flag.gvg_dungeon = 0; break;
+ case MF_NIGHTENABLED: map[m].flag.nightenabled = 0; break;
+ case MF_NOBASEEXP: map[m].flag.nobaseexp = 0; break;
+ case MF_NOJOBEXP: map[m].flag.nojobexp = 0; break;
+ case MF_NOMOBLOOT: map[m].flag.nomobloot = 0; break;
+ case MF_NOMVPLOOT: map[m].flag.nomvploot = 0; break;
+ case MF_NORETURN: map[m].flag.noreturn = 0; break;
+ case MF_NOWARPTO: map[m].flag.nowarpto = 0; break;
+ case MF_NIGHTMAREDROP: map[m].flag.pvp_nightmaredrop = 0; break;
+ case MF_RESTRICTED:
+ map[m].zone ^= 1<<(val+1);
+ if (map[m].zone == 0){
+ map[m].flag.restricted=0;
+ }
+ break;
+ case MF_NOCOMMAND: map[m].nocommand = 0; break;
+ case MF_NODROP: map[m].flag.nodrop = 0; break;
+ case MF_JEXP: map[m].jexp = 0; break;
+ case MF_BEXP: map[m].bexp = 0; break;
+ case MF_NOVENDING: map[m].flag.novending = 0; break;
+ case MF_LOADEVENT: map[m].flag.loadevent = 0; break;
+ case MF_NOCHAT: map[m].flag.nochat = 0; break;
+ case MF_NOEXPPENALTY: map[m].flag.noexppenalty = 0; break;
+ case MF_GUILDLOCK: map[m].flag.guildlock = 0; break;
+ case MF_TOWN: map[m].flag.town = 0; break;
+ case MF_AUTOTRADE: map[m].flag.autotrade = 0; break;
+ case MF_ALLOWKS: map[m].flag.allowks = 0; break;
+ case MF_MONSTER_NOTELEPORT: map[m].flag.monster_noteleport = 0; break;
+ case MF_PVP_NOCALCRANK: map[m].flag.pvp_nocalcrank = 0; break;
+ case MF_BATTLEGROUND: map[m].flag.battleground = 0; break;
+ case MF_RESET: map[m].flag.reset = 0; break;
+ }
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(pvpon)
+{
+ int16 m;
+ const char *str;
+ TBL_PC* sd = NULL;
+ struct s_mapiterator* iter;
+
+ str = script_getstr(st,2);
+ m = map_mapname2mapid(str);
+ if( m < 0 || map[m].flag.pvp )
+ return 0; // nothing to do
+
+ map[m].flag.pvp = 1;
+ clif_map_property_mapall(m, MAPPROPERTY_FREEPVPZONE);
+
+ if(battle_config.pk_mode) // disable ranking functions if pk_mode is on [Valaris]
+ return 0;
+
+ iter = mapit_getallusers();
+ for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) )
+ {
+ if( sd->bl.m != m || sd->pvp_timer != INVALID_TIMER )
+ continue; // not applicable
+
+ sd->pvp_timer = add_timer(gettick()+200,pc_calc_pvprank_timer,sd->bl.id,0);
+ sd->pvp_rank = 0;
+ sd->pvp_lastusers = 0;
+ sd->pvp_point = 5;
+ sd->pvp_won = 0;
+ sd->pvp_lost = 0;
+ }
+ mapit_free(iter);
+
+ return 0;
+}
+
+static int buildin_pvpoff_sub(struct block_list *bl,va_list ap)
+{
+ TBL_PC* sd = (TBL_PC*)bl;
+ clif_pvpset(sd, 0, 0, 2);
+ if (sd->pvp_timer != INVALID_TIMER) {
+ delete_timer(sd->pvp_timer, pc_calc_pvprank_timer);
+ sd->pvp_timer = INVALID_TIMER;
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(pvpoff)
+{
+ int16 m;
+ const char *str;
+
+ str=script_getstr(st,2);
+ m = map_mapname2mapid(str);
+ if(m < 0 || !map[m].flag.pvp)
+ return 0; //fixed Lupus
+
+ map[m].flag.pvp = 0;
+ clif_map_property_mapall(m, MAPPROPERTY_NOTHING);
+
+ if(battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris]
+ return 0;
+
+ map_foreachinmap(buildin_pvpoff_sub, m, BL_PC);
+ return 0;
+}
+
+BUILDIN_FUNC(gvgon)
+{
+ int16 m;
+ const char *str;
+
+ str=script_getstr(st,2);
+ m = map_mapname2mapid(str);
+ if(m >= 0 && !map[m].flag.gvg) {
+ map[m].flag.gvg = 1;
+ clif_map_property_mapall(m, MAPPROPERTY_AGITZONE);
+ }
+
+ return 0;
+}
+BUILDIN_FUNC(gvgoff)
+{
+ int16 m;
+ const char *str;
+
+ str=script_getstr(st,2);
+ m = map_mapname2mapid(str);
+ if(m >= 0 && map[m].flag.gvg) {
+ map[m].flag.gvg = 0;
+ clif_map_property_mapall(m, MAPPROPERTY_NOTHING);
+ }
+
+ return 0;
+}
+/*==========================================
+ * Shows an emoticon on top of the player/npc
+ * emotion emotion#, <target: 0 - NPC, 1 - PC>, <NPC/PC name>
+ *------------------------------------------*/
+//Optional second parameter added by [Skotlex]
+BUILDIN_FUNC(emotion)
+{
+ int type;
+ int player=0;
+
+ type=script_getnum(st,2);
+ if(type < 0 || type > 100)
+ return 0;
+
+ if( script_hasdata(st,3) )
+ player=script_getnum(st,3);
+
+ if (player) {
+ TBL_PC *sd = NULL;
+ if( script_hasdata(st,4) )
+ sd = map_nick2sd(script_getstr(st,4));
+ else
+ sd = script_rid2sd(st);
+ if (sd)
+ clif_emotion(&sd->bl,type);
+ } else
+ if( script_hasdata(st,4) )
+ {
+ TBL_NPC *nd = npc_name2id(script_getstr(st,4));
+ if(nd)
+ clif_emotion(&nd->bl,type);
+ }
+ else
+ clif_emotion(map_id2bl(st->oid),type);
+ return 0;
+}
+
+static int buildin_maprespawnguildid_sub_pc(struct map_session_data* sd, va_list ap)
+{
+ int16 m=va_arg(ap,int);
+ int g_id=va_arg(ap,int);
+ int flag=va_arg(ap,int);
+
+ if(!sd || sd->bl.m != m)
+ return 0;
+ if(
+ (sd->status.guild_id == g_id && flag&1) || //Warp out owners
+ (sd->status.guild_id != g_id && flag&2) || //Warp out outsiders
+ (sd->status.guild_id == 0) // Warp out players not in guild [Valaris]
+ )
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
+ return 1;
+}
+
+static int buildin_maprespawnguildid_sub_mob(struct block_list *bl,va_list ap)
+{
+ struct mob_data *md=(struct mob_data *)bl;
+
+ if(!md->guardian_data && md->class_ != MOBID_EMPERIUM)
+ status_kill(bl);
+
+ return 0;
+}
+
+BUILDIN_FUNC(maprespawnguildid)
+{
+ const char *mapname=script_getstr(st,2);
+ int g_id=script_getnum(st,3);
+ int flag=script_getnum(st,4);
+
+ int16 m=map_mapname2mapid(mapname);
+
+ if(m == -1)
+ return 0;
+
+ //Catch ALL players (in case some are 'between maps' on execution time)
+ map_foreachpc(buildin_maprespawnguildid_sub_pc,m,g_id,flag);
+ if (flag&4) //Remove script mobs.
+ map_foreachinmap(buildin_maprespawnguildid_sub_mob,m,BL_MOB);
+ return 0;
+}
+
+BUILDIN_FUNC(agitstart)
+{
+ if(agit_flag==1) return 0; // Agit already Start.
+ agit_flag=1;
+ guild_agit_start();
+ return 0;
+}
+
+BUILDIN_FUNC(agitend)
+{
+ if(agit_flag==0) return 0; // Agit already End.
+ agit_flag=0;
+ guild_agit_end();
+ return 0;
+}
+
+BUILDIN_FUNC(agitstart2)
+{
+ if(agit2_flag==1) return 0; // Agit2 already Start.
+ agit2_flag=1;
+ guild_agit2_start();
+ return 0;
+}
+
+BUILDIN_FUNC(agitend2)
+{
+ if(agit2_flag==0) return 0; // Agit2 already End.
+ agit2_flag=0;
+ guild_agit2_end();
+ return 0;
+}
+
+/*==========================================
+ * Returns whether woe is on or off. // choice script
+ *------------------------------------------*/
+BUILDIN_FUNC(agitcheck)
+{
+ script_pushint(st,agit_flag);
+ return 0;
+}
+
+/*==========================================
+ * Returns whether woese is on or off. // choice script
+ *------------------------------------------*/
+BUILDIN_FUNC(agitcheck2)
+{
+ script_pushint(st,agit2_flag);
+ return 0;
+}
+
+/// Sets the guild_id of this npc.
+///
+/// flagemblem <guild_id>;
+BUILDIN_FUNC(flagemblem)
+{
+ TBL_NPC* nd;
+ int g_id = script_getnum(st,2);
+
+ if(g_id < 0) return 0;
+
+ nd = (TBL_NPC*)map_id2nd(st->oid);
+ if( nd == NULL ) {
+ ShowError("script:flagemblem: npc %d not found\n", st->oid);
+ } else if( nd->subtype != SCRIPT ) {
+ ShowError("script:flagemblem: unexpected subtype %d for npc %d '%s'\n", nd->subtype, st->oid, nd->exname);
+ } else {
+ bool changed = ( nd->u.scr.guild_id != g_id )?true:false;
+ nd->u.scr.guild_id = g_id;
+ clif_guild_emblem_area(&nd->bl);
+ /* guild flag caching */
+ if( g_id ) /* adding a id */
+ guild_flag_add(nd);
+ else if( changed ) /* removing a flag */
+ guild_flag_remove(nd);
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(getcastlename)
+{
+ const char* mapname = mapindex_getmapname(script_getstr(st,2),NULL);
+ struct guild_castle* gc = guild_mapname2gc(mapname);
+ const char* name = (gc) ? gc->castle_name : "";
+ script_pushstrcopy(st,name);
+ return 0;
+}
+
+BUILDIN_FUNC(getcastledata)
+{
+ const char *mapname = mapindex_getmapname(script_getstr(st,2),NULL);
+ int index = script_getnum(st,3);
+ struct guild_castle *gc = guild_mapname2gc(mapname);
+
+ if (gc == NULL) {
+ script_pushint(st,0);
+ ShowWarning("buildin_setcastledata: guild castle for map '%s' not found\n", mapname);
+ return 1;
+ }
+
+ switch (index) {
+ case 1:
+ script_pushint(st,gc->guild_id); break;
+ case 2:
+ script_pushint(st,gc->economy); break;
+ case 3:
+ script_pushint(st,gc->defense); break;
+ case 4:
+ script_pushint(st,gc->triggerE); break;
+ case 5:
+ script_pushint(st,gc->triggerD); break;
+ case 6:
+ script_pushint(st,gc->nextTime); break;
+ case 7:
+ script_pushint(st,gc->payTime); break;
+ case 8:
+ script_pushint(st,gc->createTime); break;
+ case 9:
+ script_pushint(st,gc->visibleC); break;
+ default:
+ if (index > 9 && index <= 9+MAX_GUARDIANS) {
+ script_pushint(st,gc->guardian[index-10].visible);
+ break;
+ }
+ script_pushint(st,0);
+ ShowWarning("buildin_setcastledata: index = '%d' is out of allowed range\n", index);
+ return 1;
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(setcastledata)
+{
+ const char *mapname = mapindex_getmapname(script_getstr(st,2),NULL);
+ int index = script_getnum(st,3);
+ int value = script_getnum(st,4);
+ struct guild_castle *gc = guild_mapname2gc(mapname);
+
+ if (gc == NULL) {
+ ShowWarning("buildin_setcastledata: guild castle for map '%s' not found\n", mapname);
+ return 1;
+ }
+
+ if (index <= 0 || index > 9+MAX_GUARDIANS) {
+ ShowWarning("buildin_setcastledata: index = '%d' is out of allowed range\n", index);
+ return 1;
+ }
+
+ guild_castledatasave(gc->castle_id, index, value);
+ return 0;
+}
+
+/* =====================================================================
+ * ---------------------------------------------------------------------*/
+BUILDIN_FUNC(requestguildinfo)
+{
+ int guild_id=script_getnum(st,2);
+ const char *event=NULL;
+
+ if( script_hasdata(st,3) ){
+ event=script_getstr(st,3);
+ check_event(st, event);
+ }
+
+ if(guild_id>0)
+ guild_npc_request_info(guild_id,event);
+ return 0;
+}
+
+/// Returns the number of cards that have been compounded onto the specified equipped item.
+/// getequipcardcnt(<equipment slot>);
+BUILDIN_FUNC(getequipcardcnt)
+{
+ int i=-1,j,num;
+ TBL_PC *sd;
+ int count;
+
+ num=script_getnum(st,2);
+ sd=script_rid2sd(st);
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+
+ if (i < 0 || !sd->inventory_data[i]) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ if(itemdb_isspecial(sd->status.inventory[i].card[0]))
+ {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ count = 0;
+ for( j = 0; j < sd->inventory_data[i]->slot; j++ )
+ if( sd->status.inventory[i].card[j] && itemdb_type(sd->status.inventory[i].card[j]) == IT_CARD )
+ count++;
+
+ script_pushint(st,count);
+ return 0;
+}
+
+/// Removes all cards from the item found in the specified equipment slot of the invoking character,
+/// and give them to the character. If any cards were removed in this manner, it will also show a success effect.
+/// successremovecards <slot>;
+BUILDIN_FUNC(successremovecards) {
+ int i=-1,j,c,cardflag=0;
+
+ TBL_PC* sd = script_rid2sd(st);
+ int num = script_getnum(st,2);
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+
+ if (i < 0 || !sd->inventory_data[i]) {
+ return 0;
+ }
+
+ if(itemdb_isspecial(sd->status.inventory[i].card[0]))
+ return 0;
+
+ for( c = sd->inventory_data[i]->slot - 1; c >= 0; --c ) {
+ if( sd->status.inventory[i].card[c] && itemdb_type(sd->status.inventory[i].card[c]) == IT_CARD ) {// extract this card from the item
+ int flag;
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+ cardflag = 1;
+ item_tmp.nameid = sd->status.inventory[i].card[c];
+ item_tmp.identify = 1;
+
+ if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){ // get back the cart in inventory
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ }
+
+ if(cardflag == 1) {//if card was remove remplace item with no card
+ int flag;
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+
+ item_tmp.nameid = sd->status.inventory[i].nameid;
+ item_tmp.identify = 1;
+ item_tmp.refine = sd->status.inventory[i].refine;
+ item_tmp.attribute = sd->status.inventory[i].attribute;
+ item_tmp.expire_time = sd->status.inventory[i].expire_time;
+
+ for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++)
+ item_tmp.card[j]=sd->status.inventory[i].card[j];
+
+ pc_delitem(sd,i,1,0,3,LOG_TYPE_SCRIPT);
+ if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){ //chk if can be spawn in inventory otherwise put on floor
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+
+ clif_misceffect(&sd->bl,3);
+ }
+ return 0;
+}
+
+/// Removes all cards from the item found in the specified equipment slot of the invoking character.
+/// failedremovecards <slot>, <type>;
+/// <type>=0 : will destroy both the item and the cards.
+/// <type>=1 : will keep the item, but destroy the cards.
+/// <type>=2 : will keep the cards, but destroy the item.
+/// <type>=? : will just display the failure effect.
+BUILDIN_FUNC(failedremovecards) {
+ int i=-1,j,c,cardflag=0;
+
+ TBL_PC* sd = script_rid2sd(st);
+ int num = script_getnum(st,2);
+ int typefail = script_getnum(st,3);
+
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+
+ if (i < 0 || !sd->inventory_data[i])
+ return 0;
+
+ if(itemdb_isspecial(sd->status.inventory[i].card[0]))
+ return 0;
+
+ for( c = sd->inventory_data[i]->slot - 1; c >= 0; --c ) {
+ if( sd->status.inventory[i].card[c] && itemdb_type(sd->status.inventory[i].card[c]) == IT_CARD ) {
+ cardflag = 1;
+
+ if(typefail == 2) {// add cards to inventory, clear
+ int flag;
+ struct item item_tmp;
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+
+ item_tmp.nameid = sd->status.inventory[i].card[c];
+ item_tmp.identify = 1;
+
+ if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ }
+ }
+
+ if(cardflag == 1) {
+ if(typefail == 0 || typefail == 2){ // destroy the item
+ pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT);
+ }
+ if(typefail == 1){ // destroy the card
+ int flag;
+ struct item item_tmp;
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+
+ item_tmp.nameid = sd->status.inventory[i].nameid;
+ item_tmp.identify = 1;
+ item_tmp.refine = sd->status.inventory[i].refine;
+ item_tmp.attribute = sd->status.inventory[i].attribute;
+ item_tmp.expire_time = sd->status.inventory[i].expire_time;
+
+ for (j = sd->inventory_data[i]->slot; j < MAX_SLOTS; j++)
+ item_tmp.card[j]=sd->status.inventory[i].card[j];
+
+ pc_delitem(sd,i,1,0,2,LOG_TYPE_SCRIPT);
+
+ if((flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_SCRIPT))){
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ clif_misceffect(&sd->bl,2);
+ }
+
+ return 0;
+}
+
+/* ================================================================
+ * mapwarp "<from map>","<to map>",<x>,<y>,<type>,<ID for Type>;
+ * type: 0=everyone, 1=guild, 2=party; [Reddozen]
+ * improved by [Lance]
+ * ================================================================*/
+BUILDIN_FUNC(mapwarp) // Added by RoVeRT
+{
+ int x,y,m,check_val=0,check_ID=0,i=0;
+ struct guild *g = NULL;
+ struct party_data *p = NULL;
+ const char *str;
+ const char *mapname;
+ unsigned int index;
+ mapname=script_getstr(st,2);
+ str=script_getstr(st,3);
+ x=script_getnum(st,4);
+ y=script_getnum(st,5);
+ if(script_hasdata(st,7)){
+ check_val=script_getnum(st,6);
+ check_ID=script_getnum(st,7);
+ }
+
+ if((m=map_mapname2mapid(mapname))< 0)
+ return 0;
+
+ if(!(index=mapindex_name2id(str)))
+ return 0;
+
+ switch(check_val){
+ case 1:
+ g = guild_search(check_ID);
+ if (g){
+ for( i=0; i < g->max_member; i++)
+ {
+ if(g->member[i].sd && g->member[i].sd->bl.m==m){
+ pc_setpos(g->member[i].sd,index,x,y,CLR_TELEPORT);
+ }
+ }
+ }
+ break;
+ case 2:
+ p = party_search(check_ID);
+ if(p){
+ for(i=0;i<MAX_PARTY; i++){
+ if(p->data[i].sd && p->data[i].sd->bl.m == m){
+ pc_setpos(p->data[i].sd,index,x,y,CLR_TELEPORT);
+ }
+ }
+ }
+ break;
+ default:
+ map_foreachinmap(buildin_areawarp_sub,m,BL_PC,index,x,y,0,0);
+ break;
+ }
+
+ return 0;
+}
+
+static int buildin_mobcount_sub(struct block_list *bl,va_list ap) // Added by RoVeRT
+{
+ char *event=va_arg(ap,char *);
+ struct mob_data *md = ((struct mob_data *)bl);
+ if( md->status.hp > 0 && (!event || strcmp(event,md->npc_event) == 0) )
+ return 1;
+ return 0;
+}
+
+BUILDIN_FUNC(mobcount) // Added by RoVeRT
+{
+ const char *mapname,*event;
+ int16 m;
+ mapname=script_getstr(st,2);
+ event=script_getstr(st,3);
+
+ if( strcmp(event, "all") == 0 )
+ event = NULL;
+ else
+ check_event(st, event);
+
+ if( strcmp(mapname, "this") == 0 ) {
+ struct map_session_data *sd = script_rid2sd(st);
+ if( sd )
+ m = sd->bl.m;
+ else {
+ script_pushint(st,-1);
+ return 0;
+ }
+ }
+ else if( (m = map_mapname2mapid(mapname)) < 0 ) {
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ if( map[m].flag.src4instance && map[m].instance_id == 0 && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 )
+ {
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ script_pushint(st,map_foreachinmap(buildin_mobcount_sub, m, BL_MOB, event));
+
+ return 0;
+}
+
+BUILDIN_FUNC(marriage)
+{
+ const char *partner=script_getstr(st,2);
+ TBL_PC *sd=script_rid2sd(st);
+ TBL_PC *p_sd=map_nick2sd(partner);
+
+ if(sd==NULL || p_sd==NULL || pc_marriage(sd,p_sd) < 0){
+ script_pushint(st,0);
+ return 0;
+ }
+ script_pushint(st,1);
+ return 0;
+}
+BUILDIN_FUNC(wedding_effect)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ struct block_list *bl;
+
+ if(sd==NULL) {
+ bl=map_id2bl(st->oid);
+ } else
+ bl=&sd->bl;
+ clif_wedding_effect(bl);
+ return 0;
+}
+BUILDIN_FUNC(divorce)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ if(sd==NULL || pc_divorce(sd) < 0){
+ script_pushint(st,0);
+ return 0;
+ }
+ script_pushint(st,1);
+ return 0;
+}
+
+BUILDIN_FUNC(ispartneron)
+{
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || !pc_ismarried(sd) ||
+ map_charid2sd(sd->status.partner_id) == NULL) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ script_pushint(st,1);
+ return 0;
+}
+
+BUILDIN_FUNC(getpartnerid)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ script_pushint(st,sd->status.partner_id);
+ return 0;
+}
+
+BUILDIN_FUNC(getchildid)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ script_pushint(st,sd->status.child);
+ return 0;
+}
+
+BUILDIN_FUNC(getmotherid)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ script_pushint(st,sd->status.mother);
+ return 0;
+}
+
+BUILDIN_FUNC(getfatherid)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ if (sd == NULL) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ script_pushint(st,sd->status.father);
+ return 0;
+}
+
+BUILDIN_FUNC(warppartner)
+{
+ int x,y;
+ unsigned short mapindex;
+ const char *str;
+ TBL_PC *sd=script_rid2sd(st);
+ TBL_PC *p_sd=NULL;
+
+ if(sd==NULL || !pc_ismarried(sd) ||
+ (p_sd=map_charid2sd(sd->status.partner_id)) == NULL) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ str=script_getstr(st,2);
+ x=script_getnum(st,3);
+ y=script_getnum(st,4);
+
+ mapindex = mapindex_name2id(str);
+ if (mapindex) {
+ pc_setpos(p_sd,mapindex,x,y,CLR_OUTSIGHT);
+ script_pushint(st,1);
+ } else
+ script_pushint(st,0);
+ return 0;
+}
+
+/*================================================
+ * Script for Displaying MOB Information [Valaris]
+ *------------------------------------------------*/
+BUILDIN_FUNC(strmobinfo)
+{
+
+ int num=script_getnum(st,2);
+ int class_=script_getnum(st,3);
+
+ if(!mobdb_checkid(class_))
+ {
+ if (num < 3) //requested a string
+ script_pushconststr(st,"");
+ else
+ script_pushint(st,0);
+ return 0;
+ }
+
+ switch (num) {
+ case 1: script_pushstrcopy(st,mob_db(class_)->name); break;
+ case 2: script_pushstrcopy(st,mob_db(class_)->jname); break;
+ case 3: script_pushint(st,mob_db(class_)->lv); break;
+ case 4: script_pushint(st,mob_db(class_)->status.max_hp); break;
+ case 5: script_pushint(st,mob_db(class_)->status.max_sp); break;
+ case 6: script_pushint(st,mob_db(class_)->base_exp); break;
+ case 7: script_pushint(st,mob_db(class_)->job_exp); break;
+ default:
+ script_pushint(st,0);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Summon guardians [Valaris]
+ * guardian("<map name>",<x>,<y>,"<name to show>",<mob id>{,"<event label>"}{,<guardian index>}) -> <id>
+ *------------------------------------------*/
+BUILDIN_FUNC(guardian)
+{
+ int class_=0,x=0,y=0,guardian=0;
+ const char *str,*map,*evt="";
+ struct script_data *data;
+ bool has_index = false;
+
+ map =script_getstr(st,2);
+ x =script_getnum(st,3);
+ y =script_getnum(st,4);
+ str =script_getstr(st,5);
+ class_=script_getnum(st,6);
+
+ if( script_hasdata(st,8) )
+ {// "<event label>",<guardian index>
+ evt=script_getstr(st,7);
+ guardian=script_getnum(st,8);
+ has_index = true;
+ } else if( script_hasdata(st,7) ){
+ data=script_getdata(st,7);
+ get_val(st,data);
+ if( data_isstring(data) )
+ {// "<event label>"
+ evt=script_getstr(st,7);
+ } else if( data_isint(data) )
+ {// <guardian index>
+ guardian=script_getnum(st,7);
+ has_index = true;
+ } else {
+ ShowError("script:guardian: invalid data type for argument #6 (from 1)\n");
+ script_reportdata(data);
+ return 1;
+ }
+ }
+
+ check_event(st, evt);
+ script_pushint(st, mob_spawn_guardian(map,x,y,str,class_,evt,guardian,has_index));
+
+ return 0;
+}
+/*==========================================
+ * Invisible Walls [Zephyrus]
+ *------------------------------------------*/
+BUILDIN_FUNC(setwall)
+{
+ const char *map, *name;
+ int x, y, m, size, dir;
+ bool shootable;
+
+ map = script_getstr(st,2);
+ x = script_getnum(st,3);
+ y = script_getnum(st,4);
+ size = script_getnum(st,5);
+ dir = script_getnum(st,6);
+ shootable = script_getnum(st,7);
+ name = script_getstr(st,8);
+
+ if( (m = map_mapname2mapid(map)) < 0 )
+ return 0; // Invalid Map
+
+ map_iwall_set(m, x, y, size, dir, shootable, name);
+ return 0;
+}
+BUILDIN_FUNC(delwall)
+{
+ const char *name = script_getstr(st,2);
+ map_iwall_remove(name);
+
+ return 0;
+}
+
+/// Retrieves various information about the specified guardian.
+///
+/// guardianinfo("<map_name>", <index>, <type>) -> <value>
+/// type: 0 - whether it is deployed or not
+/// 1 - maximum hp
+/// 2 - current hp
+///
+BUILDIN_FUNC(guardianinfo)
+{
+ const char* mapname = mapindex_getmapname(script_getstr(st,2),NULL);
+ int id = script_getnum(st,3);
+ int type = script_getnum(st,4);
+
+ struct guild_castle* gc = guild_mapname2gc(mapname);
+ struct mob_data* gd;
+
+ if( gc == NULL || id < 0 || id >= MAX_GUARDIANS )
+ {
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ if( type == 0 )
+ script_pushint(st, gc->guardian[id].visible);
+ else
+ if( !gc->guardian[id].visible )
+ script_pushint(st,-1);
+ else
+ if( (gd = map_id2md(gc->guardian[id].id)) == NULL )
+ script_pushint(st,-1);
+ else
+ {
+ if ( type == 1 ) script_pushint(st,gd->status.max_hp);
+ else if( type == 2 ) script_pushint(st,gd->status.hp);
+ else
+ script_pushint(st,-1);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Get the item name by item_id or null
+ *------------------------------------------*/
+BUILDIN_FUNC(getitemname)
+{
+ int item_id=0;
+ struct item_data *i_data;
+ char *item_name;
+ struct script_data *data;
+
+ data=script_getdata(st,2);
+ get_val(st,data);
+
+ if( data_isstring(data) ){
+ const char *name=conv_str(st,data);
+ struct item_data *item_data = itemdb_searchname(name);
+ if( item_data )
+ item_id=item_data->nameid;
+ }else
+ item_id=conv_num(st,data);
+
+ i_data = itemdb_exists(item_id);
+ if (i_data == NULL)
+ {
+ script_pushconststr(st,"null");
+ return 0;
+ }
+ item_name=(char *)aMalloc(ITEM_NAME_LENGTH*sizeof(char));
+
+ memcpy(item_name, i_data->jname, ITEM_NAME_LENGTH);
+ script_pushstr(st,item_name);
+ return 0;
+}
+/*==========================================
+ * Returns number of slots an item has. [Skotlex]
+ *------------------------------------------*/
+BUILDIN_FUNC(getitemslots)
+{
+ int item_id;
+ struct item_data *i_data;
+
+ item_id=script_getnum(st,2);
+
+ i_data = itemdb_exists(item_id);
+
+ if (i_data)
+ script_pushint(st,i_data->slot);
+ else
+ script_pushint(st,-1);
+ return 0;
+}
+
+// TODO: add matk here if needed/once we get rid of RENEWAL
+
+/*==========================================
+ * Returns some values of an item [Lupus]
+ * Price, Weight, etc...
+ getiteminfo(itemID,n), where n
+ 0 value_buy;
+ 1 value_sell;
+ 2 type;
+ 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc..
+ if = 0, then monsters don't drop it at all (rare or a quest item)
+ if = -1, then this item is sold in NPC shops only
+ 4 sex;
+ 5 equip;
+ 6 weight;
+ 7 atk;
+ 8 def;
+ 9 range;
+ 10 slot;
+ 11 look;
+ 12 elv;
+ 13 wlv;
+ 14 view id
+ *------------------------------------------*/
+BUILDIN_FUNC(getiteminfo)
+{
+ int item_id,n;
+ int *item_arr;
+ struct item_data *i_data;
+
+ item_id = script_getnum(st,2);
+ n = script_getnum(st,3);
+ i_data = itemdb_exists(item_id);
+
+ if (i_data && n>=0 && n<=14) {
+ item_arr = (int*)&i_data->value_buy;
+ script_pushint(st,item_arr[n]);
+ } else
+ script_pushint(st,-1);
+ return 0;
+}
+
+/*==========================================
+ * Set some values of an item [Lupus]
+ * Price, Weight, etc...
+ setiteminfo(itemID,n,Value), where n
+ 0 value_buy;
+ 1 value_sell;
+ 2 type;
+ 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc..
+ if = 0, then monsters don't drop it at all (rare or a quest item)
+ if = -1, then this item is sold in NPC shops only
+ 4 sex;
+ 5 equip;
+ 6 weight;
+ 7 atk;
+ 8 def;
+ 9 range;
+ 10 slot;
+ 11 look;
+ 12 elv;
+ 13 wlv;
+ 14 view id
+ * Returns Value or -1 if the wrong field's been set
+ *------------------------------------------*/
+BUILDIN_FUNC(setiteminfo)
+{
+ int item_id,n,value;
+ int *item_arr;
+ struct item_data *i_data;
+
+ item_id = script_getnum(st,2);
+ n = script_getnum(st,3);
+ value = script_getnum(st,4);
+ i_data = itemdb_exists(item_id);
+
+ if (i_data && n>=0 && n<=14) {
+ item_arr = (int*)&i_data->value_buy;
+ item_arr[n] = value;
+ script_pushint(st,value);
+ } else
+ script_pushint(st,-1);
+ return 0;
+}
+
+/*==========================================
+ * Returns value from equipped item slot n [Lupus]
+ getequipcardid(num,slot)
+ where
+ num = eqip position slot
+ slot = 0,1,2,3 (Card Slot N)
+
+ This func returns CARD ID, 255,254,-255 (for card 0, if the item is produced)
+ it's useful when you want to check item cards or if it's signed
+ Useful for such quests as "Sign this refined item with players name" etc
+ Hat[0] +4 -> Player's Hat[0] +4
+ *------------------------------------------*/
+BUILDIN_FUNC(getequipcardid)
+{
+ int i=-1,num,slot;
+ TBL_PC *sd;
+
+ num=script_getnum(st,2);
+ slot=script_getnum(st,3);
+ sd=script_rid2sd(st);
+ if (num > 0 && num <= ARRAYLENGTH(equip))
+ i=pc_checkequip(sd,equip[num-1]);
+ if(i >= 0 && slot>=0 && slot<4)
+ script_pushint(st,sd->status.inventory[i].card[slot]);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * petskillbonus [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------*/
+BUILDIN_FUNC(petskillbonus)
+{
+ struct pet_data *pd;
+
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->bonus)
+ { //Clear previous bonus
+ if (pd->bonus->timer != INVALID_TIMER)
+ delete_timer(pd->bonus->timer, pet_skill_bonus_timer);
+ } else //init
+ pd->bonus = (struct pet_bonus *) aMalloc(sizeof(struct pet_bonus));
+
+ pd->bonus->type=script_getnum(st,2);
+ pd->bonus->val=script_getnum(st,3);
+ pd->bonus->duration=script_getnum(st,4);
+ pd->bonus->delay=script_getnum(st,5);
+
+ if (pd->state.skillbonus == 1)
+ pd->state.skillbonus=0; // waiting state
+
+ // wait for timer to start
+ if (battle_config.pet_equip_required && pd->pet.equip == 0)
+ pd->bonus->timer = INVALID_TIMER;
+ else
+ pd->bonus->timer = add_timer(gettick()+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0);
+
+ return 0;
+}
+
+/*==========================================
+ * pet looting [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------*/
+BUILDIN_FUNC(petloot)
+{
+ int max;
+ struct pet_data *pd;
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ max=script_getnum(st,2);
+
+ if(max < 1)
+ max = 1; //Let'em loot at least 1 item.
+ else if (max > MAX_PETLOOT_SIZE)
+ max = MAX_PETLOOT_SIZE;
+
+ pd = sd->pd;
+ if (pd->loot != NULL)
+ { //Release whatever was there already and reallocate memory
+ pet_lootitem_drop(pd, pd->msd);
+ aFree(pd->loot->item);
+ }
+ else
+ pd->loot = (struct pet_loot *)aMalloc(sizeof(struct pet_loot));
+
+ pd->loot->item = (struct item *)aCalloc(max,sizeof(struct item));
+
+ pd->loot->max=max;
+ pd->loot->count = 0;
+ pd->loot->weight = 0;
+
+ return 0;
+}
+/*==========================================
+ * Set arrays with info of all sd inventory :
+ * @inventorylist_id, @inventorylist_amount, @inventorylist_equip,
+ * @inventorylist_refine, @inventorylist_identify, @inventorylist_attribute,
+ * @inventorylist_card(0..3), @inventorylist_expire
+ * @inventorylist_count = scalar
+ *------------------------------------------*/
+BUILDIN_FUNC(getinventorylist)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ char card_var[NAME_LENGTH];
+
+ int i,j=0,k;
+ if(!sd) return 0;
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0){
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_id"), j),sd->status.inventory[i].nameid);
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_amount"), j),sd->status.inventory[i].amount);
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_equip"), j),sd->status.inventory[i].equip);
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_refine"), j),sd->status.inventory[i].refine);
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_identify"), j),sd->status.inventory[i].identify);
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_attribute"), j),sd->status.inventory[i].attribute);
+ for (k = 0; k < MAX_SLOTS; k++)
+ {
+ sprintf(card_var, "@inventorylist_card%d",k+1);
+ pc_setreg(sd,reference_uid(add_str(card_var), j),sd->status.inventory[i].card[k]);
+ }
+ pc_setreg(sd,reference_uid(add_str("@inventorylist_expire"), j),sd->status.inventory[i].expire_time);
+ j++;
+ }
+ }
+ pc_setreg(sd,add_str("@inventorylist_count"),j);
+ return 0;
+}
+
+BUILDIN_FUNC(getskilllist)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ int i,j=0;
+ if(!sd) return 0;
+ for(i=0;i<MAX_SKILL;i++){
+ if(sd->status.skill[i].id > 0 && sd->status.skill[i].lv > 0){
+ pc_setreg(sd,reference_uid(add_str("@skilllist_id"), j),sd->status.skill[i].id);
+ pc_setreg(sd,reference_uid(add_str("@skilllist_lv"), j),sd->status.skill[i].lv);
+ pc_setreg(sd,reference_uid(add_str("@skilllist_flag"), j),sd->status.skill[i].flag);
+ j++;
+ }
+ }
+ pc_setreg(sd,add_str("@skilllist_count"),j);
+ return 0;
+}
+
+BUILDIN_FUNC(clearitem)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ int i;
+ if(sd==NULL) return 0;
+ for (i=0; i<MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].amount) {
+ pc_delitem(sd, i, sd->status.inventory[i].amount, 0, 0, LOG_TYPE_SCRIPT);
+ }
+ }
+ return 0;
+}
+
+/*==========================================
+ * Disguise Player (returns Mob/NPC ID if success, 0 on fail)
+ *------------------------------------------*/
+BUILDIN_FUNC(disguise)
+{
+ int id;
+ TBL_PC* sd = script_rid2sd(st);
+ if (sd == NULL) return 0;
+
+ id = script_getnum(st,2);
+
+ if (mobdb_checkid(id) || npcdb_checkid(id)) {
+ pc_disguise(sd, id);
+ script_pushint(st,id);
+ } else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Undisguise Player (returns 1 if success, 0 on fail)
+ *------------------------------------------*/
+BUILDIN_FUNC(undisguise)
+{
+ TBL_PC* sd = script_rid2sd(st);
+ if (sd == NULL) return 0;
+
+ if (sd->disguise) {
+ pc_disguise(sd, 0);
+ script_pushint(st,0);
+ } else {
+ script_pushint(st,1);
+ }
+ return 0;
+}
+
+/*==========================================
+ * Transform a bl to another _class,
+ * @type unused
+ *------------------------------------------*/
+BUILDIN_FUNC(classchange)
+{
+ int _class,type;
+ struct block_list *bl=map_id2bl(st->oid);
+
+ if(bl==NULL) return 0;
+
+ _class=script_getnum(st,2);
+ type=script_getnum(st,3);
+ clif_class_change(bl,_class,type);
+ return 0;
+}
+
+/*==========================================
+ * Display an effect
+ *------------------------------------------*/
+BUILDIN_FUNC(misceffect)
+{
+ int type;
+
+ type=script_getnum(st,2);
+ if(st->oid && st->oid != fake_nd->bl.id) {
+ struct block_list *bl = map_id2bl(st->oid);
+ if (bl)
+ clif_specialeffect(bl,type,AREA);
+ } else{
+ TBL_PC *sd=script_rid2sd(st);
+ if(sd)
+ clif_specialeffect(&sd->bl,type,AREA);
+ }
+ return 0;
+}
+/*==========================================
+ * Play a BGM on a single client [Rikter/Yommy]
+ *------------------------------------------*/
+BUILDIN_FUNC(playBGM)
+{
+ const char* name;
+ struct map_session_data* sd;
+
+ if( ( sd = script_rid2sd(st) ) != NULL )
+ {
+ name = script_getstr(st,2);
+
+ clif_playBGM(sd, name);
+ }
+
+ return 0;
+}
+
+static int playBGM_sub(struct block_list* bl,va_list ap)
+{
+ const char* name = va_arg(ap,const char*);
+
+ clif_playBGM(BL_CAST(BL_PC, bl), name);
+
+ return 0;
+}
+
+static int playBGM_foreachpc_sub(struct map_session_data* sd, va_list args)
+{
+ const char* name = va_arg(args, const char*);
+
+ clif_playBGM(sd, name);
+ return 0;
+}
+
+/*==========================================
+ * Play a BGM on multiple client [Rikter/Yommy]
+ *------------------------------------------*/
+BUILDIN_FUNC(playBGMall)
+{
+ const char* name;
+
+ name = script_getstr(st,2);
+
+ if( script_hasdata(st,7) )
+ {// specified part of map
+ const char* map = script_getstr(st,3);
+ int x0 = script_getnum(st,4);
+ int y0 = script_getnum(st,5);
+ int x1 = script_getnum(st,6);
+ int y1 = script_getnum(st,7);
+
+ map_foreachinarea(playBGM_sub, map_mapname2mapid(map), x0, y0, x1, y1, BL_PC, name);
+ }
+ else if( script_hasdata(st,3) )
+ {// entire map
+ const char* map = script_getstr(st,3);
+
+ map_foreachinmap(playBGM_sub, map_mapname2mapid(map), BL_PC, name);
+ }
+ else
+ {// entire server
+ map_foreachpc(&playBGM_foreachpc_sub, name);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Play a .wav sound for sd
+ *------------------------------------------*/
+BUILDIN_FUNC(soundeffect)
+{
+ TBL_PC* sd = script_rid2sd(st);
+ const char* name = script_getstr(st,2);
+ int type = script_getnum(st,3);
+
+ if(sd)
+ {
+ clif_soundeffect(sd,&sd->bl,name,type);
+ }
+ return 0;
+}
+
+int soundeffect_sub(struct block_list* bl,va_list ap)
+{
+ char* name = va_arg(ap,char*);
+ int type = va_arg(ap,int);
+
+ clif_soundeffect((TBL_PC *)bl, bl, name, type);
+
+ return 0;
+}
+
+/*==========================================
+ * Play a sound effect (.wav) on multiple clients
+ * soundeffectall "<filepath>",<type>{,"<map name>"}{,<x0>,<y0>,<x1>,<y1>};
+ *------------------------------------------*/
+BUILDIN_FUNC(soundeffectall)
+{
+ struct block_list* bl;
+ const char* name;
+ int type;
+
+ bl = (st->rid) ? &(script_rid2sd(st)->bl) : map_id2bl(st->oid);
+ if (!bl)
+ return 0;
+
+ name = script_getstr(st,2);
+ type = script_getnum(st,3);
+
+ //FIXME: enumerating map squares (map_foreach) is slower than enumerating the list of online players (map_foreachpc?) [ultramage]
+
+ if(!script_hasdata(st,4))
+ { // area around
+ clif_soundeffectall(bl, name, type, AREA);
+ }
+ else
+ if(!script_hasdata(st,5))
+ { // entire map
+ const char* map = script_getstr(st,4);
+ map_foreachinmap(soundeffect_sub, map_mapname2mapid(map), BL_PC, name, type);
+ }
+ else
+ if(script_hasdata(st,8))
+ { // specified part of map
+ const char* map = script_getstr(st,4);
+ int x0 = script_getnum(st,5);
+ int y0 = script_getnum(st,6);
+ int x1 = script_getnum(st,7);
+ int y1 = script_getnum(st,8);
+ map_foreachinarea(soundeffect_sub, map_mapname2mapid(map), x0, y0, x1, y1, BL_PC, name, type);
+ }
+ else
+ {
+ ShowError("buildin_soundeffectall: insufficient arguments for specific area broadcast.\n");
+ }
+
+ return 0;
+}
+/*==========================================
+ * pet status recovery [Valaris] / Rewritten by [Skotlex]
+ *------------------------------------------*/
+BUILDIN_FUNC(petrecovery)
+{
+ struct pet_data *pd;
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+
+ if (pd->recovery)
+ { //Halt previous bonus
+ if (pd->recovery->timer != INVALID_TIMER)
+ delete_timer(pd->recovery->timer, pet_recovery_timer);
+ } else //Init
+ pd->recovery = (struct pet_recovery *)aMalloc(sizeof(struct pet_recovery));
+
+ pd->recovery->type = (sc_type)script_getnum(st,2);
+ pd->recovery->delay = script_getnum(st,3);
+ pd->recovery->timer = INVALID_TIMER;
+
+ return 0;
+}
+
+/*==========================================
+ * pet healing [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------*/
+BUILDIN_FUNC(petheal)
+{
+ struct pet_data *pd;
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->s_skill)
+ { //Clear previous skill
+ if (pd->s_skill->timer != INVALID_TIMER)
+ {
+ if (pd->s_skill->id)
+ delete_timer(pd->s_skill->timer, pet_skill_support_timer);
+ else
+ delete_timer(pd->s_skill->timer, pet_heal_timer);
+ }
+ } else //init memory
+ pd->s_skill = (struct pet_skill_support *) aMalloc(sizeof(struct pet_skill_support));
+
+ pd->s_skill->id=0; //This id identifies that it IS petheal rather than pet_skillsupport
+ //Use the lv as the amount to heal
+ pd->s_skill->lv=script_getnum(st,2);
+ pd->s_skill->delay=script_getnum(st,3);
+ pd->s_skill->hp=script_getnum(st,4);
+ pd->s_skill->sp=script_getnum(st,5);
+
+ //Use delay as initial offset to avoid skill/heal exploits
+ if (battle_config.pet_equip_required && pd->pet.equip == 0)
+ pd->s_skill->timer = INVALID_TIMER;
+ else
+ pd->s_skill->timer = add_timer(gettick()+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ * pet attack skills [Valaris] //Rewritten by [Skotlex]
+ *------------------------------------------*/
+/// petskillattack <skill id>,<level>,<rate>,<bonusrate>
+/// petskillattack "<skill name>",<level>,<rate>,<bonusrate>
+BUILDIN_FUNC(petskillattack)
+{
+ struct pet_data *pd;
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->a_skill == NULL)
+ pd->a_skill = (struct pet_skill_attack *)aMalloc(sizeof(struct pet_skill_attack));
+
+ pd->a_skill->id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ pd->a_skill->lv=script_getnum(st,3);
+ pd->a_skill->div_ = 0;
+ pd->a_skill->rate=script_getnum(st,4);
+ pd->a_skill->bonusrate=script_getnum(st,5);
+
+ return 0;
+}
+
+/*==========================================
+ * pet attack skills [Valaris]
+ *------------------------------------------*/
+/// petskillattack2 <skill id>,<level>,<div>,<rate>,<bonusrate>
+/// petskillattack2 "<skill name>",<level>,<div>,<rate>,<bonusrate>
+BUILDIN_FUNC(petskillattack2)
+{
+ struct pet_data *pd;
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->a_skill == NULL)
+ pd->a_skill = (struct pet_skill_attack *)aMalloc(sizeof(struct pet_skill_attack));
+
+ pd->a_skill->id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ pd->a_skill->lv=script_getnum(st,3);
+ pd->a_skill->div_ = script_getnum(st,4);
+ pd->a_skill->rate=script_getnum(st,5);
+ pd->a_skill->bonusrate=script_getnum(st,6);
+
+ return 0;
+}
+
+/*==========================================
+ * pet support skills [Skotlex]
+ *------------------------------------------*/
+/// petskillsupport <skill id>,<level>,<delay>,<hp>,<sp>
+/// petskillsupport "<skill name>",<level>,<delay>,<hp>,<sp>
+BUILDIN_FUNC(petskillsupport)
+{
+ struct pet_data *pd;
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd==NULL || sd->pd==NULL)
+ return 0;
+
+ pd=sd->pd;
+ if (pd->s_skill)
+ { //Clear previous skill
+ if (pd->s_skill->timer != INVALID_TIMER)
+ {
+ if (pd->s_skill->id)
+ delete_timer(pd->s_skill->timer, pet_skill_support_timer);
+ else
+ delete_timer(pd->s_skill->timer, pet_heal_timer);
+ }
+ } else //init memory
+ pd->s_skill = (struct pet_skill_support *) aMalloc(sizeof(struct pet_skill_support));
+
+ pd->s_skill->id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ pd->s_skill->lv=script_getnum(st,3);
+ pd->s_skill->delay=script_getnum(st,4);
+ pd->s_skill->hp=script_getnum(st,5);
+ pd->s_skill->sp=script_getnum(st,6);
+
+ //Use delay as initial offset to avoid skill/heal exploits
+ if (battle_config.pet_equip_required && pd->pet.equip == 0)
+ pd->s_skill->timer = INVALID_TIMER;
+ else
+ pd->s_skill->timer = add_timer(gettick()+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+/*==========================================
+ * Scripted skill effects [Celest]
+ *------------------------------------------*/
+/// skilleffect <skill id>,<level>
+/// skilleffect "<skill name>",<level>
+BUILDIN_FUNC(skilleffect)
+{
+ TBL_PC *sd;
+
+ uint16 skill_id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ uint16 skill_lv=script_getnum(st,3);
+ sd=script_rid2sd(st);
+
+ clif_skill_nodamage(&sd->bl,&sd->bl,skill_id,skill_lv,1);
+
+ return 0;
+}
+
+/*==========================================
+ * NPC skill effects [Valaris]
+ *------------------------------------------*/
+/// npcskilleffect <skill id>,<level>,<x>,<y>
+/// npcskilleffect "<skill name>",<level>,<x>,<y>
+BUILDIN_FUNC(npcskilleffect)
+{
+ struct block_list *bl= map_id2bl(st->oid);
+
+ uint16 skill_id=( script_isstring(st,2) ? skill_name2id(script_getstr(st,2)) : script_getnum(st,2) );
+ uint16 skill_lv=script_getnum(st,3);
+ int x=script_getnum(st,4);
+ int y=script_getnum(st,5);
+
+ if (bl)
+ clif_skill_poseffect(bl,skill_id,skill_lv,x,y,gettick());
+
+ return 0;
+}
+
+/*==========================================
+ * Special effects [Valaris]
+ *------------------------------------------*/
+BUILDIN_FUNC(specialeffect)
+{
+ struct block_list *bl=map_id2bl(st->oid);
+ int type = script_getnum(st,2);
+ enum send_target target = script_hasdata(st,3) ? (send_target)script_getnum(st,3) : AREA;
+
+ if(bl==NULL)
+ return 0;
+
+ if( script_hasdata(st,4) )
+ {
+ TBL_NPC *nd = npc_name2id(script_getstr(st,4));
+ if(nd)
+ clif_specialeffect(&nd->bl, type, target);
+ }
+ else
+ {
+ if (target == SELF) {
+ TBL_PC *sd=script_rid2sd(st);
+ if (sd)
+ clif_specialeffect_single(bl,type,sd->fd);
+ } else {
+ clif_specialeffect(bl, type, target);
+ }
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(specialeffect2)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ int type = script_getnum(st,2);
+ enum send_target target = script_hasdata(st,3) ? (send_target)script_getnum(st,3) : AREA;
+
+ if( script_hasdata(st,4) )
+ sd = map_nick2sd(script_getstr(st,4));
+
+ if (sd)
+ clif_specialeffect(&sd->bl, type, target);
+
+ return 0;
+}
+
+/*==========================================
+ * Nude [Valaris]
+ *------------------------------------------*/
+BUILDIN_FUNC(nude)
+{
+ TBL_PC *sd = script_rid2sd(st);
+ int i, calcflag = 0;
+
+ if( sd == NULL )
+ return 0;
+
+ for( i = 0 ; i < EQI_MAX; i++ ) {
+ if( sd->equip_index[ i ] >= 0 ) {
+ if( !calcflag )
+ calcflag = 1;
+ pc_unequipitem( sd , sd->equip_index[ i ] , 2);
+ }
+ }
+
+ if( calcflag )
+ status_calc_pc(sd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * gmcommand [MouseJstr]
+ *------------------------------------------*/
+BUILDIN_FUNC(atcommand)
+{
+ TBL_PC dummy_sd;
+ TBL_PC* sd;
+ int fd;
+ const char* cmd;
+
+ cmd = script_getstr(st,2);
+
+ if (st->rid) {
+ sd = script_rid2sd(st);
+ fd = sd->fd;
+ } else { //Use a dummy character.
+ sd = &dummy_sd;
+ fd = 0;
+
+ memset(&dummy_sd, 0, sizeof(TBL_PC));
+ if (st->oid)
+ {
+ struct block_list* bl = map_id2bl(st->oid);
+ memcpy(&dummy_sd.bl, bl, sizeof(struct block_list));
+ if (bl->type == BL_NPC)
+ safestrncpy(dummy_sd.status.name, ((TBL_NPC*)bl)->name, NAME_LENGTH);
+ }
+ }
+
+ if (!is_atcommand(fd, sd, cmd, 0)) {
+ ShowWarning("script: buildin_atcommand: failed to execute command '%s'\n", cmd);
+ script_reportsrc(st);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Displays a message for the player only (like system messages like "you got an apple" )
+ *------------------------------------------*/
+BUILDIN_FUNC(dispbottom)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ const char *message;
+ message=script_getstr(st,2);
+ if(sd)
+ clif_disp_onlyself(sd,message,(int)strlen(message));
+ return 0;
+}
+
+/*==========================================
+ * All The Players Full Recovery
+ * (HP/SP full restore and resurrect if need)
+ *------------------------------------------*/
+BUILDIN_FUNC(recovery)
+{
+ TBL_PC* sd;
+ struct s_mapiterator* iter;
+
+ iter = mapit_getallusers();
+ for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) )
+ {
+ if(pc_isdead(sd))
+ status_revive(&sd->bl, 100, 100);
+ else
+ status_percent_heal(&sd->bl, 100, 100);
+ clif_displaymessage(sd->fd,msg_txt(680));
+ }
+ mapit_free(iter);
+ return 0;
+}
+/*==========================================
+ * Get your pet info: getpetinfo(n)
+ * n -> 0:pet_id 1:pet_class 2:pet_name
+ * 3:friendly 4:hungry, 5: rename flag.
+ *------------------------------------------*/
+BUILDIN_FUNC(getpetinfo)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ TBL_PET *pd;
+ int type=script_getnum(st,2);
+
+ if(!sd || !sd->pd) {
+ if (type == 2)
+ script_pushconststr(st,"null");
+ else
+ script_pushint(st,0);
+ return 0;
+ }
+ pd = sd->pd;
+ switch(type){
+ case 0: script_pushint(st,pd->pet.pet_id); break;
+ case 1: script_pushint(st,pd->pet.class_); break;
+ case 2: script_pushstrcopy(st,pd->pet.name); break;
+ case 3: script_pushint(st,pd->pet.intimate); break;
+ case 4: script_pushint(st,pd->pet.hungry); break;
+ case 5: script_pushint(st,pd->pet.rename_flag); break;
+ default:
+ script_pushint(st,0);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Get your homunculus info: gethominfo(n)
+ * n -> 0:hom_id 1:class 2:name
+ * 3:friendly 4:hungry, 5: rename flag.
+ * 6: level
+ *------------------------------------------*/
+BUILDIN_FUNC(gethominfo)
+{
+ TBL_PC *sd=script_rid2sd(st);
+ TBL_HOM *hd;
+ int type=script_getnum(st,2);
+
+ hd = sd?sd->hd:NULL;
+ if(!merc_is_hom_active(hd))
+ {
+ if (type == 2)
+ script_pushconststr(st,"null");
+ else
+ script_pushint(st,0);
+ return 0;
+ }
+
+ switch(type){
+ case 0: script_pushint(st,hd->homunculus.hom_id); break;
+ case 1: script_pushint(st,hd->homunculus.class_); break;
+ case 2: script_pushstrcopy(st,hd->homunculus.name); break;
+ case 3: script_pushint(st,hd->homunculus.intimacy); break;
+ case 4: script_pushint(st,hd->homunculus.hunger); break;
+ case 5: script_pushint(st,hd->homunculus.rename_flag); break;
+ case 6: script_pushint(st,hd->homunculus.level); break;
+ default:
+ script_pushint(st,0);
+ break;
+ }
+ return 0;
+}
+
+/// Retrieves information about character's mercenary
+/// getmercinfo <type>[,<char id>];
+BUILDIN_FUNC(getmercinfo)
+{
+ int type, char_id;
+ struct map_session_data* sd;
+ struct mercenary_data* md;
+
+ type = script_getnum(st,2);
+
+ if( script_hasdata(st,3) )
+ {
+ char_id = script_getnum(st,3);
+
+ if( ( sd = map_charid2sd(char_id) ) == NULL )
+ {
+ ShowError("buildin_getmercinfo: No such character (char_id=%d).\n", char_id);
+ script_pushnil(st);
+ return 1;
+ }
+ }
+ else
+ {
+ if( ( sd = script_rid2sd(st) ) == NULL )
+ {
+ script_pushnil(st);
+ return 0;
+ }
+ }
+
+ md = ( sd->status.mer_id && sd->md ) ? sd->md : NULL;
+
+ switch( type )
+ {
+ case 0: script_pushint(st,md ? md->mercenary.mercenary_id : 0); break;
+ case 1: script_pushint(st,md ? md->mercenary.class_ : 0); break;
+ case 2:
+ if( md )
+ script_pushstrcopy(st,md->db->name);
+ else
+ script_pushconststr(st,"");
+ break;
+ case 3: script_pushint(st,md ? mercenary_get_faith(md) : 0); break;
+ case 4: script_pushint(st,md ? mercenary_get_calls(md) : 0); break;
+ case 5: script_pushint(st,md ? md->mercenary.kill_count : 0); break;
+ case 6: script_pushint(st,md ? mercenary_get_lifetime(md) : 0); break;
+ case 7: script_pushint(st,md ? md->db->lv : 0); break;
+ default:
+ ShowError("buildin_getmercinfo: Invalid type %d (char_id=%d).\n", type, sd->status.char_id);
+ script_pushnil(st);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Shows wether your inventory(and equips) contain
+ selected card or not.
+ checkequipedcard(4001);
+ *------------------------------------------*/
+BUILDIN_FUNC(checkequipedcard)
+{
+ TBL_PC *sd=script_rid2sd(st);
+
+ if(sd){
+ int n,i,c=0;
+ c=script_getnum(st,2);
+
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount && sd->inventory_data[i]){
+ if (itemdb_isspecial(sd->status.inventory[i].card[0]))
+ continue;
+ for(n=0;n<sd->inventory_data[i]->slot;n++){
+ if(sd->status.inventory[i].card[n]==c){
+ script_pushint(st,1);
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ script_pushint(st,0);
+ return 0;
+}
+
+BUILDIN_FUNC(jump_zero)
+{
+ int sel;
+ sel=script_getnum(st,2);
+ if(!sel) {
+ int pos;
+ if( !data_islabel(script_getdata(st,3)) ){
+ ShowError("script: jump_zero: not label !\n");
+ st->state=END;
+ return 1;
+ }
+
+ pos=script_getnum(st,3);
+ st->pos=pos;
+ st->state=GOTO;
+ }
+ return 0;
+}
+
+/*==========================================
+ * movenpc [MouseJstr]
+ *------------------------------------------*/
+BUILDIN_FUNC(movenpc)
+{
+ TBL_NPC *nd = NULL;
+ const char *npc;
+ int x,y;
+
+ npc = script_getstr(st,2);
+ x = script_getnum(st,3);
+ y = script_getnum(st,4);
+
+ if ((nd = npc_name2id(npc)) == NULL)
+ return -1;
+
+ if (script_hasdata(st,5))
+ nd->ud.dir = script_getnum(st,5) % 8;
+ npc_movenpc(nd, x, y);
+ return 0;
+}
+
+/*==========================================
+ * message [MouseJstr]
+ *------------------------------------------*/
+BUILDIN_FUNC(message)
+{
+ const char *msg,*player;
+ TBL_PC *pl_sd = NULL;
+
+ player = script_getstr(st,2);
+ msg = script_getstr(st,3);
+
+ if((pl_sd=map_nick2sd((char *) player)) == NULL)
+ return 0;
+ clif_displaymessage(pl_sd->fd, msg);
+
+ return 0;
+}
+
+/*==========================================
+ * npctalk (sends message to surrounding area)
+ *------------------------------------------*/
+BUILDIN_FUNC(npctalk)
+{
+ const char* str;
+ char name[NAME_LENGTH], message[256];
+
+ struct npc_data* nd = (struct npc_data *)map_id2bl(st->oid);
+ str = script_getstr(st,2);
+
+ if(nd)
+ {
+ safestrncpy(name, nd->name, sizeof(name));
+ strtok(name, "#"); // discard extra name identifier if present
+ safesnprintf(message, sizeof(message), "%s : %s", name, str);
+ clif_message(&nd->bl, message);
+ }
+
+ return 0;
+}
+
+// change npc walkspeed [Valaris]
+BUILDIN_FUNC(npcspeed)
+{
+ struct npc_data* nd;
+ int speed;
+
+ speed = script_getnum(st,2);
+ nd =(struct npc_data *)map_id2bl(st->oid);
+
+ if( nd )
+ {
+ nd->speed = speed;
+ nd->ud.state.speed_changed = 1;
+ }
+
+ return 0;
+}
+// make an npc walk to a position [Valaris]
+BUILDIN_FUNC(npcwalkto)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+ int x=0,y=0;
+
+ x=script_getnum(st,2);
+ y=script_getnum(st,3);
+
+ if(nd) {
+ if (!nd->status.hp) {
+ status_calc_npc(nd, true);
+ } else {
+ status_calc_npc(nd, false);
+ }
+ unit_walktoxy(&nd->bl,x,y,0);
+ }
+
+ return 0;
+}
+// stop an npc's movement [Valaris]
+BUILDIN_FUNC(npcstop)
+{
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ if(nd) {
+ unit_stop_walking(&nd->bl,1|4);
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * getlook char info. getlook(arg)
+ *------------------------------------------*/
+BUILDIN_FUNC(getlook)
+{
+ int type,val;
+ TBL_PC *sd;
+ sd=script_rid2sd(st);
+
+ type=script_getnum(st,2);
+ val=-1;
+ switch(type) {
+ case LOOK_HAIR: val=sd->status.hair; break; //1
+ case LOOK_WEAPON: val=sd->status.weapon; break; //2
+ case LOOK_HEAD_BOTTOM: val=sd->status.head_bottom; break; //3
+ case LOOK_HEAD_TOP: val=sd->status.head_top; break; //4
+ case LOOK_HEAD_MID: val=sd->status.head_mid; break; //5
+ case LOOK_HAIR_COLOR: val=sd->status.hair_color; break; //6
+ case LOOK_CLOTHES_COLOR: val=sd->status.clothes_color; break; //7
+ case LOOK_SHIELD: val=sd->status.shield; break; //8
+ case LOOK_SHOES: break; //9
+ }
+
+ script_pushint(st,val);
+ return 0;
+}
+
+/*==========================================
+ * get char save point. argument: 0- map name, 1- x, 2- y
+ *------------------------------------------*/
+BUILDIN_FUNC(getsavepoint)
+{
+ TBL_PC* sd;
+ int type;
+
+ sd = script_rid2sd(st);
+ if (sd == NULL) {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ type = script_getnum(st,2);
+
+ switch(type) {
+ case 0: script_pushstrcopy(st,mapindex_id2name(sd->status.save_point.map)); break;
+ case 1: script_pushint(st,sd->status.save_point.x); break;
+ case 2: script_pushint(st,sd->status.save_point.y); break;
+ default:
+ script_pushint(st,0);
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Get position for char/npc/pet/mob objects. Added by Lorky
+ *
+ * int getMapXY(MapName$,MapX,MapY,type,[CharName$]);
+ * where type:
+ * MapName$ - String variable for output map name
+ * MapX - Integer variable for output coord X
+ * MapY - Integer variable for output coord Y
+ * type - type of object
+ * 0 - Character coord
+ * 1 - NPC coord
+ * 2 - Pet coord
+ * 3 - Mob coord (not released)
+ * 4 - Homun coord
+ * 5 - Mercenary coord
+ * 6 - Elemental coord
+ * CharName$ - Name object. If miss or "this" the current object
+ *
+ * Return:
+ * 0 - success
+ * -1 - some error, MapName$,MapX,MapY contains unknown value.
+ *------------------------------------------*/
+BUILDIN_FUNC(getmapxy)
+{
+ struct block_list *bl = NULL;
+ TBL_PC *sd=NULL;
+
+ int num;
+ const char *name;
+ char prefix;
+
+ int x,y,type;
+ char mapname[MAP_NAME_LENGTH];
+
+ if( !data_isreference(script_getdata(st,2)) ){
+ ShowWarning("script: buildin_getmapxy: not mapname variable\n");
+ script_pushint(st,-1);
+ return 1;
+ }
+ if( !data_isreference(script_getdata(st,3)) ){
+ ShowWarning("script: buildin_getmapxy: not mapx variable\n");
+ script_pushint(st,-1);
+ return 1;
+ }
+ if( !data_isreference(script_getdata(st,4)) ){
+ ShowWarning("script: buildin_getmapxy: not mapy variable\n");
+ script_pushint(st,-1);
+ return 1;
+ }
+
+ // Possible needly check function parameters on C_STR,C_INT,C_INT
+ type=script_getnum(st,5);
+
+ switch (type){
+ case 0: //Get Character Position
+ if( script_hasdata(st,6) )
+ sd=map_nick2sd(script_getstr(st,6));
+ else
+ sd=script_rid2sd(st);
+
+ if (sd)
+ bl = &sd->bl;
+ break;
+ case 1: //Get NPC Position
+ if( script_hasdata(st,6) )
+ {
+ struct npc_data *nd;
+ nd=npc_name2id(script_getstr(st,6));
+ if (nd)
+ bl = &nd->bl;
+ } else //In case the origin is not an npc?
+ bl=map_id2bl(st->oid);
+ break;
+ case 2: //Get Pet Position
+ if(script_hasdata(st,6))
+ sd=map_nick2sd(script_getstr(st,6));
+ else
+ sd=script_rid2sd(st);
+
+ if (sd && sd->pd)
+ bl = &sd->pd->bl;
+ break;
+ case 3: //Get Mob Position
+ break; //Not supported?
+ case 4: //Get Homun Position
+ if(script_hasdata(st,6))
+ sd=map_nick2sd(script_getstr(st,6));
+ else
+ sd=script_rid2sd(st);
+
+ if (sd && sd->hd)
+ bl = &sd->hd->bl;
+ break;
+ case 5: //Get Mercenary Position
+ if(script_hasdata(st,6))
+ sd=map_nick2sd(script_getstr(st,6));
+ else
+ sd=script_rid2sd(st);
+
+ if (sd && sd->md)
+ bl = &sd->md->bl;
+ break;
+ case 6: //Get Elemental Position
+ if(script_hasdata(st,6))
+ sd=map_nick2sd(script_getstr(st,6));
+ else
+ sd=script_rid2sd(st);
+
+ if (sd && sd->ed)
+ bl = &sd->ed->bl;
+ break;
+ default:
+ ShowWarning("script: buildin_getmapxy: Invalid type %d\n", type);
+ script_pushint(st,-1);
+ return 1;
+ }
+ if (!bl) { //No object found.
+ script_pushint(st,-1);
+ return 0;
+ }
+
+ x= bl->x;
+ y= bl->y;
+ safestrncpy(mapname, map[bl->m].name, MAP_NAME_LENGTH);
+
+ //Set MapName$
+ num=st->stack->stack_data[st->start+2].u.num;
+ name=get_str(num&0x00ffffff);
+ prefix=*name;
+
+ if(not_server_variable(prefix))
+ sd=script_rid2sd(st);
+ else
+ sd=NULL;
+ set_reg(st,sd,num,name,(void*)mapname,script_getref(st,2));
+
+ //Set MapX
+ num=st->stack->stack_data[st->start+3].u.num;
+ name=get_str(num&0x00ffffff);
+ prefix=*name;
+
+ if(not_server_variable(prefix))
+ sd=script_rid2sd(st);
+ else
+ sd=NULL;
+ set_reg(st,sd,num,name,(void*)__64BPRTSIZE(x),script_getref(st,3));
+
+ //Set MapY
+ num=st->stack->stack_data[st->start+4].u.num;
+ name=get_str(num&0x00ffffff);
+ prefix=*name;
+
+ if(not_server_variable(prefix))
+ sd=script_rid2sd(st);
+ else
+ sd=NULL;
+ set_reg(st,sd,num,name,(void*)__64BPRTSIZE(y),script_getref(st,4));
+
+ //Return Success value
+ script_pushint(st,0);
+ return 0;
+}
+
+/*==========================================
+ * Allows player to write NPC logs (i.e. Bank NPC, etc) [Lupus]
+ *------------------------------------------*/
+BUILDIN_FUNC(logmes)
+{
+ const char *str;
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 1;
+
+ str = script_getstr(st,2);
+ log_npc(sd,str);
+ return 0;
+}
+
+BUILDIN_FUNC(summon)
+{
+ int _class, timeout=0;
+ const char *str,*event="";
+ TBL_PC *sd;
+ struct mob_data *md;
+ int tick = gettick();
+
+ sd=script_rid2sd(st);
+ if (!sd) return 0;
+
+ str =script_getstr(st,2);
+ _class=script_getnum(st,3);
+ if( script_hasdata(st,4) )
+ timeout=script_getnum(st,4);
+ if( script_hasdata(st,5) ){
+ event=script_getstr(st,5);
+ check_event(st, event);
+ }
+
+ clif_skill_poseffect(&sd->bl,AM_CALLHOMUN,1,sd->bl.x,sd->bl.y,tick);
+
+ md = mob_once_spawn_sub(&sd->bl, sd->bl.m, sd->bl.x, sd->bl.y, str, _class, event, SZ_SMALL, AI_NONE);
+ if (md) {
+ md->master_id=sd->bl.id;
+ md->special_state.ai = AI_ATTACK;
+ if( md->deletetimer != INVALID_TIMER )
+ delete_timer(md->deletetimer, mob_timer_delete);
+ md->deletetimer = add_timer(tick+(timeout>0?timeout*1000:60000),mob_timer_delete,md->bl.id,0);
+ mob_spawn (md); //Now it is ready for spawning.
+ clif_specialeffect(&md->bl,344,AREA);
+ sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000);
+ }
+ return 0;
+}
+
+/*==========================================
+ * Checks whether it is daytime/nighttime
+ *------------------------------------------*/
+BUILDIN_FUNC(isnight)
+{
+ script_pushint(st,(night_flag == 1));
+ return 0;
+}
+
+BUILDIN_FUNC(isday)
+{
+ script_pushint(st,(night_flag == 0));
+ return 0;
+}
+
+/*================================================
+ * Check how many items/cards in the list are
+ * equipped - used for 2/15's cards patch [celest]
+ *------------------------------------------------*/
+BUILDIN_FUNC(isequippedcnt)
+{
+ TBL_PC *sd;
+ int i, j, k, id = 1;
+ int ret = 0;
+
+ sd = script_rid2sd(st);
+ if (!sd) { //If the player is not attached it is a script error anyway... but better prevent the map server from crashing...
+ script_pushint(st,0);
+ return 0;
+ }
+
+ for (i=0; id!=0; i++) {
+ FETCH (i+2, id) else id = 0;
+ if (id <= 0)
+ continue;
+
+ for (j=0; j<EQI_MAX; j++) {
+ int index;
+ index = sd->equip_index[j];
+ if(index < 0) continue;
+ if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) continue;
+ if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) continue;
+ if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) continue;
+
+ if(!sd->inventory_data[index])
+ continue;
+
+ if (itemdb_type(id) != IT_CARD) { //No card. Count amount in inventory.
+ if (sd->inventory_data[index]->nameid == id)
+ ret+= sd->status.inventory[index].amount;
+ } else { //Count cards.
+ if (itemdb_isspecial(sd->status.inventory[index].card[0]))
+ continue; //No cards
+ for(k=0; k<sd->inventory_data[index]->slot; k++) {
+ if (sd->status.inventory[index].card[k] == id)
+ ret++; //[Lupus]
+ }
+ }
+ }
+ }
+
+ script_pushint(st,ret);
+ return 0;
+}
+
+/*================================================
+ * Check whether another card has been
+ * equipped - used for 2/15's cards patch [celest]
+ * -- Items checked cannot be reused in another
+ * card set to prevent exploits
+ *------------------------------------------------*/
+BUILDIN_FUNC(isequipped)
+{
+ TBL_PC *sd;
+ int i, j, k, id = 1;
+ int index, flag;
+ int ret = -1;
+ //Original hash to reverse it when full check fails.
+ unsigned int setitem_hash = 0, setitem_hash2 = 0;
+
+ sd = script_rid2sd(st);
+
+ if (!sd) { //If the player is not attached it is a script error anyway... but better prevent the map server from crashing...
+ script_pushint(st,0);
+ return 0;
+ }
+
+ setitem_hash = sd->bonus.setitem_hash;
+ setitem_hash2 = sd->bonus.setitem_hash2;
+ for (i=0; id!=0; i++) {
+ FETCH (i+2, id) else id = 0;
+ if (id <= 0)
+ continue;
+ flag = 0;
+ for (j=0; j<EQI_MAX; j++) {
+ index = sd->equip_index[j];
+ if(index < 0) continue;
+ if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index) continue;
+ if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index) continue;
+ if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index)) continue;
+
+ if(!sd->inventory_data[index])
+ continue;
+
+ if (itemdb_type(id) != IT_CARD) {
+ if (sd->inventory_data[index]->nameid != id)
+ continue;
+ flag = 1;
+ break;
+ } else { //Cards
+ if (sd->inventory_data[index]->slot == 0 ||
+ itemdb_isspecial(sd->status.inventory[index].card[0]))
+ continue;
+
+ for (k = 0; k < sd->inventory_data[index]->slot; k++)
+ { //New hash system which should support up to 4 slots on any equipment. [Skotlex]
+ unsigned int hash = 0;
+ if (sd->status.inventory[index].card[k] != id)
+ continue;
+
+ hash = 1<<((j<5?j:j-5)*4 + k);
+ // check if card is already used by another set
+ if ( ( j < 5 ? sd->bonus.setitem_hash : sd->bonus.setitem_hash2 ) & hash)
+ continue;
+
+ // We have found a match
+ flag = 1;
+ // Set hash so this card cannot be used by another
+ if (j<5)
+ sd->bonus.setitem_hash |= hash;
+ else
+ sd->bonus.setitem_hash2 |= hash;
+ break;
+ }
+ }
+ if (flag) break; //Card found
+ }
+ if (ret == -1)
+ ret = flag;
+ else
+ ret &= flag;
+ if (!ret) break;
+ }
+ if (!ret) {//When check fails, restore original hash values. [Skotlex]
+ sd->bonus.setitem_hash = setitem_hash;
+ sd->bonus.setitem_hash2 = setitem_hash2;
+ }
+ script_pushint(st,ret);
+ return 0;
+}
+
+/*================================================
+ * Check how many given inserted cards in the CURRENT
+ * weapon - used for 2/15's cards patch [Lupus]
+ *------------------------------------------------*/
+BUILDIN_FUNC(cardscnt)
+{
+ TBL_PC *sd;
+ int i, k, id = 1;
+ int ret = 0;
+ int index;
+
+ sd = script_rid2sd(st);
+
+ for (i=0; id!=0; i++) {
+ FETCH (i+2, id) else id = 0;
+ if (id <= 0)
+ continue;
+
+ index = current_equip_item_index; //we get CURRENT WEAPON inventory index from status.c [Lupus]
+ if(index < 0) continue;
+
+ if(!sd->inventory_data[index])
+ continue;
+
+ if(itemdb_type(id) != IT_CARD) {
+ if (sd->inventory_data[index]->nameid == id)
+ ret+= sd->status.inventory[index].amount;
+ } else {
+ if (itemdb_isspecial(sd->status.inventory[index].card[0]))
+ continue;
+ for(k=0; k<sd->inventory_data[index]->slot; k++) {
+ if (sd->status.inventory[index].card[k] == id)
+ ret++;
+ }
+ }
+ }
+ script_pushint(st,ret);
+// script_pushint(st,current_equip_item_index);
+ return 0;
+}
+
+/*=======================================================
+ * Returns the refined number of the current item, or an
+ * item with inventory index specified
+ *-------------------------------------------------------*/
+BUILDIN_FUNC(getrefine)
+{
+ TBL_PC *sd;
+ if ((sd = script_rid2sd(st))!= NULL)
+ script_pushint(st,sd->status.inventory[current_equip_item_index].refine);
+ else
+ script_pushint(st,0);
+ return 0;
+}
+
+/*=======================================================
+ * Day/Night controls
+ *-------------------------------------------------------*/
+BUILDIN_FUNC(night)
+{
+ if (night_flag != 1) map_night_timer(night_timer_tid, 0, 0, 1);
+ return 0;
+}
+BUILDIN_FUNC(day)
+{
+ if (night_flag != 0) map_day_timer(day_timer_tid, 0, 0, 1);
+ return 0;
+}
+
+//=======================================================
+// Unequip [Spectre]
+//-------------------------------------------------------
+BUILDIN_FUNC(unequip)
+{
+ int i;
+ size_t num;
+ TBL_PC *sd;
+
+ num = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if( sd != NULL && num >= 1 && num <= ARRAYLENGTH(equip) )
+ {
+ i = pc_checkequip(sd,equip[num-1]);
+ if (i >= 0)
+ pc_unequipitem(sd,i,1|2);
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(equip)
+{
+ int nameid=0,i;
+ TBL_PC *sd;
+ struct item_data *item_data;
+
+ sd = script_rid2sd(st);
+
+ nameid=script_getnum(st,2);
+ if((item_data = itemdb_exists(nameid)) == NULL)
+ {
+ ShowError("wrong item ID : equipitem(%i)\n",nameid);
+ return 1;
+ }
+ ARR_FIND( 0, MAX_INVENTORY, i, sd->status.inventory[i].nameid == nameid );
+ if( i < MAX_INVENTORY )
+ pc_equipitem(sd,i,item_data->equip);
+
+ return 0;
+}
+
+BUILDIN_FUNC(autoequip)
+{
+ int nameid, flag;
+ struct item_data *item_data;
+ nameid=script_getnum(st,2);
+ flag=script_getnum(st,3);
+
+ if( ( item_data = itemdb_exists(nameid) ) == NULL )
+ {
+ ShowError("buildin_autoequip: Invalid item '%d'.\n", nameid);
+ return 1;
+ }
+
+ if( !itemdb_isequip2(item_data) )
+ {
+ ShowError("buildin_autoequip: Item '%d' cannot be equipped.\n", nameid);
+ return 1;
+ }
+
+ item_data->flag.autoequip = flag>0?1:0;
+ return 0;
+}
+
+BUILDIN_FUNC(setbattleflag)
+{
+ const char *flag, *value;
+
+ flag = script_getstr(st,2);
+ value = script_getstr(st,3); // HACK: Retrieve number as string (auto-converted) for battle_set_value
+
+ if (battle_set_value(flag, value) == 0)
+ ShowWarning("buildin_setbattleflag: unknown battle_config flag '%s'\n",flag);
+ else
+ ShowInfo("buildin_setbattleflag: battle_config flag '%s' is now set to '%s'.\n",flag,value);
+
+ return 0;
+}
+
+BUILDIN_FUNC(getbattleflag)
+{
+ const char *flag;
+ flag = script_getstr(st,2);
+ script_pushint(st,battle_get_value(flag));
+ return 0;
+}
+
+//=======================================================
+// strlen [Valaris]
+//-------------------------------------------------------
+BUILDIN_FUNC(getstrlen)
+{
+
+ const char *str = script_getstr(st,2);
+ int len = (str) ? (int)strlen(str) : 0;
+
+ script_pushint(st,len);
+ return 0;
+}
+
+//=======================================================
+// isalpha [Valaris]
+//-------------------------------------------------------
+BUILDIN_FUNC(charisalpha)
+{
+ const char *str=script_getstr(st,2);
+ int pos=script_getnum(st,3);
+
+ int val = ( str && pos >= 0 && (unsigned int)pos < strlen(str) ) ? ISALPHA( str[pos] ) != 0 : 0;
+
+ script_pushint(st,val);
+ return 0;
+}
+
+//=======================================================
+// charisupper <str>, <index>
+//-------------------------------------------------------
+BUILDIN_FUNC(charisupper)
+{
+ const char *str = script_getstr(st,2);
+ int pos = script_getnum(st,3);
+
+ int val = ( str && pos >= 0 && (unsigned int)pos < strlen(str) ) ? ISUPPER( str[pos] ) : 0;
+
+ script_pushint(st,val);
+ return 0;
+}
+
+//=======================================================
+// charislower <str>, <index>
+//-------------------------------------------------------
+BUILDIN_FUNC(charislower)
+{
+ const char *str = script_getstr(st,2);
+ int pos = script_getnum(st,3);
+
+ int val = ( str && pos >= 0 && (unsigned int)pos < strlen(str) ) ? ISLOWER( str[pos] ) : 0;
+
+ script_pushint(st,val);
+ return 0;
+}
+
+//=======================================================
+// charat <str>, <index>
+//-------------------------------------------------------
+BUILDIN_FUNC(charat) {
+ const char *str = script_getstr(st,2);
+ int pos = script_getnum(st,3);
+
+ if( pos >= 0 && (unsigned int)pos < strlen(str) ) {
+ char output[2];
+ output[0] = str[pos];
+ output[1] = '\0';
+ script_pushstrcopy(st, output);
+ } else
+ script_pushconststr(st, "");
+ return 0;
+}
+
+//=======================================================
+// setchar <string>, <char>, <index>
+//-------------------------------------------------------
+BUILDIN_FUNC(setchar)
+{
+ const char *str = script_getstr(st,2);
+ const char *c = script_getstr(st,3);
+ int index = script_getnum(st,4);
+ char *output = aStrdup(str);
+
+ if(index >= 0 && index < strlen(output))
+ output[index] = *c;
+
+ script_pushstr(st, output);
+ return 0;
+}
+
+//=======================================================
+// insertchar <string>, <char>, <index>
+//-------------------------------------------------------
+BUILDIN_FUNC(insertchar)
+{
+ const char *str = script_getstr(st,2);
+ const char *c = script_getstr(st,3);
+ int index = script_getnum(st,4);
+ char *output;
+ size_t len = strlen(str);
+
+ if(index < 0)
+ index = 0;
+ else if(index > len)
+ index = len;
+
+ output = (char*)aMalloc(len + 2);
+
+ memcpy(output, str, index);
+ output[index] = c[0];
+ memcpy(&output[index+1], &str[index], len - index);
+ output[len+1] = '\0';
+
+ script_pushstr(st, output);
+ return 0;
+}
+
+//=======================================================
+// delchar <string>, <index>
+//-------------------------------------------------------
+BUILDIN_FUNC(delchar)
+{
+ const char *str = script_getstr(st,2);
+ int index = script_getnum(st,3);
+ char *output;
+ size_t len = strlen(str);
+
+ if(index < 0 || index > len) {
+ //return original
+ output = aStrdup(str);
+ script_pushstr(st, output);
+ return 0;
+ }
+
+ output = (char*)aMalloc(len);
+
+ memcpy(output, str, index);
+ memcpy(&output[index], &str[index+1], len - index);
+
+ script_pushstr(st, output);
+ return 0;
+}
+
+//=======================================================
+// strtoupper <str>
+//-------------------------------------------------------
+BUILDIN_FUNC(strtoupper)
+{
+ const char *str = script_getstr(st,2);
+ char *output = aStrdup(str);
+ char *cursor = output;
+
+ while (*cursor != '\0') {
+ *cursor = TOUPPER(*cursor);
+ cursor++;
+ }
+
+ script_pushstr(st, output);
+ return 0;
+}
+
+//=======================================================
+// strtolower <str>
+//-------------------------------------------------------
+BUILDIN_FUNC(strtolower)
+{
+ const char *str = script_getstr(st,2);
+ char *output = aStrdup(str);
+ char *cursor = output;
+
+ while (*cursor != '\0') {
+ *cursor = TOLOWER(*cursor);
+ cursor++;
+ }
+
+ script_pushstr(st, output);
+ return 0;
+}
+
+//=======================================================
+// substr <str>, <start>, <end>
+//-------------------------------------------------------
+BUILDIN_FUNC(substr)
+{
+ const char *str = script_getstr(st,2);
+ char *output;
+ int start = script_getnum(st,3);
+ int end = script_getnum(st,4);
+
+ int len = 0;
+
+ if(start >= 0 && end < strlen(str) && start <= end) {
+ len = end - start + 1;
+ output = (char*)aMalloc(len + 1);
+ memcpy(output, &str[start], len);
+ } else
+ output = (char*)aMalloc(1);
+
+ output[len] = '\0';
+
+ script_pushstr(st, output);
+ return 0;
+}
+
+//=======================================================
+// explode <dest_string_array>, <str>, <delimiter>
+// Note: delimiter is limited to 1 char
+//-------------------------------------------------------
+BUILDIN_FUNC(explode)
+{
+ struct script_data* data = script_getdata(st, 2);
+ const char *str = script_getstr(st,3);
+ const char delimiter = script_getstr(st, 4)[0];
+ int32 id;
+ size_t len = strlen(str);
+ int i = 0, j = 0;
+ int start;
+
+
+ char *temp;
+ const char* name;
+
+ TBL_PC* sd = NULL;
+
+ temp = (char*)aMalloc(len + 1);
+
+ if( !data_isreference(data) )
+ {
+ ShowError("script:explode: not a variable\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not a variable
+ }
+
+ id = reference_getid(data);
+ start = reference_getindex(data);
+ name = reference_getname(data);
+
+ if( not_array_variable(*name) )
+ {
+ ShowError("script:explode: illegal scope\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ if( !is_string_variable(name) )
+ {
+ ShowError("script:explode: not string array\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// data type mismatch
+ }
+
+ if( not_server_variable(*name) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached
+ }
+
+ while(str[i] != '\0') {
+ if(str[i] == delimiter && start < SCRIPT_MAX_ARRAYSIZE-1) { //break at delimiter but ignore after reaching last array index
+ temp[j] = '\0';
+ set_reg(st, sd, reference_uid(id, start++), name, (void*)temp, reference_getref(data));
+ j = 0;
+ ++i;
+ } else {
+ temp[j++] = str[i++];
+ }
+ }
+ //set last string
+ temp[j] = '\0';
+ set_reg(st, sd, reference_uid(id, start), name, (void*)temp, reference_getref(data));
+
+ aFree(temp);
+ return 0;
+}
+
+//=======================================================
+// implode <string_array>
+// implode <string_array>, <glue>
+//-------------------------------------------------------
+BUILDIN_FUNC(implode)
+{
+ struct script_data* data = script_getdata(st, 2);
+ const char *glue = NULL, *name, *temp;
+ int32 glue_len = 0, array_size, id;
+ size_t len = 0;
+ int i, k = 0;
+
+ TBL_PC* sd = NULL;
+
+ char *output;
+
+ if( !data_isreference(data) )
+ {
+ ShowError("script:implode: not a variable\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not a variable
+ }
+
+ id = reference_getid(data);
+ name = reference_getname(data);
+
+ if( not_array_variable(*name) )
+ {
+ ShowError("script:implode: illegal scope\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ if( !is_string_variable(name) )
+ {
+ ShowError("script:implode: not string array\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// data type mismatch
+ }
+
+ if( not_server_variable(*name) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached
+ }
+
+ //count chars
+ array_size = getarraysize(st, id, reference_getindex(data), is_string_variable(name), reference_getref(data)) - 1;
+
+ if(array_size == -1) //empty array check (AmsTaff)
+ {
+ ShowWarning("script:implode: array length = 0\n");
+ output = (char*)aMalloc(sizeof(char)*5);
+ sprintf(output,"%s","NULL");
+ } else {
+ for(i = 0; i <= array_size; ++i) {
+ temp = (char*) get_val2(st, reference_uid(id, i), reference_getref(data));
+ len += strlen(temp);
+ script_removetop(st, -1, 0);
+ }
+
+ //allocate mem
+ if( script_hasdata(st,3) ) {
+ glue = script_getstr(st,3);
+ glue_len = strlen(glue);
+ len += glue_len * (array_size);
+ }
+ output = (char*)aMalloc(len + 1);
+
+ //build output
+ for(i = 0; i < array_size; ++i) {
+ temp = (char*) get_val2(st, reference_uid(id, i), reference_getref(data));
+ len = strlen(temp);
+ memcpy(&output[k], temp, len);
+ k += len;
+ if(glue_len != 0) {
+ memcpy(&output[k], glue, glue_len);
+ k += glue_len;
+ }
+ script_removetop(st, -1, 0);
+ }
+ temp = (char*) get_val2(st, reference_uid(id, array_size), reference_getref(data));
+ len = strlen(temp);
+ memcpy(&output[k], temp, len);
+ k += len;
+ script_removetop(st, -1, 0);
+
+ output[k] = '\0';
+ }
+
+ script_pushstr(st, output);
+ return 0;
+}
+
+//=======================================================
+// sprintf(<format>, ...);
+// Implements C sprintf, except format %n. The resulting string is
+// returned, instead of being saved in variable by reference.
+//-------------------------------------------------------
+BUILDIN_FUNC(sprintf)
+{
+ unsigned int len, argc = 0, arg = 0, buf2_len = 0;
+ const char* format;
+ char* p;
+ char* q;
+ char* buf = NULL;
+ char* buf2 = NULL;
+ struct script_data* data;
+ StringBuf final_buf;
+
+ // Fetch init data
+ format = script_getstr(st, 2);
+ argc = script_lastdata(st)-2;
+ len = strlen(format);
+
+ // Skip parsing, where no parsing is required.
+ if(len==0){
+ script_pushconststr(st,"");
+ return 0;
+ }
+
+ // Pessimistic alloc
+ CREATE(buf, char, len+1);
+
+ // Need not be parsed, just solve stuff like %%.
+ if(argc==0){
+ memcpy(buf,format,len+1);
+ script_pushstrcopy(st, buf);
+ aFree(buf);
+ return 0;
+ }
+
+ safestrncpy(buf, format, len+1);
+
+ // Issue sprintf for each parameter
+ StringBuf_Init(&final_buf);
+ q = buf;
+ while((p = strchr(q, '%'))!=NULL){
+ if(p!=q){
+ len = p-q+1;
+ if(buf2_len<len){
+ RECREATE(buf2, char, len);
+ buf2_len = len;
+ }
+ safestrncpy(buf2, q, len);
+ StringBuf_AppendStr(&final_buf, buf2);
+ q = p;
+ }
+ p = q+1;
+ if(*p=='%'){ // %%
+ StringBuf_AppendStr(&final_buf, "%");
+ q+=2;
+ continue;
+ }
+ if(*p=='n'){ // %n
+ ShowWarning("buildin_sprintf: Format %%n not supported! Skipping...\n");
+ script_reportsrc(st);
+ q+=2;
+ continue;
+ }
+ if(arg>=argc){
+ ShowError("buildin_sprintf: Not enough arguments passed!\n");
+ if(buf) aFree(buf);
+ if(buf2) aFree(buf2);
+ StringBuf_Destroy(&final_buf);
+ script_pushconststr(st,"");
+ return 1;
+ }
+ if((p = strchr(q+1, '%'))==NULL){
+ p = strchr(q, 0); // EOS
+ }
+ len = p-q+1;
+ if(buf2_len<len){
+ RECREATE(buf2, char, len);
+ buf2_len = len;
+ }
+ safestrncpy(buf2, q, len);
+ q = p;
+
+ // Note: This assumes the passed value being the correct
+ // type to the current format specifier. If not, the server
+ // probably crashes or returns anything else, than expected,
+ // but it would behave in normal code the same way so it's
+ // the scripter's responsibility.
+ data = script_getdata(st, arg+3);
+ if(data_isstring(data)){ // String
+ StringBuf_Printf(&final_buf, buf2, script_getstr(st, arg+3));
+ }else if(data_isint(data)){ // Number
+ StringBuf_Printf(&final_buf, buf2, script_getnum(st, arg+3));
+ }else if(data_isreference(data)){ // Variable
+ char* name = reference_getname(data);
+ if(name[strlen(name)-1]=='$'){ // var Str
+ StringBuf_Printf(&final_buf, buf2, script_getstr(st, arg+3));
+ }else{ // var Int
+ StringBuf_Printf(&final_buf, buf2, script_getnum(st, arg+3));
+ }
+ }else{ // Unsupported type
+ ShowError("buildin_sprintf: Unknown argument type!\n");
+ if(buf) aFree(buf);
+ if(buf2) aFree(buf2);
+ StringBuf_Destroy(&final_buf);
+ script_pushconststr(st,"");
+ return 1;
+ }
+ arg++;
+ }
+
+ // Append anything left
+ if(*q){
+ StringBuf_AppendStr(&final_buf, q);
+ }
+
+ // Passed more, than needed
+ if(arg<argc){
+ ShowWarning("buildin_sprintf: Unused arguments passed.\n");
+ script_reportsrc(st);
+ }
+
+ script_pushstrcopy(st, StringBuf_Value(&final_buf));
+
+ if(buf) aFree(buf);
+ if(buf2) aFree(buf2);
+ StringBuf_Destroy(&final_buf);
+
+ return 0;
+}
+
+//=======================================================
+// sscanf(<str>, <format>, ...);
+// Implements C sscanf.
+//-------------------------------------------------------
+BUILDIN_FUNC(sscanf){
+ unsigned int argc, arg = 0, len;
+ struct script_data* data;
+ struct map_session_data* sd = NULL;
+ const char* str;
+ const char* format;
+ const char* p;
+ const char* q;
+ char* buf = NULL;
+ char* buf_p;
+ char* ref_str = NULL;
+ int ref_int;
+
+ // Get data
+ str = script_getstr(st, 2);
+ format = script_getstr(st, 3);
+ argc = script_lastdata(st)-3;
+
+ len = strlen(format);
+ CREATE(buf, char, len*2+1);
+
+ // Issue sscanf for each parameter
+ *buf = 0;
+ q = format;
+ while((p = strchr(q, '%'))){
+ if(p!=q){
+ strncat(buf, q, (size_t)(p-q));
+ q = p;
+ }
+ p = q+1;
+ if(*p=='*' || *p=='%'){ // Skip
+ strncat(buf, q, 2);
+ q+=2;
+ continue;
+ }
+ if(arg>=argc){
+ ShowError("buildin_sscanf: Not enough arguments passed!\n");
+ script_pushint(st, -1);
+ if(buf) aFree(buf);
+ if(ref_str) aFree(ref_str);
+ return 1;
+ }
+ if((p = strchr(q+1, '%'))==NULL){
+ p = strchr(q, 0); // EOS
+ }
+ len = p-q;
+ strncat(buf, q, len);
+ q = p;
+
+ // Validate output
+ data = script_getdata(st, arg+4);
+ if(!data_isreference(data) || !reference_tovariable(data)){
+ ShowError("buildin_sscanf: Target argument is not a variable!\n");
+ script_pushint(st, -1);
+ if(buf) aFree(buf);
+ if(ref_str) aFree(ref_str);
+ return 1;
+ }
+ buf_p = reference_getname(data);
+ if(not_server_variable(*buf_p) && (sd = script_rid2sd(st))==NULL){
+ script_pushint(st, -1);
+ if(buf) aFree(buf);
+ if(ref_str) aFree(ref_str);
+ return 0;
+ }
+
+ // Save value if any
+ if(buf_p[strlen(buf_p)-1]=='$'){ // String
+ if(ref_str==NULL){
+ CREATE(ref_str, char, strlen(str)+1);
+ }
+ if(sscanf(str, buf, ref_str)==0){
+ break;
+ }
+ set_reg(st, sd, add_str(buf_p), buf_p, (void *)(ref_str), reference_getref(data));
+ }else{ // Number
+ if(sscanf(str, buf, &ref_int)==0){
+ break;
+ }
+ set_reg(st, sd, add_str(buf_p), buf_p, (void *)__64BPRTSIZE(ref_int), reference_getref(data));
+ }
+ arg++;
+
+ // Disable used format (%... -> %*...)
+ buf_p = strchr(buf, 0);
+ memmove(buf_p-len+2, buf_p-len+1, len);
+ *(buf_p-len+1) = '*';
+ }
+
+ script_pushint(st, arg);
+ if(buf) aFree(buf);
+ if(ref_str) aFree(ref_str);
+
+ return 0;
+}
+
+//=======================================================
+// strpos(<haystack>, <needle>)
+// strpos(<haystack>, <needle>, <offset>)
+//
+// Implements PHP style strpos. Adapted from code from
+// http://www.daniweb.com/code/snippet313.html, Dave Sinkula
+//-------------------------------------------------------
+BUILDIN_FUNC(strpos) {
+ const char *haystack = script_getstr(st,2);
+ const char *needle = script_getstr(st,3);
+ int i;
+ size_t len;
+
+ if( script_hasdata(st,4) )
+ i = script_getnum(st,4);
+ else
+ i = 0;
+
+ if (needle[0] == '\0') {
+ script_pushint(st, -1);
+ return 0;
+ }
+
+ len = strlen(haystack);
+ for ( ; i < len; ++i ) {
+ if ( haystack[i] == *needle ) {
+ // matched starting char -- loop through remaining chars
+ const char *h, *n;
+ for ( h = &haystack[i], n = needle; *h && *n; ++h, ++n ) {
+ if ( *h != *n ) {
+ break;
+ }
+ }
+ if ( !*n ) { // matched all of 'needle' to null termination
+ script_pushint(st, i);
+ return 0;
+ }
+ }
+ }
+ script_pushint(st, -1);
+ return 0;
+}
+
+//===============================================================
+// replacestr <input>, <search>, <replace>{, <usecase>{, <count>}}
+//
+// Note: Finds all instances of <search> in <input> and replaces
+// with <replace>. If specified will only replace as many
+// instances as specified in <count>. By default will be case
+// sensitive.
+//---------------------------------------------------------------
+BUILDIN_FUNC(replacestr)
+{
+ const char *input = script_getstr(st, 2);
+ const char *find = script_getstr(st, 3);
+ const char *replace = script_getstr(st, 4);
+ size_t inputlen = strlen(input);
+ size_t findlen = strlen(find);
+ struct StringBuf output;
+ bool usecase = true;
+
+ int count = 0;
+ int numFinds = 0;
+ int i = 0, f = 0;
+
+ if(findlen == 0) {
+ ShowError("script:replacestr: Invalid search length.\n");
+ st->state = END;
+ return 1;
+ }
+
+ if(script_hasdata(st, 5)) {
+ if( !script_isstring(st,5) )
+ usecase = script_getnum(st, 5) != 0;
+ else {
+ ShowError("script:replacestr: Invalid usecase value. Expected int got string\n");
+ st->state = END;
+ return 1;
+ }
+ }
+
+ if(script_hasdata(st, 6)) {
+ count = script_getnum(st, 6);
+ if(count == 0) {
+ ShowError("script:replacestr: Invalid count value. Expected int got string\n");
+ st->state = END;
+ return 1;
+ }
+ }
+
+ StringBuf_Init(&output);
+
+ for(; i < inputlen; i++) {
+ if(count && count == numFinds) { //found enough, stop looking
+ break;
+ }
+
+ for(f = 0; f <= findlen; f++) {
+ if(f == findlen) { //complete match
+ numFinds++;
+ StringBuf_AppendStr(&output, replace);
+
+ i += findlen - 1;
+ break;
+ } else {
+ if(usecase) {
+ if((i + f) > inputlen || input[i + f] != find[f]) {
+ StringBuf_Printf(&output, "%c", input[i]);
+ break;
+ }
+ } else {
+ if(((i + f) > inputlen || input[i + f] != find[f]) && TOUPPER(input[i+f]) != TOUPPER(find[f])) {
+ StringBuf_Printf(&output, "%c", input[i]);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ //append excess after enough found
+ if(i < inputlen)
+ StringBuf_AppendStr(&output, &(input[i]));
+
+ script_pushstrcopy(st, StringBuf_Value(&output));
+ StringBuf_Destroy(&output);
+ return 0;
+}
+
+//========================================================
+// countstr <input>, <search>{, <usecase>}
+//
+// Note: Counts the number of times <search> occurs in
+// <input>. By default will be case sensitive.
+//--------------------------------------------------------
+BUILDIN_FUNC(countstr)
+{
+ const char *input = script_getstr(st, 2);
+ const char *find = script_getstr(st, 3);
+ size_t inputlen = strlen(input);
+ size_t findlen = strlen(find);
+ bool usecase = true;
+
+ int numFinds = 0;
+ int i = 0, f = 0;
+
+ if(findlen == 0) {
+ ShowError("script:countstr: Invalid search length.\n");
+ st->state = END;
+ return 1;
+ }
+
+ if(script_hasdata(st, 4)) {
+ if( !script_isstring(st,4) )
+ usecase = script_getnum(st, 4) != 0;
+ else {
+ ShowError("script:countstr: Invalid usecase value. Expected int got string\n");
+ st->state = END;
+ return 1;
+ }
+ }
+
+ for(; i < inputlen; i++) {
+ for(f = 0; f <= findlen; f++) {
+ if(f == findlen) { //complete match
+ numFinds++;
+ i += findlen - 1;
+ break;
+ } else {
+ if(usecase) {
+ if((i + f) > inputlen || input[i + f] != find[f]) {
+ break;
+ }
+ } else {
+ if(((i + f) > inputlen || input[i + f] != find[f]) && TOUPPER(input[i+f]) != TOUPPER(find[f])) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ script_pushint(st, numFinds);
+ return 0;
+}
+
+
+/// Changes the display name and/or display class of the npc.
+/// Returns 0 is successful, 1 if the npc does not exist.
+///
+/// setnpcdisplay("<npc name>", "<new display name>", <new class id>, <new size>) -> <int>
+/// setnpcdisplay("<npc name>", "<new display name>", <new class id>) -> <int>
+/// setnpcdisplay("<npc name>", "<new display name>") -> <int>
+/// setnpcdisplay("<npc name>", <new class id>) -> <int>
+BUILDIN_FUNC(setnpcdisplay)
+{
+ const char* name;
+ const char* newname = NULL;
+ int class_ = -1, size = -1;
+ struct script_data* data;
+ struct npc_data* nd;
+
+ name = script_getstr(st,2);
+ data = script_getdata(st,3);
+
+ if( script_hasdata(st,4) )
+ class_ = script_getnum(st,4);
+ if( script_hasdata(st,5) )
+ size = script_getnum(st,5);
+
+ get_val(st, data);
+ if( data_isstring(data) )
+ newname = conv_str(st,data);
+ else if( data_isint(data) )
+ class_ = conv_num(st,data);
+ else
+ {
+ ShowError("script:setnpcdisplay: expected a string or number\n");
+ script_reportdata(data);
+ return 1;
+ }
+
+ nd = npc_name2id(name);
+ if( nd == NULL )
+ {// not found
+ script_pushint(st,1);
+ return 0;
+ }
+
+ // update npc
+ if( newname )
+ npc_setdisplayname(nd, newname);
+
+ if( size != -1 && size != (int)nd->size )
+ nd->size = size;
+ else
+ size = -1;
+
+ if( class_ != -1 && nd->class_ != class_ )
+ npc_setclass(nd, class_);
+ else if( size != -1 )
+ { // Required to update the visual size
+ clif_clearunit_area(&nd->bl, CLR_OUTSIGHT);
+ clif_spawn(&nd->bl);
+ }
+
+ script_pushint(st,0);
+ return 0;
+}
+
+BUILDIN_FUNC(atoi)
+{
+ const char *value;
+ value = script_getstr(st,2);
+ script_pushint(st,atoi(value));
+ return 0;
+}
+
+// case-insensitive substring search [lordalfa]
+BUILDIN_FUNC(compare)
+{
+ const char *message;
+ const char *cmpstring;
+ message = script_getstr(st,2);
+ cmpstring = script_getstr(st,3);
+ script_pushint(st,(stristr(message,cmpstring) != NULL));
+ return 0;
+}
+
+// [zBuffer] List of mathematics commands --->
+BUILDIN_FUNC(sqrt)
+{
+ double i, a;
+ i = script_getnum(st,2);
+ a = sqrt(i);
+ script_pushint(st,(int)a);
+ return 0;
+}
+
+BUILDIN_FUNC(pow)
+{
+ double i, a, b;
+ a = script_getnum(st,2);
+ b = script_getnum(st,3);
+ i = pow(a,b);
+ script_pushint(st,(int)i);
+ return 0;
+}
+
+BUILDIN_FUNC(distance)
+{
+ int x0, y0, x1, y1;
+
+ x0 = script_getnum(st,2);
+ y0 = script_getnum(st,3);
+ x1 = script_getnum(st,4);
+ y1 = script_getnum(st,5);
+
+ script_pushint(st,distance_xy(x0,y0,x1,y1));
+ return 0;
+}
+
+// <--- [zBuffer] List of mathematics commands
+
+BUILDIN_FUNC(md5)
+{
+ const char *tmpstr;
+ char *md5str;
+
+ tmpstr = script_getstr(st,2);
+ md5str = (char *)aMalloc((32+1)*sizeof(char));
+ MD5_String(tmpstr, md5str);
+ script_pushstr(st, md5str);
+ return 0;
+}
+
+// [zBuffer] List of dynamic var commands --->
+
+BUILDIN_FUNC(setd)
+{
+ TBL_PC *sd=NULL;
+ char varname[100];
+ const char *buffer;
+ int elem;
+ buffer = script_getstr(st, 2);
+
+ if(sscanf(buffer, "%99[^[][%d]", varname, &elem) < 2)
+ elem = 0;
+
+ if( not_server_variable(*varname) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ {
+ ShowError("script:setd: no player attached for player variable '%s'\n", buffer);
+ return 0;
+ }
+ }
+
+ if( is_string_variable(varname) ) {
+ setd_sub(st, sd, varname, elem, (void *)script_getstr(st, 3), NULL);
+ } else {
+ setd_sub(st, sd, varname, elem, (void *)__64BPRTSIZE(script_getnum(st, 3)), NULL);
+ }
+
+ return 0;
+}
+
+int buildin_query_sql_sub(struct script_state* st, Sql* handle)
+{
+ int i, j;
+ TBL_PC* sd = NULL;
+ const char* query;
+ struct script_data* data;
+ const char* name;
+ int max_rows = SCRIPT_MAX_ARRAYSIZE; // maximum number of rows
+ int num_vars;
+ int num_cols;
+
+ // check target variables
+ for( i = 3; script_hasdata(st,i); ++i ) {
+ data = script_getdata(st, i);
+ if( data_isreference(data) ) { // it's a variable
+ name = reference_getname(data);
+ if( not_server_variable(*name) && sd == NULL ) { // requires a player
+ sd = script_rid2sd(st);
+ if( sd == NULL ) { // no player attached
+ script_reportdata(data);
+ st->state = END;
+ return 1;
+ }
+ }
+ if( not_array_variable(*name) )
+ max_rows = 1;// not an array, limit to one row
+ } else {
+ ShowError("script:query_sql: not a variable\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;
+ }
+ }
+ num_vars = i - 3;
+
+ // Execute the query
+ query = script_getstr(st,2);
+
+ if( SQL_ERROR == Sql_QueryStr(handle, query) ) {
+ Sql_ShowDebug(handle);
+ script_pushint(st, 0);
+ return 1;
+ }
+
+ if( Sql_NumRows(handle) == 0 ) { // No data received
+ Sql_FreeResult(handle);
+ script_pushint(st, 0);
+ return 0;
+ }
+
+ // Count the number of columns to store
+ num_cols = Sql_NumColumns(handle);
+ if( num_vars < num_cols ) {
+ ShowWarning("script:query_sql: Too many columns, discarding last %u columns.\n", (unsigned int)(num_cols-num_vars));
+ script_reportsrc(st);
+ } else if( num_vars > num_cols ) {
+ ShowWarning("script:query_sql: Too many variables (%u extra).\n", (unsigned int)(num_vars-num_cols));
+ script_reportsrc(st);
+ }
+
+ // Store data
+ for( i = 0; i < max_rows && SQL_SUCCESS == Sql_NextRow(handle); ++i ) {
+ for( j = 0; j < num_vars; ++j ) {
+ char* str = NULL;
+
+ if( j < num_cols )
+ Sql_GetData(handle, j, &str, NULL);
+
+ data = script_getdata(st, j+3);
+ name = reference_getname(data);
+ if( is_string_variable(name) )
+ setd_sub(st, sd, name, i, (void *)(str?str:""), reference_getref(data));
+ else
+ setd_sub(st, sd, name, i, (void *)__64BPRTSIZE((str?atoi(str):0)), reference_getref(data));
+ }
+ }
+ if( i == max_rows && max_rows < Sql_NumRows(handle) ) {
+ ShowWarning("script:query_sql: Only %d/%u rows have been stored.\n", max_rows, (unsigned int)Sql_NumRows(handle));
+ script_reportsrc(st);
+ }
+
+ // Free data
+ Sql_FreeResult(handle);
+ script_pushint(st, i);
+
+ return 0;
+}
+
+BUILDIN_FUNC(query_sql) {
+#ifdef BETA_THREAD_TEST
+ if( st->state != RERUNLINE ) {
+ queryThread_add(st,false);
+
+ st->state = RERUNLINE;/* will continue when the query is finished running. */
+ } else
+ st->state = RUN;
+
+ return 0;
+#else
+ return buildin_query_sql_sub(st, mmysql_handle);
+#endif
+}
+
+BUILDIN_FUNC(query_logsql) {
+ if( !log_config.sql_logs ) {// logmysql_handle == NULL
+ ShowWarning("buildin_query_logsql: SQL logs are disabled, query '%s' will not be executed.\n", script_getstr(st,2));
+ script_pushint(st,-1);
+ return 1;
+ }
+#ifdef BETA_THREAD_TEST
+ if( st->state != RERUNLINE ) {
+ queryThread_add(st,true);
+
+ st->state = RERUNLINE;/* will continue when the query is finished running. */
+ } else
+ st->state = RUN;
+
+ return 0;
+#else
+ return buildin_query_sql_sub(st, logmysql_handle);
+#endif
+}
+
+//Allows escaping of a given string.
+BUILDIN_FUNC(escape_sql)
+{
+ const char *str;
+ char *esc_str;
+ size_t len;
+
+ str = script_getstr(st,2);
+ len = strlen(str);
+ esc_str = (char*)aMalloc(len*2+1);
+ Sql_EscapeStringLen(mmysql_handle, esc_str, str, len);
+ script_pushstr(st, esc_str);
+ return 0;
+}
+
+BUILDIN_FUNC(getd)
+{
+ char varname[100];
+ const char *buffer;
+ int elem;
+
+ buffer = script_getstr(st, 2);
+
+ if(sscanf(buffer, "%[^[][%d]", varname, &elem) < 2)
+ elem = 0;
+
+ // Push the 'pointer' so it's more flexible [Lance]
+ push_val(st->stack, C_NAME, reference_uid(add_str(varname), elem));
+
+ return 0;
+}
+
+// <--- [zBuffer] List of dynamic var commands
+// Pet stat [Lance]
+BUILDIN_FUNC(petstat)
+{
+ TBL_PC *sd = NULL;
+ struct pet_data *pd;
+ int flag = script_getnum(st,2);
+ sd = script_rid2sd(st);
+ if(!sd || !sd->status.pet_id || !sd->pd){
+ if(flag == 2)
+ script_pushconststr(st, "");
+ else
+ script_pushint(st,0);
+ return 0;
+ }
+ pd = sd->pd;
+ switch(flag){
+ case 1: script_pushint(st,(int)pd->pet.class_); break;
+ case 2: script_pushstrcopy(st, pd->pet.name); break;
+ case 3: script_pushint(st,(int)pd->pet.level); break;
+ case 4: script_pushint(st,(int)pd->pet.hungry); break;
+ case 5: script_pushint(st,(int)pd->pet.intimate); break;
+ default:
+ script_pushint(st,0);
+ break;
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(callshop)
+{
+ TBL_PC *sd = NULL;
+ struct npc_data *nd;
+ const char *shopname;
+ int flag = 0;
+ sd = script_rid2sd(st);
+ if (!sd) {
+ script_pushint(st,0);
+ return 0;
+ }
+ shopname = script_getstr(st, 2);
+ if( script_hasdata(st,3) )
+ flag = script_getnum(st,3);
+ nd = npc_name2id(shopname);
+ if( !nd || nd->bl.type != BL_NPC || (nd->subtype != SHOP && nd->subtype != CASHSHOP) )
+ {
+ ShowError("buildin_callshop: Shop [%s] not found (or NPC is not shop type)\n", shopname);
+ script_pushint(st,0);
+ return 1;
+ }
+
+ if( nd->subtype == SHOP )
+ {
+ // flag the user as using a valid script call for opening the shop (for floating NPCs)
+ sd->state.callshop = 1;
+
+ switch( flag )
+ {
+ case 1: npc_buysellsel(sd,nd->bl.id,0); break; //Buy window
+ case 2: npc_buysellsel(sd,nd->bl.id,1); break; //Sell window
+ default: clif_npcbuysell(sd,nd->bl.id); break; //Show menu
+ }
+ }
+ else
+ clif_cashshop_show(sd, nd);
+
+ sd->npc_shopid = nd->bl.id;
+ script_pushint(st,1);
+ return 0;
+}
+
+BUILDIN_FUNC(npcshopitem)
+{
+ const char* npcname = script_getstr(st, 2);
+ struct npc_data* nd = npc_name2id(npcname);
+ int n, i;
+ int amount;
+
+ if( !nd || ( nd->subtype != SHOP && nd->subtype != CASHSHOP ) )
+ { //Not found.
+ script_pushint(st,0);
+ return 0;
+ }
+
+ // get the count of new entries
+ amount = (script_lastdata(st)-2)/2;
+
+ // generate new shop item list
+ RECREATE(nd->u.shop.shop_item, struct npc_item_list, amount);
+ for( n = 0, i = 3; n < amount; n++, i+=2 )
+ {
+ nd->u.shop.shop_item[n].nameid = script_getnum(st,i);
+ nd->u.shop.shop_item[n].value = script_getnum(st,i+1);
+ }
+ nd->u.shop.count = n;
+
+ script_pushint(st,1);
+ return 0;
+}
+
+BUILDIN_FUNC(npcshopadditem)
+{
+ const char* npcname = script_getstr(st,2);
+ struct npc_data* nd = npc_name2id(npcname);
+ int n, i;
+ int amount;
+
+ if( !nd || ( nd->subtype != SHOP && nd->subtype != CASHSHOP ) )
+ { //Not found.
+ script_pushint(st,0);
+ return 0;
+ }
+
+ // get the count of new entries
+ amount = (script_lastdata(st)-2)/2;
+
+ // append new items to existing shop item list
+ RECREATE(nd->u.shop.shop_item, struct npc_item_list, nd->u.shop.count+amount);
+ for( n = nd->u.shop.count, i = 3; n < nd->u.shop.count+amount; n++, i+=2 )
+ {
+ nd->u.shop.shop_item[n].nameid = script_getnum(st,i);
+ nd->u.shop.shop_item[n].value = script_getnum(st,i+1);
+ }
+ nd->u.shop.count = n;
+
+ script_pushint(st,1);
+ return 0;
+}
+
+BUILDIN_FUNC(npcshopdelitem)
+{
+ const char* npcname = script_getstr(st,2);
+ struct npc_data* nd = npc_name2id(npcname);
+ unsigned int nameid;
+ int n, i;
+ int amount;
+ int size;
+
+ if( !nd || ( nd->subtype != SHOP && nd->subtype != CASHSHOP ) )
+ { //Not found.
+ script_pushint(st,0);
+ return 0;
+ }
+
+ amount = script_lastdata(st)-2;
+ size = nd->u.shop.count;
+
+ // remove specified items from the shop item list
+ for( i = 3; i < 3 + amount; i++ )
+ {
+ nameid = script_getnum(st,i);
+
+ ARR_FIND( 0, size, n, nd->u.shop.shop_item[n].nameid == nameid );
+ if( n < size )
+ {
+ memmove(&nd->u.shop.shop_item[n], &nd->u.shop.shop_item[n+1], sizeof(nd->u.shop.shop_item[0])*(size-n));
+ size--;
+ }
+ }
+
+ RECREATE(nd->u.shop.shop_item, struct npc_item_list, size);
+ nd->u.shop.count = size;
+
+ script_pushint(st,1);
+ return 0;
+}
+
+//Sets a script to attach to a shop npc.
+BUILDIN_FUNC(npcshopattach)
+{
+ const char* npcname = script_getstr(st,2);
+ struct npc_data* nd = npc_name2id(npcname);
+ int flag = 1;
+
+ if( script_hasdata(st,3) )
+ flag = script_getnum(st,3);
+
+ if( !nd || nd->subtype != SHOP )
+ { //Not found.
+ script_pushint(st,0);
+ return 0;
+ }
+
+ if (flag)
+ nd->master_nd = ((struct npc_data *)map_id2bl(st->oid));
+ else
+ nd->master_nd = NULL;
+
+ script_pushint(st,1);
+ return 0;
+}
+
+/*==========================================
+ * Returns some values of an item [Lupus]
+ * Price, Weight, etc...
+ setitemscript(itemID,"{new item bonus script}",[n]);
+ Where n:
+ 0 - script
+ 1 - Equip script
+ 2 - Unequip script
+ *------------------------------------------*/
+BUILDIN_FUNC(setitemscript)
+{
+ int item_id,n=0;
+ const char *script;
+ struct item_data *i_data;
+ struct script_code **dstscript;
+
+ item_id = script_getnum(st,2);
+ script = script_getstr(st,3);
+ if( script_hasdata(st,4) )
+ n=script_getnum(st,4);
+ i_data = itemdb_exists(item_id);
+
+ if (!i_data || script==NULL || ( script[0] && script[0]!='{' )) {
+ script_pushint(st,0);
+ return 0;
+ }
+ switch (n) {
+ case 2:
+ dstscript = &i_data->unequip_script;
+ break;
+ case 1:
+ dstscript = &i_data->equip_script;
+ break;
+ default:
+ dstscript = &i_data->script;
+ break;
+ }
+ if(*dstscript)
+ script_free_code(*dstscript);
+
+ *dstscript = script[0] ? parse_script(script, "script_setitemscript", 0, 0) : NULL;
+ script_pushint(st,1);
+ return 0;
+}
+
+/* Work In Progress [Lupus]
+BUILDIN_FUNC(addmonsterdrop)
+{
+ int class_,item_id,chance;
+ class_=script_getnum(st,2);
+ item_id=script_getnum(st,3);
+ chance=script_getnum(st,4);
+ if(class_>1000 && item_id>500 && chance>0) {
+ script_pushint(st,1);
+ } else {
+ script_pushint(st,0);
+ }
+}
+
+BUILDIN_FUNC(delmonsterdrop)
+{
+ int class_,item_id;
+ class_=script_getnum(st,2);
+ item_id=script_getnum(st,3);
+ if(class_>1000 && item_id>500) {
+ script_pushint(st,1);
+ } else {
+ script_pushint(st,0);
+ }
+}
+*/
+
+/*==========================================
+ * Returns some values of a monster [Lupus]
+ * Name, Level, race, size, etc...
+ getmonsterinfo(monsterID,queryIndex);
+ *------------------------------------------*/
+BUILDIN_FUNC(getmonsterinfo)
+{
+ struct mob_db *mob;
+ int mob_id;
+
+ mob_id = script_getnum(st,2);
+ if (!mobdb_checkid(mob_id)) {
+ ShowError("buildin_getmonsterinfo: Wrong Monster ID: %i\n", mob_id);
+ if ( !script_getnum(st,3) ) //requested a string
+ script_pushconststr(st,"null");
+ else
+ script_pushint(st,-1);
+ return -1;
+ }
+ mob = mob_db(mob_id);
+ switch ( script_getnum(st,3) ) {
+ case 0: script_pushstrcopy(st,mob->jname); break;
+ case 1: script_pushint(st,mob->lv); break;
+ case 2: script_pushint(st,mob->status.max_hp); break;
+ case 3: script_pushint(st,mob->base_exp); break;
+ case 4: script_pushint(st,mob->job_exp); break;
+ case 5: script_pushint(st,mob->status.rhw.atk); break;
+ case 6: script_pushint(st,mob->status.rhw.atk2); break;
+ case 7: script_pushint(st,mob->status.def); break;
+ case 8: script_pushint(st,mob->status.mdef); break;
+ case 9: script_pushint(st,mob->status.str); break;
+ case 10: script_pushint(st,mob->status.agi); break;
+ case 11: script_pushint(st,mob->status.vit); break;
+ case 12: script_pushint(st,mob->status.int_); break;
+ case 13: script_pushint(st,mob->status.dex); break;
+ case 14: script_pushint(st,mob->status.luk); break;
+ case 15: script_pushint(st,mob->status.rhw.range); break;
+ case 16: script_pushint(st,mob->range2); break;
+ case 17: script_pushint(st,mob->range3); break;
+ case 18: script_pushint(st,mob->status.size); break;
+ case 19: script_pushint(st,mob->status.race); break;
+ case 20: script_pushint(st,mob->status.def_ele); break;
+ case 21: script_pushint(st,mob->status.mode); break;
+ case 22: script_pushint(st,mob->mexp); break;
+ default: script_pushint(st,-1); //wrong Index
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(checkvending) // check vending [Nab4]
+{
+ TBL_PC *sd = NULL;
+
+ if(script_hasdata(st,2))
+ sd = map_nick2sd(script_getstr(st,2));
+ else
+ sd = script_rid2sd(st);
+
+ if(sd)
+ script_pushint(st, sd->state.autotrade ? 2 : sd->state.vending);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+
+BUILDIN_FUNC(checkchatting) // check chatting [Marka]
+{
+ TBL_PC *sd = NULL;
+
+ if(script_hasdata(st,2))
+ sd = map_nick2sd(script_getstr(st,2));
+ else
+ sd = script_rid2sd(st);
+
+ if(sd)
+ script_pushint(st,(sd->chatID != 0));
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+BUILDIN_FUNC(searchitem)
+{
+ struct script_data* data = script_getdata(st, 2);
+ const char *itemname = script_getstr(st,3);
+ struct item_data *items[MAX_SEARCH];
+ int count;
+
+ char* name;
+ int32 start;
+ int32 id;
+ int32 i;
+ TBL_PC* sd = NULL;
+
+ if ((items[0] = itemdb_exists(atoi(itemname))))
+ count = 1;
+ else {
+ count = itemdb_searchname_array(items, ARRAYLENGTH(items), itemname);
+ if (count > MAX_SEARCH) count = MAX_SEARCH;
+ }
+
+ if (!count) {
+ script_pushint(st, 0);
+ return 0;
+ }
+
+ if( !data_isreference(data) )
+ {
+ ShowError("script:searchitem: not a variable\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not a variable
+ }
+
+ id = reference_getid(data);
+ start = reference_getindex(data);
+ name = reference_getname(data);
+ if( not_array_variable(*name) )
+ {
+ ShowError("script:searchitem: illegal scope\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ if( not_server_variable(*name) )
+ {
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;// no player attached
+ }
+
+ if( is_string_variable(name) )
+ {// string array
+ ShowError("script:searchitem: not an integer array reference\n");
+ script_reportdata(data);
+ st->state = END;
+ return 1;// not supported
+ }
+
+ for( i = 0; i < count; ++start, ++i )
+ {// Set array
+ void* v = (void*)__64BPRTSIZE((int)items[i]->nameid);
+ set_reg(st, sd, reference_uid(id, start), name, v, reference_getref(data));
+ }
+
+ script_pushint(st, count);
+ return 0;
+}
+
+int axtoi(const char *hexStg)
+{
+ int n = 0; // position in string
+ int16 m = 0; // position in digit[] to shift
+ int count; // loop index
+ int intValue = 0; // integer value of hex string
+ int digit[11]; // hold values to convert
+ while (n < 10) {
+ if (hexStg[n]=='\0')
+ break;
+ if (hexStg[n] > 0x29 && hexStg[n] < 0x40 ) //if 0 to 9
+ digit[n] = hexStg[n] & 0x0f; //convert to int
+ else if (hexStg[n] >='a' && hexStg[n] <= 'f') //if a to f
+ digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
+ else if (hexStg[n] >='A' && hexStg[n] <= 'F') //if A to F
+ digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
+ else break;
+ n++;
+ }
+ count = n;
+ m = n - 1;
+ n = 0;
+ while(n < count) {
+ // digit[n] is value of hex digit at position n
+ // (m << 2) is the number of positions to shift
+ // OR the bits into return value
+ intValue = intValue | (digit[n] << (m << 2));
+ m--; // adjust the position to set
+ n++; // next digit to process
+ }
+ return (intValue);
+}
+
+// [Lance] Hex string to integer converter
+BUILDIN_FUNC(axtoi)
+{
+ const char *hex = script_getstr(st,2);
+ script_pushint(st,axtoi(hex));
+ return 0;
+}
+
+// [zBuffer] List of player cont commands --->
+BUILDIN_FUNC(rid2name)
+{
+ struct block_list *bl = NULL;
+ int rid = script_getnum(st,2);
+ if((bl = map_id2bl(rid)))
+ {
+ switch(bl->type) {
+ case BL_MOB: script_pushstrcopy(st,((TBL_MOB*)bl)->name); break;
+ case BL_PC: script_pushstrcopy(st,((TBL_PC*)bl)->status.name); break;
+ case BL_NPC: script_pushstrcopy(st,((TBL_NPC*)bl)->exname); break;
+ case BL_PET: script_pushstrcopy(st,((TBL_PET*)bl)->pet.name); break;
+ case BL_HOM: script_pushstrcopy(st,((TBL_HOM*)bl)->homunculus.name); break;
+ case BL_MER: script_pushstrcopy(st,((TBL_MER*)bl)->db->name); break;
+ default:
+ ShowError("buildin_rid2name: BL type unknown.\n");
+ script_pushconststr(st,"");
+ break;
+ }
+ } else {
+ ShowError("buildin_rid2name: invalid RID\n");
+ script_pushconststr(st,"(null)");
+ }
+ return 0;
+}
+
+BUILDIN_FUNC(pcblockmove)
+{
+ int id, flag;
+ TBL_PC *sd = NULL;
+
+ id = script_getnum(st,2);
+ flag = script_getnum(st,3);
+
+ if(id)
+ sd = map_id2sd(id);
+ else
+ sd = script_rid2sd(st);
+
+ if(sd)
+ sd->state.blockedmove = flag > 0;
+
+ return 0;
+}
+
+BUILDIN_FUNC(pcfollow)
+{
+ int id, targetid;
+ TBL_PC *sd = NULL;
+
+
+ id = script_getnum(st,2);
+ targetid = script_getnum(st,3);
+
+ if(id)
+ sd = map_id2sd(id);
+ else
+ sd = script_rid2sd(st);
+
+ if(sd)
+ pc_follow(sd, targetid);
+
+ return 0;
+}
+
+BUILDIN_FUNC(pcstopfollow)
+{
+ int id;
+ TBL_PC *sd = NULL;
+
+
+ id = script_getnum(st,2);
+
+ if(id)
+ sd = map_id2sd(id);
+ else
+ sd = script_rid2sd(st);
+
+ if(sd)
+ pc_stop_following(sd);
+
+ return 0;
+}
+// <--- [zBuffer] List of player cont commands
+// [zBuffer] List of mob control commands --->
+//## TODO always return if the request/whatever was successfull [FlavioJS]
+
+/// Makes the unit walk to target position or map
+/// Returns if it was successfull
+///
+/// unitwalk(<unit_id>,<x>,<y>) -> <bool>
+/// unitwalk(<unit_id>,<map_id>) -> <bool>
+BUILDIN_FUNC(unitwalk)
+{
+ struct block_list* bl;
+
+ bl = map_id2bl(script_getnum(st,2));
+ if( bl == NULL )
+ {
+ script_pushint(st, 0);
+ }
+ else if( script_hasdata(st,4) )
+ {
+ int x = script_getnum(st,3);
+ int y = script_getnum(st,4);
+ script_pushint(st, unit_walktoxy(bl,x,y,0));// We'll use harder calculations.
+ }
+ else
+ {
+ int map_id = script_getnum(st,3);
+ script_pushint(st, unit_walktobl(bl,map_id2bl(map_id),65025,1));
+ }
+
+ return 0;
+}
+
+/// Kills the unit
+///
+/// unitkill <unit_id>;
+BUILDIN_FUNC(unitkill)
+{
+ struct block_list* bl = map_id2bl(script_getnum(st,2));
+ if( bl != NULL )
+ status_kill(bl);
+
+ return 0;
+}
+
+/// Warps the unit to the target position in the target map
+/// Returns if it was successfull
+///
+/// unitwarp(<unit_id>,"<map name>",<x>,<y>) -> <bool>
+BUILDIN_FUNC(unitwarp)
+{
+ int unit_id;
+ int map;
+ short x;
+ short y;
+ struct block_list* bl;
+ const char *mapname;
+
+ unit_id = script_getnum(st,2);
+ mapname = script_getstr(st, 3);
+ x = (short)script_getnum(st,4);
+ y = (short)script_getnum(st,5);
+
+ if (!unit_id) //Warp the script's runner
+ bl = map_id2bl(st->rid);
+ else
+ bl = map_id2bl(unit_id);
+
+ if( strcmp(mapname,"this") == 0 )
+ map = bl?bl->m:-1;
+ else
+ map = map_mapname2mapid(mapname);
+
+ if( map >= 0 && bl != NULL )
+ script_pushint(st, unit_warp(bl,map,x,y,CLR_OUTSIGHT));
+ else
+ script_pushint(st, 0);
+
+ return 0;
+}
+
+/// Makes the unit attack the target.
+/// If the unit is a player and <action type> is not 0, it does a continuous
+/// attack instead of a single attack.
+/// Returns if the request was successfull.
+///
+/// unitattack(<unit_id>,"<target name>"{,<action type>}) -> <bool>
+/// unitattack(<unit_id>,<target_id>{,<action type>}) -> <bool>
+BUILDIN_FUNC(unitattack)
+{
+ struct block_list* unit_bl;
+ struct block_list* target_bl = NULL;
+ struct script_data* data;
+ int actiontype = 0;
+
+ // get unit
+ unit_bl = map_id2bl(script_getnum(st,2));
+ if( unit_bl == NULL ) {
+ script_pushint(st, 0);
+ return 0;
+ }
+
+ data = script_getdata(st, 3);
+ get_val(st, data);
+ if( data_isstring(data) )
+ {
+ TBL_PC* sd = map_nick2sd(conv_str(st, data));
+ if( sd != NULL )
+ target_bl = &sd->bl;
+ } else
+ target_bl = map_id2bl(conv_num(st, data));
+ // request the attack
+ if( target_bl == NULL )
+ {
+ script_pushint(st, 0);
+ return 0;
+ }
+
+ // get actiontype
+ if( script_hasdata(st,4) )
+ actiontype = script_getnum(st,4);
+
+ switch( unit_bl->type )
+ {
+ case BL_PC:
+ clif_parse_ActionRequest_sub(((TBL_PC *)unit_bl), actiontype > 0 ? 0x07 : 0x00, target_bl->id, gettick());
+ script_pushint(st, 1);
+ return 0;
+ case BL_MOB:
+ ((TBL_MOB *)unit_bl)->target_id = target_bl->id;
+ break;
+ case BL_PET:
+ ((TBL_PET *)unit_bl)->target_id = target_bl->id;
+ break;
+ default:
+ ShowError("script:unitattack: unsupported source unit type %d\n", unit_bl->type);
+ script_pushint(st, 0);
+ return 1;
+ }
+ script_pushint(st, unit_walktobl(unit_bl, target_bl, 65025, 2));
+ return 0;
+}
+
+/// Makes the unit stop attacking and moving
+///
+/// unitstop <unit_id>;
+BUILDIN_FUNC(unitstop)
+{
+ int unit_id;
+ struct block_list* bl;
+
+ unit_id = script_getnum(st,2);
+
+ bl = map_id2bl(unit_id);
+ if( bl != NULL )
+ {
+ unit_stop_attack(bl);
+ unit_stop_walking(bl,4);
+ if( bl->type == BL_MOB )
+ ((TBL_MOB*)bl)->target_id = 0;
+ }
+
+ return 0;
+}
+
+/// Makes the unit say the message
+///
+/// unittalk <unit_id>,"<message>";
+BUILDIN_FUNC(unittalk)
+{
+ int unit_id;
+ const char* message;
+ struct block_list* bl;
+
+ unit_id = script_getnum(st,2);
+ message = script_getstr(st, 3);
+
+ bl = map_id2bl(unit_id);
+ if( bl != NULL )
+ {
+ struct StringBuf sbuf;
+ StringBuf_Init(&sbuf);
+ StringBuf_Printf(&sbuf, "%s : %s", status_get_name(bl), message);
+ clif_message(bl, StringBuf_Value(&sbuf));
+ if( bl->type == BL_PC )
+ clif_displaymessage(((TBL_PC*)bl)->fd, StringBuf_Value(&sbuf));
+ StringBuf_Destroy(&sbuf);
+ }
+
+ return 0;
+}
+
+/// Makes the unit do an emotion
+///
+/// unitemote <unit_id>,<emotion>;
+///
+/// @see e_* in const.txt
+BUILDIN_FUNC(unitemote)
+{
+ int unit_id;
+ int emotion;
+ struct block_list* bl;
+
+ unit_id = script_getnum(st,2);
+ emotion = script_getnum(st,3);
+ bl = map_id2bl(unit_id);
+ if( bl != NULL )
+ clif_emotion(bl, emotion);
+
+ return 0;
+}
+
+/// Makes the unit cast the skill on the target or self if no target is specified
+///
+/// unitskilluseid <unit_id>,<skill_id>,<skill_lv>{,<target_id>};
+/// unitskilluseid <unit_id>,"<skill name>",<skill_lv>{,<target_id>};
+BUILDIN_FUNC(unitskilluseid)
+{
+ int unit_id;
+ uint16 skill_id;
+ uint16 skill_lv;
+ int target_id;
+ struct block_list* bl;
+
+ unit_id = script_getnum(st,2);
+ skill_id = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) );
+ skill_lv = script_getnum(st,4);
+ target_id = ( script_hasdata(st,5) ? script_getnum(st,5) : unit_id );
+
+ bl = map_id2bl(unit_id);
+ if( bl != NULL )
+ unit_skilluse_id(bl, target_id, skill_id, skill_lv);
+
+ return 0;
+}
+
+/// Makes the unit cast the skill on the target position.
+///
+/// unitskillusepos <unit_id>,<skill_id>,<skill_lv>,<target_x>,<target_y>;
+/// unitskillusepos <unit_id>,"<skill name>",<skill_lv>,<target_x>,<target_y>;
+BUILDIN_FUNC(unitskillusepos)
+{
+ int unit_id;
+ uint16 skill_id;
+ uint16 skill_lv;
+ int skill_x;
+ int skill_y;
+ struct block_list* bl;
+
+ unit_id = script_getnum(st,2);
+ skill_id = ( script_isstring(st,3) ? skill_name2id(script_getstr(st,3)) : script_getnum(st,3) );
+ skill_lv = script_getnum(st,4);
+ skill_x = script_getnum(st,5);
+ skill_y = script_getnum(st,6);
+
+ bl = map_id2bl(unit_id);
+ if( bl != NULL )
+ unit_skilluse_pos(bl, skill_x, skill_y, skill_id, skill_lv);
+
+ return 0;
+}
+
+// <--- [zBuffer] List of mob control commands
+
+/// Pauses the execution of the script, detaching the player
+///
+/// sleep <mili seconds>;
+BUILDIN_FUNC(sleep)
+{
+ int ticks;
+
+ ticks = script_getnum(st,2);
+
+ // detach the player
+ script_detach_rid(st);
+
+ if( ticks <= 0 )
+ {// do nothing
+ }
+ else if( st->sleep.tick == 0 )
+ {// sleep for the target amount of time
+ st->state = RERUNLINE;
+ st->sleep.tick = ticks;
+ }
+ else
+ {// sleep time is over
+ st->state = RUN;
+ st->sleep.tick = 0;
+ }
+ return 0;
+}
+
+/// Pauses the execution of the script, keeping the player attached
+/// Returns if a player is still attached
+///
+/// sleep2(<mili secconds>) -> <bool>
+BUILDIN_FUNC(sleep2)
+{
+ int ticks;
+
+ ticks = script_getnum(st,2);
+
+ if( ticks <= 0 )
+ {// do nothing
+ script_pushint(st, (map_id2sd(st->rid)!=NULL));
+ }
+ else if( !st->sleep.tick )
+ {// sleep for the target amount of time
+ st->state = RERUNLINE;
+ st->sleep.tick = ticks;
+ }
+ else
+ {// sleep time is over
+ st->state = RUN;
+ st->sleep.tick = 0;
+ script_pushint(st, (map_id2sd(st->rid)!=NULL));
+ }
+ return 0;
+}
+
+/// Awakes all the sleep timers of the target npc
+///
+/// awake "<npc name>";
+BUILDIN_FUNC(awake)
+{
+ struct npc_data* nd;
+ struct linkdb_node *node = (struct linkdb_node *)sleep_db;
+
+ nd = npc_name2id(script_getstr(st, 2));
+ if( nd == NULL ) {
+ ShowError("awake: NPC \"%s\" not found\n", script_getstr(st, 2));
+ return 1;
+ }
+
+ while( node )
+ {
+ if( (int)__64BPRTSIZE(node->key) == nd->bl.id )
+ {// sleep timer for the npc
+ struct script_state* tst = (struct script_state*)node->data;
+ TBL_PC* sd = map_id2sd(tst->rid);
+
+ if( tst->sleep.timer == INVALID_TIMER )
+ {// already awake ???
+ node = node->next;
+ continue;
+ }
+ if( (sd && sd->status.char_id != tst->sleep.charid) || (tst->rid && !sd))
+ {// char not online anymore / another char of the same account is online - Cancel execution
+ tst->state = END;
+ tst->rid = 0;
+ }
+
+ delete_timer(tst->sleep.timer, run_script_timer);
+ node = script_erase_sleepdb(node);
+ tst->sleep.timer = INVALID_TIMER;
+ if(tst->state != RERUNLINE)
+ tst->sleep.tick = 0;
+ run_script_main(tst);
+ }
+ else
+ {
+ node = node->next;
+ }
+ }
+ return 0;
+}
+
+/// Returns a reference to a variable of the target NPC.
+/// Returns 0 if an error occurs.
+///
+/// getvariableofnpc(<variable>, "<npc name>") -> <reference>
+BUILDIN_FUNC(getvariableofnpc)
+{
+ struct script_data* data;
+ const char* name;
+ struct npc_data* nd;
+
+ data = script_getdata(st,2);
+ if( !data_isreference(data) )
+ {// Not a reference (aka varaible name)
+ ShowError("script:getvariableofnpc: not a variable\n");
+ script_reportdata(data);
+ script_pushnil(st);
+ st->state = END;
+ return 1;
+ }
+
+ name = reference_getname(data);
+ if( *name != '.' || name[1] == '@' )
+ {// not a npc variable
+ ShowError("script:getvariableofnpc: invalid scope (not npc variable)\n");
+ script_reportdata(data);
+ script_pushnil(st);
+ st->state = END;
+ return 1;
+ }
+
+ nd = npc_name2id(script_getstr(st,3));
+ if( nd == NULL || nd->subtype != SCRIPT || nd->u.scr.script == NULL )
+ {// NPC not found or has no script
+ ShowError("script:getvariableofnpc: can't find npc %s\n", script_getstr(st,3));
+ script_pushnil(st);
+ st->state = END;
+ return 1;
+ }
+
+ push_val2(st->stack, C_NAME, reference_getuid(data), &nd->u.scr.script->script_vars );
+ return 0;
+}
+
+/// Opens a warp portal.
+/// Has no "portal opening" effect/sound, it opens the portal immediately.
+///
+/// warpportal <source x>,<source y>,"<target map>",<target x>,<target y>;
+///
+/// @author blackhole89
+BUILDIN_FUNC(warpportal)
+{
+ int spx;
+ int spy;
+ unsigned short mapindex;
+ int tpx;
+ int tpy;
+ struct skill_unit_group* group;
+ struct block_list* bl;
+
+ bl = map_id2bl(st->oid);
+ if( bl == NULL )
+ {
+ ShowError("script:warpportal: npc is needed\n");
+ return 1;
+ }
+
+ spx = script_getnum(st,2);
+ spy = script_getnum(st,3);
+ mapindex = mapindex_name2id(script_getstr(st, 4));
+ tpx = script_getnum(st,5);
+ tpy = script_getnum(st,6);
+
+ if( mapindex == 0 )
+ return 0;// map not found
+
+ group = skill_unitsetting(bl, AL_WARP, 4, spx, spy, 0);
+ if( group == NULL )
+ return 0;// failed
+ group->val2 = (tpx<<16) | tpy;
+ group->val3 = mapindex;
+
+ return 0;
+}
+
+BUILDIN_FUNC(openmail)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ mail_openmail(sd);
+
+ return 0;
+}
+
+BUILDIN_FUNC(openauction)
+{
+ TBL_PC* sd;
+
+ sd = script_rid2sd(st);
+ if( sd == NULL )
+ return 0;
+
+ clif_Auction_openwindow(sd);
+
+ return 0;
+}
+
+/// Retrieves the value of the specified flag of the specified cell.
+///
+/// checkcell("<map name>",<x>,<y>,<type>) -> <bool>
+///
+/// @see cell_chk* constants in const.txt for the types
+BUILDIN_FUNC(checkcell)
+{
+ int16 m = map_mapname2mapid(script_getstr(st,2));
+ int16 x = script_getnum(st,3);
+ int16 y = script_getnum(st,4);
+ cell_chk type = (cell_chk)script_getnum(st,5);
+
+ script_pushint(st, map_getcell(m, x, y, type));
+
+ return 0;
+}
+
+/// Modifies flags of cells in the specified area.
+///
+/// setcell "<map name>",<x1>,<y1>,<x2>,<y2>,<type>,<flag>;
+///
+/// @see cell_* constants in const.txt for the types
+BUILDIN_FUNC(setcell)
+{
+ int16 m = map_mapname2mapid(script_getstr(st,2));
+ int16 x1 = script_getnum(st,3);
+ int16 y1 = script_getnum(st,4);
+ int16 x2 = script_getnum(st,5);
+ int16 y2 = script_getnum(st,6);
+ cell_t type = (cell_t)script_getnum(st,7);
+ bool flag = (bool)script_getnum(st,8);
+
+ int x,y;
+
+ if( x1 > x2 ) swap(x1,x2);
+ if( y1 > y2 ) swap(y1,y2);
+
+ for( y = y1; y <= y2; ++y )
+ for( x = x1; x <= x2; ++x )
+ map_setcell(m, x, y, type, flag);
+
+ return 0;
+}
+
+/*==========================================
+ * Mercenary Commands
+ *------------------------------------------*/
+BUILDIN_FUNC(mercenary_create)
+{
+ struct map_session_data *sd;
+ int class_, contract_time;
+
+ if( (sd = script_rid2sd(st)) == NULL || sd->md || sd->status.mer_id != 0 )
+ return 0;
+
+ class_ = script_getnum(st,2);
+
+ if( !merc_class(class_) )
+ return 0;
+
+ contract_time = script_getnum(st,3);
+ merc_create(sd, class_, contract_time);
+ return 0;
+}
+
+BUILDIN_FUNC(mercenary_heal)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int hp, sp;
+
+ if( sd == NULL || sd->md == NULL )
+ return 0;
+ hp = script_getnum(st,2);
+ sp = script_getnum(st,3);
+
+ status_heal(&sd->md->bl, hp, sp, 0);
+ return 0;
+}
+
+BUILDIN_FUNC(mercenary_sc_start)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ enum sc_type type;
+ int tick, val1;
+
+ if( sd == NULL || sd->md == NULL )
+ return 0;
+
+ type = (sc_type)script_getnum(st,2);
+ tick = script_getnum(st,3);
+ val1 = script_getnum(st,4);
+
+ status_change_start(&sd->md->bl, type, 10000, val1, 0, 0, 0, tick, 2);
+ return 0;
+}
+
+BUILDIN_FUNC(mercenary_get_calls)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int guild;
+
+ if( sd == NULL )
+ return 0;
+
+ guild = script_getnum(st,2);
+ switch( guild )
+ {
+ case ARCH_MERC_GUILD:
+ script_pushint(st,sd->status.arch_calls);
+ break;
+ case SPEAR_MERC_GUILD:
+ script_pushint(st,sd->status.spear_calls);
+ break;
+ case SWORD_MERC_GUILD:
+ script_pushint(st,sd->status.sword_calls);
+ break;
+ default:
+ script_pushint(st,0);
+ break;
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(mercenary_set_calls)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int guild, value, *calls;
+
+ if( sd == NULL )
+ return 0;
+
+ guild = script_getnum(st,2);
+ value = script_getnum(st,3);
+
+ switch( guild )
+ {
+ case ARCH_MERC_GUILD:
+ calls = &sd->status.arch_calls;
+ break;
+ case SPEAR_MERC_GUILD:
+ calls = &sd->status.spear_calls;
+ break;
+ case SWORD_MERC_GUILD:
+ calls = &sd->status.sword_calls;
+ break;
+ default:
+ return 0; // Invalid Guild
+ }
+
+ *calls += value;
+ *calls = cap_value(*calls, 0, INT_MAX);
+
+ return 0;
+}
+
+BUILDIN_FUNC(mercenary_get_faith)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int guild;
+
+ if( sd == NULL )
+ return 0;
+
+ guild = script_getnum(st,2);
+ switch( guild )
+ {
+ case ARCH_MERC_GUILD:
+ script_pushint(st,sd->status.arch_faith);
+ break;
+ case SPEAR_MERC_GUILD:
+ script_pushint(st,sd->status.spear_faith);
+ break;
+ case SWORD_MERC_GUILD:
+ script_pushint(st,sd->status.sword_faith);
+ break;
+ default:
+ script_pushint(st,0);
+ break;
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(mercenary_set_faith)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int guild, value, *calls;
+
+ if( sd == NULL )
+ return 0;
+
+ guild = script_getnum(st,2);
+ value = script_getnum(st,3);
+
+ switch( guild )
+ {
+ case ARCH_MERC_GUILD:
+ calls = &sd->status.arch_faith;
+ break;
+ case SPEAR_MERC_GUILD:
+ calls = &sd->status.spear_faith;
+ break;
+ case SWORD_MERC_GUILD:
+ calls = &sd->status.sword_faith;
+ break;
+ default:
+ return 0; // Invalid Guild
+ }
+
+ *calls += value;
+ *calls = cap_value(*calls, 0, INT_MAX);
+ if( mercenary_get_guild(sd->md) == guild )
+ clif_mercenary_updatestatus(sd,SP_MERCFAITH);
+
+ return 0;
+}
+
+/*------------------------------------------
+ * Book Reading
+ *------------------------------------------*/
+BUILDIN_FUNC(readbook)
+{
+ struct map_session_data *sd;
+ int book_id, page;
+
+ if( (sd = script_rid2sd(st)) == NULL )
+ return 0;
+
+ book_id = script_getnum(st,2);
+ page = script_getnum(st,3);
+
+ clif_readbook(sd->fd, book_id, page);
+ return 0;
+}
+
+/******************
+Questlog script commands
+*******************/
+
+BUILDIN_FUNC(setquest)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ nullpo_ret(sd);
+
+ quest_add(sd, script_getnum(st, 2));
+ return 0;
+}
+
+BUILDIN_FUNC(erasequest)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ nullpo_ret(sd);
+
+ quest_delete(sd, script_getnum(st, 2));
+ return 0;
+}
+
+BUILDIN_FUNC(completequest)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ nullpo_ret(sd);
+
+ quest_update_status(sd, script_getnum(st, 2), Q_COMPLETE);
+ return 0;
+}
+
+BUILDIN_FUNC(changequest)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ nullpo_ret(sd);
+
+ quest_change(sd, script_getnum(st, 2),script_getnum(st, 3));
+ return 0;
+}
+
+BUILDIN_FUNC(checkquest)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ quest_check_type type = HAVEQUEST;
+
+ nullpo_ret(sd);
+
+ if( script_hasdata(st, 3) )
+ type = (quest_check_type)script_getnum(st, 3);
+
+ script_pushint(st, quest_check(sd, script_getnum(st, 2), type));
+
+ return 0;
+}
+
+BUILDIN_FUNC(showevent)
+{
+ TBL_PC *sd = script_rid2sd(st);
+ struct npc_data *nd = map_id2nd(st->oid);
+ int state, color;
+
+ if( sd == NULL || nd == NULL )
+ return 0;
+ state = script_getnum(st, 2);
+ color = script_getnum(st, 3);
+
+ if( color < 0 || color > 3 )
+ color = 0; // set default color
+
+ clif_quest_show_event(sd, &nd->bl, state, color);
+ return 0;
+}
+
+/*==========================================
+ * BattleGround System
+ *------------------------------------------*/
+BUILDIN_FUNC(waitingroom2bg)
+{
+ struct npc_data *nd;
+ struct chat_data *cd;
+ const char *map_name, *ev = "", *dev = "";
+ int x, y, i, mapindex = 0, bg_id, n;
+ struct map_session_data *sd;
+
+ if( script_hasdata(st,7) )
+ nd = npc_name2id(script_getstr(st,7));
+ else
+ nd = (struct npc_data *)map_id2bl(st->oid);
+
+ if( nd == NULL || (cd = (struct chat_data *)map_id2bl(nd->chat_id)) == NULL )
+ {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ map_name = script_getstr(st,2);
+ if( strcmp(map_name,"-") != 0 )
+ {
+ mapindex = mapindex_name2id(map_name);
+ if( mapindex == 0 )
+ { // Invalid Map
+ script_pushint(st,0);
+ return 0;
+ }
+ }
+
+ x = script_getnum(st,3);
+ y = script_getnum(st,4);
+ ev = script_getstr(st,5); // Logout Event
+ dev = script_getstr(st,6); // Die Event
+
+ if( (bg_id = bg_create(mapindex, x, y, ev, dev)) == 0 )
+ { // Creation failed
+ script_pushint(st,0);
+ return 0;
+ }
+
+ n = cd->users;
+ for( i = 0; i < n && i < MAX_BG_MEMBERS; i++ )
+ {
+ if( (sd = cd->usersd[i]) != NULL && bg_team_join(bg_id, sd) )
+ mapreg_setreg(reference_uid(add_str("$@arenamembers"), i), sd->bl.id);
+ else
+ mapreg_setreg(reference_uid(add_str("$@arenamembers"), i), 0);
+ }
+
+ mapreg_setreg(add_str("$@arenamembersnum"), i);
+ script_pushint(st,bg_id);
+ return 0;
+}
+
+BUILDIN_FUNC(waitingroom2bg_single)
+{
+ const char* map_name;
+ struct npc_data *nd;
+ struct chat_data *cd;
+ struct map_session_data *sd;
+ int x, y, mapindex, bg_id;
+
+ bg_id = script_getnum(st,2);
+ map_name = script_getstr(st,3);
+ if( (mapindex = mapindex_name2id(map_name)) == 0 )
+ return 0; // Invalid Map
+
+ x = script_getnum(st,4);
+ y = script_getnum(st,5);
+ nd = npc_name2id(script_getstr(st,6));
+
+ if( nd == NULL || (cd = (struct chat_data *)map_id2bl(nd->chat_id)) == NULL || cd->users <= 0 )
+ return 0;
+
+ if( (sd = cd->usersd[0]) == NULL )
+ return 0;
+
+ if( bg_team_join(bg_id, sd) )
+ {
+ pc_setpos(sd, mapindex, x, y, CLR_TELEPORT);
+ script_pushint(st,1);
+ }
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+
+BUILDIN_FUNC(bg_team_setxy)
+{
+ struct battleground_data *bg;
+ int bg_id;
+
+ bg_id = script_getnum(st,2);
+ if( (bg = bg_team_search(bg_id)) == NULL )
+ return 0;
+
+ bg->x = script_getnum(st,3);
+ bg->y = script_getnum(st,4);
+ return 0;
+}
+
+BUILDIN_FUNC(bg_warp)
+{
+ int x, y, mapindex, bg_id;
+ const char* map_name;
+
+ bg_id = script_getnum(st,2);
+ map_name = script_getstr(st,3);
+ if( (mapindex = mapindex_name2id(map_name)) == 0 )
+ return 0; // Invalid Map
+ x = script_getnum(st,4);
+ y = script_getnum(st,5);
+ bg_team_warp(bg_id, mapindex, x, y);
+ return 0;
+}
+
+BUILDIN_FUNC(bg_monster)
+{
+ int class_ = 0, x = 0, y = 0, bg_id = 0;
+ const char *str,*map, *evt="";
+
+ bg_id = script_getnum(st,2);
+ map = script_getstr(st,3);
+ x = script_getnum(st,4);
+ y = script_getnum(st,5);
+ str = script_getstr(st,6);
+ class_ = script_getnum(st,7);
+ if( script_hasdata(st,8) ) evt = script_getstr(st,8);
+ check_event(st, evt);
+ script_pushint(st, mob_spawn_bg(map,x,y,str,class_,evt,bg_id));
+ return 0;
+}
+
+BUILDIN_FUNC(bg_monster_set_team)
+{
+ struct mob_data *md;
+ struct block_list *mbl;
+ int id = script_getnum(st,2),
+ bg_id = script_getnum(st,3);
+
+ if( (mbl = map_id2bl(id)) == NULL || mbl->type != BL_MOB )
+ return 0;
+ md = (TBL_MOB *)mbl;
+ md->bg_id = bg_id;
+
+ mob_stop_attack(md);
+ mob_stop_walking(md, 0);
+ md->target_id = md->attacked_id = 0;
+ clif_charnameack(0, &md->bl);
+
+ return 0;
+}
+
+BUILDIN_FUNC(bg_leave)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ if( sd == NULL || !sd->bg_id )
+ return 0;
+
+ bg_team_leave(sd,0);
+ return 0;
+}
+
+BUILDIN_FUNC(bg_destroy)
+{
+ int bg_id = script_getnum(st,2);
+ bg_team_delete(bg_id);
+ return 0;
+}
+
+BUILDIN_FUNC(bg_getareausers)
+{
+ const char *str;
+ int16 m, x0, y0, x1, y1;
+ int bg_id;
+ int i = 0, c = 0;
+ struct battleground_data *bg = NULL;
+ struct map_session_data *sd;
+
+ bg_id = script_getnum(st,2);
+ str = script_getstr(st,3);
+
+ if( (bg = bg_team_search(bg_id)) == NULL || (m = map_mapname2mapid(str)) < 0 )
+ {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ x0 = script_getnum(st,4);
+ y0 = script_getnum(st,5);
+ x1 = script_getnum(st,6);
+ y1 = script_getnum(st,7);
+
+ for( i = 0; i < MAX_BG_MEMBERS; i++ )
+ {
+ if( (sd = bg->members[i].sd) == NULL )
+ continue;
+ if( sd->bl.m != m || sd->bl.x < x0 || sd->bl.y < y0 || sd->bl.x > x1 || sd->bl.y > y1 )
+ continue;
+ c++;
+ }
+
+ script_pushint(st,c);
+ return 0;
+}
+
+BUILDIN_FUNC(bg_updatescore)
+{
+ const char *str;
+ int16 m;
+
+ str = script_getstr(st,2);
+ if( (m = map_mapname2mapid(str)) < 0 )
+ return 0;
+
+ map[m].bgscore_lion = script_getnum(st,3);
+ map[m].bgscore_eagle = script_getnum(st,4);
+
+ clif_bg_updatescore(m);
+ return 0;
+}
+
+BUILDIN_FUNC(bg_get_data)
+{
+ struct battleground_data *bg;
+ int bg_id = script_getnum(st,2),
+ type = script_getnum(st,3);
+
+ if( (bg = bg_team_search(bg_id)) == NULL )
+ {
+ script_pushint(st,0);
+ return 0;
+ }
+
+ switch( type )
+ {
+ case 0: script_pushint(st, bg->count); break;
+ default:
+ ShowError("script:bg_get_data: unknown data identifier %d\n", type);
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Instancing Script Commands
+ *------------------------------------------*/
+
+BUILDIN_FUNC(instance_create)
+{
+ const char *name;
+ int party_id, res;
+
+ name = script_getstr(st, 2);
+ party_id = script_getnum(st, 3);
+
+ res = instance_create(party_id, name);
+ if( res == -4 ) // Already exists
+ {
+ script_pushint(st, -1);
+ return 0;
+ }
+ else if( res < 0 )
+ {
+ const char *err;
+ switch(res)
+ {
+ case -3: err = "No free instances"; break;
+ case -2: err = "Invalid party ID"; break;
+ case -1: err = "Invalid type"; break;
+ default: err = "Unknown"; break;
+ }
+ ShowError("buildin_instance_create: %s [%d].\n", err, res);
+ script_pushint(st, -2);
+ return 0;
+ }
+
+ script_pushint(st, res);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_destroy)
+{
+ int instance_id;
+ struct map_session_data *sd;
+ struct party_data *p;
+
+ if( script_hasdata(st, 2) )
+ instance_id = script_getnum(st, 2);
+ else if( st->instance_id )
+ instance_id = st->instance_id;
+ else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
+ instance_id = p->instance_id;
+ else return 0;
+
+ if( instance_id <= 0 || instance_id >= MAX_INSTANCE )
+ {
+ ShowError("buildin_instance_destroy: Trying to destroy invalid instance %d.\n", instance_id);
+ return 0;
+ }
+
+ instance_destroy(instance_id);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_attachmap)
+{
+ const char *name;
+ int16 m;
+ int instance_id;
+ bool usebasename = false;
+
+ name = script_getstr(st,2);
+ instance_id = script_getnum(st,3);
+ if( script_hasdata(st,4) && script_getnum(st,4) > 0)
+ usebasename = true;
+
+ if( (m = instance_add_map(name, instance_id, usebasename)) < 0 ) // [Saithis]
+ {
+ ShowError("buildin_instance_attachmap: instance creation failed (%s): %d\n", name, m);
+ script_pushconststr(st, "");
+ return 0;
+ }
+ script_pushconststr(st, map[m].name);
+
+ return 0;
+}
+
+BUILDIN_FUNC(instance_detachmap)
+{
+ struct map_session_data *sd;
+ struct party_data *p;
+ const char *str;
+ int16 m;
+ int instance_id;
+
+ str = script_getstr(st, 2);
+ if( script_hasdata(st, 3) )
+ instance_id = script_getnum(st, 3);
+ else if( st->instance_id )
+ instance_id = st->instance_id;
+ else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
+ instance_id = p->instance_id;
+ else return 0;
+
+ if( (m = map_mapname2mapid(str)) < 0 || (m = instance_map2imap(m,instance_id)) < 0 )
+ {
+ ShowError("buildin_instance_detachmap: Trying to detach invalid map %s\n", str);
+ return 0;
+ }
+
+ instance_del_map(m);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_attach)
+{
+ int instance_id;
+
+ instance_id = script_getnum(st, 2);
+ if( instance_id <= 0 || instance_id >= MAX_INSTANCE )
+ return 0;
+
+ st->instance_id = instance_id;
+ return 0;
+}
+
+BUILDIN_FUNC(instance_id)
+{
+ int type, instance_id;
+ struct map_session_data *sd;
+ struct party_data *p;
+
+ if( script_hasdata(st, 2) )
+ {
+ type = script_getnum(st, 2);
+ if( type == 0 )
+ instance_id = st->instance_id;
+ else if( type == 1 && (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL )
+ instance_id = p->instance_id;
+ else
+ instance_id = 0;
+ }
+ else
+ instance_id = st->instance_id;
+
+ script_pushint(st, instance_id);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_set_timeout)
+{
+ int progress_timeout, idle_timeout;
+ int instance_id;
+ struct map_session_data *sd;
+ struct party_data *p;
+
+ progress_timeout = script_getnum(st, 2);
+ idle_timeout = script_getnum(st, 3);
+
+ if( script_hasdata(st, 4) )
+ instance_id = script_getnum(st, 4);
+ else if( st->instance_id )
+ instance_id = st->instance_id;
+ else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
+ instance_id = p->instance_id;
+ else return 0;
+
+ if( instance_id > 0 )
+ instance_set_timeout(instance_id, progress_timeout, idle_timeout);
+
+ return 0;
+}
+
+BUILDIN_FUNC(instance_init)
+{
+ int instance_id = script_getnum(st, 2);
+
+ if( instance[instance_id].state != INSTANCE_IDLE )
+ {
+ ShowError("instance_init: instance already initialized.\n");
+ return 0;
+ }
+
+ instance_init(instance_id);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_announce)
+{
+ int instance_id = script_getnum(st,2);
+ const char *mes = script_getstr(st,3);
+ int flag = script_getnum(st,4);
+ const char *fontColor = script_hasdata(st,5) ? script_getstr(st,5) : NULL;
+ int fontType = script_hasdata(st,6) ? script_getnum(st,6) : 0x190; // default fontType (FW_NORMAL)
+ int fontSize = script_hasdata(st,7) ? script_getnum(st,7) : 12; // default fontSize
+ int fontAlign = script_hasdata(st,8) ? script_getnum(st,8) : 0; // default fontAlign
+ int fontY = script_hasdata(st,9) ? script_getnum(st,9) : 0; // default fontY
+
+ int i;
+ struct map_session_data *sd;
+ struct party_data *p;
+
+ if( instance_id == 0 )
+ {
+ if( st->instance_id )
+ instance_id = st->instance_id;
+ else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
+ instance_id = p->instance_id;
+ else return 0;
+ }
+
+ if( instance_id <= 0 || instance_id >= MAX_INSTANCE )
+ return 0;
+
+ for( i = 0; i < instance[instance_id].num_map; i++ )
+ map_foreachinmap(buildin_announce_sub, instance[instance_id].map[i], BL_PC,
+ mes, strlen(mes)+1, flag&0xf0, fontColor, fontType, fontSize, fontAlign, fontY);
+
+ return 0;
+}
+
+BUILDIN_FUNC(instance_npcname)
+{
+ const char *str;
+ int instance_id = 0;
+
+ struct map_session_data *sd;
+ struct party_data *p;
+ struct npc_data *nd;
+
+ str = script_getstr(st, 2);
+ if( script_hasdata(st, 3) )
+ instance_id = script_getnum(st, 3);
+ else if( st->instance_id )
+ instance_id = st->instance_id;
+ else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
+ instance_id = p->instance_id;
+
+ if( instance_id && (nd = npc_name2id(str)) != NULL )
+ {
+ static char npcname[NAME_LENGTH];
+ snprintf(npcname, sizeof(npcname), "dup_%d_%d", instance_id, nd->bl.id);
+ script_pushconststr(st,npcname);
+ }
+ else
+ {
+ ShowError("script:instance_npcname: invalid instance NPC (instance_id: %d, NPC name: \"%s\".)\n", instance_id, str);
+ st->state = END;
+ return 1;
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(has_instance)
+{
+ struct map_session_data *sd;
+ struct party_data *p;
+ const char *str;
+ int16 m;
+ int instance_id = 0;
+
+ str = script_getstr(st, 2);
+ if( script_hasdata(st, 3) )
+ instance_id = script_getnum(st, 3);
+ else if( st->instance_id )
+ instance_id = st->instance_id;
+ else if( (sd = script_rid2sd(st)) != NULL && sd->status.party_id && (p = party_search(sd->status.party_id)) != NULL && p->instance_id )
+ instance_id = p->instance_id;
+
+ if( !instance_id || (m = map_mapname2mapid(str)) < 0 || (m = instance_map2imap(m, instance_id)) < 0 )
+ {
+ script_pushconststr(st, "");
+ return 0;
+ }
+
+ script_pushconststr(st, map[m].name);
+ return 0;
+}
+
+BUILDIN_FUNC(instance_warpall)
+{
+ struct map_session_data *pl_sd;
+ int16 m, i;
+ int instance_id;
+ const char *mapn;
+ int x, y;
+ unsigned short mapindex;
+ struct party_data *p = NULL;
+
+ mapn = script_getstr(st,2);
+ x = script_getnum(st,3);
+ y = script_getnum(st,4);
+ if( script_hasdata(st,5) )
+ instance_id = script_getnum(st,5);
+ else if( st->instance_id )
+ instance_id = st->instance_id;
+ else if( (pl_sd = script_rid2sd(st)) != NULL && pl_sd->status.party_id && (p = party_search(pl_sd->status.party_id)) != NULL && p->instance_id )
+ instance_id = p->instance_id;
+ else return 0;
+
+ if( (m = map_mapname2mapid(mapn)) < 0 || (map[m].flag.src4instance && (m = instance_mapid2imapid(m, instance_id)) < 0) )
+ return 0;
+
+ if( !(p = party_search(instance[instance_id].party_id)) )
+ return 0;
+
+ mapindex = map_id2index(m);
+ for( i = 0; i < MAX_PARTY; i++ )
+ if( (pl_sd = p->data[i].sd) && map[pl_sd->bl.m].instance_id == st->instance_id ) pc_setpos(pl_sd,mapindex,x,y,CLR_TELEPORT);
+
+ return 0;
+}
+
+/*==========================================
+ * instance_check_party [malufett]
+ * Values:
+ * party_id : Party ID of the invoking character. [Required Parameter]
+ * amount : Amount of needed Partymembers for the Instance. [Optional Parameter]
+ * min : Minimum Level needed to join the Instance. [Optional Parameter]
+ * max : Maxium Level allowed to join the Instance. [Optional Parameter]
+ * Example: instance_check_party (getcharid(1){,amount}{,min}{,max});
+ * Example 2: instance_check_party (getcharid(1),1,1,99);
+ *------------------------------------------*/
+BUILDIN_FUNC(instance_check_party)
+{
+ struct map_session_data *pl_sd;
+ int amount, min, max, i, party_id, c = 0;
+ struct party_data *p = NULL;
+
+ amount = script_hasdata(st,3) ? script_getnum(st,3) : 1; // Amount of needed Partymembers for the Instance.
+ min = script_hasdata(st,4) ? script_getnum(st,4) : 1; // Minimum Level needed to join the Instance.
+ max = script_hasdata(st,5) ? script_getnum(st,5) : MAX_LEVEL; // Maxium Level allowed to join the Instance.
+
+ if( min < 1 || min > MAX_LEVEL){
+ ShowError("instance_check_party: Invalid min level, %d\n", min);
+ return 0;
+ }else if( max < 1 || max > MAX_LEVEL){
+ ShowError("instance_check_party: Invalid max level, %d\n", max);
+ return 0;
+ }
+
+ if( script_hasdata(st,2) )
+ party_id = script_getnum(st,2);
+ else return 0;
+
+ if( !(p = party_search(party_id)) ){
+ script_pushint(st, 0); // Returns false if party does not exist.
+ return 0;
+ }
+
+ for( i = 0; i < MAX_PARTY; i++ )
+ if( (pl_sd = p->data[i].sd) )
+ if(map_id2bl(pl_sd->bl.id)){
+ if(pl_sd->status.base_level < min){
+ script_pushint(st, 0);
+ return 0;
+ }else if(pl_sd->status.base_level > max){
+ script_pushint(st, 0);
+ return 0;
+ }
+ c++;
+ }
+
+ if(c < amount){
+ script_pushint(st, 0); // Not enough Members in the Party to join Instance.
+ }else
+ script_pushint(st, 1);
+
+ return 0;
+}
+
+/*==========================================
+ * Custom Fonts
+ *------------------------------------------*/
+BUILDIN_FUNC(setfont)
+{
+ struct map_session_data *sd = script_rid2sd(st);
+ int font = script_getnum(st,2);
+ if( sd == NULL )
+ return 0;
+
+ if( sd->user_font != font )
+ sd->user_font = font;
+ else
+ sd->user_font = 0;
+
+ clif_font(sd);
+ return 0;
+}
+
+static int buildin_mobuseskill_sub(struct block_list *bl,va_list ap)
+{
+ TBL_MOB* md = (TBL_MOB*)bl;
+ struct block_list *tbl;
+ int mobid = va_arg(ap,int);
+ uint16 skill_id = va_arg(ap,int);
+ uint16 skill_lv = va_arg(ap,int);
+ int casttime = va_arg(ap,int);
+ int cancel = va_arg(ap,int);
+ int emotion = va_arg(ap,int);
+ int target = va_arg(ap,int);
+
+ if( md->class_ != mobid )
+ return 0;
+
+ // 0:self, 1:target, 2:master, default:random
+ switch( target )
+ {
+ case 0: tbl = map_id2bl(md->bl.id); break;
+ case 1: tbl = map_id2bl(md->target_id); break;
+ case 2: tbl = map_id2bl(md->master_id); break;
+ default:tbl = battle_getenemy(&md->bl, DEFAULT_ENEMY_TYPE(md),skill_get_range2(&md->bl, skill_id, skill_lv)); break;
+ }
+
+ if( !tbl )
+ return 0;
+
+ if( md->ud.skilltimer != INVALID_TIMER ) // Cancel the casting skill.
+ unit_skillcastcancel(bl,0);
+
+ if( skill_get_casttype(skill_id) == CAST_GROUND )
+ unit_skilluse_pos2(&md->bl, tbl->x, tbl->y, skill_id, skill_lv, casttime, cancel);
+ else
+ unit_skilluse_id2(&md->bl, tbl->id, skill_id, skill_lv, casttime, cancel);
+
+ clif_emotion(&md->bl, emotion);
+
+ return 0;
+}
+/*==========================================
+ * areamobuseskill "Map Name",<x>,<y>,<range>,<Mob ID>,"Skill Name"/<Skill ID>,<Skill Lv>,<Cast Time>,<Cancelable>,<Emotion>,<Target Type>;
+ *------------------------------------------*/
+BUILDIN_FUNC(areamobuseskill)
+{
+ struct block_list center;
+ int16 m;
+ int range,mobid,skill_id,skill_lv,casttime,emotion,target,cancel;
+
+ if( (m = map_mapname2mapid(script_getstr(st,2))) < 0 )
+ {
+ ShowError("areamobuseskill: invalid map name.\n");
+ return 0;
+ }
+
+ if( map[m].flag.src4instance && st->instance_id && (m = instance_mapid2imapid(m, st->instance_id)) < 0 )
+ return 0;
+
+ center.m = m;
+ center.x = script_getnum(st,3);
+ center.y = script_getnum(st,4);
+ range = script_getnum(st,5);
+ mobid = script_getnum(st,6);
+ skill_id = ( script_isstring(st,7) ? skill_name2id(script_getstr(st,7)) : script_getnum(st,7) );
+ if( (skill_lv = script_getnum(st,8)) > battle_config.mob_max_skilllvl )
+ skill_lv = battle_config.mob_max_skilllvl;
+
+ casttime = script_getnum(st,9);
+ cancel = script_getnum(st,10);
+ emotion = script_getnum(st,11);
+ target = script_getnum(st,12);
+
+ map_foreachinrange(buildin_mobuseskill_sub, &center, range, BL_MOB, mobid, skill_id, skill_lv, casttime, cancel, emotion, target);
+ return 0;
+}
+
+
+BUILDIN_FUNC(progressbar)
+{
+ struct map_session_data * sd = script_rid2sd(st);
+ const char * color;
+ unsigned int second;
+
+ if( !st || !sd )
+ return 0;
+
+ st->state = STOP;
+
+ color = script_getstr(st,2);
+ second = script_getnum(st,3);
+
+ sd->progressbar.npc_id = st->oid;
+ sd->progressbar.timeout = gettick() + second*1000;
+
+ clif_progressbar(sd, strtol(color, (char **)NULL, 0), second);
+ return 0;
+}
+
+BUILDIN_FUNC(pushpc)
+{
+ uint8 dir;
+ int cells, dx, dy;
+ struct map_session_data* sd;
+
+ if((sd = script_rid2sd(st))==NULL)
+ {
+ return 0;
+ }
+
+ dir = script_getnum(st,2);
+ cells = script_getnum(st,3);
+
+ if(dir>7)
+ {
+ ShowWarning("buildin_pushpc: Invalid direction %d specified.\n", dir);
+ script_reportsrc(st);
+
+ dir%= 8; // trim spin-over
+ }
+
+ if(!cells)
+ {// zero distance
+ return 0;
+ }
+ else if(cells<0)
+ {// pushing backwards
+ dir = (dir+4)%8; // turn around
+ cells = -cells;
+ }
+
+ dx = dirx[dir];
+ dy = diry[dir];
+
+ unit_blown(&sd->bl, dx, dy, cells, 0);
+ return 0;
+}
+
+
+/// Invokes buying store preparation window
+/// buyingstore <slots>;
+BUILDIN_FUNC(buyingstore)
+{
+ struct map_session_data* sd;
+
+ if( ( sd = script_rid2sd(st) ) == NULL )
+ {
+ return 0;
+ }
+
+ buyingstore_setup(sd, script_getnum(st,2));
+ return 0;
+}
+
+
+/// Invokes search store info window
+/// searchstores <uses>,<effect>;
+BUILDIN_FUNC(searchstores)
+{
+ unsigned short effect;
+ unsigned int uses;
+ struct map_session_data* sd;
+
+ if( ( sd = script_rid2sd(st) ) == NULL )
+ {
+ return 0;
+ }
+
+ uses = script_getnum(st,2);
+ effect = script_getnum(st,3);
+
+ if( !uses )
+ {
+ ShowError("buildin_searchstores: Amount of uses cannot be zero.\n");
+ return 1;
+ }
+
+ if( effect > 1 )
+ {
+ ShowError("buildin_searchstores: Invalid effect id %hu, specified.\n", effect);
+ return 1;
+ }
+
+ searchstore_open(sd, uses, effect);
+ return 0;
+}
+/// Displays a number as large digital clock.
+/// showdigit <value>[,<type>];
+BUILDIN_FUNC(showdigit)
+{
+ unsigned int type = 0;
+ int value;
+ struct map_session_data* sd;
+
+ if( ( sd = script_rid2sd(st) ) == NULL )
+ {
+ return 0;
+ }
+
+ value = script_getnum(st,2);
+
+ if( script_hasdata(st,3) )
+ {
+ type = script_getnum(st,3);
+
+ if( type > 3 )
+ {
+ ShowError("buildin_showdigit: Invalid type %u.\n", type);
+ return 1;
+ }
+ }
+
+ clif_showdigit(sd, (unsigned char)type, value);
+ return 0;
+}
+/**
+ * Rune Knight
+ **/
+BUILDIN_FUNC(makerune) {
+ TBL_PC* sd;
+ if( (sd = script_rid2sd(st)) == NULL )
+ return 0;
+ clif_skill_produce_mix_list(sd,RK_RUNEMASTERY,24);
+ sd->itemid = script_getnum(st,2);
+ return 0;
+}
+/**
+ * checkdragon() returns 1 if mounting a dragon or 0 otherwise.
+ **/
+BUILDIN_FUNC(checkdragon) {
+ TBL_PC* sd;
+ if( (sd = script_rid2sd(st)) == NULL )
+ return 0;
+ if( pc_isridingdragon(sd) )
+ script_pushint(st,1);
+ else
+ script_pushint(st,0);
+ return 0;
+}
+/**
+ * setdragon({optional Color}) returns 1 on success or 0 otherwise
+ * - Toggles the dragon on a RK if he can mount;
+ * @param Color - when not provided uses the green dragon;
+ * - 1 : Green Dragon
+ * - 2 : Brown Dragon
+ * - 3 : Gray Dragon
+ * - 4 : Blue Dragon
+ * - 5 : Red Dragon
+ **/
+BUILDIN_FUNC(setdragon) {
+ TBL_PC* sd;
+ int color = script_hasdata(st,2) ? script_getnum(st,2) : 0;
+ unsigned int option = OPTION_DRAGON1;
+ if( (sd = script_rid2sd(st)) == NULL )
+ return 0;
+ if( !pc_checkskill(sd,RK_DRAGONTRAINING) || (sd->class_&MAPID_THIRDMASK) != MAPID_RUNE_KNIGHT )
+ script_pushint(st,0);//Doesn't have the skill or it's not a Rune Knight
+ else if ( pc_isridingdragon(sd) ) {//Is mounted; release
+ pc_setoption(sd, sd->sc.option&~OPTION_DRAGON);
+ script_pushint(st,1);
+ } else {//Not mounted; Mount now.
+ if( color ) {
+ option = ( color == 1 ? OPTION_DRAGON1 :
+ color == 2 ? OPTION_DRAGON2 :
+ color == 3 ? OPTION_DRAGON3 :
+ color == 4 ? OPTION_DRAGON4 :
+ color == 5 ? OPTION_DRAGON5 : 0);
+ if( !option ) {
+ ShowWarning("script_setdragon: Unknown Color %d used; changing to green (1)\n",color);
+ option = OPTION_DRAGON1;
+ }
+ }
+ pc_setoption(sd, sd->sc.option|option);
+ script_pushint(st,1);
+ }
+ return 0;
+}
+
+/**
+ * ismounting() returns 1 if mounting a new mount or 0 otherwise
+ **/
+BUILDIN_FUNC(ismounting) {
+ TBL_PC* sd;
+ if( (sd = script_rid2sd(st)) == NULL )
+ return 0;
+ if( sd->sc.option&OPTION_MOUNTING )
+ script_pushint(st,1);
+ else
+ script_pushint(st,0);
+ return 0;
+}
+
+/**
+ * setmounting() returns 1 on success or 0 otherwise
+ * - Toggles new mounts on a player when he can mount
+ * - Will fail if the player is mounting a non-new mount, e.g. dragon, peco, wug, etc.
+ * - Will unmount the player is he is already mounting
+ **/
+BUILDIN_FUNC(setmounting) {
+ TBL_PC* sd;
+ if( (sd = script_rid2sd(st)) == NULL )
+ return 0;
+ if( sd->sc.option&(OPTION_WUGRIDER|OPTION_RIDING|OPTION_DRAGON|OPTION_MADOGEAR) )
+ script_pushint(st,0);//can't mount with one of these
+ else {
+ if( sd->sc.option&OPTION_MOUNTING )
+ pc_setoption(sd, sd->sc.option&~OPTION_MOUNTING);//release mount
+ else
+ pc_setoption(sd, sd->sc.option|OPTION_MOUNTING);//mount
+ script_pushint(st,1);//in both cases, return 1.
+ }
+ return 0;
+}
+/**
+ * Retrieves quantity of arguments provided to callfunc/callsub.
+ * getargcount() -> amount of arguments received in a function
+ **/
+BUILDIN_FUNC(getargcount) {
+ struct script_retinfo* ri;
+
+ if( st->stack->defsp < 1 || st->stack->stack_data[st->stack->defsp - 1].type != C_RETINFO ) {
+ ShowError("script:getargcount: used out of function or callsub label!\n");
+ st->state = END;
+ return 1;
+ }
+ ri = st->stack->stack_data[st->stack->defsp - 1].u.ri;
+
+ script_pushint(st, ri->nargs);
+
+ return 0;
+}
+/**
+ * getcharip(<account ID>/<character ID>/<character name>)
+ **/
+BUILDIN_FUNC(getcharip)
+{
+ struct map_session_data* sd = NULL;
+ int id = 0;
+
+ /* check if a character name is specified */
+ if( script_hasdata(st, 2) )
+ {
+ if (script_isstring(st, 2))
+ sd = map_nick2sd(script_getstr(st, 2));
+ else if (script_isint(st, 2) || script_getnum(st, 2))
+ {
+ id = script_getnum(st, 2);
+ sd = (map_id2sd(id) ? map_id2sd(id) : map_charid2sd(id));
+ }
+ }
+ else
+ sd = script_rid2sd(st);
+
+ /* check for sd and IP */
+ if (!sd || !session[sd->fd]->client_addr)
+ {
+ script_pushconststr(st, "");
+ return 0;
+ }
+
+ /* return the client ip_addr converted for output */
+ if (sd && sd->fd && session[sd->fd])
+ {
+ /* initiliaze */
+ const char *ip_addr = NULL;
+ uint32 ip;
+
+ /* set ip, ip_addr and convert to ip and push str */
+ ip = session[sd->fd]->client_addr;
+ ip_addr = ip2str(ip, NULL);
+ script_pushstrcopy(st, ip_addr);
+ }
+
+ return 0;
+}
+/**
+ * is_function(<function name>) -> 1 if function exists, 0 otherwise
+ **/
+BUILDIN_FUNC(is_function) {
+ const char* str = script_getstr(st,2);
+
+ if( strdb_exists(userfunc_db, str) )
+ script_pushint(st,1);
+ else
+ script_pushint(st,0);
+
+ return 0;
+}
+/**
+ * get_revision() -> retrieves the current svn revision (if available)
+ **/
+BUILDIN_FUNC(get_revision) {
+ const char * revision;
+
+ if ( (revision = get_svn_revision()) != 0 )
+ script_pushint(st,atoi(revision));
+ else
+ script_pushint(st,-1);//unknown
+
+ return 0;
+}
+/**
+ * freeloop(<toggle>) -> toggles this script instance's looping-check ability
+ **/
+BUILDIN_FUNC(freeloop) {
+
+ if( script_getnum(st,2) )
+ st->freeloop = 1;
+ else
+ st->freeloop = 0;
+
+ script_pushint(st, st->freeloop);
+
+ return 0;
+}
+
+/**
+ * @commands (script based)
+ **/
+BUILDIN_FUNC(bindatcmd) {
+ const char* atcmd;
+ const char* eventName;
+ int i, level = 0, level2 = 0;
+ bool create = false;
+
+ atcmd = script_getstr(st,2);
+ eventName = script_getstr(st,3);
+
+ if( *atcmd == atcommand_symbol || *atcmd == charcommand_symbol )
+ atcmd++;
+
+ if( script_hasdata(st,4) ) level = script_getnum(st,4);
+ if( script_hasdata(st,5) ) level2 = script_getnum(st,5);
+
+ if( atcmd_binding_count == 0 ) {
+ CREATE(atcmd_binding,struct atcmd_binding_data*,1);
+
+ create = true;
+ } else {
+ ARR_FIND(0, atcmd_binding_count, i, strcmp(atcmd_binding[i]->command,atcmd) == 0);
+ if( i < atcmd_binding_count ) {/* update existent entry */
+ safestrncpy(atcmd_binding[i]->npc_event, eventName, 50);
+ atcmd_binding[i]->level = level;
+ atcmd_binding[i]->level2 = level2;
+ } else
+ create = true;
+ }
+
+ if( create ) {
+ i = atcmd_binding_count;
+
+ if( atcmd_binding_count++ != 0 )
+ RECREATE(atcmd_binding,struct atcmd_binding_data*,atcmd_binding_count);
+
+ CREATE(atcmd_binding[i],struct atcmd_binding_data,1);
+
+ safestrncpy(atcmd_binding[i]->command, atcmd, 50);
+ safestrncpy(atcmd_binding[i]->npc_event, eventName, 50);
+ atcmd_binding[i]->level = level;
+ atcmd_binding[i]->level2 = level2;
+ }
+
+ return 0;
+}
+
+BUILDIN_FUNC(unbindatcmd) {
+ const char* atcmd;
+ int i = 0;
+
+ atcmd = script_getstr(st, 2);
+
+ if( *atcmd == atcommand_symbol || *atcmd == charcommand_symbol )
+ atcmd++;
+
+ if( atcmd_binding_count == 0 ) {
+ script_pushint(st, 0);
+ return 0;
+ }
+
+ ARR_FIND(0, atcmd_binding_count, i, strcmp(atcmd_binding[i]->command, atcmd) == 0);
+ if( i < atcmd_binding_count ) {
+ int cursor = 0;
+ aFree(atcmd_binding[i]);
+ atcmd_binding[i] = NULL;
+ /* compact the list now that we freed a slot somewhere */
+ for( i = 0, cursor = 0; i < atcmd_binding_count; i++ ) {
+ if( atcmd_binding[i] == NULL )
+ continue;
+
+ if( cursor != i ) {
+ memmove(&atcmd_binding[cursor], &atcmd_binding[i], sizeof(struct atcmd_binding_data*));
+ }
+
+ cursor++;
+ }
+
+ if( (atcmd_binding_count = cursor) == 0 )
+ aFree(atcmd_binding);
+
+ script_pushint(st, 1);
+ } else
+ script_pushint(st, 0);/* not found */
+
+ return 0;
+}
+
+BUILDIN_FUNC(useatcmd)
+{
+ TBL_PC dummy_sd;
+ TBL_PC* sd;
+ int fd;
+ const char* cmd;
+
+ cmd = script_getstr(st,2);
+
+ if( st->rid )
+ {
+ sd = script_rid2sd(st);
+ fd = sd->fd;
+ }
+ else
+ { // Use a dummy character.
+ sd = &dummy_sd;
+ fd = 0;
+
+ memset(&dummy_sd, 0, sizeof(TBL_PC));
+ if( st->oid )
+ {
+ struct block_list* bl = map_id2bl(st->oid);
+ memcpy(&dummy_sd.bl, bl, sizeof(struct block_list));
+ if( bl->type == BL_NPC )
+ safestrncpy(dummy_sd.status.name, ((TBL_NPC*)bl)->name, NAME_LENGTH);
+ }
+ }
+
+ // compatibility with previous implementation (deprecated!)
+ if( cmd[0] != atcommand_symbol )
+ {
+ cmd += strlen(sd->status.name);
+ while( *cmd != atcommand_symbol && *cmd != 0 )
+ cmd++;
+ }
+
+ is_atcommand(fd, sd, cmd, 1);
+ return 0;
+}
+
+BUILDIN_FUNC(checkre)
+{
+ int num;
+
+ num=script_getnum(st,2);
+ switch(num){
+ case 0:
+ #ifdef RENEWAL
+ script_pushint(st, 1);
+ #else
+ script_pushint(st, 0);
+ #endif
+ break;
+ case 1:
+ #ifdef RENEWAL_CAST
+ script_pushint(st, 1);
+ #else
+ script_pushint(st, 0);
+ #endif
+ break;
+ case 2:
+ #ifdef RENEWAL_DROP
+ script_pushint(st, 1);
+ #else
+ script_pushint(st, 0);
+ #endif
+ break;
+ case 3:
+ #ifdef RENEWAL_EXP
+ script_pushint(st, 1);
+ #else
+ script_pushint(st, 0);
+ #endif
+ break;
+ case 4:
+ #ifdef RENEWAL_LVDMG
+ script_pushint(st, 1);
+ #else
+ script_pushint(st, 0);
+ #endif
+ break;
+ case 5:
+ #ifdef RENEWAL_EDP
+ script_pushint(st, 1);
+ #else
+ script_pushint(st, 0);
+ #endif
+ break;
+ case 6:
+ #ifdef RENEWAL_ASPD
+ script_pushint(st, 1);
+ #else
+ script_pushint(st, 0);
+ #endif
+ break;
+ default:
+ ShowWarning("buildin_checkre: unknown parameter.\n");
+ break;
+ }
+ return 0;
+}
+
+/* getrandgroupitem <group_id>,<quantity> */
+BUILDIN_FUNC(getrandgroupitem) {
+ TBL_PC* sd;
+ int i, get_count = 0, flag, nameid, group = script_getnum(st, 2), qty = script_getnum(st,3);
+ struct item item_tmp;
+
+ if( !( sd = script_rid2sd(st) ) )
+ return 0;
+
+ if( qty <= 0 ) {
+ ShowError("getrandgroupitem: qty is <= 0!\n");
+ return 1;
+ }
+ if( (nameid = itemdb_searchrandomid(group)) == UNKNOWN_ITEM_ID ) {
+ return 1;/* itemdb_searchrandomid will already scream a error */
+ }
+
+ memset(&item_tmp,0,sizeof(item_tmp));
+
+ item_tmp.nameid = nameid;
+ item_tmp.identify = itemdb_isidentified(nameid);
+
+ //Check if it's stackable.
+ if (!itemdb_isstackable(nameid))
+ get_count = 1;
+ else
+ get_count = qty;
+
+ for (i = 0; i < qty; i += get_count) {
+ // if not pet egg
+ if (!pet_create_egg(sd, nameid)) {
+ if ((flag = pc_additem(sd, &item_tmp, get_count, LOG_TYPE_SCRIPT))) {
+ clif_additem(sd, 0, 0, flag);
+ if( pc_candrop(sd,&item_tmp) )
+ map_addflooritem(&item_tmp,get_count,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* cleanmap <map_name>;
+ * cleanarea <map_name>, <x0>, <y0>, <x1>, <y1>; */
+static int atcommand_cleanfloor_sub(struct block_list *bl, va_list ap)
+{
+ nullpo_ret(bl);
+ map_clearflooritem(bl);
+
+ return 0;
+}
+
+BUILDIN_FUNC(cleanmap)
+{
+ const char *map;
+ int16 m = -1;
+ int16 x0 = 0, y0 = 0, x1 = 0, y1 = 0;
+
+ map = script_getstr(st, 2);
+ m = map_mapname2mapid(map);
+ if (!m)
+ return 1;
+
+ if ((script_lastdata(st) - 2) < 4) {
+ map_foreachinmap(atcommand_cleanfloor_sub, m, BL_ITEM);
+ } else {
+ x0 = script_getnum(st, 3);
+ y0 = script_getnum(st, 4);
+ x1 = script_getnum(st, 5);
+ y1 = script_getnum(st, 6);
+ if (x0 > 0 && y0 > 0 && x1 > 0 && y1 > 0) {
+ map_foreachinarea(atcommand_cleanfloor_sub, m, x0, y0, x1, y1, BL_ITEM);
+ } else {
+ ShowError("cleanarea: invalid coordinate defined!\n");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+/* Cast a skill on the attached player.
+ * npcskill <skill id>, <skill lvl>, <stat point>, <NPC level>;
+ * npcskill "<skill name>", <skill lvl>, <stat point>, <NPC level>; */
+BUILDIN_FUNC(npcskill)
+{
+ uint16 skill_id;
+ unsigned short skill_level;
+ unsigned int stat_point;
+ unsigned int npc_level;
+ struct npc_data *nd;
+ struct map_session_data *sd;
+
+ skill_id = script_isstring(st, 2) ? skill_name2id(script_getstr(st, 2)) : script_getnum(st, 2);
+ skill_level = script_getnum(st, 3);
+ stat_point = script_getnum(st, 4);
+ npc_level = script_getnum(st, 5);
+ sd = script_rid2sd(st);
+ nd = (struct npc_data *)map_id2bl(sd->npc_id);
+
+ if (stat_point > battle_config.max_third_parameter) {
+ ShowError("npcskill: stat point exceeded maximum of %d.\n",battle_config.max_third_parameter );
+ return 1;
+ }
+ if (npc_level > MAX_LEVEL) {
+ ShowError("npcskill: level exceeded maximum of %d.\n", MAX_LEVEL);
+ return 1;
+ }
+ if (sd == NULL || nd == NULL) { //ain't possible, but I don't trust people.
+ return 1;
+ }
+
+ nd->level = npc_level;
+ nd->stat_point = stat_point;
+
+ if (!nd->status.hp) {
+ status_calc_npc(nd, true);
+ } else {
+ status_calc_npc(nd, false);
+ }
+
+ if (skill_get_inf(skill_id)&INF_GROUND_SKILL) {
+ unit_skilluse_pos(&nd->bl, sd->bl.x, sd->bl.y, skill_id, skill_level);
+ } else {
+ unit_skilluse_id(&nd->bl, sd->bl.id, skill_id, skill_level);
+ }
+
+ return 0;
+}
+
+// declarations that were supposed to be exported from npc_chat.c
+#ifdef PCRE_SUPPORT
+BUILDIN_FUNC(defpattern);
+BUILDIN_FUNC(activatepset);
+BUILDIN_FUNC(deactivatepset);
+BUILDIN_FUNC(deletepset);
+#endif
+
+/// script command definitions
+/// for an explanation on args, see add_buildin_func
+struct script_function buildin_func[] = {
+ // NPC interaction
+ BUILDIN_DEF(mes,"s*"),
+ BUILDIN_DEF(next,""),
+ BUILDIN_DEF(close,""),
+ BUILDIN_DEF(close2,""),
+ BUILDIN_DEF(menu,"sl*"),
+ BUILDIN_DEF(select,"s*"), //for future jA script compatibility
+ BUILDIN_DEF(prompt,"s*"),
+ //
+ BUILDIN_DEF(goto,"l"),
+ BUILDIN_DEF(callsub,"l*"),
+ BUILDIN_DEF(callfunc,"s*"),
+ BUILDIN_DEF(return,"?"),
+ BUILDIN_DEF(getarg,"i?"),
+ BUILDIN_DEF(jobchange,"i?"),
+ BUILDIN_DEF(jobname,"i"),
+ BUILDIN_DEF(input,"r??"),
+ BUILDIN_DEF(warp,"sii"),
+ BUILDIN_DEF(areawarp,"siiiisii??"),
+ BUILDIN_DEF(warpchar,"siii"), // [LuzZza]
+ BUILDIN_DEF(warpparty,"siii?"), // [Fredzilla] [Paradox924X]
+ BUILDIN_DEF(warpguild,"siii"), // [Fredzilla]
+ BUILDIN_DEF(setlook,"ii"),
+ BUILDIN_DEF(changelook,"ii"), // Simulates but don't Store it
+ BUILDIN_DEF(set,"rv"),
+ BUILDIN_DEF(setarray,"rv*"),
+ BUILDIN_DEF(cleararray,"rvi"),
+ BUILDIN_DEF(copyarray,"rri"),
+ BUILDIN_DEF(getarraysize,"r"),
+ BUILDIN_DEF(deletearray,"r?"),
+ BUILDIN_DEF(getelementofarray,"ri"),
+ BUILDIN_DEF(getitem,"vi?"),
+ BUILDIN_DEF(rentitem,"vi"),
+ BUILDIN_DEF(getitem2,"viiiiiiii?"),
+ BUILDIN_DEF(getnameditem,"vv"),
+ BUILDIN_DEF2(grouprandomitem,"groupranditem","i"),
+ BUILDIN_DEF(makeitem,"visii"),
+ BUILDIN_DEF(delitem,"vi?"),
+ BUILDIN_DEF(delitem2,"viiiiiiii?"),
+ BUILDIN_DEF2(enableitemuse,"enable_items",""),
+ BUILDIN_DEF2(disableitemuse,"disable_items",""),
+ BUILDIN_DEF(cutin,"si"),
+ BUILDIN_DEF(viewpoint,"iiiii"),
+ BUILDIN_DEF(heal,"ii"),
+ BUILDIN_DEF(itemheal,"ii"),
+ BUILDIN_DEF(percentheal,"ii"),
+ BUILDIN_DEF(rand,"i?"),
+ BUILDIN_DEF(countitem,"v"),
+ BUILDIN_DEF(countitem2,"viiiiiii"),
+ BUILDIN_DEF(checkweight,"vi*"),
+ BUILDIN_DEF(checkweight2,"rr"),
+ BUILDIN_DEF(readparam,"i?"),
+ BUILDIN_DEF(getcharid,"i?"),
+ BUILDIN_DEF(getnpcid,"i?"),
+ BUILDIN_DEF(getpartyname,"i"),
+ BUILDIN_DEF(getpartymember,"i?"),
+ BUILDIN_DEF(getpartyleader,"i?"),
+ BUILDIN_DEF(getguildname,"i"),
+ BUILDIN_DEF(getguildmaster,"i"),
+ BUILDIN_DEF(getguildmasterid,"i"),
+ BUILDIN_DEF(strcharinfo,"i"),
+ BUILDIN_DEF(strnpcinfo,"i"),
+ BUILDIN_DEF(getequipid,"i"),
+ BUILDIN_DEF(getequipname,"i"),
+ BUILDIN_DEF(getbrokenid,"i"), // [Valaris]
+ BUILDIN_DEF(repair,"i"), // [Valaris]
+ BUILDIN_DEF(repairall,""),
+ BUILDIN_DEF(getequipisequiped,"i"),
+ BUILDIN_DEF(getequipisenableref,"i"),
+ BUILDIN_DEF(getequipisidentify,"i"),
+ BUILDIN_DEF(getequiprefinerycnt,"i"),
+ BUILDIN_DEF(getequipweaponlv,"i"),
+ BUILDIN_DEF(getequippercentrefinery,"i"),
+ BUILDIN_DEF(successrefitem,"i"),
+ BUILDIN_DEF(failedrefitem,"i"),
+ BUILDIN_DEF(downrefitem,"i"),
+ BUILDIN_DEF(statusup,"i"),
+ BUILDIN_DEF(statusup2,"ii"),
+ BUILDIN_DEF(bonus,"iv"),
+ BUILDIN_DEF2(bonus,"bonus2","ivi"),
+ BUILDIN_DEF2(bonus,"bonus3","ivii"),
+ BUILDIN_DEF2(bonus,"bonus4","ivvii"),
+ BUILDIN_DEF2(bonus,"bonus5","ivviii"),
+ BUILDIN_DEF(autobonus,"sii??"),
+ BUILDIN_DEF(autobonus2,"sii??"),
+ BUILDIN_DEF(autobonus3,"siiv?"),
+ BUILDIN_DEF(skill,"vi?"),
+ BUILDIN_DEF(addtoskill,"vi?"), // [Valaris]
+ BUILDIN_DEF(guildskill,"vi"),
+ BUILDIN_DEF(getskilllv,"v"),
+ BUILDIN_DEF(getgdskilllv,"iv"),
+ BUILDIN_DEF(basicskillcheck,""),
+ BUILDIN_DEF(getgmlevel,""),
+ BUILDIN_DEF(getgroupid,""),
+ BUILDIN_DEF(end,""),
+ BUILDIN_DEF(checkoption,"i"),
+ BUILDIN_DEF(setoption,"i?"),
+ BUILDIN_DEF(setcart,"?"),
+ BUILDIN_DEF(checkcart,""),
+ BUILDIN_DEF(setfalcon,"?"),
+ BUILDIN_DEF(checkfalcon,""),
+ BUILDIN_DEF(setriding,"?"),
+ BUILDIN_DEF(checkriding,""),
+ BUILDIN_DEF(checkwug,""),
+ BUILDIN_DEF(checkmadogear,""),
+ BUILDIN_DEF(setmadogear,"?"),
+ BUILDIN_DEF2(savepoint,"save","sii"),
+ BUILDIN_DEF(savepoint,"sii"),
+ BUILDIN_DEF(gettimetick,"i"),
+ BUILDIN_DEF(gettime,"i"),
+ BUILDIN_DEF(gettimestr,"si"),
+ BUILDIN_DEF(openstorage,""),
+ BUILDIN_DEF(guildopenstorage,""),
+ BUILDIN_DEF(itemskill,"vi"),
+ BUILDIN_DEF(produce,"i"),
+ BUILDIN_DEF(cooking,"i"),
+ BUILDIN_DEF(monster,"siisii???"),
+ BUILDIN_DEF(getmobdrops,"i"),
+ BUILDIN_DEF(areamonster,"siiiisii???"),
+ BUILDIN_DEF(killmonster,"ss?"),
+ BUILDIN_DEF(killmonsterall,"s?"),
+ BUILDIN_DEF(clone,"siisi????"),
+ BUILDIN_DEF(doevent,"s"),
+ BUILDIN_DEF(donpcevent,"s"),
+ BUILDIN_DEF(cmdothernpc,"ss"),
+ BUILDIN_DEF(addtimer,"is"),
+ BUILDIN_DEF(deltimer,"s"),
+ BUILDIN_DEF(addtimercount,"si"),
+ BUILDIN_DEF(initnpctimer,"??"),
+ BUILDIN_DEF(stopnpctimer,"??"),
+ BUILDIN_DEF(startnpctimer,"??"),
+ BUILDIN_DEF(setnpctimer,"i?"),
+ BUILDIN_DEF(getnpctimer,"i?"),
+ BUILDIN_DEF(attachnpctimer,"?"), // attached the player id to the npc timer [Celest]
+ BUILDIN_DEF(detachnpctimer,"?"), // detached the player id from the npc timer [Celest]
+ BUILDIN_DEF(playerattached,""), // returns id of the current attached player. [Skotlex]
+ BUILDIN_DEF(announce,"si?????"),
+ BUILDIN_DEF(mapannounce,"ssi?????"),
+ BUILDIN_DEF(areaannounce,"siiiisi?????"),
+ BUILDIN_DEF(getusers,"i"),
+ BUILDIN_DEF(getmapguildusers,"si"),
+ BUILDIN_DEF(getmapusers,"s"),
+ BUILDIN_DEF(getareausers,"siiii"),
+ BUILDIN_DEF(getareadropitem,"siiiiv"),
+ BUILDIN_DEF(enablenpc,"s"),
+ BUILDIN_DEF(disablenpc,"s"),
+ BUILDIN_DEF(hideoffnpc,"s"),
+ BUILDIN_DEF(hideonnpc,"s"),
+ BUILDIN_DEF(sc_start,"iii?"),
+ BUILDIN_DEF(sc_start2,"iiii?"),
+ BUILDIN_DEF(sc_start4,"iiiiii?"),
+ BUILDIN_DEF(sc_end,"i?"),
+ BUILDIN_DEF(getstatus, "i?"),
+ BUILDIN_DEF(getscrate,"ii?"),
+ BUILDIN_DEF(debugmes,"s"),
+ BUILDIN_DEF2(catchpet,"pet","i"),
+ BUILDIN_DEF2(birthpet,"bpet",""),
+ BUILDIN_DEF(resetlvl,"i"),
+ BUILDIN_DEF(resetstatus,""),
+ BUILDIN_DEF(resetskill,""),
+ BUILDIN_DEF(skillpointcount,""),
+ BUILDIN_DEF(changebase,"i?"),
+ BUILDIN_DEF(changesex,""),
+ BUILDIN_DEF(waitingroom,"si?????"),
+ BUILDIN_DEF(delwaitingroom,"?"),
+ BUILDIN_DEF2(waitingroomkickall,"kickwaitingroomall","?"),
+ BUILDIN_DEF(enablewaitingroomevent,"?"),
+ BUILDIN_DEF(disablewaitingroomevent,"?"),
+ BUILDIN_DEF2(enablewaitingroomevent,"enablearena",""), // Added by RoVeRT
+ BUILDIN_DEF2(disablewaitingroomevent,"disablearena",""), // Added by RoVeRT
+ BUILDIN_DEF(getwaitingroomstate,"i?"),
+ BUILDIN_DEF(warpwaitingpc,"sii?"),
+ BUILDIN_DEF(attachrid,"i"),
+ BUILDIN_DEF(detachrid,""),
+ BUILDIN_DEF(isloggedin,"i?"),
+ BUILDIN_DEF(setmapflagnosave,"ssii"),
+ BUILDIN_DEF(getmapflag,"si"),
+ BUILDIN_DEF(setmapflag,"si?"),
+ BUILDIN_DEF(removemapflag,"si?"),
+ BUILDIN_DEF(pvpon,"s"),
+ BUILDIN_DEF(pvpoff,"s"),
+ BUILDIN_DEF(gvgon,"s"),
+ BUILDIN_DEF(gvgoff,"s"),
+ BUILDIN_DEF(emotion,"i??"),
+ BUILDIN_DEF(maprespawnguildid,"sii"),
+ BUILDIN_DEF(agitstart,""), // <Agit>
+ BUILDIN_DEF(agitend,""),
+ BUILDIN_DEF(agitcheck,""), // <Agitcheck>
+ BUILDIN_DEF(flagemblem,"i"), // Flag Emblem
+ BUILDIN_DEF(getcastlename,"s"),
+ BUILDIN_DEF(getcastledata,"si"),
+ BUILDIN_DEF(setcastledata,"sii"),
+ BUILDIN_DEF(requestguildinfo,"i?"),
+ BUILDIN_DEF(getequipcardcnt,"i"),
+ BUILDIN_DEF(successremovecards,"i"),
+ BUILDIN_DEF(failedremovecards,"ii"),
+ BUILDIN_DEF(marriage,"s"),
+ BUILDIN_DEF2(wedding_effect,"wedding",""),
+ BUILDIN_DEF(divorce,""),
+ BUILDIN_DEF(ispartneron,""),
+ BUILDIN_DEF(getpartnerid,""),
+ BUILDIN_DEF(getchildid,""),
+ BUILDIN_DEF(getmotherid,""),
+ BUILDIN_DEF(getfatherid,""),
+ BUILDIN_DEF(warppartner,"sii"),
+ BUILDIN_DEF(getitemname,"v"),
+ BUILDIN_DEF(getitemslots,"i"),
+ BUILDIN_DEF(makepet,"i"),
+ BUILDIN_DEF(getexp,"ii"),
+ BUILDIN_DEF(getinventorylist,""),
+ BUILDIN_DEF(getskilllist,""),
+ BUILDIN_DEF(clearitem,""),
+ BUILDIN_DEF(classchange,"ii"),
+ BUILDIN_DEF(misceffect,"i"),
+ BUILDIN_DEF(playBGM,"s"),
+ BUILDIN_DEF(playBGMall,"s?????"),
+ BUILDIN_DEF(soundeffect,"si"),
+ BUILDIN_DEF(soundeffectall,"si?????"), // SoundEffectAll [Codemaster]
+ BUILDIN_DEF(strmobinfo,"ii"), // display mob data [Valaris]
+ BUILDIN_DEF(guardian,"siisi??"), // summon guardians
+ BUILDIN_DEF(guardianinfo,"sii"), // display guardian data [Valaris]
+ BUILDIN_DEF(petskillbonus,"iiii"), // [Valaris]
+ BUILDIN_DEF(petrecovery,"ii"), // [Valaris]
+ BUILDIN_DEF(petloot,"i"), // [Valaris]
+ BUILDIN_DEF(petheal,"iiii"), // [Valaris]
+ BUILDIN_DEF(petskillattack,"viii"), // [Skotlex]
+ BUILDIN_DEF(petskillattack2,"viiii"), // [Valaris]
+ BUILDIN_DEF(petskillsupport,"viiii"), // [Skotlex]
+ BUILDIN_DEF(skilleffect,"vi"), // skill effect [Celest]
+ BUILDIN_DEF(npcskilleffect,"viii"), // npc skill effect [Valaris]
+ BUILDIN_DEF(specialeffect,"i??"), // npc skill effect [Valaris]
+ BUILDIN_DEF(specialeffect2,"i??"), // skill effect on players[Valaris]
+ BUILDIN_DEF(nude,""), // nude command [Valaris]
+ BUILDIN_DEF(mapwarp,"ssii??"), // Added by RoVeRT
+ BUILDIN_DEF(atcommand,"s"), // [MouseJstr]
+ BUILDIN_DEF2(atcommand,"charcommand","s"), // [MouseJstr]
+ BUILDIN_DEF(movenpc,"sii?"), // [MouseJstr]
+ BUILDIN_DEF(message,"ss"), // [MouseJstr]
+ BUILDIN_DEF(npctalk,"s"), // [Valaris]
+ BUILDIN_DEF(mobcount,"ss"),
+ BUILDIN_DEF(getlook,"i"),
+ BUILDIN_DEF(getsavepoint,"i"),
+ BUILDIN_DEF(npcspeed,"i"), // [Valaris]
+ BUILDIN_DEF(npcwalkto,"ii"), // [Valaris]
+ BUILDIN_DEF(npcstop,""), // [Valaris]
+ BUILDIN_DEF(getmapxy,"rrri?"), //by Lorky [Lupus]
+ BUILDIN_DEF(checkoption1,"i"),
+ BUILDIN_DEF(checkoption2,"i"),
+ BUILDIN_DEF(guildgetexp,"i"),
+ BUILDIN_DEF(guildchangegm,"is"),
+ BUILDIN_DEF(logmes,"s"), //this command actls as MES but rints info into LOG file either SQL/TXT [Lupus]
+ BUILDIN_DEF(summon,"si??"), // summons a slave monster [Celest]
+ BUILDIN_DEF(isnight,""), // check whether it is night time [Celest]
+ BUILDIN_DEF(isday,""), // check whether it is day time [Celest]
+ BUILDIN_DEF(isequipped,"i*"), // check whether another item/card has been equipped [Celest]
+ BUILDIN_DEF(isequippedcnt,"i*"), // check how many items/cards are being equipped [Celest]
+ BUILDIN_DEF(cardscnt,"i*"), // check how many items/cards are being equipped in the same arm [Lupus]
+ BUILDIN_DEF(getrefine,""), // returns the refined number of the current item, or an item with index specified [celest]
+ BUILDIN_DEF(night,""), // sets the server to night time
+ BUILDIN_DEF(day,""), // sets the server to day time
+#ifdef PCRE_SUPPORT
+ BUILDIN_DEF(defpattern,"iss"), // Define pattern to listen for [MouseJstr]
+ BUILDIN_DEF(activatepset,"i"), // Activate a pattern set [MouseJstr]
+ BUILDIN_DEF(deactivatepset,"i"), // Deactive a pattern set [MouseJstr]
+ BUILDIN_DEF(deletepset,"i"), // Delete a pattern set [MouseJstr]
+#endif
+ BUILDIN_DEF(dispbottom,"s"), //added from jA [Lupus]
+ BUILDIN_DEF(getusersname,""),
+ BUILDIN_DEF(recovery,""),
+ BUILDIN_DEF(getpetinfo,"i"),
+ BUILDIN_DEF(gethominfo,"i"),
+ BUILDIN_DEF(getmercinfo,"i?"),
+ BUILDIN_DEF(checkequipedcard,"i"),
+ BUILDIN_DEF(jump_zero,"il"), //for future jA script compatibility
+ BUILDIN_DEF(globalmes,"s?"), //end jA addition
+ BUILDIN_DEF(unequip,"i"), // unequip command [Spectre]
+ BUILDIN_DEF(getstrlen,"s"), //strlen [Valaris]
+ BUILDIN_DEF(charisalpha,"si"), //isalpha [Valaris]
+ BUILDIN_DEF(charat,"si"),
+ BUILDIN_DEF(setchar,"ssi"),
+ BUILDIN_DEF(insertchar,"ssi"),
+ BUILDIN_DEF(delchar,"si"),
+ BUILDIN_DEF(strtoupper,"s"),
+ BUILDIN_DEF(strtolower,"s"),
+ BUILDIN_DEF(charisupper, "si"),
+ BUILDIN_DEF(charislower, "si"),
+ BUILDIN_DEF(substr,"sii"),
+ BUILDIN_DEF(explode, "rss"),
+ BUILDIN_DEF(implode, "r?"),
+ BUILDIN_DEF(sprintf,"s*"), // [Mirei]
+ BUILDIN_DEF(sscanf,"ss*"), // [Mirei]
+ BUILDIN_DEF(strpos,"ss?"),
+ BUILDIN_DEF(replacestr,"sss??"),
+ BUILDIN_DEF(countstr,"ss?"),
+ BUILDIN_DEF(setnpcdisplay,"sv??"),
+ BUILDIN_DEF(compare,"ss"), // Lordalfa - To bring strstr to scripting Engine.
+ BUILDIN_DEF(getiteminfo,"ii"), //[Lupus] returns Items Buy / sell Price, etc info
+ BUILDIN_DEF(setiteminfo,"iii"), //[Lupus] set Items Buy / sell Price, etc info
+ BUILDIN_DEF(getequipcardid,"ii"), //[Lupus] returns CARD ID or other info from CARD slot N of equipped item
+ // [zBuffer] List of mathematics commands --->
+ BUILDIN_DEF(sqrt,"i"),
+ BUILDIN_DEF(pow,"ii"),
+ BUILDIN_DEF(distance,"iiii"),
+ // <--- [zBuffer] List of mathematics commands
+ BUILDIN_DEF(md5,"s"),
+ // [zBuffer] List of dynamic var commands --->
+ BUILDIN_DEF(getd,"s"),
+ BUILDIN_DEF(setd,"sv"),
+ // <--- [zBuffer] List of dynamic var commands
+ BUILDIN_DEF(petstat,"i"),
+ BUILDIN_DEF(callshop,"s?"), // [Skotlex]
+ BUILDIN_DEF(npcshopitem,"sii*"), // [Lance]
+ BUILDIN_DEF(npcshopadditem,"sii*"),
+ BUILDIN_DEF(npcshopdelitem,"si*"),
+ BUILDIN_DEF(npcshopattach,"s?"),
+ BUILDIN_DEF(equip,"i"),
+ BUILDIN_DEF(autoequip,"ii"),
+ BUILDIN_DEF(setbattleflag,"si"),
+ BUILDIN_DEF(getbattleflag,"s"),
+ BUILDIN_DEF(setitemscript,"is?"), //Set NEW item bonus script. Lupus
+ BUILDIN_DEF(disguise,"i"), //disguise player. Lupus
+ BUILDIN_DEF(undisguise,""), //undisguise player. Lupus
+ BUILDIN_DEF(getmonsterinfo,"ii"), //Lupus
+ BUILDIN_DEF(axtoi,"s"),
+ BUILDIN_DEF(query_sql,"s*"),
+ BUILDIN_DEF(query_logsql,"s*"),
+ BUILDIN_DEF(escape_sql,"v"),
+ BUILDIN_DEF(atoi,"s"),
+ // [zBuffer] List of player cont commands --->
+ BUILDIN_DEF(rid2name,"i"),
+ BUILDIN_DEF(pcfollow,"ii"),
+ BUILDIN_DEF(pcstopfollow,"i"),
+ BUILDIN_DEF(pcblockmove,"ii"),
+ // <--- [zBuffer] List of player cont commands
+ // [zBuffer] List of mob control commands --->
+ BUILDIN_DEF(unitwalk,"ii?"),
+ BUILDIN_DEF(unitkill,"i"),
+ BUILDIN_DEF(unitwarp,"isii"),
+ BUILDIN_DEF(unitattack,"iv?"),
+ BUILDIN_DEF(unitstop,"i"),
+ BUILDIN_DEF(unittalk,"is"),
+ BUILDIN_DEF(unitemote,"ii"),
+ BUILDIN_DEF(unitskilluseid,"ivi?"), // originally by Qamera [Celest]
+ BUILDIN_DEF(unitskillusepos,"iviii"), // [Celest]
+// <--- [zBuffer] List of mob control commands
+ BUILDIN_DEF(sleep,"i"),
+ BUILDIN_DEF(sleep2,"i"),
+ BUILDIN_DEF(awake,"s"),
+ BUILDIN_DEF(getvariableofnpc,"rs"),
+ BUILDIN_DEF(warpportal,"iisii"),
+ BUILDIN_DEF2(homunculus_evolution,"homevolution",""), //[orn]
+ BUILDIN_DEF2(homunculus_mutate,"hommutate","?"),
+ BUILDIN_DEF2(homunculus_shuffle,"homshuffle",""), //[Zephyrus]
+ BUILDIN_DEF(eaclass,"?"), //[Skotlex]
+ BUILDIN_DEF(roclass,"i?"), //[Skotlex]
+ BUILDIN_DEF(checkvending,"?"),
+ BUILDIN_DEF(checkchatting,"?"),
+ BUILDIN_DEF(openmail,""),
+ BUILDIN_DEF(openauction,""),
+ BUILDIN_DEF(checkcell,"siii"),
+ BUILDIN_DEF(setcell,"siiiiii"),
+ BUILDIN_DEF(setwall,"siiiiis"),
+ BUILDIN_DEF(delwall,"s"),
+ BUILDIN_DEF(searchitem,"rs"),
+ BUILDIN_DEF(mercenary_create,"ii"),
+ BUILDIN_DEF(mercenary_heal,"ii"),
+ BUILDIN_DEF(mercenary_sc_start,"iii"),
+ BUILDIN_DEF(mercenary_get_calls,"i"),
+ BUILDIN_DEF(mercenary_get_faith,"i"),
+ BUILDIN_DEF(mercenary_set_calls,"ii"),
+ BUILDIN_DEF(mercenary_set_faith,"ii"),
+ BUILDIN_DEF(readbook,"ii"),
+ BUILDIN_DEF(setfont,"i"),
+ BUILDIN_DEF(areamobuseskill,"siiiiviiiii"),
+ BUILDIN_DEF(progressbar,"si"),
+ BUILDIN_DEF(pushpc,"ii"),
+ BUILDIN_DEF(buyingstore,"i"),
+ BUILDIN_DEF(searchstores,"ii"),
+ BUILDIN_DEF(showdigit,"i?"),
+ // WoE SE
+ BUILDIN_DEF(agitstart2,""),
+ BUILDIN_DEF(agitend2,""),
+ BUILDIN_DEF(agitcheck2,""),
+ // BattleGround
+ BUILDIN_DEF(waitingroom2bg,"siiss?"),
+ BUILDIN_DEF(waitingroom2bg_single,"isiis"),
+ BUILDIN_DEF(bg_team_setxy,"iii"),
+ BUILDIN_DEF(bg_warp,"isii"),
+ BUILDIN_DEF(bg_monster,"isiisi?"),
+ BUILDIN_DEF(bg_monster_set_team,"ii"),
+ BUILDIN_DEF(bg_leave,""),
+ BUILDIN_DEF(bg_destroy,"i"),
+ BUILDIN_DEF(areapercentheal,"siiiiii"),
+ BUILDIN_DEF(bg_get_data,"ii"),
+ BUILDIN_DEF(bg_getareausers,"isiiii"),
+ BUILDIN_DEF(bg_updatescore,"sii"),
+
+ // Instancing
+ BUILDIN_DEF(instance_create,"si"),
+ BUILDIN_DEF(instance_destroy,"?"),
+ BUILDIN_DEF(instance_attachmap,"si?"),
+ BUILDIN_DEF(instance_detachmap,"s?"),
+ BUILDIN_DEF(instance_attach,"i"),
+ BUILDIN_DEF(instance_id,"?"),
+ BUILDIN_DEF(instance_set_timeout,"ii?"),
+ BUILDIN_DEF(instance_init,"i"),
+ BUILDIN_DEF(instance_announce,"isi?????"),
+ BUILDIN_DEF(instance_npcname,"s?"),
+ BUILDIN_DEF(has_instance,"s?"),
+ BUILDIN_DEF(instance_warpall,"sii?"),
+ BUILDIN_DEF(instance_check_party,"i???"),
+ /**
+ * 3rd-related
+ **/
+ BUILDIN_DEF(makerune,"i"),
+ BUILDIN_DEF(checkdragon,""),//[Ind]
+ BUILDIN_DEF(setdragon,"?"),//[Ind]
+ BUILDIN_DEF(ismounting,""),//[Ind]
+ BUILDIN_DEF(setmounting,""),//[Ind]
+ BUILDIN_DEF(checkre,"i"),
+ /**
+ * rAthena and beyond!
+ **/
+ BUILDIN_DEF(getargcount,""),
+ BUILDIN_DEF(getcharip,"?"),
+ BUILDIN_DEF(is_function,"s"),
+ BUILDIN_DEF(get_revision,""),
+ BUILDIN_DEF(freeloop,"i"),
+ BUILDIN_DEF(getrandgroupitem,"ii"),
+ BUILDIN_DEF(cleanmap,"s"),
+ BUILDIN_DEF2(cleanmap,"cleanarea","siiii"),
+ BUILDIN_DEF(npcskill,"viii"),
+ /**
+ * @commands (script based)
+ **/
+ BUILDIN_DEF(bindatcmd, "ss??"),
+ BUILDIN_DEF(unbindatcmd, "s"),
+ BUILDIN_DEF(useatcmd, "s"),
+
+ //Quest Log System [Inkfish]
+ BUILDIN_DEF(setquest, "i"),
+ BUILDIN_DEF(erasequest, "i"),
+ BUILDIN_DEF(completequest, "i"),
+ BUILDIN_DEF(checkquest, "i?"),
+ BUILDIN_DEF(changequest, "ii"),
+ BUILDIN_DEF(showevent, "ii"),
+ {NULL,NULL,NULL},
+};
diff --git a/src/map/skill.c b/src/map/skill.c
index 757165107..3b3ac547d 100644
--- a/src/map/skill.c
+++ b/src/map/skill.c
@@ -1,17983 +1,17998 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include "../common/cbasetypes.h"
-#include "../common/timer.h"
-#include "../common/nullpo.h"
-#include "../common/malloc.h"
-#include "../common/random.h"
-#include "../common/showmsg.h"
-#include "../common/strlib.h"
-#include "../common/utils.h"
-#include "../common/ers.h"
-
-#include "map.h"
-#include "path.h"
-#include "clif.h"
-#include "pc.h"
-#include "status.h"
-#include "skill.h"
-#include "pet.h"
-#include "homunculus.h"
-#include "mercenary.h"
-#include "elemental.h"
-#include "mob.h"
-#include "npc.h"
-#include "battle.h"
-#include "battleground.h"
-#include "party.h"
-#include "itemdb.h"
-#include "script.h"
-#include "intif.h"
-#include "log.h"
-#include "chrif.h"
-#include "guild.h"
-#include "date.h"
-#include "unit.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <math.h>
-
-
-#define SKILLUNITTIMER_INTERVAL 100
-
-// ranges reserved for mapping skill ids to skilldb offsets
-#define HM_SKILLRANGEMIN 700
-#define HM_SKILLRANGEMAX HM_SKILLRANGEMIN + MAX_HOMUNSKILL
-#define MC_SKILLRANGEMIN HM_SKILLRANGEMAX + 1
-#define MC_SKILLRANGEMAX MC_SKILLRANGEMIN + MAX_MERCSKILL
-#define EL_SKILLRANGEMIN MC_SKILLRANGEMAX + 1
-#define EL_SKILLRANGEMAX EL_SKILLRANGEMIN + MAX_ELEMENTALSKILL
-#define GD_SKILLRANGEMIN EL_SKILLRANGEMAX + 1
-#define GD_SKILLRANGEMAX GD_SKILLRANGEMIN + MAX_GUILDSKILL
-
-#if GD_SKILLRANGEMAX > 999
- #error GD_SKILLRANGEMAX is greater than 999
-#endif
-static struct eri *skill_unit_ers = NULL; //For handling skill_unit's [Skotlex]
-static struct eri *skill_timer_ers = NULL; //For handling skill_timerskills [Skotlex]
-
-DBMap* skillunit_db = NULL; // int id -> struct skill_unit*
-
-DBMap* skilldb_name2id = NULL;
-
-/**
- * Skill Cool Down Delay Saving
- * Struct skill_cd is not a member of struct map_session_data
- * to keep cooldowns in memory between player log-ins.
- * All cooldowns are reset when server is restarted.
- **/
-DBMap* skillcd_db = NULL; // char_id -> struct skill_cd
-struct skill_cd {
- int duration[MAX_SKILL_TREE];//milliseconds
- short skidx[MAX_SKILL_TREE];//the skill index entries belong to
- short nameid[MAX_SKILL_TREE];//skill id
- unsigned char cursor;
-};
-
-/**
- * Skill Unit Persistency during endack routes (mostly for songs see bugreport:4574)
- **/
-DBMap* skillusave_db = NULL; // char_id -> struct skill_usave
-struct skill_usave {
- uint16 skill_id, skill_lv;
-};
-
-struct s_skill_db skill_db[MAX_SKILL_DB];
-struct s_skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB];
-struct s_skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB];
-struct s_skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB];
-struct s_skill_improvise_db {
- uint16 skill_id;
- short per;//1-10000
-};
-struct s_skill_improvise_db skill_improvise_db[MAX_SKILL_IMPROVISE_DB];
-bool skill_reproduce_db[MAX_SKILL_DB];
-struct s_skill_changematerial_db {
- int itemid;
- short rate;
- int qty[5];
- short qty_rate[5];
-};
-struct s_skill_changematerial_db skill_changematerial_db[MAX_SKILL_PRODUCE_DB];
-
-//Warlock
-struct s_skill_spellbook_db {
- int nameid;
- uint16 skill_id;
- int point;
-};
-
-struct s_skill_spellbook_db skill_spellbook_db[MAX_SKILL_SPELLBOOK_DB];
-//Guillotine Cross
-struct s_skill_magicmushroom_db skill_magicmushroom_db[MAX_SKILL_MAGICMUSHROOM_DB];
-
-struct s_skill_unit_layout skill_unit_layout[MAX_SKILL_UNIT_LAYOUT];
-int firewall_unit_pos;
-int icewall_unit_pos;
-int earthstrain_unit_pos;
-//early declaration
-int skill_block_check(struct block_list *bl, enum sc_type type, uint16 skill_id);
-static int skill_check_unit_range (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv);
-static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv);
-static int skill_destroy_trap( struct block_list *bl, va_list ap );
-//Since only mob-casted splash skills can hit ice-walls
-static inline int splash_target(struct block_list* bl)
-{
-#ifndef RENEWAL
- return ( bl->type == BL_MOB ) ? BL_SKILL|BL_CHAR : BL_CHAR;
-#else // Some skills can now hit ground skills(traps, ice wall & etc.)
- return BL_SKILL|BL_CHAR;
-#endif
-}
-
-/// Returns the id of the skill, or 0 if not found.
-int skill_name2id(const char* name)
-{
- if( name == NULL )
- return 0;
-
- return strdb_iget(skilldb_name2id, name);
-}
-
-/// Maps skill ids to skill db offsets.
-/// Returns the skill's array index, or 0 (Unknown Skill).
-int skill_get_index( uint16 skill_id )
-{
- // avoid ranges reserved for mapping guild/homun/mercenary skills
- if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX)
- || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX)
- || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX)
- || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) )
- return 0;
-
- // map skill id to skill db index
- if( skill_id >= GD_SKILLBASE )
- skill_id = GD_SKILLRANGEMIN + skill_id - GD_SKILLBASE;
- else if( skill_id >= EL_SKILLBASE )
- skill_id = EL_SKILLRANGEMIN + skill_id - EL_SKILLBASE;
- else if( skill_id >= MC_SKILLBASE )
- skill_id = MC_SKILLRANGEMIN + skill_id - MC_SKILLBASE;
- else if( skill_id >= HM_SKILLBASE )
- skill_id = HM_SKILLRANGEMIN + skill_id - HM_SKILLBASE;
-
- // validate result
- if( !skill_id || skill_id >= MAX_SKILL_DB )
- return 0;
-
- return skill_id;
-}
-
-const char* skill_get_name( uint16 skill_id )
-{
- return skill_db[skill_get_index(skill_id)].name;
-}
-
-const char* skill_get_desc( uint16 skill_id )
-{
- return skill_db[skill_get_index(skill_id)].desc;
-}
-
-// out of bounds error checking [celest]
-static void skill_chk(int16* skill_id, uint16 skill_lv)
-{
- *skill_id = skill_get_index(*skill_id); // checks/adjusts id
- if( skill_lv > MAX_SKILL_LEVEL ) *skill_id = 0;
-}
-
-#define skill_get(var,id,lv) { skill_chk(&id,lv); if(!id) return 0; return var; }
-
-// Skill DB
-int skill_get_hit( uint16 skill_id ) { skill_get (skill_db[skill_id].hit, skill_id, 1); }
-int skill_get_inf( uint16 skill_id ) { skill_get (skill_db[skill_id].inf, skill_id, 1); }
-int skill_get_ele( uint16 skill_id , uint16 skill_lv ) { skill_get (skill_db[skill_id].element[skill_lv-1], skill_id, skill_lv); }
-int skill_get_nk( uint16 skill_id ) { skill_get (skill_db[skill_id].nk, skill_id, 1); }
-int skill_get_max( uint16 skill_id ) { skill_get (skill_db[skill_id].max, skill_id, 1); }
-int skill_get_range( uint16 skill_id , uint16 skill_lv ) { skill_get (skill_db[skill_id].range[skill_lv-1], skill_id, skill_lv); }
-int skill_get_splash( uint16 skill_id , uint16 skill_lv ) { skill_get ( (skill_db[skill_id].splash[skill_lv-1]>=0?skill_db[skill_id].splash[skill_lv-1]:AREA_SIZE), skill_id, skill_lv); }
-int skill_get_hp( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].hp[skill_lv-1], skill_id, skill_lv); }
-int skill_get_sp( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].sp[skill_lv-1], skill_id, skill_lv); }
-int skill_get_hp_rate(uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].hp_rate[skill_lv-1], skill_id, skill_lv); }
-int skill_get_sp_rate(uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].sp_rate[skill_lv-1], skill_id, skill_lv); }
-int skill_get_state(uint16 skill_id) { skill_get (skill_db[skill_id].state, skill_id, 1); }
-int skill_get_spiritball(uint16 skill_id, uint16 skill_lv) { skill_get (skill_db[skill_id].spiritball[skill_lv-1], skill_id, skill_lv); }
-int skill_get_itemid(uint16 skill_id, int idx) { skill_get (skill_db[skill_id].itemid[idx], skill_id, 1); }
-int skill_get_itemqty(uint16 skill_id, int idx) { skill_get (skill_db[skill_id].amount[idx], skill_id, 1); }
-int skill_get_zeny( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].zeny[skill_lv-1], skill_id, skill_lv); }
-int skill_get_num( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].num[skill_lv-1], skill_id, skill_lv); }
-int skill_get_cast( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].cast[skill_lv-1], skill_id, skill_lv); }
-int skill_get_delay( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].delay[skill_lv-1], skill_id, skill_lv); }
-int skill_get_walkdelay( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].walkdelay[skill_lv-1], skill_id, skill_lv); }
-int skill_get_time( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].upkeep_time[skill_lv-1], skill_id, skill_lv); }
-int skill_get_time2( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].upkeep_time2[skill_lv-1], skill_id, skill_lv); }
-int skill_get_castdef( uint16 skill_id ) { skill_get (skill_db[skill_id].cast_def_rate, skill_id, 1); }
-int skill_get_weapontype( uint16 skill_id ) { skill_get (skill_db[skill_id].weapon, skill_id, 1); }
-int skill_get_ammotype( uint16 skill_id ) { skill_get (skill_db[skill_id].ammo, skill_id, 1); }
-int skill_get_ammo_qty( uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].ammo_qty[skill_lv-1], skill_id, skill_lv); }
-int skill_get_inf2( uint16 skill_id ) { skill_get (skill_db[skill_id].inf2, skill_id, 1); }
-int skill_get_castcancel( uint16 skill_id ) { skill_get (skill_db[skill_id].castcancel, skill_id, 1); }
-int skill_get_maxcount( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].maxcount[skill_lv-1], skill_id, skill_lv); }
-int skill_get_blewcount( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].blewcount[skill_lv-1], skill_id, skill_lv); }
-int skill_get_mhp( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].mhp[skill_lv-1], skill_id, skill_lv); }
-int skill_get_castnodex( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].castnodex[skill_lv-1], skill_id, skill_lv); }
-int skill_get_delaynodex( uint16 skill_id ,uint16 skill_lv ){ skill_get (skill_db[skill_id].delaynodex[skill_lv-1], skill_id, skill_lv); }
-int skill_get_nocast ( uint16 skill_id ) { skill_get (skill_db[skill_id].nocast, skill_id, 1); }
-int skill_get_type( uint16 skill_id ) { skill_get (skill_db[skill_id].skill_type, skill_id, 1); }
-int skill_get_unit_id ( uint16 skill_id, int flag ){ skill_get (skill_db[skill_id].unit_id[flag], skill_id, 1); }
-int skill_get_unit_interval( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_interval, skill_id, 1); }
-int skill_get_unit_range( uint16 skill_id, uint16 skill_lv ){ skill_get (skill_db[skill_id].unit_range[skill_lv-1], skill_id, skill_lv); }
-int skill_get_unit_target( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_target&BCT_ALL, skill_id, 1); }
-int skill_get_unit_bl_target( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_target&BL_ALL, skill_id, 1); }
-int skill_get_unit_flag( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_flag, skill_id, 1); }
-int skill_get_unit_layout_type( uint16 skill_id ,uint16 skill_lv ){ skill_get (skill_db[skill_id].unit_layout_type[skill_lv-1], skill_id, skill_lv); }
-int skill_get_cooldown( uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].cooldown[skill_lv-1], skill_id, skill_lv); }
-#ifdef RENEWAL_CAST
-int skill_get_fixed_cast( uint16 skill_id ,uint16 skill_lv ){ skill_get (skill_db[skill_id].fixed_cast[skill_lv-1], skill_id, skill_lv); }
-#endif
-int skill_tree_get_max(uint16 skill_id, int b_class)
-{
- int i;
- b_class = pc_class2idx(b_class);
-
- ARR_FIND( 0, MAX_SKILL_TREE, i, skill_tree[b_class][i].id == 0 || skill_tree[b_class][i].id == skill_id );
- if( i < MAX_SKILL_TREE && skill_tree[b_class][i].id == skill_id )
- return skill_tree[b_class][i].max;
- else
- return skill_get_max(skill_id);
-}
-
-int skill_frostjoke_scream(struct block_list *bl,va_list ap);
-int skill_attack_area(struct block_list *bl,va_list ap);
-struct skill_unit_group *skill_locate_element_field(struct block_list *bl); // [Skotlex]
-int skill_graffitiremover(struct block_list *bl, va_list ap); // [Valaris]
-int skill_greed(struct block_list *bl, va_list ap);
-static void skill_toggle_magicpower(struct block_list *bl, uint16 skill_id);
-static int skill_cell_overlap(struct block_list *bl, va_list ap);
-static int skill_trap_splash(struct block_list *bl, va_list ap);
-struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list *bl,struct skill_unit_group *sg,int tick);
-static int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,unsigned int tick);
-static int skill_unit_onleft(uint16 skill_id, struct block_list *bl,unsigned int tick);
-static int skill_unit_effect(struct block_list *bl,va_list ap);
-
-int enchant_eff[5] = { 10, 14, 17, 19, 20 };
-int deluge_eff[5] = { 5, 9, 12, 14, 15 };
-
-int skill_get_casttype (uint16 skill_id)
-{
- int inf = skill_get_inf(skill_id);
- if (inf&(INF_GROUND_SKILL))
- return CAST_GROUND;
- if (inf&INF_SUPPORT_SKILL)
- return CAST_NODAMAGE;
- if (inf&INF_SELF_SKILL) {
- if(skill_get_inf2(skill_id)&INF2_NO_TARGET_SELF)
- return CAST_DAMAGE; //Combo skill.
- return CAST_NODAMAGE;
- }
- if (skill_get_nk(skill_id)&NK_NO_DAMAGE)
- return CAST_NODAMAGE;
- return CAST_DAMAGE;
-}
-
-//Returns actual skill range taking into account attack range and AC_OWL [Skotlex]
-int skill_get_range2 (struct block_list *bl, uint16 skill_id, uint16 skill_lv)
-{
- int range;
- if( bl->type == BL_MOB && battle_config.mob_ai&0x400 )
- return 9; //Mobs have a range of 9 regardless of skill used.
-
- range = skill_get_range(skill_id, skill_lv);
-
- if( range < 0 )
- {
- if( battle_config.use_weapon_skill_range&bl->type )
- return status_get_range(bl);
- range *=-1;
- }
-
- //TODO: Find a way better than hardcoding the list of skills affected by AC_VULTURE
- switch( skill_id )
- {
- case AC_SHOWER: case MA_SHOWER:
- case AC_DOUBLE: case MA_DOUBLE:
- case HT_BLITZBEAT:
- case AC_CHARGEARROW:
- case MA_CHARGEARROW:
- case SN_FALCONASSAULT:
- case HT_POWER:
- /**
- * Ranger
- **/
- case RA_ARROWSTORM:
- case RA_AIMEDBOLT:
- case RA_WUGBITE:
- if( bl->type == BL_PC )
- range += pc_checkskill((TBL_PC*)bl, AC_VULTURE);
- else
- range += 10; //Assume level 10?
- break;
- // added to allow GS skills to be effected by the range of Snake Eyes [Reddozen]
- case GS_RAPIDSHOWER:
- case GS_PIERCINGSHOT:
- case GS_FULLBUSTER:
- case GS_SPREADATTACK:
- case GS_GROUNDDRIFT:
- if (bl->type == BL_PC)
- range += pc_checkskill((TBL_PC*)bl, GS_SNAKEEYE);
- else
- range += 10; //Assume level 10?
- break;
- case NJ_KIRIKAGE:
- if (bl->type == BL_PC)
- range = skill_get_range(NJ_SHADOWJUMP,pc_checkskill((TBL_PC*)bl,NJ_SHADOWJUMP));
- break;
- /**
- * Warlock
- **/
- case WL_WHITEIMPRISON:
- case WL_SOULEXPANSION:
- case WL_FROSTMISTY:
- case WL_MARSHOFABYSS:
- case WL_SIENNAEXECRATE:
- case WL_DRAINLIFE:
- case WL_CRIMSONROCK:
- case WL_HELLINFERNO:
- case WL_COMET:
- case WL_CHAINLIGHTNING:
- case WL_TETRAVORTEX:
- case WL_RELEASE:
- if( bl->type == BL_PC )
- range += pc_checkskill((TBL_PC*)bl, WL_RADIUS);
- break;
- /**
- * Ranger Bonus
- **/
- case HT_LANDMINE:
- case HT_FREEZINGTRAP:
- case HT_BLASTMINE:
- case HT_CLAYMORETRAP:
- case RA_CLUSTERBOMB:
- case RA_FIRINGTRAP:
- case RA_ICEBOUNDTRAP:
- if( bl->type == BL_PC )
- range += (1 + pc_checkskill((TBL_PC*)bl, RA_RESEARCHTRAP))/2;
- }
-
- if( !range && bl->type != BL_PC )
- return 9; // Enable non players to use self skills on others. [Skotlex]
- return range;
-}
-
-int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, bool heal) {
- int skill, hp;
- struct map_session_data *sd = BL_CAST(BL_PC, src);
- struct map_session_data *tsd = BL_CAST(BL_PC, target);
- struct status_change* sc;
-
- switch( skill_id ) {
- case BA_APPLEIDUN:
- #ifdef RENEWAL
- hp = 100+5*skill_lv+5*(status_get_vit(src)/10); // HP recovery
- #else
- hp = 30+5*skill_lv+5*(status_get_vit(src)/10); // HP recovery
- #endif
- if( sd )
- hp += 5*pc_checkskill(sd,BA_MUSICALLESSON);
- break;
- case PR_SANCTUARY:
- hp = (skill_lv>6)?777:skill_lv*100;
- break;
- case NPC_EVILLAND:
- hp = (skill_lv>6)?666:skill_lv*100;
- break;
- default:
- if (skill_lv >= battle_config.max_heal_lv)
- return battle_config.max_heal;
- #ifdef RENEWAL
- /**
- * Renewal Heal Formula
- * Formula: ( [(Base Level + INT) / 5] × 30 ) × (Heal Level / 10) × (Modifiers) + MATK
- **/
- hp = (status_get_lv(src) + status_get_int(src)) / 5 * 30 * skill_lv / 10;
- #else
- hp = ( status_get_lv(src) + status_get_int(src) ) / 8 * (4 + ( skill_id == AB_HIGHNESSHEAL ? ( sd ? pc_checkskill(sd,AL_HEAL) : 10 ) : skill_lv ) * 8);
- #endif
- if( sd && ((skill = pc_checkskill(sd, HP_MEDITATIO)) > 0) )
- hp += hp * skill * 2 / 100;
- else if( src->type == BL_HOM && (skill = merc_hom_checkskill(((TBL_HOM*)src), HLIF_BRAIN)) > 0 )
- hp += hp * skill * 2 / 100;
- break;
- }
-
- if( ( (target && target->type == BL_MER) || !heal ) && skill_id != NPC_EVILLAND )
- hp >>= 1;
-
- if( sd && (skill = pc_skillheal_bonus(sd, skill_id)) )
- hp += hp*skill/100;
-
- if( tsd && (skill = pc_skillheal2_bonus(tsd, skill_id)) )
- hp += hp*skill/100;
-
- sc = status_get_sc(target);
- if( sc && sc->count ) {
- if( sc->data[SC_CRITICALWOUND] && heal ) // Critical Wound has no effect on offensive heal. [Inkfish]
- hp -= hp * sc->data[SC_CRITICALWOUND]->val2/100;
- if( sc->data[SC_DEATHHURT] && heal )
- hp -= hp * 20/100;
- if( sc->data[SC_INCHEALRATE] && skill_id != NPC_EVILLAND && skill_id != BA_APPLEIDUN )
- hp += hp * sc->data[SC_INCHEALRATE]->val1/100; // Only affects Heal, Sanctuary and PotionPitcher.(like bHealPower) [Inkfish]
- if( sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2)
- hp += hp / 10;
- }
-
-#ifdef RENEWAL
- // MATK part of the RE heal formula [malufett]
- // Note: in this part matk bonuses from items or skills are not applied
- switch( skill_id ) {
- case BA_APPLEIDUN: case PR_SANCTUARY:
- case NPC_EVILLAND: break;
- default:
- {
- struct status_data *status = status_get_status_data(src);
- int min, max, wMatk, variance;
-
- min = max = status_base_matk(status, status_get_lv(src));
- if( status->rhw.matk > 0 ){
- wMatk = status->rhw.matk;
- variance = wMatk * status->rhw.wlv / 10;
- min += wMatk - variance;
- max += wMatk + variance;
- }
-
- if( sc && sc->data[SC_RECOGNIZEDSPELL] )
- min = max;
-
- if( sd && sd->right_weapon.overrefine > 0 ){
- min++;
- max += sd->right_weapon.overrefine - 1;
- }
-
- if(max > min)
- hp += min+rnd()%(max-min);
- else
- hp += min;
- }
- }
-#endif
- return hp;
-}
-
-// Making plagiarize check its own function [Aru]
-int can_copy (struct map_session_data *sd, uint16 skill_id, struct block_list* bl)
-{
- // Never copy NPC/Wedding Skills
- if (skill_get_inf2(skill_id)&(INF2_NPC_SKILL|INF2_WEDDING_SKILL))
- return 0;
-
- // High-class skills
- if((skill_id >= LK_AURABLADE && skill_id <= ASC_CDP) || (skill_id >= ST_PRESERVE && skill_id <= CR_CULTIVATION))
- {
- if(battle_config.copyskill_restrict == 2)
- return 0;
- else if(battle_config.copyskill_restrict)
- return (sd->status.class_ == JOB_STALKER);
- }
-
- //Added so plagarize can't copy agi/bless if you're undead since it damages you
- if ((skill_id == AL_INCAGI || skill_id == AL_BLESSING ||
- skill_id == CASH_BLESSING || skill_id == CASH_INCAGI ||
- skill_id == MER_INCAGI || skill_id == MER_BLESSING))
- return 0;
-
- // Couldn't preserve 3rd Class skills except only when using Reproduce skill. [Jobbie]
- if( !(sd->sc.data[SC__REPRODUCE]) && (skill_id >= RK_ENCHANTBLADE && skill_id <= SR_RIDEINLIGHTNING) )
- return 0;
- // Reproduce will only copy skills according on the list. [Jobbie]
- else if( sd->sc.data[SC__REPRODUCE] && !skill_reproduce_db[skill_id] )
- return 0;
-
- return 1;
-}
-
-// [MouseJstr] - skill ok to cast? and when?
-int skillnotok (uint16 skill_id, struct map_session_data *sd)
-{
- int16 idx,m;
- nullpo_retr (1, sd);
- m = sd->bl.m;
- idx = skill_get_index(skill_id);
-
- if (idx == 0)
- return 1; // invalid skill id
-
- if (pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL))
- return 0; // can do any damn thing they want
-
- if( skill_id == AL_TELEPORT && sd->skillitem == skill_id && sd->skillitemlv > 2 )
- return 0; // Teleport lv 3 bypasses this check.[Inkfish]
-
- // Epoque:
- // This code will compare the player's attack motion value which is influenced by ASPD before
- // allowing a skill to be cast. This is to prevent no-delay ACT files from spamming skills such as
- // AC_DOUBLE which do not have a skill delay and are not regarded in terms of attack motion.
- if( !sd->state.autocast && sd->skillitem != skill_id && sd->canskill_tick &&
- DIFF_TICK(gettick(), sd->canskill_tick) < (sd->battle_status.amotion * (battle_config.skill_amotion_leniency) / 100) )
- {// attempted to cast a skill before the attack motion has finished
- return 1;
- }
-
- if (sd->blockskill[idx] > 0){
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0);
- return 1;
- }
- /**
- * It has been confirmed on a official server (thanks to Yommy) that item-cast skills bypass all the restrictions above
- * Also, without this check, an exploit where an item casting + healing (or any other kind buff) isn't deleted after used on a restricted map
- **/
- if( sd->skillitem == skill_id )
- return 0;
- // Check skill restrictions [Celest]
- if( (!map_flag_vs(m) && skill_get_nocast (skill_id) & 1) ||
- (map[m].flag.pvp && skill_get_nocast (skill_id) & 2) ||
- (map_flag_gvg(m) && skill_get_nocast (skill_id) & 4) ||
- (map[m].flag.battleground && skill_get_nocast (skill_id) & 8) ||
- (map[m].flag.restricted && map[m].zone && skill_get_nocast (skill_id) & (8*map[m].zone)) ){
- clif_msg(sd, 0x536); // This skill cannot be used within this area
- return 1;
- }
-
- if( sd->sc.option&OPTION_MOUNTING )
- return 1;//You can't use skills while in the new mounts (The client doesn't let you, this is to make cheat-safe)
-
- switch (skill_id) {
- case AL_WARP:
- case RETURN_TO_ELDICASTES:
- case ALL_GUARDIAN_RECALL:
- if(map[m].flag.nowarp) {
- clif_skill_teleportmessage(sd,0);
- return 1;
- }
- return 0;
- case AL_TELEPORT:
- case SC_FATALMENACE:
- case SC_DIMENSIONDOOR:
- if(map[m].flag.noteleport) {
- clif_skill_teleportmessage(sd,0);
- return 1;
- }
- return 0; // gonna be checked in 'skill_castend_nodamage_id'
- case WE_CALLPARTNER:
- case WE_CALLPARENT:
- case WE_CALLBABY:
- if (map[m].flag.nomemo) {
- clif_skill_teleportmessage(sd,1);
- return 1;
- }
- break;
- case MC_VENDING:
- case MC_IDENTIFY:
- case ALL_BUYING_STORE:
- return 0; // always allowed
- case WZ_ICEWALL:
- // noicewall flag [Valaris]
- if (map[m].flag.noicewall) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 1;
- }
- break;
- case GC_DARKILLUSION:
- if( map_flag_gvg(m) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 1;
- }
- break;
- case GD_EMERGENCYCALL:
- if (
- !(battle_config.emergency_call&((agit_flag || agit2_flag)?2:1)) ||
- !(battle_config.emergency_call&(map[m].flag.gvg || map[m].flag.gvg_castle?8:4)) ||
- (battle_config.emergency_call&16 && map[m].flag.nowarpto && !map[m].flag.gvg_castle)
- ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 1;
- }
- break;
- case BS_GREED:
- case WS_CARTBOOST:
- case BS_HAMMERFALL:
- case BS_ADRENALINE:
- case MC_CARTREVOLUTION:
- case MC_MAMMONITE:
- case WS_MELTDOWN:
- case MG_SIGHT:
- case TF_HIDING:
- /**
- * These skills cannot be used while in mado gear (credits to Xantara)
- **/
- if( pc_ismadogear(sd) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 1;
- }
- break;
-
- case WM_SIRCLEOFNATURE:
- case WM_SOUND_OF_DESTRUCTION:
- case SC_MANHOLE:
- case WM_LULLABY_DEEPSLEEP:
- case WM_SATURDAY_NIGHT_FEVER:
- if( !map_flag_vs(m) ) {
- clif_skill_teleportmessage(sd,2); // This skill uses this msg instead of skill fails.
- return 1;
- }
- break;
-
- }
- return (map[m].flag.noskill);
-}
-
-int skillnotok_hom(uint16 skill_id, struct homun_data *hd)
-{
- uint16 idx = skill_get_index(skill_id);
- nullpo_retr(1,hd);
-
- if (idx == 0)
- return 1; // invalid skill id
-
- if (hd->blockskill[idx] > 0)
- return 1;
- switch(skill_id){
- case MH_LIGHT_OF_REGENE:
- if(hd->homunculus.intimacy <= 750) //if not cordial
- return 1;
- break;
- case MH_OVERED_BOOST:
- if(hd->homunculus.hunger <= 1) //if we starving
- return 1;
- case MH_GOLDENE_FERSE: //can be used with angriff
- if(hd->sc.data[SC_ANGRIFFS_MODUS])
- return 1;
- case MH_ANGRIFFS_MODUS:
- if(hd->sc.data[SC_GOLDENE_FERSE])
- return 1;
- break;
- }
-
- //Use master's criteria.
- return skillnotok(skill_id, hd->master);
-}
-
-int skillnotok_mercenary(uint16 skill_id, struct mercenary_data *md)
-{
- uint16 idx = skill_get_index(skill_id);
- nullpo_retr(1,md);
-
- if( idx == 0 )
- return 1; // Invalid Skill ID
- if( md->blockskill[idx] > 0 )
- return 1;
-
- return skillnotok(skill_id, md->master);
-}
-
-struct s_skill_unit_layout* skill_get_unit_layout (uint16 skill_id, uint16 skill_lv, struct block_list* src, int x, int y)
-{
- int pos = skill_get_unit_layout_type(skill_id,skill_lv);
- uint8 dir;
-
- if (pos < -1 || pos >= MAX_SKILL_UNIT_LAYOUT) {
- ShowError("skill_get_unit_layout: unsupported layout type %d for skill %d (level %d)\n", pos, skill_id, skill_lv);
- pos = cap_value(pos, 0, MAX_SQUARE_LAYOUT); // cap to nearest square layout
- }
-
- if (pos != -1) // simple single-definition layout
- return &skill_unit_layout[pos];
-
- dir = (src->x == x && src->y == y) ? 6 : map_calc_dir(src,x,y); // 6 - default aegis direction
-
- if (skill_id == MG_FIREWALL)
- return &skill_unit_layout [firewall_unit_pos + dir];
- else if (skill_id == WZ_ICEWALL)
- return &skill_unit_layout [icewall_unit_pos + dir];
- else if( skill_id == WL_EARTHSTRAIN ) //Warlock
- return &skill_unit_layout [earthstrain_unit_pos + dir];
-
- ShowError("skill_get_unit_layout: unknown unit layout for skill %d (level %d)\n", skill_id, skill_lv);
- return &skill_unit_layout[0]; // default 1x1 layout
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_additional_effect (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, int dmg_lv, unsigned int tick)
-{
- struct map_session_data *sd, *dstsd;
- struct mob_data *md, *dstmd;
- struct status_data *sstatus, *tstatus;
- struct status_change *sc, *tsc;
-
- enum sc_type status;
- int skill;
- int rate;
-
- nullpo_ret(src);
- nullpo_ret(bl);
-
- if(skill_id > 0 && !skill_lv) return 0; // don't forget auto attacks! - celest
-
- if( dmg_lv < ATK_BLOCK ) // Don't apply effect if miss.
- return 0;
-
- sd = BL_CAST(BL_PC, src);
- md = BL_CAST(BL_MOB, src);
- dstsd = BL_CAST(BL_PC, bl);
- dstmd = BL_CAST(BL_MOB, bl);
-
- sc = status_get_sc(src);
- tsc = status_get_sc(bl);
- sstatus = status_get_status_data(src);
- tstatus = status_get_status_data(bl);
- if (!tsc) //skill additional effect is about adding effects to the target...
- //So if the target can't be inflicted with statuses, this is pointless.
- return 0;
-
- if( sd )
- { // These statuses would be applied anyway even if the damage was blocked by some skills. [Inkfish]
- if( skill_id != WS_CARTTERMINATION && skill_id != AM_DEMONSTRATION && skill_id != CR_REFLECTSHIELD && skill_id != MS_REFLECTSHIELD && skill_id != ASC_BREAKER )
- { // Trigger status effects
- enum sc_type type;
- int i;
- for( i = 0; i < ARRAYLENGTH(sd->addeff) && sd->addeff[i].flag; i++ )
- {
- rate = sd->addeff[i].rate;
- if( attack_type&BF_LONG ) // Any ranged physical attack takes status arrows into account (Grimtooth...) [DracoRPG]
- rate += sd->addeff[i].arrow_rate;
- if( !rate ) continue;
-
- if( (sd->addeff[i].flag&(ATF_WEAPON|ATF_MAGIC|ATF_MISC)) != (ATF_WEAPON|ATF_MAGIC|ATF_MISC) )
- { // Trigger has attack type consideration.
- if( (sd->addeff[i].flag&ATF_WEAPON && attack_type&BF_WEAPON) ||
- (sd->addeff[i].flag&ATF_MAGIC && attack_type&BF_MAGIC) ||
- (sd->addeff[i].flag&ATF_MISC && attack_type&BF_MISC) ) ;
- else
- continue;
- }
-
- if( (sd->addeff[i].flag&(ATF_LONG|ATF_SHORT)) != (ATF_LONG|ATF_SHORT) )
- { // Trigger has range consideration.
- if((sd->addeff[i].flag&ATF_LONG && !(attack_type&BF_LONG)) ||
- (sd->addeff[i].flag&ATF_SHORT && !(attack_type&BF_SHORT)))
- continue; //Range Failed.
- }
-
- type = sd->addeff[i].id;
- skill = skill_get_time2(status_sc2skill(type),7);
-
- if (sd->addeff[i].flag&ATF_TARGET)
- status_change_start(bl,type,rate,7,0,0,0,skill,0);
-
- if (sd->addeff[i].flag&ATF_SELF)
- status_change_start(src,type,rate,7,0,0,0,skill,0);
- }
- }
-
- if( skill_id )
- { // Trigger status effects on skills
- enum sc_type type;
- int i;
- for( i = 0; i < ARRAYLENGTH(sd->addeff3) && sd->addeff3[i].skill; i++ )
- {
- if( skill_id != sd->addeff3[i].skill || !sd->addeff3[i].rate )
- continue;
- type = sd->addeff3[i].id;
- skill = skill_get_time2(status_sc2skill(type),7);
-
- if( sd->addeff3[i].target&ATF_TARGET )
- status_change_start(bl,type,sd->addeff3[i].rate,7,0,0,0,skill,0);
- if( sd->addeff3[i].target&ATF_SELF )
- status_change_start(src,type,sd->addeff3[i].rate,7,0,0,0,skill,0);
- }
- }
- }
-
- if( dmg_lv < ATK_DEF ) // no damage, return;
- return 0;
-
- switch(skill_id)
- {
- case 0: // Normal attacks (no skill used)
- {
- if( attack_type&BF_SKILL )
- break; // If a normal attack is a skill, it's splash damage. [Inkfish]
- if(sd) {
- // Automatic trigger of Blitz Beat
- if (pc_isfalcon(sd) && sd->status.weapon == W_BOW && (skill=pc_checkskill(sd,HT_BLITZBEAT))>0 &&
- rnd()%1000 <= sstatus->luk*10/3+1 ) {
- rate=(sd->status.job_level+9)/10;
- skill_castend_damage_id(src,bl,HT_BLITZBEAT,(skill<rate)?skill:rate,tick,SD_LEVEL);
- }
- // Automatic trigger of Warg Strike [Jobbie]
- if( pc_iswug(sd) && (sd->status.weapon == W_BOW || sd->status.weapon == W_FIST) && (skill=pc_checkskill(sd,RA_WUGSTRIKE)) > 0 && rnd()%1000 <= sstatus->luk*10/3+1 )
- skill_castend_damage_id(src,bl,RA_WUGSTRIKE,skill,tick,0);
- // Gank
- if(dstmd && sd->status.weapon != W_BOW &&
- (skill=pc_checkskill(sd,RG_SNATCHER)) > 0 &&
- (skill*15 + 55) + pc_checkskill(sd,TF_STEAL)*10 > rnd()%1000) {
- if(pc_steal_item(sd,bl,pc_checkskill(sd,TF_STEAL)))
- clif_skill_nodamage(src,bl,TF_STEAL,skill,1);
- else
- clif_skill_fail(sd,RG_SNATCHER,USESKILL_FAIL_LEVEL,0);
- }
- // Chance to trigger Taekwon kicks [Dralnu]
- if(sc && !sc->data[SC_COMBO]) {
- if(sc->data[SC_READYSTORM] &&
- sc_start(src,SC_COMBO, 15, TK_STORMKICK,
- (2000 - 4*sstatus->agi - 2*sstatus->dex)))
- ; //Stance triggered
- else if(sc->data[SC_READYDOWN] &&
- sc_start(src,SC_COMBO, 15, TK_DOWNKICK,
- (2000 - 4*sstatus->agi - 2*sstatus->dex)))
- ; //Stance triggered
- else if(sc->data[SC_READYTURN] &&
- sc_start(src,SC_COMBO, 15, TK_TURNKICK,
- (2000 - 4*sstatus->agi - 2*sstatus->dex)))
- ; //Stance triggered
- else if (sc->data[SC_READYCOUNTER]) { //additional chance from SG_FRIEND [Komurka]
- rate = 20;
- if (sc->data[SC_SKILLRATE_UP] && sc->data[SC_SKILLRATE_UP]->val1 == TK_COUNTER) {
- rate += rate*sc->data[SC_SKILLRATE_UP]->val2/100;
- status_change_end(src, SC_SKILLRATE_UP, INVALID_TIMER);
- }
- sc_start2(src, SC_COMBO, rate, TK_COUNTER, bl->id,
- (2000 - 4*sstatus->agi - 2*sstatus->dex));
- }
- }
- if(sc && sc->data[SC_PYROCLASTIC] && (rnd() % 1000 <= sstatus->luk * 10 / 3 + 1) )
- skill_castend_pos2(src, bl->x, bl->y, BS_HAMMERFALL,sc->data[SC_PYROCLASTIC]->val1, tick, 0);
- }
-
- if (sc) {
- struct status_change_entry *sce;
- // Enchant Poison gives a chance to poison attacked enemies
- if((sce=sc->data[SC_ENCPOISON])) //Don't use sc_start since chance comes in 1/10000 rate.
- status_change_start(bl,SC_POISON,sce->val2, sce->val1,src->id,0,0,
- skill_get_time2(AS_ENCHANTPOISON,sce->val1),0);
- // Enchant Deadly Poison gives a chance to deadly poison attacked enemies
- if((sce=sc->data[SC_EDP]))
- sc_start4(bl,SC_DPOISON,sce->val2, sce->val1,src->id,0,0,
- skill_get_time2(ASC_EDP,sce->val1));
- }
- }
- break;
-
- case SM_BASH:
- if( sd && skill_lv > 5 && pc_checkskill(sd,SM_FATALBLOW)>0 ){
- //TODO: How much % per base level it actually is?
- sc_start(bl,SC_STUN,(5*(skill_lv-5)+(int)sd->status.base_level/10),
- skill_lv,skill_get_time2(SM_FATALBLOW,skill_lv));
- }
- break;
-
- case MER_CRASH:
- sc_start(bl,SC_STUN,(6*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case AS_VENOMKNIFE:
- if (sd) //Poison chance must be that of Envenom. [Skotlex]
- skill_lv = pc_checkskill(sd, TF_POISON);
- case TF_POISON:
- case AS_SPLASHER:
- if(!sc_start2(bl,SC_POISON,(4*skill_lv+10),skill_lv,src->id,skill_get_time2(skill_id,skill_lv))
- && sd && skill_id==TF_POISON
- )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
-
- case AS_SONICBLOW:
- sc_start(bl,SC_STUN,(2*skill_lv+10),skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case WZ_FIREPILLAR:
- unit_set_walkdelay(bl, tick, skill_get_time2(skill_id, skill_lv), 1);
- break;
-
- case MG_FROSTDIVER:
-#ifndef RENEWAL
- case WZ_FROSTNOVA:
-#endif
- sc_start(bl,SC_FREEZE,skill_lv*3+35,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
-#ifdef RENEWAL
- case WZ_FROSTNOVA:
- sc_start(bl,SC_FREEZE,skill_lv*5+33,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-#endif
-
- case WZ_STORMGUST:
- /**
- * Storm Gust counter was dropped in renewal
- **/
- #ifdef RENEWAL
- sc_start(bl,SC_FREEZE,65-(5*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
- #else
- //Tharis pointed out that this is normal freeze chance with a base of 300%
- if(tsc->sg_counter >= 3 &&
- sc_start(bl,SC_FREEZE,300,skill_lv,skill_get_time2(skill_id,skill_lv)))
- tsc->sg_counter = 0;
- /**
- * being it only resets on success it'd keep stacking and eventually overflowing on mvps, so we reset at a high value
- **/
- else if( tsc->sg_counter > 250 )
- tsc->sg_counter = 0;
- #endif
- break;
-
- case WZ_METEOR:
- sc_start(bl,SC_STUN,3*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case WZ_VERMILION:
- sc_start(bl,SC_BLIND,4*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case HT_FREEZINGTRAP:
- case MA_FREEZINGTRAP:
- sc_start(bl,SC_FREEZE,(3*skill_lv+35),skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case HT_FLASHER:
- sc_start(bl,SC_BLIND,(10*skill_lv+30),skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case HT_LANDMINE:
- case MA_LANDMINE:
- sc_start(bl,SC_STUN,(5*skill_lv+30),skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case HT_SHOCKWAVE:
- status_percent_damage(src, bl, 0, 15*skill_lv+5, false);
- break;
-
- case HT_SANDMAN:
- case MA_SANDMAN:
- sc_start(bl,SC_SLEEP,(10*skill_lv+40),skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case TF_SPRINKLESAND:
- sc_start(bl,SC_BLIND,20,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case TF_THROWSTONE:
- sc_start(bl,SC_STUN,3,skill_lv,skill_get_time(skill_id,skill_lv));
- sc_start(bl,SC_BLIND,3,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case NPC_DARKCROSS:
- case CR_HOLYCROSS:
- sc_start(bl,SC_BLIND,3*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case CR_GRANDCROSS:
- case NPC_GRANDDARKNESS:
- //Chance to cause blind status vs demon and undead element, but not against players
- if(!dstsd && (battle_check_undead(tstatus->race,tstatus->def_ele) || tstatus->race == RC_DEMON))
- sc_start(bl,SC_BLIND,100,skill_lv,skill_get_time2(skill_id,skill_lv));
- attack_type |= BF_WEAPON;
- break;
-
- case AM_ACIDTERROR:
- sc_start(bl,SC_BLEEDING,(skill_lv*3),skill_lv,skill_get_time2(skill_id,skill_lv));
- if (skill_break_equip(bl, EQP_ARMOR, 100*skill_get_time(skill_id,skill_lv), BCT_ENEMY))
- clif_emotion(bl,E_OMG);
- break;
-
- case AM_DEMONSTRATION:
- skill_break_equip(bl, EQP_WEAPON, 100*skill_lv, BCT_ENEMY);
- break;
-
- case CR_SHIELDCHARGE:
- sc_start(bl,SC_STUN,(15+skill_lv*5),skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case PA_PRESSURE:
- status_percent_damage(src, bl, 0, 15+5*skill_lv, false);
- break;
-
- case RG_RAID:
- sc_start(bl,SC_STUN,(10+3*skill_lv),skill_lv,skill_get_time(skill_id,skill_lv));
- sc_start(bl,SC_BLIND,(10+3*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
-
-#ifdef RENEWAL
- sc_start(bl,SC_RAID,100,7,5000);
- break;
-
- case RG_BACKSTAP:
- sc_start(bl,SC_STUN,(5+2*skill_lv),skill_lv,skill_get_time(skill_id,skill_lv));
-#endif
- break;
-
- case BA_FROSTJOKER:
- sc_start(bl,SC_FREEZE,(15+5*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case DC_SCREAM:
- sc_start(bl,SC_STUN,(25+5*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case BD_LULLABY:
- sc_start(bl,SC_SLEEP,15,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case DC_UGLYDANCE:
- rate = 5+5*skill_lv;
- if(sd && (skill=pc_checkskill(sd,DC_DANCINGLESSON)))
- rate += 5+skill;
- status_zap(bl, 0, rate);
- break;
- case SL_STUN:
- if (tstatus->size==SZ_MEDIUM) //Only stuns mid-sized mobs.
- sc_start(bl,SC_STUN,(30+10*skill_lv),skill_lv,skill_get_time(skill_id,skill_lv));
- break;
-
- case NPC_PETRIFYATTACK:
- sc_start4(bl,status_skill2sc(skill_id),50+10*skill_lv,
- skill_lv,0,0,skill_get_time(skill_id,skill_lv),
- skill_get_time2(skill_id,skill_lv));
- break;
- case NPC_CURSEATTACK:
- case NPC_SLEEPATTACK:
- case NPC_BLINDATTACK:
- case NPC_POISON:
- case NPC_SILENCEATTACK:
- case NPC_STUNATTACK:
- case NPC_HELLPOWER:
- sc_start(bl,status_skill2sc(skill_id),50+10*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
- case NPC_ACIDBREATH:
- case NPC_ICEBREATH:
- sc_start(bl,status_skill2sc(skill_id),70,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
- case NPC_BLEEDING:
- sc_start(bl,SC_BLEEDING,(20*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
- case NPC_MENTALBREAKER:
- { //Based on observations by Tharis, Mental Breaker should do SP damage
- //equal to Matk*skLevel.
- rate = sstatus->matk_min;
- if (rate < sstatus->matk_max)
- rate += rnd()%(sstatus->matk_max - sstatus->matk_min);
- rate*=skill_lv;
- status_zap(bl, 0, rate);
- break;
- }
- // Equipment breaking monster skills [Celest]
- case NPC_WEAPONBRAKER:
- skill_break_equip(bl, EQP_WEAPON, 150*skill_lv, BCT_ENEMY);
- break;
- case NPC_ARMORBRAKE:
- skill_break_equip(bl, EQP_ARMOR, 150*skill_lv, BCT_ENEMY);
- break;
- case NPC_HELMBRAKE:
- skill_break_equip(bl, EQP_HELM, 150*skill_lv, BCT_ENEMY);
- break;
- case NPC_SHIELDBRAKE:
- skill_break_equip(bl, EQP_SHIELD, 150*skill_lv, BCT_ENEMY);
- break;
-
- case CH_TIGERFIST:
- sc_start(bl,SC_STOP,(10+skill_lv*10),0,skill_get_time2(skill_id,skill_lv));
- break;
-
- case LK_SPIRALPIERCE:
- case ML_SPIRALPIERCE:
- sc_start(bl,SC_STOP,(15+skill_lv*5),0,skill_get_time2(skill_id,skill_lv));
- break;
-
- case ST_REJECTSWORD:
- sc_start(bl,SC_AUTOCOUNTER,(skill_lv*15),skill_lv,skill_get_time(skill_id,skill_lv));
- break;
-
- case PF_FOGWALL:
- if (src != bl && !tsc->data[SC_DELUGE])
- sc_start(bl,SC_BLIND,100,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case LK_HEADCRUSH: //Headcrush has chance of causing Bleeding status, except on demon and undead element
- if (!(battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race == RC_DEMON))
- sc_start(bl, SC_BLEEDING,50, skill_lv, skill_get_time2(skill_id,skill_lv));
- break;
-
- case LK_JOINTBEAT:
- status = status_skill2sc(skill_id);
- if (tsc->jb_flag) {
- sc_start2(bl,status,(5*skill_lv+5),skill_lv,tsc->jb_flag&BREAK_FLAGS,skill_get_time2(skill_id,skill_lv));
- tsc->jb_flag = 0;
- }
- break;
- case ASC_METEORASSAULT:
- //Any enemies hit by this skill will receive Stun, Darkness, or external bleeding status ailment with a 5%+5*skill_lv% chance.
- switch(rnd()%3) {
- case 0:
- sc_start(bl,SC_BLIND,(5+skill_lv*5),skill_lv,skill_get_time2(skill_id,1));
- break;
- case 1:
- sc_start(bl,SC_STUN,(5+skill_lv*5),skill_lv,skill_get_time2(skill_id,2));
- break;
- default:
- sc_start(bl,SC_BLEEDING,(5+skill_lv*5),skill_lv,skill_get_time2(skill_id,3));
- }
- break;
-
- case HW_NAPALMVULCAN:
- sc_start(bl,SC_CURSE,5*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case WS_CARTTERMINATION: // Cart termination
- sc_start(bl,SC_STUN,5*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case CR_ACIDDEMONSTRATION:
- skill_break_equip(bl, EQP_WEAPON|EQP_ARMOR, 100*skill_lv, BCT_ENEMY);
- break;
-
- case TK_DOWNKICK:
- sc_start(bl,SC_STUN,100,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case TK_JUMPKICK:
- if( dstsd && dstsd->class_ != MAPID_SOUL_LINKER && !tsc->data[SC_PRESERVE] )
- {// debuff the following statuses
- status_change_end(bl, SC_SPIRIT, INVALID_TIMER);
- status_change_end(bl, SC_ADRENALINE2, INVALID_TIMER);
- status_change_end(bl, SC_KAITE, INVALID_TIMER);
- status_change_end(bl, SC_KAAHI, INVALID_TIMER);
- status_change_end(bl, SC_ONEHAND, INVALID_TIMER);
- status_change_end(bl, SC_ASPDPOTION2, INVALID_TIMER);
- }
- break;
- case TK_TURNKICK:
- case MO_BALKYOUNG: //Note: attack_type is passed as BF_WEAPON for the actual target, BF_MISC for the splash-affected mobs.
- if(attack_type&BF_MISC) //70% base stun chance...
- sc_start(bl,SC_STUN,70,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
- case GS_BULLSEYE: //0.1% coma rate.
- if(tstatus->race == RC_BRUTE || tstatus->race == RC_DEMIHUMAN)
- status_change_start(bl,SC_COMA,10,skill_lv,0,src->id,0,0,0);
- break;
- case GS_PIERCINGSHOT:
- sc_start(bl,SC_BLEEDING,(skill_lv*3),skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
- case NJ_HYOUSYOURAKU:
- sc_start(bl,SC_FREEZE,(10+10*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
- case GS_FLING:
- sc_start(bl,SC_FLING,100, sd?sd->spiritball_old:5,skill_get_time(skill_id,skill_lv));
- break;
- case GS_DISARM:
- rate = 3*skill_lv;
- if (sstatus->dex > tstatus->dex)
- rate += (sstatus->dex - tstatus->dex)/5; //TODO: Made up formula
- skill_strip_equip(bl, EQP_WEAPON, rate, skill_lv, skill_get_time(skill_id,skill_lv));
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
- case NPC_EVILLAND:
- sc_start(bl,SC_BLIND,5*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
- case NPC_HELLJUDGEMENT:
- sc_start(bl,SC_CURSE,100,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
- case NPC_CRITICALWOUND:
- sc_start(bl,SC_CRITICALWOUND,100,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
- case RK_HUNDREDSPEAR:
- if( !sd || pc_checkskill(sd,KN_SPEARBOOMERANG) == 0 )
- break; // Spear Boomerang auto cast chance only works if you have mastered Spear Boomerang.
- rate = 10 + 3 * skill_lv;
- if( rnd()%100 < rate )
- skill_castend_damage_id(src,bl,KN_SPEARBOOMERANG,1,tick,0);
- break;
- case RK_WINDCUTTER:
- sc_start(bl,SC_FEAR,3+2*skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
- break;
- case RK_DRAGONBREATH:
- sc_start4(bl,SC_BURNING,5+5*skill_lv,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv));
- break;
- case AB_ADORAMUS:
- if( tsc && !tsc->data[SC_DECREASEAGI] ) //Prevent duplicate agi-down effect.
- sc_start(bl, SC_ADORAMUS, 100, skill_lv, skill_get_time(skill_id, skill_lv));
- break;
- case WL_CRIMSONROCK:
- sc_start(bl, SC_STUN, 40, skill_lv, skill_get_time(skill_id, skill_lv));
- break;
- case WL_COMET:
- sc_start4(bl,SC_BURNING,100,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv));
- break;
- case WL_EARTHSTRAIN:
- {
- int rate = 0, i;
- const int pos[5] = { EQP_WEAPON, EQP_HELM, EQP_SHIELD, EQP_ARMOR, EQP_ACC };
- rate = 6 * skill_lv + sstatus->dex / 10 + (sd? sd->status.job_level / 4 : 0) - tstatus->dex /5;// The tstatus->dex / 5 part is unofficial, but players gotta have some kind of way to have resistance. [Rytech]
- //rate -= rate * tstatus->dex / 200; // Disabled until official resistance is found.
-
- for( i = 0; i < skill_lv; i++ )
- skill_strip_equip(bl,pos[i],rate,skill_lv,skill_get_time2(skill_id,skill_lv));
- }
- break;
- case WL_JACKFROST:
- sc_start(bl,SC_FREEZE,100,skill_lv,skill_get_time(skill_id,skill_lv));
- break;
- case RA_WUGBITE:
- sc_start(bl, SC_BITE, (sd ? pc_checkskill(sd,RA_TOOTHOFWUG)*2 : 0), skill_lv, (skill_get_time(skill_id,skill_lv) + (sd ? pc_checkskill(sd,RA_TOOTHOFWUG)*500 : 0)) );
- break;
- case RA_SENSITIVEKEEN:
- if( rnd()%100 < 8 * skill_lv )
- skill_castend_damage_id(src, bl, RA_WUGBITE, sd ? pc_checkskill(sd, RA_WUGBITE):skill_lv, tick, SD_ANIMATION);
- break;
- case RA_FIRINGTRAP:
- case RA_ICEBOUNDTRAP:
- sc_start(bl, (skill_id == RA_FIRINGTRAP) ? SC_BURNING:SC_FREEZING, 40 + 10 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv));
- break;
- case NC_PILEBUNKER:
- if( rnd()%100 < 5 + 15*skill_lv )
- { //Deactivatable Statuses: Kyrie Eleison, Auto Guard, Steel Body, Assumptio, and Millennium Shield
- status_change_end(bl, SC_KYRIE, INVALID_TIMER);
- status_change_end(bl, SC_AUTOGUARD, INVALID_TIMER);
- status_change_end(bl, SC_STEELBODY, INVALID_TIMER);
- status_change_end(bl, SC_ASSUMPTIO, INVALID_TIMER);
- status_change_end(bl, SC_MILLENNIUMSHIELD, INVALID_TIMER);
- }
- break;
- case NC_FLAMELAUNCHER:
- sc_start4(bl, SC_BURNING, 50 + 10 * skill_lv, skill_lv, 1000, src->id, 0, skill_get_time2(skill_id, skill_lv));
- break;
- case NC_COLDSLOWER:
- sc_start(bl, SC_FREEZE, 10 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
- sc_start(bl, SC_FREEZING, 20 + 10 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
- break;
- case NC_POWERSWING:
- sc_start(bl, SC_STUN, 5*skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
- if( rnd()%100 < 5*skill_lv )
- skill_castend_damage_id(src, bl, NC_AXEBOOMERANG, pc_checkskill(sd, NC_AXEBOOMERANG), tick, 1);
- break;
- case GC_WEAPONCRUSH:
- skill_castend_nodamage_id(src,bl,skill_id,skill_lv,tick,BCT_ENEMY);
- break;
- case LG_SHIELDPRESS:
- sc_start(bl, SC_STUN, 30 + 8 * skill_lv, skill_lv, skill_get_time(skill_id,skill_lv));
- break;
- case LG_PINPOINTATTACK:
- rate = 30 + (((5 * (sd?pc_checkskill(sd,LG_PINPOINTATTACK):skill_lv)) + (sstatus->agi + status_get_lv(src))) / 10);
- switch( skill_lv ) {
- case 1:
- sc_start(bl,SC_BLEEDING,rate,skill_lv,skill_get_time(skill_id,skill_lv));
- break;
- case 2:
- if( dstsd && dstsd->spiritball && rnd()%100 < rate )
- pc_delspiritball(dstsd, dstsd->spiritball, 0);
- break;
- default:
- skill_break_equip(bl,(skill_lv == 3) ? EQP_SHIELD : (skill_lv == 4) ? EQP_ARMOR : EQP_WEAPON,rate * 100,BCT_ENEMY);
- break;
- }
- break;
- case LG_MOONSLASHER:
- rate = 32 + 8 * skill_lv;
- if( rnd()%100 < rate && dstsd ) // Uses skill_addtimerskill to avoid damage and setsit packet overlaping. Officially clif_setsit is received about 500 ms after damage packet.
- skill_addtimerskill(src,tick+500,bl->id,0,0,skill_id,skill_lv,BF_WEAPON,0);
- else if( dstmd && !is_boss(bl) )
- sc_start(bl,SC_STOP,100,skill_lv,skill_get_time(skill_id,skill_lv));
- break;
- case LG_RAYOFGENESIS: // 50% chance to cause Blind on Undead and Demon monsters.
- if ( battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race == RC_DEMON )
- sc_start(bl, SC_BLIND,50, skill_lv, skill_get_time(skill_id,skill_lv));
- break;
- case LG_EARTHDRIVE:
- skill_break_equip(src, EQP_SHIELD, 500, BCT_SELF);
- sc_start(bl, SC_EARTHDRIVE, 100, skill_lv, skill_get_time(skill_id, skill_lv));
- break;
- case SR_DRAGONCOMBO:
- sc_start(bl, SC_STUN, 1 + skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
- break;
- case SR_FALLENEMPIRE:
- sc_start(bl, SC_STOP, 100, skill_lv, skill_get_time(skill_id, skill_lv));
- break;
- case SR_WINDMILL:
- if( dstsd )
- skill_addtimerskill(src,tick+status_get_amotion(src),bl->id,0,0,skill_id,skill_lv,BF_WEAPON,0);
- else if( dstmd && !is_boss(bl) )
- sc_start(bl, SC_STUN, 100, skill_lv, 1000 + 1000 * (rnd() %3));
- break;
- case SR_GENTLETOUCH_QUIET: // [(Skill Level x 5) + (Caster?s DEX + Caster?s Base Level) / 10]
- sc_start(bl, SC_SILENCE, 5 * skill_lv + (sstatus->dex + status_get_lv(src)) / 10, skill_lv, skill_get_time(skill_id, skill_lv));
- break;
- case SR_EARTHSHAKER:
- sc_start(bl,SC_STUN, 25 + 5 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
- break;
- case SR_HOWLINGOFLION:
- sc_start(bl, SC_FEAR, 5 + 5 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
- break;
- case WM_SOUND_OF_DESTRUCTION:
- if( rnd()%100 < 5 + 5 * skill_lv ) { // Temporarly Check Until We Get the Official Formula
- status_change_end(bl, SC_DANCING, INVALID_TIMER);
- status_change_end(bl, SC_RICHMANKIM, INVALID_TIMER);
- status_change_end(bl, SC_ETERNALCHAOS, INVALID_TIMER);
- status_change_end(bl, SC_DRUMBATTLE, INVALID_TIMER);
- status_change_end(bl, SC_NIBELUNGEN, INVALID_TIMER);
- status_change_end(bl, SC_INTOABYSS, INVALID_TIMER);
- status_change_end(bl, SC_SIEGFRIED, INVALID_TIMER);
- status_change_end(bl, SC_WHISTLE, INVALID_TIMER);
- status_change_end(bl, SC_ASSNCROS, INVALID_TIMER);
- status_change_end(bl, SC_POEMBRAGI, INVALID_TIMER);
- status_change_end(bl, SC_APPLEIDUN, INVALID_TIMER);
- status_change_end(bl, SC_HUMMING, INVALID_TIMER);
- status_change_end(bl, SC_FORTUNE, INVALID_TIMER);
- status_change_end(bl, SC_SERVICE4U, INVALID_TIMER);
- status_change_end(bl, SC_LONGING, INVALID_TIMER);
- status_change_end(bl, SC_SWINGDANCE, INVALID_TIMER);
- status_change_end(bl, SC_SYMPHONYOFLOVER, INVALID_TIMER);
- status_change_end(bl, SC_MOONLITSERENADE, INVALID_TIMER);
- status_change_end(bl, SC_RUSHWINDMILL, INVALID_TIMER);
- status_change_end(bl, SC_ECHOSONG, INVALID_TIMER);
- status_change_end(bl, SC_HARMONIZE, INVALID_TIMER);
- status_change_end(bl, SC_WINKCHARM, INVALID_TIMER);
- status_change_end(bl, SC_SONGOFMANA, INVALID_TIMER);
- status_change_end(bl, SC_DANCEWITHWUG, INVALID_TIMER);
- status_change_end(bl, SC_LERADSDEW, INVALID_TIMER);
- status_change_end(bl, SC_MELODYOFSINK, INVALID_TIMER);
- status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER);
- status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER);
- }
- break;
- case SO_EARTHGRAVE:
- sc_start(bl, SC_BLEEDING, 5 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv)); // Need official rate. [LimitLine]
- break;
- case SO_DIAMONDDUST:
- rate = 5 + 5 * skill_lv;
- if( sc && sc->data[SC_COOLER_OPTION] )
- rate += rate * sc->data[SC_COOLER_OPTION]->val2 / 100;
- sc_start(bl, SC_CRYSTALIZE, rate, skill_lv, skill_get_time2(skill_id, skill_lv));
- break;
- case SO_VARETYR_SPEAR:
- sc_start(bl, SC_STUN, 5 + 5 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv));
- break;
- case GN_SLINGITEM_RANGEMELEEATK:
- if( sd ) {
- switch( sd->itemid ) { // Starting SCs here instead of do it in skill_additional_effect to simplify the code.
- case 13261:
- sc_start(bl, SC_STUN, 100, skill_lv, skill_get_time2(GN_SLINGITEM, skill_lv));
- sc_start(bl, SC_BLEEDING, 100, skill_lv, skill_get_time2(GN_SLINGITEM, skill_lv));
- break;
- case 13262:
- sc_start(bl, SC_MELON_BOMB, 100, skill_lv, skill_get_time(GN_SLINGITEM, skill_lv)); // Reduces ASPD and moviment speed
- break;
- case 13264:
- sc_start(bl, SC_BANANA_BOMB, 100, skill_lv, skill_get_time(GN_SLINGITEM, skill_lv)); // Reduces LUK ??Needed confirm it, may be it's bugged in kRORE?
- sc_start(bl, SC_BANANA_BOMB_SITDOWN, 75, skill_lv, skill_get_time(GN_SLINGITEM_RANGEMELEEATK,skill_lv)); // Sitdown for 3 seconds.
- break;
- }
- sd->itemid = -1;
- }
- break;
- case GN_HELLS_PLANT_ATK:
- sc_start(bl, SC_STUN, 5 + 5 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv));
- sc_start(bl, SC_BLEEDING, 20 + 10 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv));
- break;
- case EL_WIND_SLASH: // Non confirmed rate.
- sc_start(bl, SC_BLEEDING, 25, skill_lv, skill_get_time(skill_id,skill_lv));
- break;
- case EL_STONE_HAMMER:
- rate = 10 * skill_lv;
- sc_start(bl, SC_STUN, rate, skill_lv, skill_get_time(skill_id,skill_lv));
- break;
- case EL_ROCK_CRUSHER:
- case EL_ROCK_CRUSHER_ATK:
- sc_start(bl,status_skill2sc(skill_id),50,skill_lv,skill_get_time(EL_ROCK_CRUSHER,skill_lv));
- break;
- case EL_TYPOON_MIS:
- sc_start(bl,SC_SILENCE,10*skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
- break;
- case KO_JYUMONJIKIRI: // needs more info
- sc_start(bl,SC_JYUMONJIKIRI,25,skill_lv,skill_get_time(skill_id,skill_lv));
- break;
- case KO_MAKIBISHI:
- sc_start(bl, SC_STUN, 100, skill_lv, skill_get_time2(skill_id,skill_lv));
- break;
- case MH_LAVA_SLIDE:
- if (tsc && !tsc->data[SC_BURNING]) sc_start4(bl, SC_BURNING, 10 * skill_lv, skill_lv, 1000, src->id, 0, skill_get_time(skill_id, skill_lv));
- break;
- case MH_STAHL_HORN:
- sc_start(bl, SC_STUN, (20 + 4 * (skill_lv-1)), skill_lv, skill_get_time(skill_id, skill_lv));
- break;
- case MH_NEEDLE_OF_PARALYZE:
- sc_start(bl, SC_PARALYSIS, 40 + (5*skill_lv), skill_lv, skill_get_time(skill_id, skill_lv));
- break;
- }
-
- if (md && battle_config.summons_trigger_autospells && md->master_id && md->special_state.ai)
- { //Pass heritage to Master for status causing effects. [Skotlex]
- sd = map_id2sd(md->master_id);
- src = sd?&sd->bl:src;
- }
-
- if( attack_type&BF_WEAPON )
- { // Coma, Breaking Equipment
- if( sd && sd->special_state.bonus_coma )
- {
- rate = sd->weapon_coma_ele[tstatus->def_ele];
- rate += sd->weapon_coma_race[tstatus->race];
- rate += sd->weapon_coma_race[tstatus->mode&MD_BOSS?RC_BOSS:RC_NONBOSS];
- if (rate)
- status_change_start(bl, SC_COMA, rate, 0, 0, src->id, 0, 0, 0);
- }
- if( sd && battle_config.equip_self_break_rate )
- { // Self weapon breaking
- rate = battle_config.equip_natural_break_rate;
- if( sc )
- {
- if(sc->data[SC_OVERTHRUST])
- rate += 10;
- if(sc->data[SC_MAXOVERTHRUST])
- rate += 10;
- }
- if( rate )
- skill_break_equip(src, EQP_WEAPON, rate, BCT_SELF);
- }
- if( battle_config.equip_skill_break_rate && skill_id != WS_CARTTERMINATION && skill_id != ITM_TOMAHAWK )
- { // Cart Termination/Tomahawk won't trigger breaking data. Why? No idea, go ask Gravity.
- // Target weapon breaking
- rate = 0;
- if( sd )
- rate += sd->bonus.break_weapon_rate;
- if( sc && sc->data[SC_MELTDOWN] )
- rate += sc->data[SC_MELTDOWN]->val2;
- if( rate )
- skill_break_equip(bl, EQP_WEAPON, rate, BCT_ENEMY);
-
- // Target armor breaking
- rate = 0;
- if( sd )
- rate += sd->bonus.break_armor_rate;
- if( sc && sc->data[SC_MELTDOWN] )
- rate += sc->data[SC_MELTDOWN]->val3;
- if( rate )
- skill_break_equip(bl, EQP_ARMOR, rate, BCT_ENEMY);
- }
- }
-
- if( sd && sd->ed && sc && !status_isdead(bl) && !skill_id ){
- struct unit_data *ud = unit_bl2ud(src);
-
- if( sc->data[SC_WILD_STORM_OPTION] )
- skill = sc->data[SC_WILD_STORM_OPTION]->val2;
- else if( sc->data[SC_UPHEAVAL_OPTION] )
- skill = sc->data[SC_UPHEAVAL_OPTION]->val2;
- else if( sc->data[SC_TROPIC_OPTION] )
- skill = sc->data[SC_TROPIC_OPTION]->val3;
- else if( sc->data[SC_CHILLY_AIR_OPTION] )
- skill = sc->data[SC_CHILLY_AIR_OPTION]->val3;
- else
- skill = 0;
-
- if ( rnd()%100 < 25 && skill ){
- skill_castend_damage_id(src, bl, skill, 5, tick, 0);
-
- if (ud) {
- rate = skill_delayfix(src, skill, skill_lv);
- if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){
- ud->canact_tick = tick+rate;
- if ( battle_config.display_status_timers )
- clif_status_change(src, SI_ACTIONDELAY, 1, rate, 0, 0, 0);
- }
- }
- }
- }
-
- // Autospell when attacking
- if( sd && !status_isdead(bl) && sd->autospell[0].id )
- {
- struct block_list *tbl;
- struct unit_data *ud;
- int i, skill_lv, type, notok;
-
- for (i = 0; i < ARRAYLENGTH(sd->autospell) && sd->autospell[i].id; i++) {
-
- if(!(sd->autospell[i].flag&attack_type&BF_WEAPONMASK &&
- sd->autospell[i].flag&attack_type&BF_RANGEMASK &&
- sd->autospell[i].flag&attack_type&BF_SKILLMASK))
- continue; // one or more trigger conditions were not fulfilled
-
- skill = (sd->autospell[i].id > 0) ? sd->autospell[i].id : -sd->autospell[i].id;
-
- sd->state.autocast = 1;
- notok = skillnotok(skill, sd);
- sd->state.autocast = 0;
-
- if ( notok )
- continue;
-
- skill_lv = sd->autospell[i].lv?sd->autospell[i].lv:1;
- if (skill_lv < 0) skill_lv = 1+rnd()%(-skill_lv);
-
- rate = (!sd->state.arrow_atk) ? sd->autospell[i].rate : sd->autospell[i].rate / 2;
-
- if (rnd()%1000 >= rate)
- continue;
-
- tbl = (sd->autospell[i].id < 0) ? src : bl;
-
- if( (type = skill_get_casttype(skill)) == CAST_GROUND ) {
- int maxcount = 0;
- if( !(BL_PC&battle_config.skill_reiteration) &&
- skill_get_unit_flag(skill)&UF_NOREITERATION &&
- skill_check_unit_range(src,tbl->x,tbl->y,skill,skill_lv)
- ) {
- continue;
- }
- if( BL_PC&battle_config.skill_nofootset &&
- skill_get_unit_flag(skill)&UF_NOFOOTSET &&
- skill_check_unit_range2(src,tbl->x,tbl->y,skill,skill_lv)
- ) {
- continue;
- }
- if( BL_PC&battle_config.land_skill_limit &&
- (maxcount = skill_get_maxcount(skill, skill_lv)) > 0
- ) {
- int v;
- for(v=0;v<MAX_SKILLUNITGROUP && sd->ud.skillunit[v] && maxcount;v++) {
- if(sd->ud.skillunit[v]->skill_id == skill)
- maxcount--;
- }
- if( maxcount == 0 ) {
- continue;
- }
- }
- }
- if( battle_config.autospell_check_range &&
- !battle_check_range(src, tbl, skill_get_range2(src, skill,skill_lv) + (skill == RG_CLOSECONFINE?0:1)) )
- continue;
-
- if (skill == AS_SONICBLOW)
- pc_stop_attack(sd); //Special case, Sonic Blow autospell should stop the player attacking.
- if (skill == PF_SPIDERWEB) //Special case, due to its nature of coding.
- type = CAST_GROUND;
-
- sd->state.autocast = 1;
- skill_consume_requirement(sd,skill,skill_lv,1);
- skill_toggle_magicpower(src, skill);
- switch (type) {
- case CAST_GROUND:
- skill_castend_pos2(src, tbl->x, tbl->y, skill, skill_lv, tick, 0);
- break;
- case CAST_NODAMAGE:
- skill_castend_nodamage_id(src, tbl, skill, skill_lv, tick, 0);
- break;
- case CAST_DAMAGE:
- skill_castend_damage_id(src, tbl, skill, skill_lv, tick, 0);
- break;
- }
- sd->state.autocast = 0;
- //Set canact delay. [Skotlex]
- ud = unit_bl2ud(src);
- if (ud) {
- rate = skill_delayfix(src, skill, skill_lv);
- if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){
- ud->canact_tick = tick+rate;
- if ( battle_config.display_status_timers && sd )
- clif_status_change(src, SI_ACTIONDELAY, 1, rate, 0, 0, 0);
- }
- }
- }
- }
-
- //Autobonus when attacking
- if( sd && sd->autobonus[0].rate )
- {
- int i;
- for( i = 0; i < ARRAYLENGTH(sd->autobonus); i++ )
- {
- if( rnd()%1000 >= sd->autobonus[i].rate )
- continue;
- if( sd->autobonus[i].active != INVALID_TIMER )
- continue;
- if(!(sd->autobonus[i].atk_type&attack_type&BF_WEAPONMASK &&
- sd->autobonus[i].atk_type&attack_type&BF_RANGEMASK &&
- sd->autobonus[i].atk_type&attack_type&BF_SKILLMASK))
- continue; // one or more trigger conditions were not fulfilled
- pc_exeautobonus(sd,&sd->autobonus[i]);
- }
- }
-
- //Polymorph
- if(sd && sd->bonus.classchange && attack_type&BF_WEAPON &&
- dstmd && !(tstatus->mode&MD_BOSS) &&
- (rnd()%10000 < sd->bonus.classchange))
- {
- struct mob_db *mob;
- int class_;
- skill = 0;
- do {
- do {
- class_ = rnd() % MAX_MOB_DB;
- } while (!mobdb_checkid(class_));
-
- rate = rnd() % 1000000;
- mob = mob_db(class_);
- } while (
- (mob->status.mode&(MD_BOSS|MD_PLANT) || mob->summonper[0] <= rate) &&
- (skill++) < 2000);
- if (skill < 2000)
- mob_class_change(dstmd,class_);
- }
-
- return 0;
-}
-
-int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, uint16 skill_id, unsigned int tick) {
- int skill, skill_lv, i, type, notok;
- struct block_list *tbl;
-
- if( sd == NULL || !skill_id )
- return 0;
-
- for( i = 0; i < ARRAYLENGTH(sd->autospell3) && sd->autospell3[i].flag; i++ ) {
- if( sd->autospell3[i].flag != skill_id )
- continue;
-
- if( sd->autospell3[i].lock )
- continue; // autospell already being executed
-
- skill = (sd->autospell3[i].id > 0) ? sd->autospell3[i].id : -sd->autospell3[i].id;
-
- sd->state.autocast = 1;
- notok = skillnotok(skill, sd);
- sd->state.autocast = 0;
-
- if ( notok )
- continue;
-
- skill_lv = sd->autospell3[i].lv ? sd->autospell3[i].lv : 1;
- if( skill_lv < 0 ) skill_lv = 1 + rnd()%(-skill_lv);
-
- if( sd->autospell3[i].id >= 0 && bl == NULL )
- continue; // No target
- if( rnd()%1000 >= sd->autospell3[i].rate )
- continue;
-
- tbl = (sd->autospell3[i].id < 0) ? &sd->bl : bl;
-
- if( (type = skill_get_casttype(skill)) == CAST_GROUND ) {
- int maxcount = 0;
- if( !(BL_PC&battle_config.skill_reiteration) &&
- skill_get_unit_flag(skill)&UF_NOREITERATION &&
- skill_check_unit_range(&sd->bl,tbl->x,tbl->y,skill,skill_lv)
- ) {
- continue;
- }
- if( BL_PC&battle_config.skill_nofootset &&
- skill_get_unit_flag(skill)&UF_NOFOOTSET &&
- skill_check_unit_range2(&sd->bl,tbl->x,tbl->y,skill,skill_lv)
- ) {
- continue;
- }
- if( BL_PC&battle_config.land_skill_limit &&
- (maxcount = skill_get_maxcount(skill, skill_lv)) > 0
- ) {
- int v;
- for(v=0;v<MAX_SKILLUNITGROUP && sd->ud.skillunit[v] && maxcount;v++) {
- if(sd->ud.skillunit[v]->skill_id == skill)
- maxcount--;
- }
- if( maxcount == 0 ) {
- continue;
- }
- }
- }
- if( battle_config.autospell_check_range &&
- !battle_check_range(&sd->bl, tbl, skill_get_range2(&sd->bl, skill,skill_lv) + (skill == RG_CLOSECONFINE?0:1)) )
- continue;
-
- sd->state.autocast = 1;
- sd->autospell3[i].lock = true;
- skill_consume_requirement(sd,skill,skill_lv,1);
- switch( type )
- {
- case CAST_GROUND: skill_castend_pos2(&sd->bl, tbl->x, tbl->y, skill, skill_lv, tick, 0); break;
- case CAST_NODAMAGE: skill_castend_nodamage_id(&sd->bl, tbl, skill, skill_lv, tick, 0); break;
- case CAST_DAMAGE: skill_castend_damage_id(&sd->bl, tbl, skill, skill_lv, tick, 0); break;
- }
- sd->autospell3[i].lock = false;
- sd->state.autocast = 0;
- }
-
- if( sd && sd->autobonus3[0].rate )
- {
- for( i = 0; i < ARRAYLENGTH(sd->autobonus3); i++ )
- {
- if( rnd()%1000 >= sd->autobonus3[i].rate )
- continue;
- if( sd->autobonus3[i].active != INVALID_TIMER )
- continue;
- if( sd->autobonus3[i].atk_type != skill_id )
- continue;
- pc_exeautobonus(sd,&sd->autobonus3[i]);
- }
- }
-
- return 1;
-}
-
-/* Splitted off from skill_additional_effect, which is never called when the
- * attack skill kills the enemy. Place in this function counter status effects
- * when using skills (eg: Asura's sp regen penalty, or counter-status effects
- * from cards) that will take effect on the source, not the target. [Skotlex]
- * Note: Currently this function only applies to Extremity Fist and BF_WEAPON
- * type of skills, so not every instance of skill_additional_effect needs a call
- * to this one.
- */
-int skill_counter_additional_effect (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, unsigned int tick)
-{
- int rate;
- struct map_session_data *sd=NULL;
- struct map_session_data *dstsd=NULL;
-
- nullpo_ret(src);
- nullpo_ret(bl);
-
- if(skill_id > 0 && !skill_lv) return 0; // don't forget auto attacks! - celest
-
- sd = BL_CAST(BL_PC, src);
- dstsd = BL_CAST(BL_PC, bl);
-
- if(dstsd && attack_type&BF_WEAPON)
- { //Counter effects.
- enum sc_type type;
- int i, time;
- for(i=0; i < ARRAYLENGTH(dstsd->addeff2) && dstsd->addeff2[i].flag; i++)
- {
- rate = dstsd->addeff2[i].rate;
- if (attack_type&BF_LONG)
- rate+=dstsd->addeff2[i].arrow_rate;
- if (!rate) continue;
-
- if ((dstsd->addeff2[i].flag&(ATF_LONG|ATF_SHORT)) != (ATF_LONG|ATF_SHORT))
- { //Trigger has range consideration.
- if((dstsd->addeff2[i].flag&ATF_LONG && !(attack_type&BF_LONG)) ||
- (dstsd->addeff2[i].flag&ATF_SHORT && !(attack_type&BF_SHORT)))
- continue; //Range Failed.
- }
- type = dstsd->addeff2[i].id;
- time = skill_get_time2(status_sc2skill(type),7);
-
- if (dstsd->addeff2[i].flag&ATF_TARGET)
- status_change_start(src,type,rate,7,0,0,0,time,0);
-
- if (dstsd->addeff2[i].flag&ATF_SELF && !status_isdead(bl))
- status_change_start(bl,type,rate,7,0,0,0,time,0);
- }
- }
-
- switch(skill_id){
- case MO_EXTREMITYFIST:
- sc_start(src,SC_EXTREMITYFIST,100,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
- case GS_FULLBUSTER:
- sc_start(src,SC_BLIND,2*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
- case HFLI_SBR44: //[orn]
- case HVAN_EXPLOSION:
- if(src->type == BL_HOM){
- TBL_HOM *hd = (TBL_HOM*)src;
- hd->homunculus.intimacy = 200;
- if (hd->master)
- clif_send_homdata(hd->master,SP_INTIMATE,hd->homunculus.intimacy/100);
- }
- break;
- case CR_GRANDCROSS:
- case NPC_GRANDDARKNESS:
- attack_type |= BF_WEAPON;
- break;
- }
-
- if(sd && (sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR &&
- rnd()%10000 < battle_config.sg_miracle_skill_ratio) //SG_MIRACLE [Komurka]
- sc_start(src,SC_MIRACLE,100,1,battle_config.sg_miracle_skill_duration);
-
- if(sd && skill_id && attack_type&BF_MAGIC && status_isdead(bl) &&
- !(skill_get_inf(skill_id)&(INF_GROUND_SKILL|INF_SELF_SKILL)) &&
- (rate=pc_checkskill(sd,HW_SOULDRAIN))>0
- ){ //Soul Drain should only work on targetted spells [Skotlex]
- if (pc_issit(sd)) pc_setstand(sd); //Character stuck in attacking animation while 'sitting' fix. [Skotlex]
- clif_skill_nodamage(src,bl,HW_SOULDRAIN,rate,1);
- status_heal(src, 0, status_get_lv(bl)*(95+15*rate)/100, 2);
- }
-
- if( sd && status_isdead(bl) ) {
- int sp = 0, hp = 0;
- if( attack_type&BF_WEAPON ) {
- sp += sd->bonus.sp_gain_value;
- sp += sd->sp_gain_race[status_get_race(bl)];
- sp += sd->sp_gain_race[is_boss(bl)?RC_BOSS:RC_NONBOSS];
- hp += sd->bonus.hp_gain_value;
- }
- if( attack_type&BF_MAGIC ) {
- sp += sd->bonus.magic_sp_gain_value;
- hp += sd->bonus.magic_hp_gain_value;
- if( skill_id == WZ_WATERBALL ) {//(bugreport:5303)
- struct status_change *sc = NULL;
- if( ( sc = status_get_sc(src) ) ) {
- if(sc->data[SC_SPIRIT] &&
- sc->data[SC_SPIRIT]->val2 == SL_WIZARD &&
- sc->data[SC_SPIRIT]->val3 == WZ_WATERBALL)
- sc->data[SC_SPIRIT]->val3 = 0; //Clear bounced spell check.
- }
- }
- }
- if( hp || sp ) { // updated to force healing to allow healing through berserk
- status_heal(src, hp, sp, battle_config.show_hp_sp_gain ? 3 : 1);
- }
- }
-
- // Trigger counter-spells to retaliate against damage causing skills.
- if(dstsd && !status_isdead(bl) && dstsd->autospell2[0].id &&
- !(skill_id && skill_get_nk(skill_id)&NK_NO_DAMAGE))
- {
- struct block_list *tbl;
- struct unit_data *ud;
- int i, skill_id, skill_lv, rate, type, notok;
-
- for (i = 0; i < ARRAYLENGTH(dstsd->autospell2) && dstsd->autospell2[i].id; i++) {
-
- if(!(dstsd->autospell2[i].flag&attack_type&BF_WEAPONMASK &&
- dstsd->autospell2[i].flag&attack_type&BF_RANGEMASK &&
- dstsd->autospell2[i].flag&attack_type&BF_SKILLMASK))
- continue; // one or more trigger conditions were not fulfilled
-
- skill_id = (dstsd->autospell2[i].id > 0) ? dstsd->autospell2[i].id : -dstsd->autospell2[i].id;
- skill_lv = dstsd->autospell2[i].lv?dstsd->autospell2[i].lv:1;
- if (skill_lv < 0) skill_lv = 1+rnd()%(-skill_lv);
-
- rate = dstsd->autospell2[i].rate;
- if (attack_type&BF_LONG)
- rate>>=1;
-
- dstsd->state.autocast = 1;
- notok = skillnotok(skill_id, dstsd);
- dstsd->state.autocast = 0;
-
- if ( notok )
- continue;
-
- if (rnd()%1000 >= rate)
- continue;
-
- tbl = (dstsd->autospell2[i].id < 0) ? bl : src;
-
- if( (type = skill_get_casttype(skill_id)) == CAST_GROUND ) {
- int maxcount = 0;
- if( !(BL_PC&battle_config.skill_reiteration) &&
- skill_get_unit_flag(skill_id)&UF_NOREITERATION &&
- skill_check_unit_range(bl,tbl->x,tbl->y,skill_id,skill_lv)
- ) {
- continue;
- }
- if( BL_PC&battle_config.skill_nofootset &&
- skill_get_unit_flag(skill_id)&UF_NOFOOTSET &&
- skill_check_unit_range2(bl,tbl->x,tbl->y,skill_id,skill_lv)
- ) {
- continue;
- }
- if( BL_PC&battle_config.land_skill_limit &&
- (maxcount = skill_get_maxcount(skill_id, skill_lv)) > 0
- ) {
- int v;
- for(v=0;v<MAX_SKILLUNITGROUP && dstsd->ud.skillunit[v] && maxcount;v++) {
- if(dstsd->ud.skillunit[v]->skill_id == skill_id)
- maxcount--;
- }
- if( maxcount == 0 ) {
- continue;
- }
- }
- }
-
- if( !battle_check_range(src, tbl, skill_get_range2(src, skill_id,skill_lv) + (skill_id == RG_CLOSECONFINE?0:1)) && battle_config.autospell_check_range )
- continue;
-
- dstsd->state.autocast = 1;
- skill_consume_requirement(dstsd,skill_id,skill_lv,1);
- switch (type) {
- case CAST_GROUND:
- skill_castend_pos2(bl, tbl->x, tbl->y, skill_id, skill_lv, tick, 0);
- break;
- case CAST_NODAMAGE:
- skill_castend_nodamage_id(bl, tbl, skill_id, skill_lv, tick, 0);
- break;
- case CAST_DAMAGE:
- skill_castend_damage_id(bl, tbl, skill_id, skill_lv, tick, 0);
- break;
- }
- dstsd->state.autocast = 0;
- //Set canact delay. [Skotlex]
- ud = unit_bl2ud(bl);
- if (ud) {
- rate = skill_delayfix(bl, skill_id, skill_lv);
- if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){
- ud->canact_tick = tick+rate;
- if ( battle_config.display_status_timers && dstsd )
- clif_status_change(bl, SI_ACTIONDELAY, 1, rate, 0, 0, 0);
- }
- }
- }
- }
-
- //Autobonus when attacked
- if( dstsd && !status_isdead(bl) && dstsd->autobonus2[0].rate && !(skill_id && skill_get_nk(skill_id)&NK_NO_DAMAGE) )
- {
- int i;
- for( i = 0; i < ARRAYLENGTH(dstsd->autobonus2); i++ )
- {
- if( rnd()%1000 >= dstsd->autobonus2[i].rate )
- continue;
- if( dstsd->autobonus2[i].active != INVALID_TIMER )
- continue;
- if(!(dstsd->autobonus2[i].atk_type&attack_type&BF_WEAPONMASK &&
- dstsd->autobonus2[i].atk_type&attack_type&BF_RANGEMASK &&
- dstsd->autobonus2[i].atk_type&attack_type&BF_SKILLMASK))
- continue; // one or more trigger conditions were not fulfilled
- pc_exeautobonus(dstsd,&dstsd->autobonus2[i]);
- }
- }
-
- return 0;
-}
-/*=========================================================================
- Breaks equipment. On-non players causes the corresponding strip effect.
- - rate goes from 0 to 10000 (100.00%)
- - flag is a BCT_ flag to indicate which type of adjustment should be used
- (BCT_ENEMY/BCT_PARTY/BCT_SELF) are the valid values.
---------------------------------------------------------------------------*/
-int skill_break_equip (struct block_list *bl, unsigned short where, int rate, int flag)
-{
- const int where_list[4] = {EQP_WEAPON, EQP_ARMOR, EQP_SHIELD, EQP_HELM};
- const enum sc_type scatk[4] = {SC_STRIPWEAPON, SC_STRIPARMOR, SC_STRIPSHIELD, SC_STRIPHELM};
- const enum sc_type scdef[4] = {SC_CP_WEAPON, SC_CP_ARMOR, SC_CP_SHIELD, SC_CP_HELM};
- struct status_change *sc = status_get_sc(bl);
- int i,j;
- TBL_PC *sd;
- sd = BL_CAST(BL_PC, bl);
- if (sc && !sc->count)
- sc = NULL;
-
- if (sd) {
- if (sd->bonus.unbreakable_equip)
- where &= ~sd->bonus.unbreakable_equip;
- if (sd->bonus.unbreakable)
- rate -= rate*sd->bonus.unbreakable/100;
- if (where&EQP_WEAPON) {
- switch (sd->status.weapon) {
- case W_FIST: //Bare fists should not break :P
- case W_1HAXE:
- case W_2HAXE:
- case W_MACE: // Axes and Maces can't be broken [DracoRPG]
- case W_2HMACE:
- case W_STAFF:
- case W_2HSTAFF:
- case W_BOOK: //Rods and Books can't be broken [Skotlex]
- case W_HUUMA:
- where &= ~EQP_WEAPON;
- }
- }
- }
- if (flag&BCT_ENEMY) {
- if (battle_config.equip_skill_break_rate != 100)
- rate = rate*battle_config.equip_skill_break_rate/100;
- } else if (flag&(BCT_PARTY|BCT_SELF)) {
- if (battle_config.equip_self_break_rate != 100)
- rate = rate*battle_config.equip_self_break_rate/100;
- }
-
- for (i = 0; i < 4; i++) {
- if (where&where_list[i]) {
- if (sc && sc->count && sc->data[scdef[i]])
- where&=~where_list[i];
- else if (rnd()%10000 >= rate)
- where&=~where_list[i];
- else if (!sd && !(status_get_mode(bl)&MD_BOSS)) //Cause Strip effect.
- sc_start(bl,scatk[i],100,0,skill_get_time(status_sc2skill(scatk[i]),1));
- }
- }
- if (!where) //Nothing to break.
- return 0;
- if (sd) {
- for (i = 0; i < EQI_MAX; i++) {
- j = sd->equip_index[i];
- if (j < 0 || sd->status.inventory[j].attribute == 1 || !sd->inventory_data[j])
- continue;
-
- switch(i) {
- case EQI_HEAD_TOP: //Upper Head
- flag = (where&EQP_HELM);
- break;
- case EQI_ARMOR: //Body
- flag = (where&EQP_ARMOR);
- break;
- case EQI_HAND_R: //Left/Right hands
- case EQI_HAND_L:
- flag = (
- (where&EQP_WEAPON && sd->inventory_data[j]->type == IT_WEAPON) ||
- (where&EQP_SHIELD && sd->inventory_data[j]->type == IT_ARMOR));
- break;
- case EQI_SHOES:
- flag = (where&EQP_SHOES);
- break;
- case EQI_GARMENT:
- flag = (where&EQP_GARMENT);
- break;
- default:
- continue;
- }
- if (flag) {
- sd->status.inventory[j].attribute = 1;
- pc_unequipitem(sd, j, 3);
- }
- }
- clif_equiplist(sd);
- }
-
- return where; //Return list of pieces broken.
-}
-
-int skill_strip_equip(struct block_list *bl, unsigned short where, int rate, int lv, int time)
-{
- struct status_change *sc;
- const int pos[5] = {EQP_WEAPON, EQP_SHIELD, EQP_ARMOR, EQP_HELM, EQP_ACC};
- const enum sc_type sc_atk[5] = {SC_STRIPWEAPON, SC_STRIPSHIELD, SC_STRIPARMOR, SC_STRIPHELM, SC__STRIPACCESSORY};
- const enum sc_type sc_def[5] = {SC_CP_WEAPON, SC_CP_SHIELD, SC_CP_ARMOR, SC_CP_HELM, 0};
- int i;
-
- if (rnd()%100 >= rate)
- return 0;
-
- sc = status_get_sc(bl);
- if (!sc || sc->option&OPTION_MADOGEAR ) //Mado Gear cannot be divested [Ind]
- return 0;
-
- for (i = 0; i < ARRAYLENGTH(pos); i++) {
- if (where&pos[i] && sc->data[sc_def[i]])
- where&=~pos[i];
- }
- if (!where) return 0;
-
- for (i = 0; i < ARRAYLENGTH(pos); i++) {
- if (where&pos[i] && !sc_start(bl, sc_atk[i], 100, lv, time))
- where&=~pos[i];
- }
- return where?1:0;
-}
-//Early declaration
-static int skill_area_temp[8];
-/*=========================================================================
- Used to knock back players, monsters, traps, etc
- - 'count' is the number of squares to knock back
- - 'direction' indicates the way OPPOSITE to the knockback direction (or -1 for default behavior)
- - if 'flag&0x1', position update packets must not be sent.
- - if 'flag&0x2', skill blown ignores players' special_state.no_knockback
- -------------------------------------------------------------------------*/
-int skill_blown(struct block_list* src, struct block_list* target, int count, int8 dir, int flag)
-{
- int dx = 0, dy = 0;
- struct skill_unit* su = NULL;
-
- nullpo_ret(src);
-
- if (src != target && (map_flag_gvg(target->m) || map[target->m].flag.battleground))
- return 0; //No knocking back in WoE
- if (count == 0)
- return 0; //Actual knockback distance is 0.
-
- switch (target->type) {
- case BL_MOB: {
- struct mob_data* md = BL_CAST(BL_MOB, target);
- if( md->class_ == MOBID_EMPERIUM )
- return 0;
- if(src != target && is_boss(target)) //Bosses can't be knocked-back
- return 0;
- }
- break;
- case BL_PC: {
- struct map_session_data *sd = BL_CAST(BL_PC, target);
- if( sd->sc.data[SC_BASILICA] && sd->sc.data[SC_BASILICA]->val4 == sd->bl.id && !is_boss(src))
- return 0; // Basilica caster can't be knocked-back by normal monsters.
- if( !(flag&0x2) && src != target && sd->special_state.no_knockback )
- return 0;
- }
- break;
- case BL_SKILL:
- su = (struct skill_unit *)target;
- if( su && su->group && su->group->unit_id == UNT_ANKLESNARE )
- return 0; // ankle snare cannot be knocked back
- break;
- }
-
- if (dir == -1) // <optimized>: do the computation here instead of outside
- dir = map_calc_dir(target, src->x, src->y); // direction from src to target, reversed
-
- if (dir >= 0 && dir < 8)
- { // take the reversed 'direction' and reverse it
- dx = -dirx[dir];
- dy = -diry[dir];
- }
-
- return unit_blown(target, dx, dy, count, flag); // send over the proper flag
-}
-
-
-//Checks if 'bl' should reflect back a spell cast by 'src'.
-//type is the type of magic attack: 0: indirect (aoe), 1: direct (targetted)
-static int skill_magic_reflect(struct block_list* src, struct block_list* bl, int type)
-{
- struct status_change *sc = status_get_sc(bl);
- struct map_session_data* sd = BL_CAST(BL_PC, bl);
-
- if( sc && sc->data[SC_KYOMU] ) // Nullify reflecting ability
- return 0;
-
- // item-based reflection
- if( sd && sd->bonus.magic_damage_return && type && rnd()%100 < sd->bonus.magic_damage_return )
- return 1;
-
- if( is_boss(src) )
- return 0;
-
- // status-based reflection
- if( !sc || sc->count == 0 )
- return 0;
-
- if( sc->data[SC_MAGICMIRROR] && rnd()%100 < sc->data[SC_MAGICMIRROR]->val2 )
- return 1;
-
- if( sc->data[SC_KAITE] && (src->type == BL_PC || status_get_lv(src) <= 80) )
- {// Kaite only works against non-players if they are low-level.
- clif_specialeffect(bl, 438, AREA);
- if( --sc->data[SC_KAITE]->val2 <= 0 )
- status_change_end(bl, SC_KAITE, INVALID_TIMER);
- return 2;
- }
-
- return 0;
-}
-
-/*
- * =========================================================================
- * Does a skill attack with the given properties.
- * src is the master behind the attack (player/mob/pet)
- * dsrc is the actual originator of the damage, can be the same as src, or a BL_SKILL
- * bl is the target to be attacked.
- * flag can hold a bunch of information:
- * flag&0xFFF is passed to the underlying battle_calc_attack for processing
- * (usually holds number of targets, or just 1 for simple splash attacks)
- * flag&0x1000 is used to tag that this is a splash-attack (so the damage
- * packet shouldn't display a skill animation)
- * flag&0x2000 is used to signal that the skill_lv should be passed as -1 to the
- * client (causes player characters to not scream skill name)
- *-------------------------------------------------------------------------*/
-int skill_attack (int attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
-{
- struct Damage dmg;
- struct status_data *sstatus, *tstatus;
- struct status_change *sc;
- struct map_session_data *sd, *tsd;
- int type,damage,rdamage=0;
- int8 rmdamage=0;//magic reflected
-
- if(skill_id > 0 && !skill_lv) return 0;
-
- nullpo_ret(src); //Source is the master behind the attack (player/mob/pet)
- nullpo_ret(dsrc); //dsrc is the actual originator of the damage, can be the same as src, or a skill casted by src.
- nullpo_ret(bl); //Target to be attacked.
-
- if (src != dsrc) {
- //When caster is not the src of attack, this is a ground skill, and as such, do the relevant target checking. [Skotlex]
- if (!status_check_skilluse(battle_config.skill_caster_check?src:NULL, bl, skill_id, 2))
- return 0;
- } else if ((flag&SD_ANIMATION) && skill_get_nk(skill_id)&NK_SPLASH) {
- //Note that splash attacks often only check versus the targetted mob, those around the splash area normally don't get checked for being hidden/cloaked/etc. [Skotlex]
- if (!status_check_skilluse(src, bl, skill_id, 2))
- return 0;
- }
-
- sd = BL_CAST(BL_PC, src);
- tsd = BL_CAST(BL_PC, bl);
-
- sstatus = status_get_status_data(src);
- tstatus = status_get_status_data(bl);
- sc= status_get_sc(bl);
- if (sc && !sc->count) sc = NULL; //Don't need it.
-
- // Is this check really needed? FrostNova won't hurt you if you step right where the caster is?
- if(skill_id == WZ_FROSTNOVA && dsrc->x == bl->x && dsrc->y == bl->y)
- return 0;
- //Trick Dead protects you from damage, but not from buffs and the like, hence it's placed here.
- if (sc && sc->data[SC_TRICKDEAD] && !(sstatus->mode&MD_BOSS))
- return 0;
-
- dmg = battle_calc_attack(attack_type,src,bl,skill_id,skill_lv,flag&0xFFF);
-
- //Skotlex: Adjusted to the new system
- if(src->type==BL_PET)
- { // [Valaris]
- struct pet_data *pd = (TBL_PET*)src;
- if (pd->a_skill && pd->a_skill->div_ && pd->a_skill->id == skill_id)
- {
- int element = skill_get_ele(skill_id, skill_lv);
- /*if (skill_id == -1) Does it ever worked?
- element = sstatus->rhw.ele;*/
- if (element != ELE_NEUTRAL || !(battle_config.attack_attr_none&BL_PET))
- dmg.damage=battle_attr_fix(src, bl, skill_lv, element, tstatus->def_ele, tstatus->ele_lv);
- else
- dmg.damage= skill_lv;
- dmg.damage2=0;
- dmg.div_= pd->a_skill->div_;
- }
- }
-
- if( dmg.flag&BF_MAGIC && ( skill_id != NPC_EARTHQUAKE || (battle_config.eq_single_target_reflectable && (flag&0xFFF) == 1) ) )
- { // Earthquake on multiple targets is not counted as a target skill. [Inkfish]
- if( (dmg.damage || dmg.damage2) && (type = skill_magic_reflect(src, bl, src==dsrc)) )
- { //Magic reflection, switch caster/target
- struct block_list *tbl = bl;
- rmdamage = 1;
- bl = src;
- src = tbl;
- sd = BL_CAST(BL_PC, src);
- tsd = BL_CAST(BL_PC, bl);
- sc = status_get_sc(bl);
- if (sc && !sc->count)
- sc = NULL; //Don't need it.
- /* bugreport:2564 flag&2 disables double casting trigger */
- flag |= 2;
-
- //Spirit of Wizard blocks Kaite's reflection
- if( type == 2 && sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_WIZARD )
- { //Consume one Fragment per hit of the casted skill? [Skotlex]
- type = tsd?pc_search_inventory (tsd, 7321):0;
- if (type >= 0) {
- if ( tsd ) pc_delitem(tsd, type, 1, 0, 1, LOG_TYPE_CONSUME);
- dmg.damage = dmg.damage2 = 0;
- dmg.dmg_lv = ATK_MISS;
- sc->data[SC_SPIRIT]->val3 = skill_id;
- sc->data[SC_SPIRIT]->val4 = dsrc->id;
- }
- }
-
- /**
- * Official Magic Reflection Behavior : damage reflected depends on gears caster wears, not target
- **/
- #if MAGIC_REFLECTION_TYPE
- if( dmg.dmg_lv != ATK_MISS )//Wiz SL cancelled and consumed fragment
- dmg = battle_calc_attack(BF_MAGIC,bl,bl,skill_id,skill_lv,flag&0xFFF);
- #endif
- }
- if(sc && sc->data[SC_MAGICROD] && src == dsrc) {
- int sp = skill_get_sp(skill_id,skill_lv);
- dmg.damage = dmg.damage2 = 0;
- dmg.dmg_lv = ATK_MISS; //This will prevent skill additional effect from taking effect. [Skotlex]
- sp = sp * sc->data[SC_MAGICROD]->val2 / 100;
- if(skill_id == WZ_WATERBALL && skill_lv > 1)
- sp = sp/((skill_lv|1)*(skill_lv|1)); //Estimate SP cost of a single water-ball
- status_heal(bl, 0, sp, 2);
- }
- }
-
- damage = dmg.damage + dmg.damage2;
-
- if( (skill_id == AL_INCAGI || skill_id == AL_BLESSING ||
- skill_id == CASH_BLESSING || skill_id == CASH_INCAGI ||
- skill_id == MER_INCAGI || skill_id == MER_BLESSING) && tsd->sc.data[SC_CHANGEUNDEAD] )
- damage = 1;
-
- if( damage > 0 && (( dmg.flag&BF_WEAPON && src != bl && ( src == dsrc || ( dsrc->type == BL_SKILL && ( skill_id == SG_SUN_WARM || skill_id == SG_MOON_WARM || skill_id == SG_STAR_WARM ) ) ))
- || (sc && sc->data[SC_REFLECTDAMAGE])) )
- rdamage = battle_calc_return_damage(bl,src, &damage, dmg.flag, skill_id);
-
- if( damage && sc && sc->data[SC_GENSOU] && dmg.flag&BF_MAGIC ){
- struct block_list *nbl = NULL;
- nbl = battle_getenemyarea(bl,bl->x,bl->y,2,BL_CHAR,bl->id);
- if( nbl ){ // Only one target is chosen.
- damage = damage / 2; // Deflect half of the damage to a target nearby
- clif_skill_damage(bl, nbl, tick, status_get_amotion(src), 0, status_fix_damage(bl,nbl,damage,0), dmg.div_, OB_OBOROGENSOU_TRANSITION_ATK, -1, 6);
- }
- }
-
- //Skill hit type
- type=(skill_id==0)?5:skill_get_hit(skill_id);
-
- if(damage < dmg.div_
- //Only skills that knockback even when they miss. [Skotlex]
- && skill_id != CH_PALMSTRIKE)
- dmg.blewcount = 0;
-
- if(skill_id == CR_GRANDCROSS||skill_id == NPC_GRANDDARKNESS) {
- if(battle_config.gx_disptype) dsrc = src;
- if(src == bl) type = 4;
- else flag|=SD_ANIMATION;
- }
- if(skill_id == NJ_TATAMIGAESHI) {
- dsrc = src; //For correct knockback.
- flag|=SD_ANIMATION;
- }
-
- if(sd) {
- int flag = 0; //Used to signal if this skill can be combo'ed later on.
- struct status_change_entry *sce;
- if ((sce = sd->sc.data[SC_COMBO])) {//End combo state after skill is invoked. [Skotlex]
- switch (skill_id) {
- case TK_TURNKICK:
- case TK_STORMKICK:
- case TK_DOWNKICK:
- case TK_COUNTER:
- if (pc_famerank(sd->status.char_id,MAPID_TAEKWON)) {//Extend combo time.
- sce->val1 = skill_id; //Update combo-skill
- sce->val3 = skill_id;
- if( sce->timer != INVALID_TIMER )
- delete_timer(sce->timer, status_change_timer);
- sce->timer = add_timer(tick+sce->val4, status_change_timer, src->id, SC_COMBO);
- break;
- }
- unit_cancel_combo(src); // Cancel combo wait
- break;
- default:
- if( src == dsrc ) // Ground skills are exceptions. [Inkfish]
- status_change_end(src, SC_COMBO, INVALID_TIMER);
- }
- }
- switch(skill_id) {
- case MO_TRIPLEATTACK:
- if (pc_checkskill(sd, MO_CHAINCOMBO) > 0 || pc_checkskill(sd, SR_DRAGONCOMBO) > 0)
- flag=1;
- break;
- case MO_CHAINCOMBO:
- if(pc_checkskill(sd, MO_COMBOFINISH) > 0 && sd->spiritball > 0)
- flag=1;
- break;
- case MO_COMBOFINISH:
- if (sd->status.party_id>0) //bonus from SG_FRIEND [Komurka]
- party_skill_check(sd, sd->status.party_id, MO_COMBOFINISH, skill_lv);
- if (pc_checkskill(sd, CH_TIGERFIST) > 0 && sd->spiritball > 0)
- flag=1;
- case CH_TIGERFIST:
- if (!flag && pc_checkskill(sd, CH_CHAINCRUSH) > 0 && sd->spiritball > 1)
- flag=1;
- case CH_CHAINCRUSH:
- if (!flag && pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball > 0 && sd->sc.data[SC_EXPLOSIONSPIRITS])
- flag=1;
- break;
- case AC_DOUBLE:
- if( (tstatus->race == RC_BRUTE || tstatus->race == RC_INSECT) && pc_checkskill(sd, HT_POWER))
- {
- //TODO: This code was taken from Triple Blows, is this even how it should be? [Skotlex]
- sc_start2(src,SC_COMBO,100,HT_POWER,bl->id,2000);
- clif_combo_delay(src,2000);
- }
- break;
- case TK_COUNTER:
- { //bonus from SG_FRIEND [Komurka]
- int level;
- if(sd->status.party_id>0 && (level = pc_checkskill(sd,SG_FRIEND)))
- party_skill_check(sd, sd->status.party_id, TK_COUNTER,level);
- }
- break;
- case SL_STIN:
- case SL_STUN:
- if (skill_lv >= 7 && !sd->sc.data[SC_SMA])
- sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA, skill_lv));
- break;
- case GS_FULLBUSTER:
- //Can't attack nor use items until skill's delay expires. [Skotlex]
- sd->ud.attackabletime = sd->canuseitem_tick = sd->ud.canact_tick;
- break;
- case SR_DRAGONCOMBO:
- if( pc_checkskill(sd, SR_FALLENEMPIRE) > 0 )
- flag = 1;
- break;
- case SR_FALLENEMPIRE:
- if( pc_checkskill(sd, SR_TIGERCANNON) > 0 || pc_checkskill(sd, SR_GATEOFHELL) > 0 )
- flag = 1;
- break;
- } //Switch End
- if (flag) { //Possible to chain
- flag = DIFF_TICK(sd->ud.canact_tick, tick);
- if (flag < 1) flag = 1;
- sc_start2(src,SC_COMBO,100,skill_id,bl->id,flag);
- clif_combo_delay(src, flag);
- }
- }
-
- //Display damage.
- switch( skill_id )
- {
- case PA_GOSPEL: //Should look like Holy Cross [Skotlex]
- dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, CR_HOLYCROSS, -1, 5);
- break;
- //Skills that need be passed as a normal attack for the client to display correctly.
- case HVAN_EXPLOSION:
- case NPC_SELFDESTRUCTION:
- if(src->type==BL_PC)
- dmg.blewcount = 10;
- dmg.amotion = 0; //Disable delay or attack will do no damage since source is dead by the time it takes effect. [Skotlex]
- // fall through
- case KN_AUTOCOUNTER:
- case NPC_CRITICALSLASH:
- case TF_DOUBLE:
- case GS_CHAINACTION:
- dmg.dmotion = clif_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,dmg.type,dmg.damage2);
- break;
-
- case AS_SPLASHER:
- if( flag&SD_ANIMATION ) // the surrounding targets
- dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -1, 5); // needs -1 as skill level
- else // the central target doesn't display an animation
- dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -2, 5); // needs -2(!) as skill level
- break;
- case WL_HELLINFERNO:
- case SR_EARTHSHAKER:
- dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,skill_id,-2,6);
- break;
- case WL_SOULEXPANSION:
- case WL_COMET:
- case KO_MUCHANAGE:
- case NJ_HUUMA:
- dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,skill_lv,8);
- break;
- case WL_CHAINLIGHTNING_ATK:
- dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,WL_CHAINLIGHTNING,-2,6);
- break;
- case LG_OVERBRAND_BRANDISH:
- case LG_OVERBRAND_PLUSATK:
- case EL_FIRE_BOMB:
- case EL_FIRE_BOMB_ATK:
- case EL_FIRE_WAVE:
- case EL_FIRE_WAVE_ATK:
- case EL_FIRE_MANTLE:
- case EL_CIRCLE_OF_FIRE:
- case EL_FIRE_ARROW:
- case EL_ICE_NEEDLE:
- case EL_WATER_SCREW:
- case EL_WATER_SCREW_ATK:
- case EL_WIND_SLASH:
- case EL_TIDAL_WEAPON:
- case EL_ROCK_CRUSHER:
- case EL_ROCK_CRUSHER_ATK:
- case EL_HURRICANE:
- case EL_HURRICANE_ATK:
- case KO_BAKURETSU:
- case GN_CRAZYWEED_ATK:
- dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,5);
- break;
- case GN_SLINGITEM_RANGEMELEEATK:
- dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,GN_SLINGITEM,-2,6);
- break;
- case EL_STONE_RAIN:
- dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,(flag&1)?8:5);
- break;
- case WM_SEVERE_RAINSTORM_MELEE:
- dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,WM_SEVERE_RAINSTORM,skill_lv,5);
- break;
- case WM_REVERBERATION_MELEE:
- case WM_REVERBERATION_MAGIC:
- dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,WM_REVERBERATION,-2,6);
- break;
- case HT_CLAYMORETRAP:
- case HT_BLASTMINE:
- case HT_FLASHER:
- case HT_FREEZINGTRAP:
- case RA_CLUSTERBOMB:
- case RA_FIRINGTRAP:
- case RA_ICEBOUNDTRAP:
- dmg.dmotion = clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id,flag&SD_LEVEL?-1:skill_lv, 5);
- if( dsrc != src ) // avoid damage display redundancy
- break;
- case HT_LANDMINE:
- dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -1, type);
- break;
- case WZ_SIGHTBLASTER:
- dmg.dmotion = clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, 5);
- break;
- case AB_DUPLELIGHT_MELEE:
- case AB_DUPLELIGHT_MAGIC:
- dmg.amotion = 300;/* makes the damage value not overlap with previous damage (when displayed by the client) */
- default:
- if( flag&SD_ANIMATION && dmg.div_ < 2 ) //Disabling skill animation doesn't works on multi-hit.
- type = 5;
- if( bl->type == BL_SKILL ){
- TBL_SKILL *su = (TBL_SKILL*)bl;
- if( su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP )// show damage on trap targets
- clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, 5);
- }
- dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, type);
- break;
- }
-
- map_freeblock_lock();
-
- if(damage > 0 && dmg.flag&BF_SKILL && tsd
- && pc_checkskill(tsd,RG_PLAGIARISM)
- && (!sc || !sc->data[SC_PRESERVE])
- && damage < tsd->battle_status.hp)
- { //Updated to not be able to copy skills if the blow will kill you. [Skotlex]
- int copy_skill = skill_id;
- /**
- * Copy Referal: dummy skills should point to their source upon copying
- **/
- switch( skill_id ) {
- case AB_DUPLELIGHT_MELEE:
- case AB_DUPLELIGHT_MAGIC:
- copy_skill = AB_DUPLELIGHT;
- break;
- case WL_CHAINLIGHTNING_ATK:
- copy_skill = WL_CHAINLIGHTNING;
- break;
- case WM_REVERBERATION_MELEE:
- case WM_REVERBERATION_MAGIC:
- copy_skill = WM_REVERBERATION;
- break;
- case WM_SEVERE_RAINSTORM_MELEE:
- copy_skill = WM_SEVERE_RAINSTORM;
- break;
- case GN_CRAZYWEED_ATK:
- copy_skill = GN_CRAZYWEED;
- break;
- case GN_HELLS_PLANT_ATK:
- copy_skill = GN_HELLS_PLANT;
- break;
- case LG_OVERBRAND_BRANDISH:
- case LG_OVERBRAND_PLUSATK:
- copy_skill = LG_OVERBRAND;
- break;
- }
-
- if ((tsd->status.skill[copy_skill].id == 0 || tsd->status.skill[copy_skill].flag == SKILL_FLAG_PLAGIARIZED) &&
- can_copy(tsd,copy_skill,bl)) // Split all the check into their own function [Aru]
- {
- int lv;
- if( sc && sc->data[SC__REPRODUCE] && (lv = sc->data[SC__REPRODUCE]->val1) ) {
- //Level dependent and limitation.
- lv = min(lv,skill_get_max(copy_skill));
- if( tsd->reproduceskill_id && tsd->status.skill[tsd->reproduceskill_id].flag == SKILL_FLAG_PLAGIARIZED ) {
- tsd->status.skill[tsd->reproduceskill_id].id = 0;
- tsd->status.skill[tsd->reproduceskill_id].lv = 0;
- tsd->status.skill[tsd->reproduceskill_id].flag = 0;
- clif_deleteskill(tsd,tsd->reproduceskill_id);
- }
-
- tsd->reproduceskill_id = copy_skill;
- pc_setglobalreg(tsd, "REPRODUCE_SKILL", copy_skill);
- pc_setglobalreg(tsd, "REPRODUCE_SKILL_LV", lv);
-
- tsd->status.skill[copy_skill].id = copy_skill;
- tsd->status.skill[copy_skill].lv = lv;
- tsd->status.skill[copy_skill].flag = SKILL_FLAG_PLAGIARIZED;
- clif_addskill(tsd,copy_skill);
- } else {
- lv = skill_lv;
- if (tsd->cloneskill_id && tsd->status.skill[tsd->cloneskill_id].flag == SKILL_FLAG_PLAGIARIZED){
- tsd->status.skill[tsd->cloneskill_id].id = 0;
- tsd->status.skill[tsd->cloneskill_id].lv = 0;
- tsd->status.skill[tsd->cloneskill_id].flag = 0;
- clif_deleteskill(tsd,tsd->cloneskill_id);
- }
-
- if ((type = pc_checkskill(tsd,RG_PLAGIARISM)) < lv)
- lv = type;
-
- tsd->cloneskill_id = copy_skill;
- pc_setglobalreg(tsd, "CLONE_SKILL", copy_skill);
- pc_setglobalreg(tsd, "CLONE_SKILL_LV", lv);
-
- tsd->status.skill[skill_id].id = copy_skill;
- tsd->status.skill[skill_id].lv = lv;
- tsd->status.skill[skill_id].flag = SKILL_FLAG_PLAGIARIZED;
- clif_addskill(tsd,skill_id);
- }
- }
- }
-
- if (dmg.dmg_lv >= ATK_MISS && (type = skill_get_walkdelay(skill_id, skill_lv)) > 0)
- { //Skills with can't walk delay also stop normal attacking for that
- //duration when the attack connects. [Skotlex]
- struct unit_data *ud = unit_bl2ud(src);
- if (ud && DIFF_TICK(ud->attackabletime, tick + type) < 0)
- ud->attackabletime = tick + type;
- }
-
- if( !dmg.amotion )
- { //Instant damage
- if( !sc || (!sc->data[SC_DEVOTION] && skill_id != CR_REFLECTSHIELD) )
- status_fix_damage(src,bl,damage,dmg.dmotion); //Deal damage before knockback to allow stuff like firewall+storm gust combo.
- if( !status_isdead(bl) )
- skill_additional_effect(src,bl,skill_id,skill_lv,dmg.flag,dmg.dmg_lv,tick);
- if( damage > 0 ) //Counter status effects [Skotlex]
- skill_counter_additional_effect(src,bl,skill_id,skill_lv,dmg.flag,tick);
- }
- // Hell Inferno burning status only starts if Fire part hits.
- if( skill_id == WL_HELLINFERNO && dmg.damage > 0 )
- sc_start4(bl,SC_BURNING,55+5*skill_lv,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv));
- // Apply knock back chance in SC_TRIANGLESHOT skill.
- else if( skill_id == SC_TRIANGLESHOT && rnd()%100 > (1 + skill_lv) )
- dmg.blewcount = 0;
-
- //Only knockback if it's still alive, otherwise a "ghost" is left behind. [Skotlex]
- //Reflected spells do not bounce back (bl == dsrc since it only happens for direct skills)
- if (dmg.blewcount > 0 && bl!=dsrc && !status_isdead(bl)) {
- int8 dir = -1; // default
- switch(skill_id) {//direction
- case MG_FIREWALL:
- case PR_SANCTUARY:
- case SC_TRIANGLESHOT:
- case LG_OVERBRAND:
- case SR_KNUCKLEARROW:
- case GN_WALLOFTHORN:
- case EL_FIRE_MANTLE:
- dir = unit_getdir(bl);// backwards
- break;
- // This ensures the storm randomly pushes instead of exactly a cell backwards per official mechanics.
- case WZ_STORMGUST:
- dir = rand()%8;
- break;
- case WL_CRIMSONROCK:
- dir = map_calc_dir(bl,skill_area_temp[4],skill_area_temp[5]);
- break;
-
- }
- //blown-specific handling
- switch( skill_id ) {
- case LG_OVERBRAND:
- if( skill_blown(dsrc,bl,dmg.blewcount,dir,0) ) {
- short dir_x, dir_y;
- dir_x = dirx[(dir+4)%8];
- dir_y = diry[(dir+4)%8];
- if( map_getcell(bl->m, bl->x+dir_x, bl->y+dir_y, CELL_CHKNOPASS) != 0 )
- skill_addtimerskill(src, tick + status_get_amotion(src), bl->id, 0, 0, LG_OVERBRAND_PLUSATK, skill_lv, BF_WEAPON, flag );
- } else
- skill_addtimerskill(src, tick + status_get_amotion(src), bl->id, 0, 0, LG_OVERBRAND_PLUSATK, skill_lv, BF_WEAPON, flag );
- break;
- case SR_KNUCKLEARROW:
- if( skill_blown(dsrc,bl,dmg.blewcount,dir,0) && !(flag&4) ) {
- short dir_x, dir_y;
- dir_x = dirx[(dir+4)%8];
- dir_y = diry[(dir+4)%8];
- if( map_getcell(bl->m, bl->x+dir_x, bl->y+dir_y, CELL_CHKNOPASS) != 0 )
- skill_addtimerskill(src, tick + 300 * ((flag&2) ? 1 : 2), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|4);
- }
- break;
- case GN_WALLOFTHORN:
- unit_stop_walking(bl,1);
- skill_blown(dsrc,bl,dmg.blewcount,dir, 0x2 );
- clif_fixpos(bl);
- break;
- default:
- skill_blown(dsrc,bl,dmg.blewcount,dir, 0x0 );
- if ( !dmg.blewcount && bl->type == BL_SKILL && damage > 0 ){
- TBL_SKILL *su = (TBL_SKILL*)bl;
- if( su->group && su->group->skill_id == HT_BLASTMINE)
- skill_blown(src, bl, 3, -1, 0);
- }
- break;
- }
- }
-
- //Delayed damage must be dealt after the knockback (it needs to know actual position of target)
- if (dmg.amotion)
- battle_delay_damage(tick, dmg.amotion,src,bl,dmg.flag,skill_id,skill_lv,damage,dmg.dmg_lv,dmg.dmotion);
-
- if( sc && sc->data[SC_DEVOTION] && skill_id != PA_PRESSURE )
- {
- struct status_change_entry *sce = sc->data[SC_DEVOTION];
- struct block_list *d_bl = map_id2bl(sce->val1);
-
- if( d_bl && (
- (d_bl->type == BL_MER && ((TBL_MER*)d_bl)->master && ((TBL_MER*)d_bl)->master->bl.id == bl->id) ||
- (d_bl->type == BL_PC && ((TBL_PC*)d_bl)->devotion[sce->val2] == bl->id)
- ) && check_distance_bl(bl, d_bl, sce->val3) )
- {
- if(!rmdamage){
- clif_damage(d_bl,d_bl, gettick(), 0, 0, damage, 0, 0, 0);
- status_fix_damage(NULL,d_bl, damage, 0);
- }
- else{//Reflected magics are done directly on the target not on paladin
- //This check is only for magical skill.
- //For BF_WEAPON skills types track var rdamage and function battle_calc_return_damage
- clif_damage(bl,bl, gettick(), 0, 0, damage, 0, 0, 0);
- status_fix_damage(bl,bl, damage, 0);
- }
- }
- else {
- status_change_end(bl, SC_DEVOTION, INVALID_TIMER);
- if( !dmg.amotion )
- status_fix_damage(src,bl,damage,dmg.dmotion);
- }
- }
-
- if(damage > 0 && !(tstatus->mode&MD_BOSS)) {
- if( skill_id == RG_INTIMIDATE ) {
- int rate = 50 + skill_lv * 5;
- rate = rate + (status_get_lv(src) - status_get_lv(bl));
- if(rnd()%100 < rate)
- skill_addtimerskill(src,tick + 800,bl->id,0,0,skill_id,skill_lv,0,flag);
- } else if( skill_id == SC_FATALMENACE )
- skill_addtimerskill(src,tick + 800,bl->id,skill_area_temp[4],skill_area_temp[5],skill_id,skill_lv,0,flag);
- }
-
- if(skill_id == CR_GRANDCROSS || skill_id == NPC_GRANDDARKNESS)
- dmg.flag |= BF_WEAPON;
-
- if( sd && src != bl && damage > 0 && ( dmg.flag&BF_WEAPON ||
- (dmg.flag&BF_MISC && (skill_id == RA_CLUSTERBOMB || skill_id == RA_FIRINGTRAP || skill_id == RA_ICEBOUNDTRAP || skill_id == RK_DRAGONBREATH)) ) )
- {
- if (battle_config.left_cardfix_to_right)
- battle_drain(sd, bl, dmg.damage, dmg.damage, tstatus->race, tstatus->mode&MD_BOSS);
- else
- battle_drain(sd, bl, dmg.damage, dmg.damage2, tstatus->race, tstatus->mode&MD_BOSS);
- }
-
- if( rdamage > 0 ) {
- if( sc && sc->data[SC_REFLECTDAMAGE] ) {
- if( src != bl )// Don't reflect your own damage (Grand Cross)
- map_foreachinshootrange(battle_damage_area,bl,skill_get_splash(LG_REFLECTDAMAGE,1),BL_CHAR,tick,bl,dmg.amotion,sstatus->dmotion,rdamage,tstatus->race);
- } else {
- if( dmg.amotion )
- battle_delay_damage(tick, dmg.amotion,bl,src,0,CR_REFLECTSHIELD,0,rdamage,ATK_DEF,0);
- else
- status_fix_damage(bl,src,rdamage,0);
- clif_damage(src,src,tick, dmg.amotion,0,rdamage,1,4,0); // in aegis damage reflected is shown in single hit.
- //Use Reflect Shield to signal this kind of skill trigger. [Skotlex]
- if( tsd && src != bl )
- battle_drain(tsd, src, rdamage, rdamage, sstatus->race, is_boss(src));
- skill_additional_effect(bl, src, CR_REFLECTSHIELD, 1, BF_WEAPON|BF_SHORT|BF_NORMAL,ATK_DEF,tick);
- }
- }
- if( damage > 0 ) {
- /**
- * Post-damage effects
- **/
- switch( skill_id ) {
- case RK_CRUSHSTRIKE:
- skill_break_equip(src,EQP_WEAPON,2000,BCT_SELF); // 20% chance to destroy the weapon.
- break;
- case GC_VENOMPRESSURE: {
- struct status_change *ssc = status_get_sc(src);
- if( ssc && ssc->data[SC_POISONINGWEAPON] && rnd()%100 < 70 + 5*skill_lv ) {
- sc_start(bl,ssc->data[SC_POISONINGWEAPON]->val2,100,ssc->data[SC_POISONINGWEAPON]->val1,skill_get_time2(GC_POISONINGWEAPON, 1));
- status_change_end(src,SC_POISONINGWEAPON,INVALID_TIMER);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- }
- break;
- case WM_METALICSOUND:
- status_zap(bl, 0, damage*100/(100*(110-pc_checkskill(sd,WM_LESSON)*10)));
- break;
- case SR_TIGERCANNON:
- status_zap(bl, 0, damage/10); // 10% of damage dealt
- break;
- }
- if( sd )
- skill_onskillusage(sd, bl, skill_id, tick);
- }
-
- if (!(flag&2) &&
- (
- skill_id == MG_COLDBOLT || skill_id == MG_FIREBOLT || skill_id == MG_LIGHTNINGBOLT
- ) &&
- (sc = status_get_sc(src)) &&
- sc->data[SC_DOUBLECAST] &&
- rnd() % 100 < sc->data[SC_DOUBLECAST]->val2)
- {
-// skill_addtimerskill(src, tick + dmg.div_*dmg.amotion, bl->id, 0, 0, skill_id, skill_lv, BF_MAGIC, flag|2);
- skill_addtimerskill(src, tick + dmg.amotion, bl->id, 0, 0, skill_id, skill_lv, BF_MAGIC, flag|2);
- }
-
- map_freeblock_unlock();
-
- return damage;
-}
-
-/*==========================================
- * sub fonction for recursive skill call.
- * Checking bl battle flag and display dammage
- * then call func with source,target,skill_id,skill_lv,tick,flag
- *------------------------------------------*/
-typedef int (*SkillFunc)(struct block_list *, struct block_list *, int, int, unsigned int, int);
-int skill_area_sub (struct block_list *bl, va_list ap)
-{
- struct block_list *src;
- uint16 skill_id,skill_lv;
- int flag;
- unsigned int tick;
- SkillFunc func;
-
- nullpo_ret(bl);
-
- src=va_arg(ap,struct block_list *);
- 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)
- {
- // several splash skills need this initial dummy packet to display correctly
- if (flag&SD_PREAMBLE && skill_area_temp[2] == 0)
- clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
-
- if (flag&(SD_SPLASH|SD_PREAMBLE))
- skill_area_temp[2]++;
-
- return func(src,bl,skill_id,skill_lv,tick,flag);
- }
- return 0;
-}
-
-static int skill_check_unit_range_sub (struct block_list *bl, va_list ap)
-{
- struct skill_unit *unit;
- uint16 skill_id,g_skill_id;
-
- unit = (struct skill_unit *)bl;
-
- if(bl->prev == NULL || bl->type != BL_SKILL)
- return 0;
-
- if(!unit->alive)
- return 0;
-
- skill_id = va_arg(ap,int);
- g_skill_id = unit->group->skill_id;
-
- switch (skill_id) {
- case MH_STEINWAND:
- case MG_SAFETYWALL:
- case AL_PNEUMA:
- case SC_MAELSTROM:
- if(g_skill_id != MH_STEINWAND && g_skill_id != MG_SAFETYWALL && g_skill_id != AL_PNEUMA && g_skill_id != SC_MAELSTROM)
- return 0;
- break;
- case AL_WARP:
- case HT_SKIDTRAP:
- case MA_SKIDTRAP:
- case HT_LANDMINE:
- case MA_LANDMINE:
- case HT_ANKLESNARE:
- case HT_SHOCKWAVE:
- case HT_SANDMAN:
- case MA_SANDMAN:
- case HT_FLASHER:
- case HT_FREEZINGTRAP:
- case MA_FREEZINGTRAP:
- case HT_BLASTMINE:
- case HT_CLAYMORETRAP:
- case HT_TALKIEBOX:
- case HP_BASILICA:
- case RA_ELECTRICSHOCKER:
- case RA_CLUSTERBOMB:
- case RA_MAGENTATRAP:
- case RA_COBALTTRAP:
- case RA_MAIZETRAP:
- case RA_VERDURETRAP:
- case RA_FIRINGTRAP:
- case RA_ICEBOUNDTRAP:
- case SC_DIMENSIONDOOR:
- case SC_BLOODYLUST:
- //Non stackable on themselves and traps (including venom dust which does not has the trap inf2 set)
- if (skill_id != g_skill_id && !(skill_get_inf2(g_skill_id)&INF2_TRAP) && g_skill_id != AS_VENOMDUST && g_skill_id != MH_POISON_MIST)
- return 0;
- break;
- default: //Avoid stacking with same kind of trap. [Skotlex]
- if (g_skill_id != skill_id)
- return 0;
- break;
- }
-
- return 1;
-}
-
-static int skill_check_unit_range (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv)
-{
- //Non players do not check for the skill's splash-trigger area.
- int range = bl->type==BL_PC?skill_get_unit_range(skill_id, skill_lv):0;
- int layout_type = skill_get_unit_layout_type(skill_id,skill_lv);
- if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) {
- ShowError("skill_check_unit_range: unsupported layout type %d for skill %d\n",layout_type,skill_id);
- return 0;
- }
-
- range += layout_type;
- return map_foreachinarea(skill_check_unit_range_sub,bl->m,x-range,y-range,x+range,y+range,BL_SKILL,skill_id);
-}
-
-static int skill_check_unit_range2_sub (struct block_list *bl, va_list ap)
-{
- uint16 skill_id;
-
- if(bl->prev == NULL)
- return 0;
-
- skill_id = va_arg(ap,int);
-
- if( status_isdead(bl) && skill_id != AL_WARP )
- return 0;
-
- if( skill_id == HP_BASILICA && bl->type == BL_PC )
- return 0;
-
- if( skill_id == AM_DEMONSTRATION && bl->type == BL_MOB && ((TBL_MOB*)bl)->class_ == MOBID_EMPERIUM )
- return 0; //Allow casting Bomb/Demonstration Right under emperium [Skotlex]
- return 1;
-}
-
-static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv)
-{
- int range, type;
-
- switch (skill_id) { // to be expanded later
- case WZ_ICEWALL:
- range = 2;
- break;
- default:
- {
- int layout_type = skill_get_unit_layout_type(skill_id,skill_lv);
- if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) {
- ShowError("skill_check_unit_range2: unsupported layout type %d for skill %d\n",layout_type,skill_id);
- return 0;
- }
- range = skill_get_unit_range(skill_id,skill_lv) + layout_type;
- }
- break;
- }
-
- // if the caster is a monster/NPC, only check for players
- // otherwise just check characters
- if (bl->type == BL_PC)
- type = BL_CHAR;
- else
- type = BL_PC;
-
- return map_foreachinarea(skill_check_unit_range2_sub, bl->m,
- x - range, y - range, x + range, y + range,
- type, skill_id);
-}
-
-int skill_guildaura_sub (struct map_session_data* sd, int id, int strvit, int agidex)
-{
- if(id == sd->bl.id && battle_config.guild_aura&16)
- return 0; // Do not affect guild leader
-
- if (sd->sc.data[SC_GUILDAURA]) {
- struct status_change_entry *sce = sd->sc.data[SC_GUILDAURA];
- if( sce->val3 != strvit || sce->val4 != agidex ) {
- sce->val3 = strvit;
- sce->val4 = agidex;
- status_calc_bl(&sd->bl, status_sc2scb_flag(SC_GUILDAURA));
- }
- return 0;
- }
- sc_start4(&sd->bl, SC_GUILDAURA,100, 1, id, strvit, agidex, 1000);
- return 1;
-}
-
-/*==========================================
- * Checks that you have the requirements for casting a skill for homunculus/mercenary.
- * Flag:
- * &1: finished casting the skill (invoke hp/sp/item consumption)
- * &2: picked menu entry (Warp Portal, Teleport and other menu based skills)
- *------------------------------------------*/
-static int skill_check_condition_mercenary(struct block_list *bl, int skill, int lv, int type)
-{
- struct status_data *status;
- struct map_session_data *sd = NULL;
- int i, hp, sp, hp_rate, sp_rate, state, mhp;
- uint16 idx;
- int itemid[MAX_SKILL_ITEM_REQUIRE],amount[ARRAYLENGTH(itemid)],index[ARRAYLENGTH(itemid)];
-
- if( lv < 1 || lv > MAX_SKILL_LEVEL )
- return 0;
- nullpo_ret(bl);
-
- switch( bl->type )
- {
- case BL_HOM: sd = ((TBL_HOM*)bl)->master; break;
- case BL_MER: sd = ((TBL_MER*)bl)->master; break;
- }
-
- status = status_get_status_data(bl);
- if( (idx = skill_get_index(skill)) == 0 )
- return 0;
-
- // Requeriments
- for( i = 0; i < ARRAYLENGTH(itemid); i++ )
- {
- itemid[i] = skill_db[idx].itemid[i];
- amount[i] = skill_db[idx].amount[i];
- }
- hp = skill_db[idx].hp[lv-1];
- sp = skill_db[idx].sp[lv-1];
- hp_rate = skill_db[idx].hp_rate[lv-1];
- sp_rate = skill_db[idx].sp_rate[lv-1];
- state = skill_db[idx].state;
- if( (mhp = skill_db[idx].mhp[lv-1]) > 0 )
- hp += (status->max_hp * mhp) / 100;
- if( hp_rate > 0 )
- hp += (status->hp * hp_rate) / 100;
- else
- hp += (status->max_hp * (-hp_rate)) / 100;
- if( sp_rate > 0 )
- sp += (status->sp * sp_rate) / 100;
- else
- sp += (status->max_sp * (-sp_rate)) / 100;
-
- if( bl->type == BL_HOM )
- { // Intimacy Requeriments
- struct homun_data *hd = BL_CAST(BL_HOM, bl);
- switch( skill )
- {
- case HFLI_SBR44:
- if( hd->homunculus.intimacy <= 200 )
- return 0;
- break;
- case HVAN_EXPLOSION:
- if( hd->homunculus.intimacy < (unsigned int)battle_config.hvan_explosion_intimate )
- return 0;
- break;
- }
- }
-
- if( !(type&2) )
- {
- if( hp > 0 && status->hp <= (unsigned int)hp )
- {
- clif_skill_fail(sd, skill, USESKILL_FAIL_HP_INSUFFICIENT, 0);
- return 0;
- }
- if( sp > 0 && status->sp <= (unsigned int)sp )
- {
- clif_skill_fail(sd, skill, USESKILL_FAIL_SP_INSUFFICIENT, 0);
- return 0;
- }
- }
-
- if( !type )
- switch( state )
- {
- case ST_MOVE_ENABLE:
- if( !unit_can_move(bl) )
- {
- clif_skill_fail(sd, skill, USESKILL_FAIL_LEVEL, 0);
- return 0;
- }
- break;
- }
- if( !(type&1) )
- return 1;
-
- // Check item existences
- for( i = 0; i < ARRAYLENGTH(itemid); i++ )
- {
- index[i] = -1;
- if( itemid[i] < 1 ) continue; // No item
- index[i] = pc_search_inventory(sd, itemid[i]);
- if( index[i] < 0 || sd->status.inventory[index[i]].amount < amount[i] )
- {
- clif_skill_fail(sd, skill, USESKILL_FAIL_LEVEL, 0);
- return 0;
- }
- }
-
- // Consume items
- for( i = 0; i < ARRAYLENGTH(itemid); i++ )
- {
- if( index[i] >= 0 ) pc_delitem(sd, index[i], amount[i], 0, 1, LOG_TYPE_CONSUME);
- }
-
- if( type&2 )
- return 1;
-
- if( sp || hp )
- status_zap(bl, hp, sp);
-
- return 1;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_area_sub_count (struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
-{
- return 1;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data)
-{
- struct block_list *src = map_id2bl(id),*target;
- struct unit_data *ud = unit_bl2ud(src);
- struct skill_timerskill *skl = NULL;
- int range;
-
- nullpo_ret(src);
- nullpo_ret(ud);
- skl = ud->skilltimerskill[data];
- nullpo_ret(skl);
- ud->skilltimerskill[data] = NULL;
-
- do {
- if(src->prev == NULL)
- break; // Source not on Map
- if(skl->target_id) {
- target = map_id2bl(skl->target_id);
- if( ( skl->skill_id == RG_INTIMIDATE || skl->skill_id == SC_FATALMENACE ) && (!target || target->prev == NULL || !check_distance_bl(src,target,AREA_SIZE)) )
- target = src; //Required since it has to warp.
- if(target == NULL)
- break; // Target offline?
- if(target->prev == NULL)
- break; // Target not on Map
- if(src->m != target->m)
- break; // Different Maps
- if(status_isdead(src))
- break; // Caster is Dead
- if(status_isdead(target) && skl->skill_id != RG_INTIMIDATE && skl->skill_id != WZ_WATERBALL)
- break;
-
- switch(skl->skill_id) {
- case RG_INTIMIDATE:
- if (unit_warp(src,-1,-1,-1,CLR_TELEPORT) == 0) {
- short x,y;
- map_search_freecell(src, 0, &x, &y, 1, 1, 0);
- if (target != src && !status_isdead(target))
- unit_warp(target, -1, x, y, CLR_TELEPORT);
- }
- break;
- case BA_FROSTJOKER:
- case DC_SCREAM:
- range= skill_get_splash(skl->skill_id, skl->skill_lv);
- map_foreachinarea(skill_frostjoke_scream,skl->map,skl->x-range,skl->y-range,
- skl->x+range,skl->y+range,BL_CHAR,src,skl->skill_id,skl->skill_lv,tick);
- break;
- case NPC_EARTHQUAKE:
- if( skl->type > 1 )
- skill_addtimerskill(src,tick+250,src->id,0,0,skl->skill_id,skl->skill_lv,skl->type-1,skl->flag);
- skill_area_temp[0] = map_foreachinrange(skill_area_sub, src, skill_get_splash(skl->skill_id, skl->skill_lv), BL_CHAR, src, skl->skill_id, skl->skill_lv, tick, BCT_ENEMY, skill_area_sub_count);
- skill_area_temp[1] = src->id;
- skill_area_temp[2] = 0;
- map_foreachinrange(skill_area_sub, src, skill_get_splash(skl->skill_id, skl->skill_lv), splash_target(src), src, skl->skill_id, skl->skill_lv, tick, skl->flag, skill_castend_damage_id);
- break;
- case WZ_WATERBALL:
- skill_toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify
- if (!status_isdead(target))
- skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag);
- if (skl->type>1 && !status_isdead(target) && !status_isdead(src)) {
- skill_addtimerskill(src,tick+125,target->id,0,0,skl->skill_id,skl->skill_lv,skl->type-1,skl->flag);
- } else {
- struct status_change *sc = status_get_sc(src);
- if(sc) {
- if(sc->data[SC_SPIRIT] &&
- sc->data[SC_SPIRIT]->val2 == SL_WIZARD &&
- sc->data[SC_SPIRIT]->val3 == skl->skill_id)
- sc->data[SC_SPIRIT]->val3 = 0; //Clear bounced spell check.
- }
- }
- break;
- /**
- * Warlock
- **/
- case WL_CHAINLIGHTNING_ATK:
- {
- struct block_list *nbl = NULL; // Next Target of Chain
- skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); // Hit a Lightning on the current Target
- skill_toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify
- if( skl->type > 1 )
- { // Remaining Chains Hit
- nbl = battle_getenemyarea(src,target->x,target->y,2,BL_CHAR|BL_SKILL,target->id); // Search for a new Target around current one...
- if( nbl == NULL && skl->x > 1 )
- {
- nbl = target;
- skl->x--;
- }
- else skl->x = 3;
- }
-
- if( nbl )
- skill_addtimerskill(src,tick+status_get_adelay(src),nbl->id,skl->x,0,WL_CHAINLIGHTNING_ATK,skl->skill_lv,skl->type-1,skl->flag);
- }
- break;
- case WL_TETRAVORTEX_FIRE:
- case WL_TETRAVORTEX_WATER:
- case WL_TETRAVORTEX_WIND:
- case WL_TETRAVORTEX_GROUND:
- skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag|SD_ANIMATION);
- skill_toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify
- if( skl->type >= 3 )
- { // Final Hit
- if( !status_isdead(target) )
- { // Final Status Effect
- int effects[4] = { SC_BURNING, SC_FREEZING, SC_BLEEDING, SC_STUN },
- applyeffects[4] = { 0, 0, 0, 0 },
- i, j = 0, k = 0;
- for( i = 1; i <= 8; i = i + i )
- {
- if( skl->x&i )
- {
- applyeffects[j] = effects[k];
- j++;
- }
- k++;
- }
- if( j )
- {
- i = applyeffects[rnd()%j];
- status_change_start(target, i, 10000, skl->skill_lv,
- (i == SC_BURNING ? 1000 : 0),
- (i == SC_BURNING ? src->id : 0),
- 0, skill_get_time(WL_TETRAVORTEX,skl->skill_lv), 0);
- }
- }
- }
- break;
- case WM_REVERBERATION_MELEE:
- case WM_REVERBERATION_MAGIC:
- skill_castend_damage_id(src, target, skl->skill_id, skl->skill_lv, tick, skl->flag|SD_LEVEL); // damage should split among targets
- break;
- case SC_FATALMENACE:
- if( src == target ) // Casters Part
- unit_warp(src, -1, skl->x, skl->y, 3);
- else { // Target's Part
- short x = skl->x, y = skl->y;
- map_search_freecell(NULL, target->m, &x, &y, 2, 2, 1);
- unit_warp(target,-1,x,y,3);
- }
- break;
- case LG_MOONSLASHER:
- case SR_WINDMILL:
- if( target->type == BL_PC ) {
- struct map_session_data *tsd = NULL;
- if( (tsd = ((TBL_PC*)target)) && !pc_issit(tsd) ) {
- pc_setsit(tsd);
- skill_sit(tsd,1);
- clif_sitting(&tsd->bl);
- }
- }
- break;
- case LG_OVERBRAND_BRANDISH:
- case LG_OVERBRAND_PLUSATK:
- case SR_KNUCKLEARROW:
- skill_attack(BF_WEAPON, src, src, target, skl->skill_id, skl->skill_lv, tick, skl->flag|SD_LEVEL);
- break;
- case GN_SPORE_EXPLOSION:
- map_foreachinrange(skill_area_sub, target, skill_get_splash(skl->skill_id, skl->skill_lv), BL_CHAR,
- src, skl->skill_id, skl->skill_lv, 0, skl->flag|1|BCT_ENEMY, skill_castend_damage_id);
- break;
- case CH_PALMSTRIKE:
- {
- struct status_change* tsc = status_get_sc(target);
- struct status_change* sc = status_get_sc(src);
- if( tsc && tsc->option&OPTION_HIDE ||
- sc && sc->option&OPTION_HIDE ){
- skill_blown(src,target,skill_get_blewcount(skl->skill_id, skl->skill_lv), -1, 0x0 );
- 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)
- break;
- switch( skl->skill_id )
- {
- case WZ_METEOR:
- if( skl->type >= 0 )
- {
- int x = skl->type>>16, y = skl->type&0xFFFF;
- if( path_search_long(NULL, src->m, src->x, src->y, x, y, CELL_CHKWALL) )
- skill_unitsetting(src,skl->skill_id,skl->skill_lv,x,y,skl->flag);
- if( path_search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) )
- clif_skill_poseffect(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,tick);
- }
- else if( path_search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) )
- skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,skl->flag);
- break;
- case GN_CRAZYWEED_ATK:
- {
- int dummy = 1, i = skill_get_unit_range(skl->skill_id,skl->skill_lv);
- map_foreachinarea(skill_cell_overlap, src->m, skl->x-i, skl->y-i, skl->x+i, skl->y+i, BL_SKILL, skl->skill_id, &dummy, src);
- }
- case WL_EARTHSTRAIN:
- skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,(skl->type<<16)|skl->flag);
- break;
-
- }
- }
- } while (0);
- //Free skl now that it is no longer needed.
- ers_free(skill_timer_ers, skl);
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_addtimerskill (struct block_list *src, unsigned int tick, int target, int x,int y, uint16 skill_id, uint16 skill_lv, int type, int flag)
-{
- int i;
- struct unit_data *ud;
- nullpo_retr(1, src);
- if (src->prev == NULL)
- return 0;
- ud = unit_bl2ud(src);
- nullpo_retr(1, ud);
-
- ARR_FIND( 0, MAX_SKILLTIMERSKILL, i, ud->skilltimerskill[i] == 0 );
- if( i == MAX_SKILLTIMERSKILL ) return 1;
-
- ud->skilltimerskill[i] = ers_alloc(skill_timer_ers, struct skill_timerskill);
- ud->skilltimerskill[i]->timer = add_timer(tick, skill_timerskill, src->id, i);
- ud->skilltimerskill[i]->src_id = src->id;
- ud->skilltimerskill[i]->target_id = target;
- ud->skilltimerskill[i]->skill_id = skill_id;
- ud->skilltimerskill[i]->skill_lv = skill_lv;
- ud->skilltimerskill[i]->map = src->m;
- ud->skilltimerskill[i]->x = x;
- ud->skilltimerskill[i]->y = y;
- ud->skilltimerskill[i]->type = type;
- ud->skilltimerskill[i]->flag = flag;
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_cleartimerskill (struct block_list *src)
-{
- int i;
- struct unit_data *ud;
- nullpo_ret(src);
- ud = unit_bl2ud(src);
- nullpo_ret(ud);
-
- for(i=0;i<MAX_SKILLTIMERSKILL;i++) {
- if(ud->skilltimerskill[i]) {
- delete_timer(ud->skilltimerskill[i]->timer, skill_timerskill);
- ers_free(skill_timer_ers, ud->skilltimerskill[i]);
- ud->skilltimerskill[i]=NULL;
- }
- }
- return 1;
-}
-static int skill_ative_reverberation( struct block_list *bl, va_list ap) {
- struct skill_unit *su = (TBL_SKILL*)bl;
- struct skill_unit_group *sg;
- if( bl->type != BL_SKILL )
- return 0;
- if( su->alive && (sg = su->group) && sg->skill_id == WM_REVERBERATION ) {
- map_foreachinrange(skill_trap_splash, bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, bl, gettick());
- su->limit=DIFF_TICK(gettick(),sg->tick);
- sg->unit_id = UNT_USED_TRAPS;
- }
- return 0;
-}
-
-static int skill_reveal_trap (struct block_list *bl, va_list ap)
-{
- TBL_SKILL *su = (TBL_SKILL*)bl;
- if (su->alive && su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP)
- { //Reveal trap.
- //Change look is not good enough, the client ignores it as an actual trap still. [Skotlex]
- //clif_changetraplook(bl, su->group->unit_id);
- clif_skill_setunit(su);
- return 1;
- }
- return 0;
-}
-
-/*==========================================
- *
- *
- *------------------------------------------*/
-int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
-{
- struct map_session_data *sd = NULL;
- struct status_data *tstatus;
- struct status_change *sc;
-
- if (skill_id > 0 && !skill_lv) return 0;
-
- nullpo_retr(1, src);
- nullpo_retr(1, bl);
-
- if (src->m != bl->m)
- return 1;
-
- if (bl->prev == NULL)
- return 1;
-
- sd = BL_CAST(BL_PC, src);
-
- if (status_isdead(bl))
- return 1;
-
- if (skill_id && skill_get_type(skill_id) == BF_MAGIC && status_isimmune(bl) == 100)
- { //GTB makes all targetted magic display miss with a single bolt.
- sc_type sct = status_skill2sc(skill_id);
- if(sct != SC_NONE)
- status_change_end(bl, sct, INVALID_TIMER);
- clif_skill_damage(src, bl, tick, status_get_amotion(src), status_get_dmotion(bl), 0, 1, skill_id, skill_lv, skill_get_hit(skill_id));
- return 1;
- }
-
- sc = status_get_sc(src);
- if (sc && !sc->count)
- sc = NULL; //Unneeded
-
- tstatus = status_get_status_data(bl);
-
- map_freeblock_lock();
-
- switch(skill_id)
- {
- case MER_CRASH:
- case SM_BASH:
- case MS_BASH:
- case MC_MAMMONITE:
- case TF_DOUBLE:
- case AC_DOUBLE:
- case MA_DOUBLE:
- case AS_SONICBLOW:
- case KN_PIERCE:
- case ML_PIERCE:
- case KN_SPEARBOOMERANG:
- case TF_POISON:
- case TF_SPRINKLESAND:
- case AC_CHARGEARROW:
- case MA_CHARGEARROW:
- case RG_INTIMIDATE:
- case AM_ACIDTERROR:
- case BA_MUSICALSTRIKE:
- case DC_THROWARROW:
- case BA_DISSONANCE:
- case CR_HOLYCROSS:
- case NPC_DARKCROSS:
- case CR_SHIELDCHARGE:
- case CR_SHIELDBOOMERANG:
- case NPC_PIERCINGATT:
- case NPC_MENTALBREAKER:
- case NPC_RANGEATTACK:
- case NPC_CRITICALSLASH:
- case NPC_COMBOATTACK:
- case NPC_GUIDEDATTACK:
- case NPC_POISON:
- case NPC_RANDOMATTACK:
- case NPC_WATERATTACK:
- case NPC_GROUNDATTACK:
- case NPC_FIREATTACK:
- case NPC_WINDATTACK:
- case NPC_POISONATTACK:
- case NPC_HOLYATTACK:
- case NPC_DARKNESSATTACK:
- case NPC_TELEKINESISATTACK:
- case NPC_UNDEADATTACK:
- case NPC_ARMORBRAKE:
- case NPC_WEAPONBRAKER:
- case NPC_HELMBRAKE:
- case NPC_SHIELDBRAKE:
- case NPC_BLINDATTACK:
- case NPC_SILENCEATTACK:
- case NPC_STUNATTACK:
- case NPC_PETRIFYATTACK:
- case NPC_CURSEATTACK:
- case NPC_SLEEPATTACK:
- case LK_AURABLADE:
- case LK_SPIRALPIERCE:
- case ML_SPIRALPIERCE:
- case LK_HEADCRUSH:
- case CG_ARROWVULCAN:
- case HW_MAGICCRASHER:
- case ITM_TOMAHAWK:
- case MO_TRIPLEATTACK:
- case CH_CHAINCRUSH:
- case CH_TIGERFIST:
- case PA_SHIELDCHAIN: // Shield Chain
- case PA_SACRIFICE:
- case WS_CARTTERMINATION: // Cart Termination
- case AS_VENOMKNIFE:
- case HT_PHANTASMIC:
- case HT_POWER:
- case TK_DOWNKICK:
- case TK_COUNTER:
- case GS_CHAINACTION:
- case GS_TRIPLEACTION:
- case GS_MAGICALBULLET:
- case GS_TRACKING:
- case GS_PIERCINGSHOT:
- case GS_RAPIDSHOWER:
- case GS_DUST:
- case GS_DISARM: // Added disarm. [Reddozen]
- case GS_FULLBUSTER:
- case NJ_SYURIKEN:
- case NJ_KUNAI:
- case ASC_BREAKER:
- case HFLI_MOON: //[orn]
- case HFLI_SBR44: //[orn]
- case NPC_BLEEDING:
- case NPC_CRITICALWOUND:
- case NPC_HELLPOWER:
- case RK_SONICWAVE:
- case RK_HUNDREDSPEAR:
- case AB_DUPLELIGHT_MELEE:
- case RA_AIMEDBOLT:
- case NC_AXEBOOMERANG:
- case NC_POWERSWING:
- case GC_CROSSIMPACT:
- case GC_VENOMPRESSURE:
- case SC_TRIANGLESHOT:
- case SC_FEINTBOMB:
- case LG_BANISHINGPOINT:
- case LG_SHIELDPRESS:
- case LG_RAGEBURST:
- case LG_RAYOFGENESIS:
- case LG_HESPERUSLIT:
- case SR_FALLENEMPIRE:
- case SR_CRESCENTELBOW_AUTOSPELL:
- case SR_GATEOFHELL:
- case SR_GENTLETOUCH_QUIET:
- case WM_SEVERE_RAINSTORM_MELEE:
- case WM_GREAT_ECHO:
- case GN_SLINGITEM_RANGEMELEEATK:
- case KO_JYUMONJIKIRI:
- case KO_SETSUDAN:
- case KO_KAIHOU:
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
-
- /**
- * Mechanic (MADO GEAR)
- **/
- case NC_BOOSTKNUCKLE:
- case NC_PILEBUNKER:
- case NC_VULCANARM:
- case NC_COLDSLOWER:
- case NC_ARMSCANNON:
- if (sd) pc_overheat(sd,1);
- case RK_WINDCUTTER:
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag|SD_ANIMATION);
- break;
-
- case LK_JOINTBEAT: // decide the ailment first (affects attack damage and effect)
- switch( rnd()%6 ){
- case 0: flag |= BREAK_ANKLE; break;
- case 1: flag |= BREAK_WRIST; break;
- case 2: flag |= BREAK_KNEE; break;
- case 3: flag |= BREAK_SHOULDER; break;
- case 4: flag |= BREAK_WAIST; break;
- case 5: flag |= BREAK_NECK; break;
- }
- //TODO: is there really no cleaner way to do this?
- sc = status_get_sc(bl);
- if (sc) sc->jb_flag = flag;
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
-
- case MO_COMBOFINISH:
- if (!(flag&1) && sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_MONK)
- { //Becomes a splash attack when Soul Linked.
- map_foreachinrange(skill_area_sub, bl,
- skill_get_splash(skill_id, skill_lv),splash_target(src),
- src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1,
- skill_castend_damage_id);
- } else
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
-
- case TK_STORMKICK: // Taekwon kicks [Dralnu]
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- skill_area_temp[1] = 0;
- map_foreachinrange(skill_attack_area, src,
- skill_get_splash(skill_id, skill_lv), splash_target(src),
- BF_WEAPON, src, src, skill_id, skill_lv, tick, flag, BCT_ENEMY);
- break;
-
- case KN_CHARGEATK:
- {
- bool path = path_search_long(NULL, src->m, src->x, src->y, bl->x, bl->y,CELL_CHKWALL);
- unsigned int dist = distance_bl(src, bl);
- uint8 dir = map_calc_dir(bl, src->x, src->y);
-
- // teleport to target (if not on WoE grounds)
- if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground && unit_movepos(src, bl->x, bl->y, 0, 1) )
- clif_slide(src, bl->x, bl->y);
-
- // cause damage and knockback if the path to target was a straight one
- if( path )
- {
- skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, dist);
- skill_blown(src, bl, dist, dir, 0);
- //HACK: since knockback officially defaults to the left, the client also turns to the left... therefore,
- // make the caster look in the direction of the target
- unit_setdir(src, (dir+4)%8);
- }
-
- }
- break;
-
- case NC_FLAMELAUNCHER:
- if (sd) pc_overheat(sd,1);
- case SN_SHARPSHOOTING:
- case MA_SHARPSHOOTING:
- case NJ_KAMAITACHI:
- case LG_CANNONSPEAR:
- //It won't shoot through walls since on castend there has to be a direct
- //line of sight between caster and target.
- skill_area_temp[1] = bl->id;
- map_foreachinpath (skill_attack_area,src->m,src->x,src->y,bl->x,bl->y,
- skill_get_splash(skill_id, skill_lv),skill_get_maxcount(skill_id,skill_lv), splash_target(src),
- skill_get_type(skill_id),src,src,skill_id,skill_lv,tick,flag,BCT_ENEMY);
- break;
-
- case NPC_ACIDBREATH:
- case NPC_DARKNESSBREATH:
- case NPC_FIREBREATH:
- case NPC_ICEBREATH:
- case NPC_THUNDERBREATH:
- skill_area_temp[1] = bl->id;
- map_foreachinpath(skill_attack_area,src->m,src->x,src->y,bl->x,bl->y,
- skill_get_splash(skill_id, skill_lv),skill_get_maxcount(skill_id,skill_lv), splash_target(src),
- skill_get_type(skill_id),src,src,skill_id,skill_lv,tick,flag,BCT_ENEMY);
- break;
-
- case MO_INVESTIGATE:
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
- break;
-
- case RG_BACKSTAP:
- {
- uint8 dir = map_calc_dir(src, bl->x, bl->y), t_dir = unit_getdir(bl);
- if ((!check_distance_bl(src, bl, 0) && !map_check_dir(dir, t_dir)) || bl->type == BL_SKILL) {
- status_change_end(src, SC_HIDING, INVALID_TIMER);
- skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
- dir = dir < 4 ? dir+4 : dir-4; // change direction [Celest]
- unit_setdir(bl,dir);
- }
- else if (sd)
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- }
- break;
-
- case MO_FINGEROFFENSIVE:
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- if (battle_config.finger_offensive_type && sd) {
- int i;
- for (i = 1; i < sd->spiritball_old; i++)
- skill_addtimerskill(src, tick + i * 200, bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag);
- }
- status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
- break;
-
- case MO_CHAINCOMBO:
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
- break;
-
- case NJ_ISSEN:
- status_change_end(src, SC_NEN, INVALID_TIMER);
- status_change_end(src, SC_HIDING, INVALID_TIMER);
- // fall through
- case MO_EXTREMITYFIST:
- {
- short x, y, i = 2; // Move 2 cells for Issen(from target)
- struct block_list *mbl = bl;
- short dir = 0;
-
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
-
- if( skill_id == MO_EXTREMITYFIST )
- {
- mbl = src;
- i = 3; // for Asura(from caster)
- status_set_sp(src, 0, 0);
- status_change_end(src, SC_EXPLOSIONSPIRITS, INVALID_TIMER);
- status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
-#ifdef RENEWAL
- sc_start(src,SC_EXTREMITYFIST2,100,skill_lv,skill_get_time(skill_id,skill_lv));
-#endif
- }else
- status_set_hp(src,
-#ifdef RENEWAL
- max(status_get_max_hp(src)/100, 1)
-#else
- 1
-#endif
- , 0);
-
- dir = map_calc_dir(src,bl->x,bl->y);
- if( dir > 0 && dir < 4) x = -i;
- else if( dir > 4 ) x = i;
- else x = 0;
- if( dir > 2 && dir < 6 ) y = -i;
- else if( dir == 7 || dir < 2 ) y = i;
- else y = 0;
- if( (mbl == src || !map_flag_gvg(src->m) && !map[src->m].flag.battleground) && // only NJ_ISSEN don't have slide effect in GVG
- unit_movepos(src, mbl->x+x, mbl->y+y, 1, 1) ) {
- clif_slide(src, src->x, src->y);
- //uncomment this if you want to remove MO_EXTREMITYFIST glitchy walking effect. [malufett]
- //clif_fixpos(src);
- }
- }
- break;
-
- //Splash attack skills.
- case AS_GRIMTOOTH:
- case MC_CARTREVOLUTION:
- case NPC_SPLASHATTACK:
- flag |= SD_PREAMBLE; // a fake packet will be sent for the first target to be hit
- case AS_SPLASHER:
- case SM_MAGNUM:
- case MS_MAGNUM:
- case HT_BLITZBEAT:
- case AC_SHOWER:
- case MA_SHOWER:
- case MG_NAPALMBEAT:
- case MG_FIREBALL:
- case RG_RAID:
- case HW_NAPALMVULCAN:
- case NJ_HUUMA:
- case NJ_BAKUENRYU:
- case ASC_METEORASSAULT:
- case GS_DESPERADO:
- case GS_SPREADATTACK:
- case NPC_EARTHQUAKE:
- case NPC_PULSESTRIKE:
- case NPC_HELLJUDGEMENT:
- case NPC_VAMPIRE_GIFT:
- case RK_IGNITIONBREAK:
- case AB_JUDEX:
- case WL_SOULEXPANSION:
- case WL_CRIMSONROCK:
- case WL_COMET:
- case WL_JACKFROST:
- case RA_ARROWSTORM:
- case RA_WUGDASH:
- case NC_SELFDESTRUCTION:
- case NC_AXETORNADO:
- case GC_ROLLINGCUTTER:
- case GC_COUNTERSLASH:
- case LG_MOONSLASHER:
- case LG_EARTHDRIVE:
- case SR_TIGERCANNON:
- case SR_RAMPAGEBLASTER:
- case SR_SKYNETBLOW:
- case SR_WINDMILL:
- case SR_RIDEINLIGHTNING:
- case WM_SOUND_OF_DESTRUCTION:
- case WM_REVERBERATION_MELEE:
- case WM_REVERBERATION_MAGIC:
- case SO_VARETYR_SPEAR:
- case GN_CART_TORNADO:
- case GN_CARTCANNON:
- case KO_HAPPOKUNAI:
- case KO_HUUMARANKA:
- case KO_MUCHANAGE:
- case KO_BAKURETSU:
- if( flag&1 ) {//Recursive invocation
- // skill_area_temp[0] holds number of targets in area
- // skill_area_temp[1] holds the id of the original target
- // skill_area_temp[2] counts how many targets have already been processed
- int sflag = skill_area_temp[0] & 0xFFF, heal;
- if( flag&SD_LEVEL )
- sflag |= SD_LEVEL; // -1 will be used in packets instead of the skill level
- if( skill_area_temp[1] != bl->id && !(skill_get_inf2(skill_id)&INF2_NPC_SKILL) )
- sflag |= SD_ANIMATION; // original target gets no animation (as well as all NPC skills)
-
- heal = skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, sflag);
- if( skill_id == NPC_VAMPIRE_GIFT && heal > 0 ) {
- clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1);
- status_heal(src,heal,0,0);
- }
- } else {
- switch ( skill_id ) {
- case NJ_BAKUENRYU:
- case LG_EARTHDRIVE:
- case GN_CARTCANNON:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
- case LG_MOONSLASHER:
- clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- break;
- case NPC_EARTHQUAKE://FIXME: Isn't EarthQuake a ground skill after all?
- skill_addtimerskill(src,tick+250,src->id,0,0,skill_id,skill_lv,2,flag|BCT_ENEMY|SD_SPLASH|1);
- default:
- break;
- }
-
- skill_area_temp[0] = 0;
- skill_area_temp[1] = bl->id;
- skill_area_temp[2] = 0;
- if( skill_id == WL_CRIMSONROCK ) {
- skill_area_temp[4] = bl->x;
- skill_area_temp[5] = bl->y;
- }
- if( skill_id == WM_REVERBERATION_MELEE || skill_id == WM_REVERBERATION_MAGIC )
- skill_area_temp[1] = 0;
- // if skill damage should be split among targets, count them
- //SD_LEVEL -> Forced splash damage for Auto Blitz-Beat -> count targets
- //special case: Venom Splasher uses a different range for searching than for splashing
- if( flag&SD_LEVEL || skill_get_nk(skill_id)&NK_SPLASHSPLIT )
- skill_area_temp[0] = map_foreachinrange(skill_area_sub, bl, (skill_id == AS_SPLASHER)?1:skill_get_splash(skill_id, skill_lv), BL_CHAR, src, skill_id, skill_lv, tick, BCT_ENEMY, skill_area_sub_count);
-
- // recursive invocation of skill_castend_damage_id() with flag|1
- map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), ( skill_id == WM_REVERBERATION_MELEE || skill_id == WM_REVERBERATION_MAGIC )?BL_CHAR:splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id);
- }
- break;
-
- case KN_BRANDISHSPEAR:
- case ML_BRANDISH:
- //Coded apart for it needs the flag passed to the damage calculation.
- if (skill_area_temp[1] != bl->id)
- skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag|SD_ANIMATION);
- else
- skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
- break;
-
- case KN_BOWLINGBASH:
- case MS_BOWLINGBASH:
- if(flag&1){
- if(bl->id==skill_area_temp[1])
- break;
- //two hits for 500%
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,SD_ANIMATION);
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,SD_ANIMATION);
- } else {
- int i,c;
- c = skill_get_blewcount(skill_id,skill_lv);
- // keep moving target in the direction that src is looking, square by square
- for(i=0;i<c;i++){
- if (!skill_blown(src,bl,1,(unit_getdir(src)+4)%8,0x1))
- break; //Can't knockback
- skill_area_temp[0] = map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR, src, skill_id, skill_lv, tick, flag|BCT_ENEMY, skill_area_sub_count);
- if( skill_area_temp[0] > 1 ) break; // collision
- }
- clif_blown(bl); //Update target pos.
- if (i!=c) { //Splash
- skill_area_temp[1] = bl->id;
- map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_damage_id);
- }
- //Weirdo dual-hit property, two attacks for 500%
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,0);
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,0);
- }
- break;
-
- case KN_SPEARSTAB:
- if(flag&1) {
- if (bl->id==skill_area_temp[1])
- break;
- if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,SD_ANIMATION))
- skill_blown(src,bl,skill_area_temp[2],-1,0);
- } else {
- int x=bl->x,y=bl->y,i,dir;
- dir = map_calc_dir(bl,src->x,src->y);
- skill_area_temp[1] = bl->id;
- skill_area_temp[2] = skill_get_blewcount(skill_id,skill_lv);
- // all the enemies between the caster and the target are hit, as well as the target
- if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,0))
- skill_blown(src,bl,skill_area_temp[2],-1,0);
- for (i=0;i<4;i++) {
- map_foreachincell(skill_area_sub,bl->m,x,y,BL_CHAR,
- src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
- x += dirx[dir];
- y += diry[dir];
- }
- }
- break;
-
- case TK_TURNKICK:
- case MO_BALKYOUNG: //Active part of the attack. Skill-attack [Skotlex]
- {
- skill_area_temp[1] = bl->id; //NOTE: This is used in skill_castend_nodamage_id to avoid affecting the target.
- if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag))
- map_foreachinrange(skill_area_sub,bl,
- skill_get_splash(skill_id, skill_lv),BL_CHAR,
- src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,
- skill_castend_nodamage_id);
- }
- break;
- case CH_PALMSTRIKE: // Palm Strike takes effect 1sec after casting. [Skotlex]
- // clif_skill_nodamage(src,bl,skill_id,skill_lv,0); //Can't make this one display the correct attack animation delay :/
- clif_damage(src,bl,tick,status_get_amotion(src),0,-1,1,4,0); //Display an absorbed damage attack.
- skill_addtimerskill(src, tick + (1000+status_get_amotion(src)), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag);
- break;
-
- case PR_TURNUNDEAD:
- case ALL_RESURRECTION:
- if (!battle_check_undead(tstatus->race, tstatus->def_ele))
- break;
- skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
-
- case MG_SOULSTRIKE:
- case NPC_DARKSTRIKE:
- case MG_COLDBOLT:
- case MG_FIREBOLT:
- case MG_LIGHTNINGBOLT:
- case WZ_EARTHSPIKE:
- case AL_HEAL:
- case AL_HOLYLIGHT:
- case WZ_JUPITEL:
- case NPC_DARKTHUNDER:
- case PR_ASPERSIO:
- case MG_FROSTDIVER:
- case WZ_SIGHTBLASTER:
- case WZ_SIGHTRASHER:
- case NJ_KOUENKA:
- case NJ_HYOUSENSOU:
- case NJ_HUUJIN:
- case AB_ADORAMUS:
- case AB_RENOVATIO:
- case AB_HIGHNESSHEAL:
- case AB_DUPLELIGHT_MAGIC:
- case WM_METALICSOUND:
- case MH_ERASER_CUTTER:
- skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
-
- case NPC_MAGICALATTACK:
- skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
- sc_start(src,status_skill2sc(skill_id),100,skill_lv,skill_get_time(skill_id,skill_lv));
- break;
-
- case HVAN_CAPRICE: //[blackhole89]
- {
- int ran=rnd()%4;
- int sid = 0;
- switch(ran)
- {
- case 0: sid=MG_COLDBOLT; break;
- case 1: sid=MG_FIREBOLT; break;
- case 2: sid=MG_LIGHTNINGBOLT; break;
- case 3: sid=WZ_EARTHSPIKE; break;
- }
- skill_attack(BF_MAGIC,src,src,bl,sid,skill_lv,tick,flag|SD_LEVEL);
- }
- break;
- case WZ_WATERBALL:
- {
- int range = skill_lv / 2;
- int maxlv = skill_get_max(skill_id); // learnable level
- int count = 0;
- int x, y;
- struct skill_unit* unit;
-
- if( skill_lv > maxlv )
- {
- if( src->type == BL_MOB && skill_lv == 10 )
- range = 4;
- else
- range = maxlv / 2;
- }
-
- for( y = src->y - range; y <= src->y + range; ++y )
- for( x = src->x - range; x <= src->x + range; ++x )
- {
- if( !map_find_skill_unit_oncell(src,x,y,SA_LANDPROTECTOR,NULL,1) )
- {
- if( src->type != BL_PC || map_getcell(src->m,x,y,CELL_CHKWATER) ) // non-players bypass the water requirement
- count++; // natural water cell
- else if( (unit = map_find_skill_unit_oncell(src,x,y,SA_DELUGE,NULL,1)) != NULL || (unit = map_find_skill_unit_oncell(src,x,y,NJ_SUITON,NULL,1)) != NULL )
- {
- count++; // skill-induced water cell
- skill_delunit(unit); // consume cell
- }
- }
- }
-
- if( count > 1 ) // queue the remaining count - 1 timerskill Waterballs
- skill_addtimerskill(src,tick+150,bl->id,0,0,skill_id,skill_lv,count-1,flag);
- }
- skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
-
- case PR_BENEDICTIO:
- //Should attack undead and demons. [Skotlex]
- if (battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race == RC_DEMON)
- skill_attack(BF_MAGIC, src, src, bl, skill_id, skill_lv, tick, flag);
- break;
-
- case SL_SMA:
- status_change_end(src, SC_SMA, INVALID_TIMER);
- case SL_STIN:
- case SL_STUN:
- if (sd && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) {
- status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,500,10);
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
-
- case NPC_DARKBREATH:
- clif_emotion(src,E_AG);
- case SN_FALCONASSAULT:
- case PA_PRESSURE:
- case CR_ACIDDEMONSTRATION:
- case TF_THROWSTONE:
- case NPC_SMOKING:
- case GS_FLING:
- case NJ_ZENYNAGE:
- case GN_THORNS_TRAP:
- case GN_HELLS_PLANT_ATK:
- skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
- /**
- * Rune Knight
- **/
- case RK_DRAGONBREATH: {
- struct status_change *tsc = NULL;
- if( (tsc = status_get_sc(bl)) && (tsc->data[SC_HIDING] )) {
- clif_skill_nodamage(src,src,skill_id,skill_lv,1);
- } else
- skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag);
- }
- break;
-
- case NPC_SELFDESTRUCTION: {
- struct status_change *tsc = NULL;
- if( (tsc = status_get_sc(bl)) && tsc->data[SC_HIDING] )
- break;
- }
- case HVAN_EXPLOSION:
- if (src != bl)
- skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
-
- // Celest
- case PF_SOULBURN:
- if (rnd()%100 < (skill_lv < 5 ? 30 + skill_lv * 10 : 70)) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if (skill_lv == 5)
- skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
- status_percent_damage(src, bl, 0, 100, false);
- } else {
- clif_skill_nodamage(src,src,skill_id,skill_lv,1);
- if (skill_lv == 5)
- skill_attack(BF_MAGIC,src,src,src,skill_id,skill_lv,tick,flag);
- status_percent_damage(src, src, 0, 100, false);
- }
- break;
-
- case NPC_BLOODDRAIN:
- case NPC_ENERGYDRAIN:
- {
- int heal = skill_attack( (skill_id == NPC_BLOODDRAIN) ? BF_WEAPON : BF_MAGIC,
- src, src, bl, skill_id, skill_lv, tick, flag);
- if (heal > 0){
- clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1);
- status_heal(src, heal, 0, 0);
- }
- }
- break;
-
- case GS_BULLSEYE:
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
-
- case NJ_KASUMIKIRI:
- if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag) > 0)
- sc_start(src,SC_HIDING,100,skill_lv,skill_get_time(skill_id,skill_lv));
- break;
- case NJ_KIRIKAGE:
- if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground )
- { //You don't move on GVG grounds.
- short x, y;
- map_search_freecell(bl, 0, &x, &y, 1, 1, 0);
- if (unit_movepos(src, x, y, 0, 0))
- clif_slide(src,src->x,src->y);
- }
- status_change_end(src, SC_HIDING, INVALID_TIMER);
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
- case RK_PHANTOMTHRUST:
- unit_setdir(src,map_calc_dir(src, bl->x, bl->y));
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
-
- skill_blown(src,bl,distance_bl(src,bl)-1,unit_getdir(src),0);
- if( battle_check_target(src,bl,BCT_ENEMY) )
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
-
- case RK_STORMBLAST:
- case RK_CRUSHSTRIKE:
- if( sd ) {
- if( pc_checkskill(sd,RK_RUNEMASTERY) >= ( skill_id == RK_CRUSHSTRIKE ? 7 : 3 ) )
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- else
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- } else //non-sd support
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
- case GC_DARKILLUSION:
- {
- short x, y;
- short dir = map_calc_dir(src,bl->x,bl->y);
-
- if( dir > 0 && dir < 4) x = 2;
- else if( dir > 4 ) x = -2;
- else x = 0;
- if( dir > 2 && dir < 6 ) y = 2;
- else if( dir == 7 || dir < 2 ) y = -2;
- else y = 0;
-
- if( unit_movepos(src, bl->x+x, bl->y+y, 1, 1) )
- {
- clif_slide(src,bl->x+x,bl->y+y);
- clif_fixpos(src); // the official server send these two packts.
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- if( rnd()%100 < 4 * skill_lv )
- skill_castend_damage_id(src,bl,GC_CROSSIMPACT,skill_lv,tick,flag);
- }
-
- }
- break;
-
- case GC_WEAPONCRUSH:
- if( sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == GC_WEAPONBLOCKING )
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- else if( sd )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_GC_WEAPONBLOCKING,0);
- break;
-
- case GC_CROSSRIPPERSLASHER:
- if( sd && !(sc && sc->data[SC_ROLLINGCUTTER]) )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_CONDITION,0);
- else
- {
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- status_change_end(src,SC_ROLLINGCUTTER,INVALID_TIMER);
- }
- break;
-
- case GC_PHANTOMMENACE:
- if( flag&1 )
- { // Only Hits Invisible Targets
- struct status_change *tsc = status_get_sc(bl);
- if(tsc && (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC__INVISIBILITY]) )
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- }
- break;
- case WL_CHAINLIGHTNING:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- skill_addtimerskill(src,tick + 150,bl->id,3,0,WL_CHAINLIGHTNING_ATK,skill_lv,4+skill_lv,flag);
- break;
- case WL_DRAINLIFE:
- {
- int heal = skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
- int rate = 70 + 5 * skill_lv;
-
- heal = heal * (5 + 5 * skill_lv) / 100;
-
- if( bl->type == BL_SKILL )
- heal = 0; // Don't absorb heal from Ice Walls or other skill units.
-
- if( heal && rnd()%100 < rate )
- {
- status_heal(src, heal, 0, 0);
- clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1);
- }
- }
- break;
-
- case WL_TETRAVORTEX:
- if( sd )
- {
- int spheres[5] = { 0, 0, 0, 0, 0 },
- positions[5] = {-1,-1,-1,-1,-1 },
- i, j = 0, k, subskill = 0;
-
- for( i = SC_SPHERE_1; i <= SC_SPHERE_5; i++ )
- if( sc && sc->data[i] )
- {
- spheres[j] = i;
- positions[j] = sc->data[i]->val2;
- j++; //
- }
-
- if( j < 4 )
- { // Need 4 spheres minimum
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
-
- // Sphere Sort, this time from new to old
- for( i = 0; i <= j - 2; i++ )
- for( k = i + 1; k <= j - 1; k++ )
- if( positions[i] < positions[k] )
- {
- swap(positions[i],positions[k]);
- swap(spheres[i],spheres[k]);
- }
-
- k = 0;
- for( i = 0; i < 4; i++ )
- {
- switch( sc->data[spheres[i]]->val1 )
- {
- case WLS_FIRE: subskill = WL_TETRAVORTEX_FIRE; k |= 1; break;
- case WLS_WIND: subskill = WL_TETRAVORTEX_WIND; k |= 4; break;
- case WLS_WATER: subskill = WL_TETRAVORTEX_WATER; k |= 2; break;
- case WLS_STONE: subskill = WL_TETRAVORTEX_GROUND; k |= 8; break;
- }
- skill_addtimerskill(src, tick + i * 200, bl->id, k, 0, subskill, skill_lv, i, flag);
- clif_skill_nodamage(src, bl, subskill, skill_lv, 1);
- status_change_end(src, spheres[i], INVALID_TIMER);
- }
- }
- break;
-
- case WL_RELEASE:
- if( sd )
- {
- int i;
- // Priority is to release SpellBook
- if( sc && sc->data[SC_READING_SB] )
- { // SpellBook
- uint16 skill_id, skill_lv, point, s = 0;
- int spell[SC_MAXSPELLBOOK-SC_SPELLBOOK1 + 1];
-
- for(i = SC_MAXSPELLBOOK; i >= SC_SPELLBOOK1; i--) // List all available spell to be released
- if( sc->data[i] ) spell[s++] = i;
-
- if ( s == 0 )
- break;
-
- i = spell[s==1?0:rand()%s];// Random select of spell to be released.
- if( s && sc->data[i] ){// Now extract the data from the preserved spell
- skill_id = sc->data[i]->val1;
- skill_lv = sc->data[i]->val2;
- point = sc->data[i]->val3;
- status_change_end(src, (sc_type)i, INVALID_TIMER);
- }else //something went wrong :(
- break;
-
- if( sc->data[SC_READING_SB]->val2 > point )
- sc->data[SC_READING_SB]->val2 -= point;
- else // Last spell to be released
- status_change_end(src, SC_READING_SB, INVALID_TIMER);
-
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- if( !skill_check_condition_castbegin(sd, skill_id, skill_lv) )
- break;
-
- switch( skill_get_casttype(skill_id) )
- {
- case CAST_GROUND:
- skill_castend_pos2(src, bl->x, bl->y, skill_id, skill_lv, tick, 0);
- break;
- case CAST_NODAMAGE:
- skill_castend_nodamage_id(src, bl, skill_id, skill_lv, tick, 0);
- break;
- case CAST_DAMAGE:
- skill_castend_damage_id(src, bl, skill_id, skill_lv, tick, 0);
- break;
- }
-
- sd->ud.canact_tick = tick + skill_delayfix(src, skill_id, skill_lv);
- clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, skill_id, skill_lv), 0, 0, 0);
- }
- else
- { // Summon Balls
- int j = 0, k, skele;
- int spheres[5] = { 0, 0, 0, 0, 0 },
- positions[5] = {-1,-1,-1,-1,-1 };
-
- for( i = SC_SPHERE_1; i <= SC_SPHERE_5; i++ )
- if( sc && sc->data[i] )
- {
- spheres[j] = i;
- positions[j] = sc->data[i]->val2;
- sc->data[i]->val2--; // Prepares for next position
- j++;
- }
-
- if( j == 0 )
- { // No Spheres
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_SUMMON_NONE,0);
- break;
- }
-
- // Sphere Sort
- for( i = 0; i <= j - 2; i++ )
- for( k = i + 1; k <= j - 1; k++ )
- if( positions[i] > positions[k] )
- {
- swap(positions[i],positions[k]);
- swap(spheres[i],spheres[k]);
- }
-
- if( skill_lv == 1 ) j = 1; // Limit only to one ball
- for( i = 0; i < j; i++ )
- {
- skele = WL_RELEASE - 5 + sc->data[spheres[i]]->val1 - WLS_FIRE; // Convert Ball Element into Skill ATK for balls
- // WL_SUMMON_ATK_FIRE, WL_SUMMON_ATK_WIND, WL_SUMMON_ATK_WATER, WL_SUMMON_ATK_GROUND
- skill_addtimerskill(src,tick+status_get_adelay(src)*i,bl->id,0,0,skele,sc->data[spheres[i]]->val3,BF_MAGIC,flag|SD_LEVEL);
- status_change_end(src, spheres[i], INVALID_TIMER); // Eliminate ball
- }
- clif_skill_nodamage(src,bl,skill_id,0,1);
- }
- }
- break;
- case WL_FROSTMISTY:
- // Causes Freezing status through walls.
- sc_start(bl,status_skill2sc(skill_id),20+12*skill_lv+(sd ? sd->status.job_level : 50)/5,skill_lv,skill_get_time(skill_id,skill_lv));
- // Doesn't deal damage through non-shootable walls.
- if( path_search(NULL,src->m,src->x,src->y,bl->x,bl->y,1,CELL_CHKWALL) )
- skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag|SD_ANIMATION);
- break;
- case WL_HELLINFERNO:
- skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
- skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag|ELE_DARK);
- break;
- case RA_WUGSTRIKE:
- if( sd && pc_isridingwug(sd) ){
- short x[8]={0,-1,-1,-1,0,1,1,1};
- short y[8]={1,1,0,-1,-1,-1,0,1};
- uint8 dir = map_calc_dir(bl, src->x, src->y);
-
- if( unit_movepos(src, bl->x+x[dir], bl->y+y[dir], 1, 1) )
- {
- clif_slide(src, bl->x+x[dir], bl->y+y[dir]);
- clif_fixpos(src);
- skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
- }
- break;
- }
- case RA_WUGBITE:
- if( path_search(NULL,src->m,src->x,src->y,bl->x,bl->y,1,CELL_CHKNOREACH) ) {
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- }else if( sd && skill_id == RA_WUGBITE ) // Only RA_WUGBITE has the skill fail message.
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
-
- break;
-
- case RA_SENSITIVEKEEN:
- if( bl->type != BL_SKILL ) { // Only Hits Invisible Targets
- struct status_change * tsc = status_get_sc(bl);
- if( tsc && tsc->option&(OPTION_HIDE|OPTION_CLOAK) ){
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
- }
- }
- else
- {
- struct skill_unit *su = BL_CAST(BL_SKILL,bl);
- struct skill_unit_group* sg;
-
- if( su && (sg=su->group) && skill_get_inf2(sg->skill_id)&INF2_TRAP )
- {
- if( !(sg->unit_id == UNT_USED_TRAPS || (sg->unit_id == UNT_ANKLESNARE && sg->val2 != 0 )) )
- {
- struct item item_tmp;
- memset(&item_tmp,0,sizeof(item_tmp));
- item_tmp.nameid = sg->item_id?sg->item_id:ITEMID_TRAP;
- item_tmp.identify = 1;
- if( item_tmp.nameid )
- map_addflooritem(&item_tmp,1,bl->m,bl->x,bl->y,0,0,0,0);
- }
- skill_delunit(su);
- }
- }
- break;
- case NC_INFRAREDSCAN:
- if( flag&1 )
- { //TODO: Need a confirmation if the other type of hidden status is included to be scanned. [Jobbie]
- if( rnd()%100 < 50 )
- sc_start(bl, SC_INFRAREDSCAN, 10000, skill_lv, skill_get_time(skill_id, skill_lv));
- status_change_end(bl, SC_HIDING, INVALID_TIMER);
- status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
- status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); // Need confirm it.
- }
- else
- {
- map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id);
- clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- if( sd ) pc_overheat(sd,1);
- }
- break;
-
- case NC_MAGNETICFIELD:
- sc_start2(bl,SC_MAGNETICFIELD,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv));
- break;
- case SC_FATALMENACE:
- if( flag&1 )
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- else
- {
- short x, y;
- map_search_freecell(src, 0, &x, &y, -1, -1, 0);
- // Destination area
- skill_area_temp[4] = x;
- skill_area_temp[5] = y;
- map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_damage_id);
- skill_addtimerskill(src,tick + 800,src->id,x,y,skill_id,skill_lv,0,flag); // To teleport Self
- clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,6);
- }
- break;
- case LG_PINPOINTATTACK:
- if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground && unit_movepos(src, bl->x, bl->y, 1, 1) )
- clif_slide(src,bl->x,bl->y);
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
-
- case LG_SHIELDSPELL:
- // flag&1: Phisycal Attack, flag&2: Magic Attack.
- skill_attack((flag&1)?BF_WEAPON:BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
-
- case LG_OVERBRAND:
- skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag|SD_LEVEL);
- break;
-
- case LG_OVERBRAND_BRANDISH:
- skill_addtimerskill(src, tick + status_get_amotion(src)*8/10, bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|SD_LEVEL);
- break;
- case SR_DRAGONCOMBO:
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
-
- case SR_KNUCKLEARROW:
- if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground && unit_movepos(src, bl->x, bl->y, 1, 1) ) {
- clif_slide(src,bl->x,bl->y);
- clif_fixpos(src); // Aegis send this packet too.
- }
-
- if( flag&1 )
- skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag|SD_LEVEL);
- else
- skill_addtimerskill(src, tick + 300, bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|SD_LEVEL|2);
- break;
-
- case SR_HOWLINGOFLION:
- status_change_end(bl, SC_SWINGDANCE, INVALID_TIMER);
- status_change_end(bl, SC_SYMPHONYOFLOVER, INVALID_TIMER);
- status_change_end(bl, SC_MOONLITSERENADE, INVALID_TIMER);
- status_change_end(bl, SC_RUSHWINDMILL, INVALID_TIMER);
- status_change_end(bl, SC_ECHOSONG, INVALID_TIMER);
- status_change_end(bl, SC_HARMONIZE, INVALID_TIMER);
- status_change_end(bl, SC_SIRCLEOFNATURE, INVALID_TIMER);
- status_change_end(bl, SC_SATURDAYNIGHTFEVER, INVALID_TIMER);
- status_change_end(bl, SC_DANCEWITHWUG, INVALID_TIMER);
- status_change_end(bl, SC_LERADSDEW, INVALID_TIMER);
- status_change_end(bl, SC_MELODYOFSINK, INVALID_TIMER);
- status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER);
- status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER);
- skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag|SD_ANIMATION);
- break;
-
- case SR_EARTHSHAKER:
- if( flag&1 ) { //by default cloaking skills are remove by aoe skills so no more checking/removing except hiding and cloaking exceed.
- skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
- status_change_end(bl, SC_HIDING, INVALID_TIMER);
- status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
- } else{
- map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id);
- clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- }
- break;
-
- case WM_LULLABY_DEEPSLEEP:
- if( bl != src && rnd()%100 < 88 + 2 * skill_lv )
- sc_start(bl,status_skill2sc(skill_id),100,skill_lv,skill_get_time(skill_id,skill_lv));
- break;
-
- case SO_POISON_BUSTER: {
- struct status_change *tsc = status_get_sc(bl);
- if( tsc && tsc->data[SC_POISON] ) {
- skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
- status_change_end(bl, SC_POISON, INVALID_TIMER);
- }
- else if( sd )
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
- }
- break;
-
- case GN_SPORE_EXPLOSION:
- if( flag&1 )
- skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
- else {
- clif_skill_nodamage(src, bl, skill_id, 0, 1);
- skill_addtimerskill(src, gettick() + skill_get_time(skill_id, skill_lv) - 1000, bl->id, 0, 0, skill_id, skill_lv, 0, 0);
- }
- break;
-
- case EL_FIRE_BOMB:
- case EL_FIRE_WAVE:
- case EL_WATER_SCREW:
- case EL_HURRICANE:
- case EL_TYPOON_MIS:
- if( flag&1 )
- skill_attack(skill_get_type(skill_id+1),src,src,bl,skill_id+1,skill_lv,tick,flag);
- else {
- int i = skill_get_splash(skill_id,skill_lv);
- clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
- clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- if( rnd()%100 < 30 )
- map_foreachinrange(skill_area_sub,bl,i,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
- else
- skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
- }
- break;
-
- case EL_ROCK_CRUSHER:
- clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
- clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- if( rnd()%100 < 50 )
- skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
- else
- skill_attack(BF_WEAPON,src,src,bl,EL_ROCK_CRUSHER_ATK,skill_lv,tick,flag);
- break;
-
- case EL_STONE_RAIN:
- if( flag&1 )
- skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
- else {
- int i = skill_get_splash(skill_id,skill_lv);
- clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
- clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- if( rnd()%100 < 30 )
- map_foreachinrange(skill_area_sub,bl,i,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
- else
- skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
- }
- break;
-
- case EL_FIRE_ARROW:
- case EL_ICE_NEEDLE:
- case EL_WIND_SLASH:
- case EL_STONE_HAMMER:
- clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
- clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
- break;
-
- case EL_TIDAL_WEAPON:
- if( src->type == BL_ELEM ) {
- struct elemental_data *ele = BL_CAST(BL_ELEM,src);
- struct status_change *sc = status_get_sc(&ele->bl);
- struct status_change *tsc = status_get_sc(bl);
- sc_type type = status_skill2sc(skill_id), type2;
- type2 = type-1;
-
- clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
- clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
- elemental_clean_single_effect(ele, skill_id);
- }
- if( rnd()%100 < 50 )
- skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
- else {
- sc_start(src,type2,100,skill_lv,skill_get_time(skill_id,skill_lv));
- sc_start(battle_get_master(src),type,100,ele->bl.id,skill_get_time(skill_id,skill_lv));
- }
- clif_skill_nodamage(src,src,skill_id,skill_lv,1);
- }
- break;
-
-
- //recursive homon skill
- case MH_MAGMA_FLOW:
- case MH_XENO_SLASHER:
- case MH_HEILIGE_STANGE:
- if(flag & 1)
- skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
- else {
- map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag | BCT_ENEMY | SD_SPLASH | 1, skill_castend_damage_id);
- }
- break;
-
- case MH_STAHL_HORN:
- case MH_NEEDLE_OF_PARALYZE:
- skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
- break;
- case MH_TINDER_BREAKER:
- if (unit_movepos(src, bl->x, bl->y, 1, 1)) {
-#if PACKETVER >= 20111005
- clif_snap(src, bl->x, bl->y);
-#else
- clif_skill_poseffect(src,skill_id,skill_lv,bl->x,bl->y,tick);
-#endif
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start4(bl,SC_CLOSECONFINE2,100,skill_lv,src->id,0,0,skill_get_time(skill_id,skill_lv)));
- skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
- break;
-
- case 0:/* no skill - basic/normal attack */
- if(sd) {
- if (flag & 3){
- if (bl->id != skill_area_temp[1])
- skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, SD_LEVEL|flag);
- } else {
- skill_area_temp[1] = bl->id;
- map_foreachinrange(skill_area_sub, bl,
- sd->bonus.splash_range, BL_CHAR,
- src, skill_id, skill_lv, tick, flag | BCT_ENEMY | 1,
- skill_castend_damage_id);
- flag|=1; //Set flag to 1 so ammo is not double-consumed. [Skotlex]
- }
- }
- break;
-
- default:
- ShowWarning("skill_castend_damage_id: Unknown skill used:%d\n",skill_id);
- clif_skill_damage(src, bl, tick, status_get_amotion(src), tstatus->dmotion,
- 0, abs(skill_get_num(skill_id, skill_lv)),
- skill_id, skill_lv, skill_get_hit(skill_id));
- map_freeblock_unlock();
- return 1;
- }
-
- if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] ) //Should only remove after the skill has been casted.
- status_change_end(src,SC_CURSEDCIRCLE_ATKER,INVALID_TIMER);
-
- map_freeblock_unlock();
-
- if( sd && !(flag&1) )
- {// ensure that the skill last-cast tick is recorded
- sd->canskill_tick = gettick();
-
- if( sd->state.arrow_atk )
- {// consume arrow on last invocation to this skill.
- battle_consume_ammo(sd, skill_id, skill_lv);
- }
-
- // perform skill requirement consumption
- skill_consume_requirement(sd,skill_id,skill_lv,2);
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
-{
- struct map_session_data *sd, *dstsd;
- struct mob_data *md, *dstmd;
- struct homun_data *hd;
- struct mercenary_data *mer;
- struct status_data *sstatus, *tstatus;
- struct status_change *tsc;
- struct status_change_entry *tsce;
-
- int i = 0;
- enum sc_type type;
-
- if(skill_id > 0 && !skill_lv) return 0; // celest
-
- nullpo_retr(1, src);
- nullpo_retr(1, bl);
-
- if (src->m != bl->m)
- return 1;
-
- sd = BL_CAST(BL_PC, src);
- hd = BL_CAST(BL_HOM, src);
- md = BL_CAST(BL_MOB, src);
- mer = BL_CAST(BL_MER, src);
-
- dstsd = BL_CAST(BL_PC, bl);
- dstmd = BL_CAST(BL_MOB, bl);
-
- if(bl->prev == NULL)
- return 1;
- if(status_isdead(src))
- return 1;
-
- if( src != bl && status_isdead(bl) ) {
- /**
- * Skills that may be cast on dead targets
- **/
- switch( skill_id ) {
- case NPC_WIDESOULDRAIN:
- case PR_REDEMPTIO:
- case ALL_RESURRECTION:
- case WM_DEADHILLHERE:
- break;
- default:
- return 1;
- }
- }
-
- tstatus = status_get_status_data(bl);
- sstatus = status_get_status_data(src);
-
- //Check for undead skills that convert a no-damage skill into a damage one. [Skotlex]
- switch (skill_id) {
- case HLIF_HEAL: //[orn]
- if (bl->type != BL_HOM) {
- if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0) ;
- break ;
- }
- case AL_HEAL:
- case ALL_RESURRECTION:
- case PR_ASPERSIO:
- /**
- * Arch Bishop
- **/
- case AB_RENOVATIO:
- case AB_HIGHNESSHEAL:
- //Apparently only player casted skills can be offensive like this.
- if (sd && battle_check_undead(tstatus->race,tstatus->def_ele)) {
- if (battle_check_target(src, bl, BCT_ENEMY) < 1) {
- //Offensive heal does not works on non-enemies. [Skotlex]
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- return skill_castend_damage_id (src, bl, skill_id, skill_lv, tick, flag);
- }
- break;
- case NPC_SMOKING: //Since it is a self skill, this one ends here rather than in damage_id. [Skotlex]
- return skill_castend_damage_id (src, bl, skill_id, skill_lv, tick, flag);
- case MH_STEINWAND: {
- struct block_list *s_src = battle_get_master(src);
- short ret = 0;
- if(!skill_check_unit_range(src, src->x, src->y, skill_id, skill_lv)) //prevent reiteration
- ret = skill_castend_pos2(src,src->x,src->y,skill_id,skill_lv,tick,flag); //cast on homon
- if(s_src && !skill_check_unit_range(s_src, s_src->x, s_src->y, skill_id, skill_lv))
- ret |= skill_castend_pos2(s_src,s_src->x,s_src->y,skill_id,skill_lv,tick,flag); //cast on master
- if (hd)
- skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
- return ret;
- }
- break;
- default:
- //Skill is actually ground placed.
- if (src == bl && skill_get_unit_id(skill_id,0))
- return skill_castend_pos2(src,bl->x,bl->y,skill_id,skill_lv,tick,0);
- }
-
- type = status_skill2sc(skill_id);
- tsc = status_get_sc(bl);
- tsce = (tsc && type != -1)?tsc->data[type]:NULL;
-
- if (src!=bl && type > -1 &&
- (i = skill_get_ele(skill_id, skill_lv)) > ELE_NEUTRAL &&
- skill_get_inf(skill_id) != INF_SUPPORT_SKILL &&
- battle_attr_fix(NULL, NULL, 100, i, tstatus->def_ele, tstatus->ele_lv) <= 0)
- return 1; //Skills that cause an status should be blocked if the target element blocks its element.
-
- map_freeblock_lock();
- switch(skill_id)
- {
- case HLIF_HEAL: //[orn]
- case AL_HEAL:
- /**
- * Arch Bishop
- **/
- case AB_HIGHNESSHEAL:
- {
- int heal = skill_calc_heal(src, bl, (skill_id == AB_HIGHNESSHEAL)?AL_HEAL:skill_id, (skill_id == AB_HIGHNESSHEAL)?10:skill_lv, true);
- int heal_get_jobexp;
- //Highness Heal: starts at 1.5 boost + 0.5 for each level
- if( skill_id == AB_HIGHNESSHEAL ) {
- heal = heal * ( 15 + 5 * skill_lv ) / 10;
- }
- if( status_isimmune(bl) ||
- (dstmd && (dstmd->class_ == MOBID_EMPERIUM || mob_is_battleground(dstmd))) ||
- (dstsd && pc_ismadogear(dstsd)) )//Mado is immune to heal
- heal=0;
-
- if( sd && dstsd && sd->status.partner_id == dstsd->status.char_id && (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.sex == 0 )
- heal = heal*2;
-
- if( tsc && tsc->count )
- {
- if( tsc->data[SC_KAITE] && !(sstatus->mode&MD_BOSS) )
- { //Bounce back heal
- if (--tsc->data[SC_KAITE]->val2 <= 0)
- status_change_end(bl, SC_KAITE, INVALID_TIMER);
- if (src == bl)
- heal=0; //When you try to heal yourself under Kaite, the heal is voided.
- else {
- bl = src;
- dstsd = sd;
- }
- }
- else if (tsc->data[SC_BERSERK] || tsc->data[SC_SATURDAYNIGHTFEVER] || tsc->data[SC__BLOODYLUST])
- heal = 0; //Needed so that it actually displays 0 when healing.
- }
- clif_skill_nodamage (src, bl, skill_id, heal, 1);
- if( tsc && tsc->data[SC_AKAITSUKI] && heal && skill_id != HLIF_HEAL )
- heal = ~heal + 1;
- heal_get_jobexp = status_heal(bl,heal,0,0);
-
- if(sd && dstsd && heal > 0 && sd != dstsd && 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 (sd, bl, 0, heal_get_jobexp, false);
- }
- }
- break;
-
- case PR_REDEMPTIO:
- if (sd && !(flag&1)) {
- if (sd->status.party_id == 0) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- skill_area_temp[0] = 0;
- party_foreachsamemap(skill_area_sub,
- sd,skill_get_splash(skill_id, skill_lv),
- src,skill_id,skill_lv,tick, flag|BCT_PARTY|1,
- skill_castend_nodamage_id);
- if (skill_area_temp[0] == 0) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- skill_area_temp[0] = 5 - skill_area_temp[0]; // The actual penalty...
- if (skill_area_temp[0] > 0 && !map[src->m].flag.noexppenalty) { //Apply penalty
- sd->status.base_exp -= min(sd->status.base_exp, pc_nextbaseexp(sd) * skill_area_temp[0] * 2/1000); //0.2% penalty per each.
- sd->status.job_exp -= min(sd->status.job_exp, pc_nextjobexp(sd) * skill_area_temp[0] * 2/1000);
- clif_updatestatus(sd,SP_BASEEXP);
- clif_updatestatus(sd,SP_JOBEXP);
- }
- status_set_hp(src, 1, 0);
- status_set_sp(src, 0, 0);
- break;
- } else if (status_isdead(bl) && flag&1) { //Revive
- skill_area_temp[0]++; //Count it in, then fall-through to the Resurrection code.
- skill_lv = 3; //Resurrection level 3 is used
- } else //Invalid target, skip resurrection.
- break;
-
- case ALL_RESURRECTION:
- if(sd && (map_flag_gvg(bl->m) || map[bl->m].flag.battleground))
- { //No reviving in WoE grounds!
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- if (!status_isdead(bl))
- break;
- {
- int per = 0, sper = 0;
- if (tsc && tsc->data[SC_HELLPOWER])
- break;
-
- if (map[bl->m].flag.pvp && dstsd && dstsd->pvp_point < 0)
- break;
-
- switch(skill_lv){
- case 1: per=10; break;
- case 2: per=30; break;
- case 3: per=50; break;
- case 4: per=80; break;
- }
- if(dstsd && dstsd->special_state.restart_full_recover)
- per = sper = 100;
- if (status_revive(bl, per, sper))
- {
- clif_skill_nodamage(src,bl,ALL_RESURRECTION,skill_lv,1); //Both Redemptio and Res show this skill-animation.
- if(sd && dstsd && battle_config.resurrection_exp > 0)
- {
- int exp = 0,jexp = 0;
- int lv = dstsd->status.base_level - sd->status.base_level, jlv = dstsd->status.job_level - sd->status.job_level;
- if(lv > 0 && pc_nextbaseexp(dstsd)) {
- exp = (int)((double)dstsd->status.base_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.);
- if (exp < 1) exp = 1;
- }
- if(jlv > 0 && pc_nextjobexp(dstsd)) {
- jexp = (int)((double)dstsd->status.job_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.);
- if (jexp < 1) jexp = 1;
- }
- if(exp > 0 || jexp > 0)
- pc_gainexp (sd, bl, exp, jexp, false);
- }
- }
- }
- break;
-
- case AL_DECAGI:
- case MER_DECAGI:
- clif_skill_nodamage (src, bl, skill_id, skill_lv,
- sc_start(bl, type, (40 + skill_lv * 2 + (status_get_lv(src) + sstatus->int_)/5), skill_lv, skill_get_time(skill_id,skill_lv)));
- break;
-
- case AL_CRUCIS:
- if (flag&1)
- sc_start(bl,type, 23+skill_lv*4 +status_get_lv(src) -status_get_lv(bl), skill_lv,skill_get_time(skill_id,skill_lv));
- else {
- map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv), BL_CHAR,
- src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- }
- break;
-
- case PR_LEXDIVINA:
- case MER_LEXDIVINA:
- if( tsce )
- status_change_end(bl,type, INVALID_TIMER);
- else
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
- clif_skill_nodamage (src, bl, skill_id, skill_lv, 1);
- break;
-
- case SA_ABRACADABRA:
- {
- int abra_skill_id = 0, abra_skill_lv;
- do {
- i = rnd() % MAX_SKILL_ABRA_DB;
- abra_skill_id = skill_abra_db[i].skill_id;
- } while (abra_skill_id == 0 ||
- skill_abra_db[i].req_lv > skill_lv || //Required lv for it to appear
- rnd()%10000 >= skill_abra_db[i].per
- );
- abra_skill_lv = min(skill_lv, skill_get_max(abra_skill_id));
- clif_skill_nodamage (src, bl, skill_id, skill_lv, 1);
-
- if( sd )
- {// player-casted
- sd->state.abra_flag = 1;
- sd->skillitem = abra_skill_id;
- sd->skillitemlv = abra_skill_lv;
- clif_item_skill(sd, abra_skill_id, abra_skill_lv);
- }
- else
- {// mob-casted
- struct unit_data *ud = unit_bl2ud(src);
- int inf = skill_get_inf(abra_skill_id);
- int target_id = 0;
- if (!ud) break;
- if (inf&INF_SELF_SKILL || inf&INF_SUPPORT_SKILL) {
- if (src->type == BL_PET)
- bl = (struct block_list*)((TBL_PET*)src)->msd;
- if (!bl) bl = src;
- unit_skilluse_id(src, bl->id, abra_skill_id, abra_skill_lv);
- } else { //Assume offensive skills
- if (ud->target)
- target_id = ud->target;
- else switch (src->type) {
- case BL_MOB: target_id = ((TBL_MOB*)src)->target_id; break;
- case BL_PET: target_id = ((TBL_PET*)src)->target_id; break;
- }
- if (!target_id)
- break;
- if (skill_get_casttype(abra_skill_id) == CAST_GROUND) {
- bl = map_id2bl(target_id);
- if (!bl) bl = src;
- unit_skilluse_pos(src, bl->x, bl->y, abra_skill_id, abra_skill_lv);
- } else
- unit_skilluse_id(src, target_id, abra_skill_id, abra_skill_lv);
- }
- }
- }
- break;
-
- case SA_COMA:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time2(skill_id,skill_lv)));
- break;
- case SA_FULLRECOVERY:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if (status_isimmune(bl))
- break;
- status_percent_heal(bl, 100, 100);
- break;
- case NPC_ALLHEAL:
- {
- int heal;
- if( status_isimmune(bl) )
- break;
- heal = status_percent_heal(bl, 100, 0);
- clif_skill_nodamage(NULL, bl, AL_HEAL, heal, 1);
- if( dstmd )
- { // Reset Damage Logs
- memset(dstmd->dmglog, 0, sizeof(dstmd->dmglog));
- dstmd->tdmg = 0;
- }
- }
- break;
- case SA_SUMMONMONSTER:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if (sd) mob_once_spawn(sd, src->m, src->x, src->y," --ja--", -1, 1, "", SZ_SMALL, AI_NONE);
- break;
- case SA_LEVELUP:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if (sd && pc_nextbaseexp(sd)) pc_gainexp(sd, NULL, pc_nextbaseexp(sd) * 10 / 100, 0, false);
- break;
- case SA_INSTANTDEATH:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- status_set_hp(bl,1,0);
- break;
- case SA_QUESTION:
- case SA_GRAVITY:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
- case SA_CLASSCHANGE:
- case SA_MONOCELL:
- if (dstmd)
- {
- int class_;
- if ( sd && dstmd->status.mode&MD_BOSS )
- {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- class_ = skill_id==SA_MONOCELL?1002:mob_get_random_id(4, 1, 0);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- mob_class_change(dstmd,class_);
- if( tsc && dstmd->status.mode&MD_BOSS )
- {
- const enum sc_type scs[] = { SC_QUAGMIRE, SC_PROVOKE, SC_ROKISWEIL, SC_GRAVITATION, SC_SUITON, SC_STRIPWEAPON, SC_STRIPSHIELD, SC_STRIPARMOR, SC_STRIPHELM, SC_BLADESTOP };
- for (i = SC_COMMON_MIN; i <= SC_COMMON_MAX; i++)
- if (tsc->data[i]) status_change_end(bl, (sc_type)i, INVALID_TIMER);
- for (i = 0; i < ARRAYLENGTH(scs); i++)
- if (tsc->data[scs[i]]) status_change_end(bl, scs[i], INVALID_TIMER);
- }
- }
- break;
- case SA_DEATH:
- if ( sd && dstmd && dstmd->status.mode&MD_BOSS )
- {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- status_kill(bl);
- break;
- case SA_REVERSEORCISH:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv)));
- break;
- case SA_FORTUNE:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if(sd) pc_getzeny(sd,status_get_lv(bl)*100,LOG_TYPE_STEAL,NULL);
- break;
- case SA_TAMINGMONSTER:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if (sd && dstmd) {
- ARR_FIND( 0, MAX_PET_DB, i, dstmd->class_ == pet_db[i].class_ );
- if( i < MAX_PET_DB )
- pet_catch_process1(sd, dstmd->class_);
- }
- break;
-
- case CR_PROVIDENCE:
- if(sd && dstsd){ //Check they are not another crusader [Skotlex]
- if ((dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock();
- return 1;
- }
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- break;
-
- case CG_MARIONETTE:
- {
- struct status_change* sc = status_get_sc(src);
-
- if( sd && dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER && dstsd->status.sex == sd->status.sex )
- {// Cannot cast on another bard/dancer-type class of the same gender as caster
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock();
- return 1;
- }
-
- if( sc && tsc )
- {
- if( !sc->data[SC_MARIONETTE] && !tsc->data[SC_MARIONETTE2] )
- {
- sc_start(src,SC_MARIONETTE,100,bl->id,skill_get_time(skill_id,skill_lv));
- sc_start(bl,SC_MARIONETTE2,100,src->id,skill_get_time(skill_id,skill_lv));
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- else
- if( sc->data[SC_MARIONETTE ] && sc->data[SC_MARIONETTE ]->val1 == bl->id &&
- tsc->data[SC_MARIONETTE2] && tsc->data[SC_MARIONETTE2]->val1 == src->id )
- {
- status_change_end(src, SC_MARIONETTE, INVALID_TIMER);
- status_change_end(bl, SC_MARIONETTE2, INVALID_TIMER);
- }
- else
- {
- if( sd )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
-
- map_freeblock_unlock();
- return 1;
- }
- }
- }
- break;
-
- case RG_CLOSECONFINE:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start4(bl,type,100,skill_lv,src->id,0,0,skill_get_time(skill_id,skill_lv)));
- 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 (dstsd) {
- if(dstsd->status.weapon == W_FIST ||
- (dstsd->sc.count && !dstsd->sc.data[type] &&
- ( //Allow re-enchanting to lenghten time. [Skotlex]
- dstsd->sc.data[SC_FIREWEAPON] ||
- dstsd->sc.data[SC_WATERWEAPON] ||
- dstsd->sc.data[SC_WINDWEAPON] ||
- dstsd->sc.data[SC_EARTHWEAPON] ||
- dstsd->sc.data[SC_SHADOWWEAPON] ||
- dstsd->sc.data[SC_GHOSTWEAPON] ||
- dstsd->sc.data[SC_ENCPOISON]
- ))
- ) {
- if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
- break;
- }
- }
- // 100% success rate at lv4 & 5, but lasts longer at lv5
- if(!clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start(bl,type,(60+skill_lv*10),skill_lv, skill_get_time(skill_id,skill_lv)))) {
- if (sd)
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- if (skill_break_equip(bl, EQP_WEAPON, 10000, BCT_PARTY) && sd && sd != dstsd)
- clif_displaymessage(sd->fd, msg_txt(669));
- }
- break;
-
- case PR_ASPERSIO:
- if (sd && dstmd) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
- break;
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- break;
-
- case ITEM_ENCHANTARMS:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start2(bl,type,100,skill_lv,
- skill_get_ele(skill_id,skill_lv), skill_get_time(skill_id,skill_lv)));
- break;
-
- case TK_SEVENWIND:
- switch(skill_get_ele(skill_id,skill_lv)) {
- case ELE_EARTH : type = SC_EARTHWEAPON; break;
- case ELE_WIND : type = SC_WINDWEAPON; break;
- case ELE_WATER : type = SC_WATERWEAPON; break;
- case ELE_FIRE : type = SC_FIREWEAPON; break;
- case ELE_GHOST : type = SC_GHOSTWEAPON; break;
- case ELE_DARK : type = SC_SHADOWWEAPON; break;
- case ELE_HOLY : type = SC_ASPERSIO; break;
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
-
- sc_start(bl,SC_SEVENWIND,100,skill_lv,skill_get_time(skill_id,skill_lv));
-
- break;
-
- case PR_KYRIE:
- case MER_KYRIE:
- clif_skill_nodamage(bl,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- break;
- //Passive Magnum, should had been casted on yourself.
- case SM_MAGNUM:
- case MS_MAGNUM:
- skill_area_temp[1] = 0;
- map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv), BL_SKILL|BL_CHAR,
- src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1, skill_castend_damage_id);
- clif_skill_nodamage (src,src,skill_id,skill_lv,1);
- // Initiate 10% of your damage becomes fire element.
- sc_start4(src,SC_WATK_ELEMENT,100,3,20,0,0,skill_get_time2(skill_id, skill_lv));
- if( sd )
- skill_blockpc_start(sd, skill_id, skill_get_time(skill_id, skill_lv));
- else if( bl->type == BL_MER )
- skill_blockmerc_start((TBL_MER*)bl, skill_id, skill_get_time(skill_id, skill_lv));
- break;
-
- case TK_JUMPKICK:
- /* Check if the target is an enemy; if not, skill should fail so the character doesn't unit_movepos (exploitable) */
- if( battle_check_target(src, bl, BCT_ENEMY) > 0 )
- {
- if( unit_movepos(src, bl->x, bl->y, 1, 1) )
- {
- skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
- clif_slide(src,bl->x,bl->y);
- }
- }
- else
- clif_skill_fail(sd,skill_id,USESKILL_FAIL,0);
- break;
-
- case AL_INCAGI:
- case AL_BLESSING:
- case MER_INCAGI:
- case MER_BLESSING:
- if (dstsd != NULL && tsc->data[SC_CHANGEUNDEAD]) {
- skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag);
- break;
- }
- case PR_SLOWPOISON:
- case PR_IMPOSITIO:
- case PR_LEXAETERNA:
- case PR_SUFFRAGIUM:
- case PR_BENEDICTIO:
- case LK_BERSERK:
- case MS_BERSERK:
- case KN_AUTOCOUNTER:
- case KN_TWOHANDQUICKEN:
- case KN_ONEHAND:
- case MER_QUICKEN:
- case CR_SPEARQUICKEN:
- case CR_REFLECTSHIELD:
- case MS_REFLECTSHIELD:
- case AS_POISONREACT:
- case MC_LOUD:
- case MG_ENERGYCOAT:
- case MO_EXPLOSIONSPIRITS:
- case MO_STEELBODY:
- case MO_BLADESTOP:
- case LK_AURABLADE:
- case LK_PARRYING:
- case MS_PARRYING:
- case LK_CONCENTRATION:
- case WS_CARTBOOST:
- case SN_SIGHT:
- case WS_MELTDOWN:
- case WS_OVERTHRUSTMAX:
- case ST_REJECTSWORD:
- case HW_MAGICPOWER:
- case PF_MEMORIZE:
- case PA_SACRIFICE:
- case ASC_EDP:
- case PF_DOUBLECASTING:
- case SG_SUN_COMFORT:
- case SG_MOON_COMFORT:
- case SG_STAR_COMFORT:
- case NPC_HALLUCINATION:
- case GS_MADNESSCANCEL:
- case GS_ADJUSTMENT:
- case GS_INCREASING:
- case NJ_KASUMIKIRI:
- case NJ_UTSUSEMI:
- case NJ_NEN:
- case NPC_DEFENDER:
- case NPC_MAGICMIRROR:
- case ST_PRESERVE:
- case NPC_INVINCIBLE:
- case NPC_INVINCIBLEOFF:
- case RK_DEATHBOUND:
- case AB_RENOVATIO:
- case AB_EXPIATIO:
- case AB_DUPLELIGHT:
- case AB_SECRAMENT:
- case NC_ACCELERATION:
- case NC_HOVERING:
- case NC_SHAPESHIFT:
- case WL_RECOGNIZEDSPELL:
- case GC_VENOMIMPRESS:
- case SC_DEADLYINFECT:
- case LG_EXEEDBREAK:
- case LG_PRESTIGE:
- case SR_CRESCENTELBOW:
- case SR_LIGHTNINGWALK:
- case SR_GENTLETOUCH_ENERGYGAIN:
- case GN_CARTBOOST:
- case KO_MEIKYOUSISUI:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- break;
-
- case SO_STRIKING:
- if (sd) {
- int bonus = 25 + 10 * skill_lv;
- bonus += (pc_checkskill(sd, SA_FLAMELAUNCHER)+pc_checkskill(sd, SA_FROSTWEAPON)+pc_checkskill(sd, SA_LIGHTNINGLOADER)+pc_checkskill(sd, SA_SEISMICWEAPON))*5;
- clif_skill_nodamage( src, bl, skill_id, skill_lv,
- battle_check_target(src,bl,BCT_PARTY) ?
- sc_start2(bl, type, 100, skill_lv, bonus, skill_get_time(skill_id,skill_lv)) :
- 0
- );
- }
- break;
-
- case NPC_STOP:
- if( clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start2(bl,type,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv)) ) )
- sc_start2(src,type,100,skill_lv,bl->id,skill_get_time(skill_id,skill_lv));
- break;
- case HP_ASSUMPTIO:
- if( sd && dstmd )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- else
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- break;
- case MG_SIGHT:
- case MER_SIGHT:
- case AL_RUWACH:
- case WZ_SIGHTBLASTER:
- case NPC_WIDESIGHT:
- case NPC_STONESKIN:
- case NPC_ANTIMAGIC:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start2(bl,type,100,skill_lv,skill_id,skill_get_time(skill_id,skill_lv)));
- break;
- case HLIF_AVOID:
- case HAMI_DEFENCE:
- i = skill_get_time(skill_id,skill_lv);
- clif_skill_nodamage(bl,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,i)); // Master
- clif_skill_nodamage(src,src,skill_id,skill_lv,sc_start(src,type,100,skill_lv,i)); // Homunc
- break;
- case NJ_BUNSINJYUTSU:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- status_change_end(bl, SC_NEN, INVALID_TIMER);
- break;
-/* Was modified to only affect targetted char. [Skotlex]
- case HP_ASSUMPTIO:
- if (flag&1)
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
- else
- {
- map_foreachinrange(skill_area_sub, bl,
- skill_get_splash(skill_id, skill_lv), BL_PC,
- src, skill_id, skill_lv, tick, flag|BCT_ALL|1,
- skill_castend_nodamage_id);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-*/
- case SM_ENDURE:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- if (sd)
- skill_blockpc_start (sd, skill_id, skill_get_time2(skill_id,skill_lv));
- break;
-
- case AS_ENCHANTPOISON: // Prevent spamming [Valaris]
- if (sd && dstsd && dstsd->sc.count) {
- if (dstsd->sc.data[SC_FIREWEAPON] ||
- dstsd->sc.data[SC_WATERWEAPON] ||
- dstsd->sc.data[SC_WINDWEAPON] ||
- dstsd->sc.data[SC_EARTHWEAPON] ||
- dstsd->sc.data[SC_SHADOWWEAPON] ||
- dstsd->sc.data[SC_GHOSTWEAPON]
- // dstsd->sc.data[SC_ENCPOISON] //People say you should be able to recast to lengthen the timer. [Skotlex]
- ) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- break;
-
- case LK_TENSIONRELAX:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start4(bl,type,100,skill_lv,0,0,skill_get_time2(skill_id,skill_lv),
- skill_get_time(skill_id,skill_lv)));
- break;
-
- case MC_CHANGECART:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
-
- case TK_MISSION:
- if (sd) {
- int id;
- if (sd->mission_mobid && (sd->mission_count || rnd()%100)) { //Cannot change target when already have one
- clif_mission_info(sd, sd->mission_mobid, sd->mission_count);
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- id = mob_get_random_id(0,0xF, sd->status.base_level);
- if (!id) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- sd->mission_mobid = id;
- sd->mission_count = 0;
- pc_setglobalreg(sd,"TK_MISSION_ID", id);
- clif_mission_info(sd, id, 0);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case AC_CONCENTRATION:
- {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- map_foreachinrange( status_change_timer_sub, src,
- skill_get_splash(skill_id, skill_lv), BL_CHAR,
- src,NULL,type,tick);
- }
- break;
-
- case SM_PROVOKE:
- case SM_SELFPROVOKE:
- case MER_PROVOKE:
- if( (tstatus->mode&MD_BOSS) || battle_check_undead(tstatus->race,tstatus->def_ele) )
- {
- map_freeblock_unlock();
- return 1;
- }
- //TODO: How much does base level affects? Dummy value of 1% per level difference used. [Skotlex]
- clif_skill_nodamage(src,bl,skill_id == SM_SELFPROVOKE ? SM_PROVOKE : skill_id,skill_lv,
- (i = sc_start(bl,type, skill_id == SM_SELFPROVOKE ? 100:( 50 + 3*skill_lv + status_get_lv(src) - status_get_lv(bl)), skill_lv, skill_get_time(skill_id,skill_lv))));
- if( !i )
- {
- if( sd )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock();
- return 0;
- }
- unit_skillcastcancel(bl, 2);
-
- if( tsc && tsc->count )
- {
- status_change_end(bl, SC_FREEZE, INVALID_TIMER);
- if( tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE )
- status_change_end(bl, SC_STONE, INVALID_TIMER);
- status_change_end(bl, SC_SLEEP, INVALID_TIMER);
- status_change_end(bl, SC_TRICKDEAD, INVALID_TIMER);
- }
-
- if( dstmd )
- {
- dstmd->state.provoke_flag = src->id;
- mob_target(dstmd, src, skill_get_range2(src,skill_id,skill_lv));
- }
- break;
-
- case ML_DEVOTION:
- case CR_DEVOTION:
- {
- int count, lv;
- if( !dstsd || (!sd && !mer) )
- { // Only players can be devoted
- if( sd )
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
- break;
- }
-
- if( (lv = status_get_lv(src) - dstsd->status.base_level) < 0 )
- lv = -lv;
- if( lv > battle_config.devotion_level_difference || // Level difference requeriments
- (dstsd->sc.data[type] && dstsd->sc.data[type]->val1 != src->id) || // Cannot Devote a player devoted from another source
- (skill_id == ML_DEVOTION && (!mer || mer != dstsd->md)) || // Mercenary only can devote owner
- (dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER || // Crusader Cannot be devoted
- (dstsd->sc.data[SC_HELLPOWER])) // Players affected by SC_HELLPOWERR cannot be devoted.
- {
- if( sd )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock();
- return 1;
- }
-
- i = 0;
- count = (sd)? min(skill_lv,5) : 1; // Mercenary only can Devote owner
- if( sd )
- { // Player Devoting Player
- ARR_FIND(0, count, i, sd->devotion[i] == bl->id );
- if( i == count )
- {
- ARR_FIND(0, count, i, sd->devotion[i] == 0 );
- if( i == count )
- { // No free slots, skill Fail
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
- map_freeblock_unlock();
- return 1;
- }
- }
-
- sd->devotion[i] = bl->id;
- }
- else
- mer->devotion_flag = 1; // Mercenary Devoting Owner
-
- clif_skill_nodamage(src, bl, skill_id, skill_lv,
- sc_start4(bl, type, 100, src->id, i, skill_get_range2(src,skill_id,skill_lv),0, skill_get_time2(skill_id, skill_lv)));
- clif_devotion(src, NULL);
- }
- break;
-
- case MO_CALLSPIRITS:
- if(sd) {
- int limit = skill_lv;
- if( sd->sc.data[SC_RAISINGDRAGON] )
- limit += sd->sc.data[SC_RAISINGDRAGON]->val1;
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- pc_addspiritball(sd,skill_get_time(skill_id,skill_lv),limit);
- }
- break;
-
- case CH_SOULCOLLECT:
- if(sd) {
- int limit = 5;
- if( sd->sc.data[SC_RAISINGDRAGON] )
- limit += sd->sc.data[SC_RAISINGDRAGON]->val1;
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- for (i = 0; i < limit; i++)
- pc_addspiritball(sd,skill_get_time(skill_id,skill_lv),limit);
- }
- break;
-
- case MO_KITRANSLATION:
- if(dstsd && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER) {
- pc_addspiritball(dstsd,skill_get_time(skill_id,skill_lv),5);
- }
- break;
-
- case TK_TURNKICK:
- case MO_BALKYOUNG: //Passive part of the attack. Splash knock-back+stun. [Skotlex]
- if (skill_area_temp[1] != bl->id) {
- skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),-1,0);
- skill_additional_effect(src,bl,skill_id,skill_lv,BF_MISC,ATK_DEF,tick); //Use Misc rather than weapon to signal passive pushback
- }
- break;
-
- case MO_ABSORBSPIRITS:
- i = 0;
- if (dstsd && dstsd->spiritball && (sd == dstsd || map_flag_vs(src->m)) && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER)
- { // split the if for readability, and included gunslingers in the check so that their coins cannot be removed [Reddozen]
- i = dstsd->spiritball * 7;
- pc_delspiritball(dstsd,dstsd->spiritball,0);
- } else if (dstmd && !(tstatus->mode&MD_BOSS) && rnd() % 100 < 20)
- { // check if target is a monster and not a Boss, for the 20% chance to absorb 2 SP per monster's level [Reddozen]
- i = 2 * dstmd->level;
- mob_target(dstmd,src,0);
- }
- if (i) status_heal(src, 0, i, 3);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,i?1:0);
- break;
-
- case AC_MAKINGARROW:
- if(sd) {
- clif_arrow_create_list(sd);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case AM_PHARMACY:
- if(sd) {
- clif_skill_produce_mix_list(sd,skill_id,22);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case SA_CREATECON:
- if(sd) {
- clif_elementalconverter_list(sd);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case BS_HAMMERFALL:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,SC_STUN,(20 + 10 * skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv)));
- break;
- case RG_RAID:
- skill_area_temp[1] = 0;
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- map_foreachinrange(skill_area_sub, bl,
- skill_get_splash(skill_id, skill_lv), splash_target(src),
- src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1,
- skill_castend_damage_id);
- status_change_end(src, SC_HIDING, INVALID_TIMER);
- break;
-
- case ASC_METEORASSAULT:
- case GS_SPREADATTACK:
- case RK_STORMBLAST:
- case NC_AXETORNADO:
- case GC_COUNTERSLASH:
- case SR_SKYNETBLOW:
- case SR_RAMPAGEBLASTER:
- case SR_HOWLINGOFLION:
- case KO_HAPPOKUNAI:
- skill_area_temp[1] = 0;
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- i = map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src),
- src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id);
- if( !i && ( skill_id == NC_AXETORNADO || skill_id == SR_SKYNETBLOW || skill_id == KO_HAPPOKUNAI ) )
- clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- break;
-
- case NC_EMERGENCYCOOL:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- status_change_end(src,SC_OVERHEAT_LIMITPOINT,INVALID_TIMER);
- status_change_end(src,SC_OVERHEAT,INVALID_TIMER);
- break;
- case SR_WINDMILL:
- case GN_CART_TORNADO:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- case SR_EARTHSHAKER:
- case NC_INFRAREDSCAN:
- case NPC_EARTHQUAKE:
- case NPC_VAMPIRE_GIFT:
- case NPC_HELLJUDGEMENT:
- case NPC_PULSESTRIKE:
- case LG_MOONSLASHER:
- skill_castend_damage_id(src, src, skill_id, skill_lv, tick, flag);
- break;
-
- case KN_BRANDISHSPEAR:
- case ML_BRANDISH:
- skill_brandishspear(src, bl, skill_id, skill_lv, tick, flag);
- break;
-
- case WZ_SIGHTRASHER:
- //Passive side of the attack.
- status_change_end(src, SC_SIGHT, INVALID_TIMER);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- map_foreachinrange(skill_area_sub,src,
- skill_get_splash(skill_id, skill_lv),BL_CHAR|BL_SKILL,
- src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1,
- skill_castend_damage_id);
- break;
-
- case NJ_HYOUSYOURAKU:
- case NJ_RAIGEKISAI:
- case WZ_FROSTNOVA:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- skill_area_temp[1] = 0;
- map_foreachinrange(skill_attack_area, src,
- skill_get_splash(skill_id, skill_lv), splash_target(src),
- BF_MAGIC, src, src, skill_id, skill_lv, tick, flag, BCT_ENEMY);
- break;
-
- case HVAN_EXPLOSION: //[orn]
- case NPC_SELFDESTRUCTION:
- //Self Destruction hits everyone in range (allies+enemies)
- //Except for Summoned Marine spheres on non-versus maps, where it's just enemy.
- i = ((!md || md->special_state.ai == 2) && !map_flag_vs(src->m))?
- BCT_ENEMY:BCT_ALL;
- clif_skill_nodamage(src, src, skill_id, -1, 1);
- map_delblock(src); //Required to prevent chain-self-destructions hitting back.
- map_foreachinrange(skill_area_sub, bl,
- skill_get_splash(skill_id, skill_lv), splash_target(src),
- src, skill_id, skill_lv, tick, flag|i,
- skill_castend_damage_id);
- map_addblock(src);
- status_damage(src, src, sstatus->max_hp,0,0,1);
- break;
-
- case AL_ANGELUS:
- case PR_MAGNIFICAT:
- case PR_GLORIA:
- case SN_WINDWALK:
- case CASH_BLESSING:
- case CASH_INCAGI:
- case CASH_ASSUMPTIO:
- if( sd == NULL || sd->status.party_id == 0 || (flag & 1) )
- clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- else if( sd )
- party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
- break;
- case MER_MAGNIFICAT:
- if( mer != NULL )
- {
- clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- if( mer->master && mer->master->status.party_id != 0 && !(flag&1) )
- party_foreachsamemap(skill_area_sub, mer->master, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
- else if( mer->master && !(flag&1) )
- clif_skill_nodamage(src, &mer->master->bl, skill_id, skill_lv, sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- }
- break;
-
- case BS_ADRENALINE:
- case BS_ADRENALINE2:
- case BS_WEAPONPERFECT:
- case BS_OVERTHRUST:
- if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) {
- clif_skill_nodamage(bl,bl,skill_id,skill_lv,
- sc_start2(bl,type,100,skill_lv,(src == bl)? 1:0,skill_get_time(skill_id,skill_lv)));
- } else if (sd) {
- party_foreachsamemap(skill_area_sub,
- sd,skill_get_splash(skill_id, skill_lv),
- src,skill_id,skill_lv,tick, flag|BCT_PARTY|1,
- skill_castend_nodamage_id);
- }
- break;
-
- case BS_MAXIMIZE:
- case NV_TRICKDEAD:
- case CR_DEFENDER:
- case ML_DEFENDER:
- case CR_AUTOGUARD:
- case ML_AUTOGUARD:
- case TK_READYSTORM:
- case TK_READYDOWN:
- case TK_READYTURN:
- case TK_READYCOUNTER:
- case TK_DODGE:
- case CR_SHRINK:
- case SG_FUSION:
- case GS_GATLINGFEVER:
- if( tsce )
- {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,status_change_end(bl, type, INVALID_TIMER));
- map_freeblock_unlock();
- return 0;
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- break;
- case SL_KAITE:
- case SL_KAAHI:
- case SL_KAIZEL:
- case SL_KAUPE:
- if (sd) {
- if (!dstsd || !(
- (sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_SOULLINKER) ||
- (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER ||
- dstsd->status.char_id == sd->status.char_id ||
- dstsd->status.char_id == sd->status.partner_id ||
- dstsd->status.char_id == sd->status.child
- )) {
- status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,500,8);
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv)));
- break;
- case SM_AUTOBERSERK:
- case MER_AUTOBERSERK:
- if( tsce )
- i = status_change_end(bl, type, INVALID_TIMER);
- else
- i = sc_start(bl,type,100,skill_lv,60000);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,i);
- break;
- case TF_HIDING:
- case ST_CHASEWALK:
- case KO_YAMIKUMO:
- if (tsce)
- {
- clif_skill_nodamage(src,bl,skill_id,-1,status_change_end(bl, type, INVALID_TIMER)); //Hide skill-scream animation.
- map_freeblock_unlock();
- return 0;
- } else if( tsc && tsc->option&OPTION_MADOGEAR ) {
- //Mado Gear cannot hide
- if( sd ) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock();
- return 0;
- }
- clif_skill_nodamage(src,bl,skill_id,-1,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- break;
- case TK_RUN:
- if (tsce)
- {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,status_change_end(bl, type, INVALID_TIMER));
- map_freeblock_unlock();
- return 0;
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start4(bl,type,100,skill_lv,unit_getdir(bl),0,0,0));
- if (sd) // If the client receives a skill-use packet inmediately before a walkok packet, it will discard the walk packet! [Skotlex]
- clif_walkok(sd); // So aegis has to resend the walk ok.
- break;
- case AS_CLOAKING:
- case GC_CLOAKINGEXCEED:
- case LG_FORCEOFVANGUARD:
- case SC_REPRODUCE:
- case SC_INVISIBILITY:
- if (tsce) {
- i = status_change_end(bl, type, INVALID_TIMER);
- if( i )
- clif_skill_nodamage(src,bl,skill_id,( skill_id == LG_FORCEOFVANGUARD ) ? skill_lv : -1,i);
- else if( sd )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock();
- return 0;
- }
- case RA_CAMOUFLAGE:
- i = sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
- if( i )
- clif_skill_nodamage(src,bl,skill_id,( skill_id == LG_FORCEOFVANGUARD ) ? skill_lv : -1,i);
- else if( sd )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
-
- case BD_ADAPTATION:
- if(tsc && tsc->data[SC_DANCING]){
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- status_change_end(bl, SC_DANCING, INVALID_TIMER);
- }
- break;
-
- case BA_FROSTJOKER:
- case DC_SCREAM:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- skill_addtimerskill(src,tick+2000,bl->id,src->x,src->y,skill_id,skill_lv,0,flag);
-
- if (md) {
- // custom hack to make the mob display the skill, because these skills don't show the skill use text themselves
- //NOTE: mobs don't have the sprite animation that is used when performing this skill (will cause glitches)
- char temp[70];
- snprintf(temp, sizeof(temp), "%s : %s !!",md->name,skill_db[skill_id].desc);
- clif_message(&md->bl,temp);
- }
- break;
-
- case BA_PANGVOICE:
- clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start(bl,SC_CONFUSION,50,7,skill_get_time(skill_id,skill_lv)));
- break;
-
- case DC_WINKCHARM:
- if( dstsd )
- clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start(bl,SC_CONFUSION,30,7,skill_get_time2(skill_id,skill_lv)));
- else
- if( dstmd )
- {
- if( status_get_lv(src) > status_get_lv(bl)
- && (tstatus->race == RC_DEMON || tstatus->race == RC_DEMIHUMAN || tstatus->race == RC_ANGEL)
- && !(tstatus->mode&MD_BOSS) )
- clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start2(bl,type,70,skill_lv,src->id,skill_get_time(skill_id,skill_lv)));
- else
- {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
- if(sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- }
- }
- break;
-
- case TF_STEAL:
- if(sd) {
- if(pc_steal_item(sd,bl,skill_lv))
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- else
- clif_skill_fail(sd,skill_id,USESKILL_FAIL,0);
- }
- break;
-
- case RG_STEALCOIN:
- if(sd) {
- if(pc_steal_coin(sd,bl))
- {
- dstmd->state.provoke_flag = src->id;
- mob_target(dstmd, src, skill_get_range2(src,skill_id,skill_lv));
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
-
- }
- else
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- }
- break;
-
- case MG_STONECURSE:
- {
- int brate = 0;
- if (tstatus->mode&MD_BOSS) {
- if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- if(status_isimmune(bl) || !tsc)
- break;
-
- if (sd && sd->sc.data[SC_PETROLOGY_OPTION])
- brate = sd->sc.data[SC_PETROLOGY_OPTION]->val3;
-
- if (tsc->data[SC_STONE]) {
- status_change_end(bl, SC_STONE, INVALID_TIMER);
- if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- if (sc_start4(bl,SC_STONE,(skill_lv*4+20)+brate,
- skill_lv, 0, 0, skill_get_time(skill_id, skill_lv),
- skill_get_time2(skill_id,skill_lv)))
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- else if(sd) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- // Level 6-10 doesn't consume a red gem if it fails [celest]
- if (skill_lv > 5)
- { // not to consume items
- map_freeblock_unlock();
- return 0;
- }
- }
- }
- break;
-
- case NV_FIRSTAID:
- clif_skill_nodamage(src,bl,skill_id,5,1);
- status_heal(bl,5,0,0);
- break;
-
- case AL_CURE:
- if(status_isimmune(bl)) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
- break;
- }
- status_change_end(bl, SC_SILENCE, INVALID_TIMER);
- status_change_end(bl, SC_BLIND, INVALID_TIMER);
- status_change_end(bl, SC_CONFUSION, INVALID_TIMER);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
-
- case TF_DETOXIFY:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- status_change_end(bl, SC_POISON, INVALID_TIMER);
- status_change_end(bl, SC_DPOISON, INVALID_TIMER);
- break;
-
- case PR_STRECOVERY:
- if(status_isimmune(bl)) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
- break;
- }
- if (tsc && tsc->opt1) {
- status_change_end(bl, SC_FREEZE, INVALID_TIMER);
- status_change_end(bl, SC_STONE, INVALID_TIMER);
- status_change_end(bl, SC_SLEEP, INVALID_TIMER);
- status_change_end(bl, SC_STUN, INVALID_TIMER);
- status_change_end(bl, SC_WHITEIMPRISON, INVALID_TIMER);
- }
- //Is this equation really right? It looks so... special.
- if(battle_check_undead(tstatus->race,tstatus->def_ele))
- {
- status_change_start(bl, SC_BLIND,
- 100*(100-(tstatus->int_/2+tstatus->vit/3+tstatus->luk/10)),
- 1,0,0,0,
- skill_get_time2(skill_id, skill_lv) * (100-(tstatus->int_+tstatus->vit)/2)/100,0);
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if(dstmd)
- mob_unlocktarget(dstmd,tick);
- break;
-
- // Mercenary Supportive Skills
- case MER_BENEDICTION:
- status_change_end(bl, SC_CURSE, INVALID_TIMER);
- status_change_end(bl, SC_BLIND, INVALID_TIMER);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
- case MER_COMPRESS:
- status_change_end(bl, SC_BLEEDING, INVALID_TIMER);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
- case MER_MENTALCURE:
- status_change_end(bl, SC_CONFUSION, INVALID_TIMER);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
- case MER_RECUPERATE:
- status_change_end(bl, SC_POISON, INVALID_TIMER);
- status_change_end(bl, SC_SILENCE, INVALID_TIMER);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
- case MER_REGAIN:
- status_change_end(bl, SC_SLEEP, INVALID_TIMER);
- status_change_end(bl, SC_STUN, INVALID_TIMER);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
- case MER_TENDER:
- status_change_end(bl, SC_FREEZE, INVALID_TIMER);
- status_change_end(bl, SC_STONE, INVALID_TIMER);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
-
- case MER_SCAPEGOAT:
- if( mer && mer->master )
- {
- status_heal(&mer->master->bl, mer->battle_status.hp, 0, 2);
- status_damage(src, src, mer->battle_status.max_hp, 0, 0, 1);
- }
- break;
-
- case MER_ESTIMATION:
- if( !mer )
- break;
- sd = mer->master;
- case WZ_ESTIMATION:
- if( sd == NULL )
- break;
- if( dstsd )
- { // Fail on Players
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- if( dstmd && dstmd->class_ == MOBID_EMPERIUM )
- break; // Cannot be Used on Emperium
-
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- clif_skill_estimation(sd, bl);
- if( skill_id == MER_ESTIMATION )
- sd = NULL;
- break;
-
- case BS_REPAIRWEAPON:
- if(sd && dstsd)
- clif_item_repair_list(sd,dstsd,skill_lv);
- break;
-
- case MC_IDENTIFY:
- if(sd)
- clif_item_identify_list(sd);
- break;
-
- // Weapon Refining [Celest]
- case WS_WEAPONREFINE:
- if(sd)
- clif_item_refine_list(sd);
- break;
-
- case MC_VENDING:
- if(sd)
- { //Prevent vending of GMs with unnecessary Level to trade/drop. [Skotlex]
- if ( !pc_can_give_items(sd) )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- else {
- sd->state.prevend = 1;
- clif_openvendingreq(sd,2+skill_lv);
- }
- }
- break;
-
- case AL_TELEPORT:
- if(sd)
- {
- if (map[bl->m].flag.noteleport && skill_lv <= 2) {
- clif_skill_teleportmessage(sd,0);
- break;
- }
- if(!battle_config.duel_allow_teleport && sd->duel_group && skill_lv <= 2) { // duel restriction [LuzZza]
- char output[128]; sprintf(output, msg_txt(365), skill_get_name(AL_TELEPORT));
- clif_displaymessage(sd->fd, output); //"Duel: Can't use %s in duel."
- break;
- }
-
- if( sd->state.autocast || ( (sd->skillitem == AL_TELEPORT || battle_config.skip_teleport_lv1_menu) && skill_lv == 1 ) || skill_lv == 3 )
- {
- if( skill_lv == 1 )
- pc_randomwarp(sd,CLR_TELEPORT);
- else
- pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
- break;
- }
-
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if( skill_lv == 1 )
- clif_skill_warppoint(sd,skill_id,skill_lv, (unsigned short)-1,0,0,0);
- else
- clif_skill_warppoint(sd,skill_id,skill_lv, (unsigned short)-1,sd->status.save_point.map,0,0);
- } else
- unit_warp(bl,-1,-1,-1,CLR_TELEPORT);
- break;
-
- case NPC_EXPULSION:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- unit_warp(bl,-1,-1,-1,CLR_TELEPORT);
- break;
-
- case AL_HOLYWATER:
- if(sd) {
- if (skill_produce_mix(sd, skill_id, 523, 0, 0, 0, 1))
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- else
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- }
- break;
-
- case TF_PICKSTONE:
- if(sd) {
- int eflag;
- struct item item_tmp;
- struct block_list tbl;
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- memset(&item_tmp,0,sizeof(item_tmp));
- memset(&tbl,0,sizeof(tbl)); // [MouseJstr]
- item_tmp.nameid = ITEMID_STONE;
- item_tmp.identify = 1;
- tbl.id = 0;
- clif_takeitem(&sd->bl,&tbl);
- eflag = pc_additem(sd,&item_tmp,1,LOG_TYPE_PRODUCE);
- if(eflag) {
- clif_additem(sd,0,0,eflag);
- map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
- }
- break;
- case ASC_CDP:
- if(sd) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- skill_produce_mix(sd, skill_id, 678, 0, 0, 0, 1); //Produce a Deadly Poison Bottle.
- }
- break;
-
- case RG_STRIPWEAPON:
- case RG_STRIPSHIELD:
- case RG_STRIPARMOR:
- case RG_STRIPHELM:
- case ST_FULLSTRIP:
- case GC_WEAPONCRUSH:
- case SC_STRIPACCESSARY: {
- unsigned short location = 0;
- int d = 0;
-
- //Rate in percent
- if ( skill_id == ST_FULLSTRIP ) {
- i = 5 + 2*skill_lv + (sstatus->dex - tstatus->dex)/5;
- } else if( skill_id == SC_STRIPACCESSARY ) {
- i = 12 + 2 * skill_lv + (sstatus->dex - tstatus->dex)/5;
- } else {
- i = 5 + 5*skill_lv + (sstatus->dex - tstatus->dex)/5;
- }
-
- if (i < 5) i = 5; //Minimum rate 5%
-
- //Duration in ms
- if( skill_id == GC_WEAPONCRUSH){
- d = skill_get_time(skill_id,skill_lv);
- if(bl->type == BL_PC)
- d += skill_lv * 15 + (sstatus->dex - tstatus->dex);
- else
- d += skill_lv * 30 + (sstatus->dex - tstatus->dex) / 2;
- }else
- d = skill_get_time(skill_id,skill_lv) + (sstatus->dex - tstatus->dex)*500;
-
- if (d < 0) d = 0; //Minimum duration 0ms
-
- switch (skill_id) {
- case RG_STRIPWEAPON:
- case GC_WEAPONCRUSH:
- location = EQP_WEAPON;
- break;
- case RG_STRIPSHIELD:
- location = EQP_SHIELD;
- break;
- case RG_STRIPARMOR:
- location = EQP_ARMOR;
- break;
- case RG_STRIPHELM:
- location = EQP_HELM;
- break;
- case ST_FULLSTRIP:
- location = EQP_WEAPON|EQP_SHIELD|EQP_ARMOR|EQP_HELM;
- break;
- case SC_STRIPACCESSARY:
- location = EQP_ACC;
- break;
- }
-
- //Special message when trying to use strip on FCP [Jobbie]
- if( sd && skill_id == ST_FULLSTRIP && tsc && tsc->data[SC_CP_WEAPON] && tsc->data[SC_CP_HELM] && tsc->data[SC_CP_ARMOR] && tsc->data[SC_CP_SHIELD])
- {
- clif_gospel_info(sd, 0x28);
- break;
- }
-
- //Attempts to strip at rate i and duration d
- if( (i = skill_strip_equip(bl, location, i, skill_lv, d)) || (skill_id != ST_FULLSTRIP && skill_id != GC_WEAPONCRUSH ) )
- clif_skill_nodamage(src,bl,skill_id,skill_lv,i);
-
- //Nothing stripped.
- if( sd && !i )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
-
- case AM_BERSERKPITCHER:
- case AM_POTIONPITCHER: {
- int i,x,hp = 0,sp = 0,bonus=100;
- if( dstmd && dstmd->class_ == MOBID_EMPERIUM ) {
- map_freeblock_unlock();
- return 1;
- }
- if( sd ) {
- x = skill_lv%11 - 1;
- i = pc_search_inventory(sd,skill_db[skill_id].itemid[x]);
- if( i < 0 || skill_db[skill_id].itemid[x] <= 0 ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock();
- return 1;
- }
- if(sd->inventory_data[i] == NULL || sd->status.inventory[i].amount < skill_db[skill_id].amount[x]) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock();
- return 1;
- }
- if( skill_id == AM_BERSERKPITCHER ) {
- if( dstsd && dstsd->status.base_level < (unsigned int)sd->inventory_data[i]->elv ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock();
- return 1;
- }
- }
- potion_flag = 1;
- potion_hp = potion_sp = potion_per_hp = potion_per_sp = 0;
- potion_target = bl->id;
- run_script(sd->inventory_data[i]->script,0,sd->bl.id,0);
- potion_flag = potion_target = 0;
- if( sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_ALCHEMIST )
- bonus += sd->status.base_level;
- if( potion_per_hp > 0 || potion_per_sp > 0 ) {
- hp = tstatus->max_hp * potion_per_hp / 100;
- hp = hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
- if( dstsd ) {
- sp = dstsd->status.max_sp * potion_per_sp / 100;
- sp = sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
- }
- } else {
- if( potion_hp > 0 ) {
- hp = potion_hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
- hp = hp * (100 + (tstatus->vit<<1)) / 100;
- if( dstsd )
- hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100;
- }
- if( potion_sp > 0 ) {
- sp = potion_sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
- sp = sp * (100 + (tstatus->int_<<1)) / 100;
- if( dstsd )
- sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10) / 100;
- }
- }
-
- if (sd->itemgrouphealrate[IG_POTION]>0) {
- hp += hp * sd->itemgrouphealrate[IG_POTION] / 100;
- sp += sp * sd->itemgrouphealrate[IG_POTION] / 100;
- }
-
- if( (i = pc_skillheal_bonus(sd, skill_id)) ) {
- hp += hp * i / 100;
- sp += sp * i / 100;
- }
- } else {
- hp = (1 + rnd()%400) * (100 + skill_lv*10) / 100;
- hp = hp * (100 + (tstatus->vit<<1)) / 100;
- if( dstsd )
- hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100;
- }
- if( dstsd && (i = pc_skillheal2_bonus(dstsd, skill_id)) ) {
- hp += hp * i / 100;
- sp += sp * i / 100;
- }
- if( tsc && tsc->count ) {
- if( tsc->data[SC_CRITICALWOUND] ) {
- hp -= hp * tsc->data[SC_CRITICALWOUND]->val2 / 100;
- sp -= sp * tsc->data[SC_CRITICALWOUND]->val2 / 100;
- }
- if( tsc->data[SC_DEATHHURT] ) {
- hp -= hp * 20 / 100;
- sp -= sp * 20 / 100;
- }
- if( tsc->data[SC_WATER_INSIGNIA] && tsc->data[SC_WATER_INSIGNIA]->val1 == 2 ) {
- hp += hp / 10;
- sp += sp / 10;
- }
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if( hp > 0 || (skill_id == AM_POTIONPITCHER && sp <= 0) )
- clif_skill_nodamage(NULL,bl,AL_HEAL,hp,1);
- if( sp > 0 )
- clif_skill_nodamage(NULL,bl,MG_SRECOVERY,sp,1);
-#ifdef RENEWAL
- if( tsc && tsc->data[SC_EXTREMITYFIST2] )
- sp = 0;
-#endif
- status_heal(bl,hp,sp,0);
- }
- break;
- case AM_CP_WEAPON:
- case AM_CP_SHIELD:
- case AM_CP_ARMOR:
- case AM_CP_HELM:
- {
- unsigned int equip[] = {EQP_WEAPON, EQP_SHIELD, EQP_ARMOR, EQP_HEAD_TOP};
-
- if( sd && ( bl->type != BL_PC || ( dstsd && pc_checkequip(dstsd,equip[skill_id - AM_CP_WEAPON]) < 0 ) ) ){
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock(); // Don't consume item requirements
- return 0;
- }
-
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- }
- break;
- case AM_TWILIGHT1:
- if (sd) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- //Prepare 200 White Potions.
- if (!skill_produce_mix(sd, skill_id, 504, 0, 0, 0, 200))
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- }
- break;
- case AM_TWILIGHT2:
- if (sd) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- //Prepare 200 Slim White Potions.
- if (!skill_produce_mix(sd, skill_id, 547, 0, 0, 0, 200))
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- }
- break;
- case AM_TWILIGHT3:
- if (sd) {
- int ebottle = pc_search_inventory(sd,713);
- if( ebottle >= 0 )
- ebottle = sd->status.inventory[ebottle].amount;
- //check if you can produce all three, if not, then fail:
- if (!skill_can_produce_mix(sd,970,-1, 100) //100 Alcohol
- || !skill_can_produce_mix(sd,7136,-1, 50) //50 Acid Bottle
- || !skill_can_produce_mix(sd,7135,-1, 50) //50 Flame Bottle
- || ebottle < 200 //200 empty bottle are required at total.
- ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- skill_produce_mix(sd, skill_id, 970, 0, 0, 0, 100);
- skill_produce_mix(sd, skill_id, 7136, 0, 0, 0, 50);
- skill_produce_mix(sd, skill_id, 7135, 0, 0, 0, 50);
- }
- break;
- case SA_DISPELL:
- if (flag&1 || (i = skill_get_splash(skill_id, skill_lv)) < 1)
- {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if((dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER)
- || (tsc && tsc->data[SC_SPIRIT] && tsc->data[SC_SPIRIT]->val2 == SL_ROGUE) //Rogue's spirit defends againt dispel.
- || rnd()%100 >= 50+10*skill_lv
- || ( tsc && tsc->option&OPTION_MADOGEAR ) )//Mado Gear is immune to dispell according to bug report 49 [Ind]
- {
- if (sd)
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- if(status_isimmune(bl) || !tsc || !tsc->count)
- break;
- for(i=0;i<SC_MAX;i++)
- {
- if (!tsc->data[i])
- continue;
- switch (i) {
- case SC_WEIGHT50: case SC_WEIGHT90: case SC_HALLUCINATION:
- 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: case SC_COMBO:
- case SC_STRFOOD: case SC_AGIFOOD: case SC_VITFOOD:
- case SC_INTFOOD: case SC_DEXFOOD: case SC_LUKFOOD:
- case SC_HITFOOD: case SC_FLEEFOOD: case SC_BATKFOOD:
- case SC_WATKFOOD: case SC_MATKFOOD: case SC_DANCING:
- case SC_EDP: case SC_AUTOBERSERK:
- case SC_CARTBOOST: case SC_MELTDOWN: case SC_SAFETYWALL:
- case SC_SMA: case SC_SPEEDUP0: case SC_NOCHAT:
- case SC_ANKLE: case SC_SPIDERWEB: case SC_JAILED:
- case SC_ITEMBOOST: case SC_EXPBOOST: case SC_LIFEINSURANCE:
- case SC_BOSSMAPINFO: case SC_PNEUMA: case SC_AUTOSPELL:
- case SC_INCHITRATE: case SC_INCATKRATE: case SC_NEN:
- case SC_READYSTORM: case SC_READYDOWN: case SC_READYTURN:
- case SC_READYCOUNTER: case SC_DODGE: case SC_WARM:
- case SC_SPEEDUP1: case SC_AUTOTRADE: case SC_CRITICALWOUND:
- case SC_JEXPBOOST: case SC_INVINCIBLE: case SC_INVINCIBLEOFF:
- case SC_HELLPOWER: case SC_MANU_ATK: case SC_MANU_DEF:
- case SC_SPL_ATK: case SC_SPL_DEF: case SC_MANU_MATK:
- case SC_SPL_MATK: case SC_RICHMANKIM: case SC_ETERNALCHAOS:
- case SC_DRUMBATTLE: case SC_NIBELUNGEN: case SC_ROKISWEIL:
- case SC_INTOABYSS: case SC_SIEGFRIED: case SC_FOOD_STR_CASH:
- case SC_FOOD_AGI_CASH: case SC_FOOD_VIT_CASH: case SC_FOOD_DEX_CASH:
- case SC_FOOD_INT_CASH: case SC_FOOD_LUK_CASH: case SC_SEVENWIND:
- case SC_MIRACLE: case SC_S_LIFEPOTION: case SC_L_LIFEPOTION:
- case SC_INCHEALRATE: case SC_ELECTRICSHOCKER: case SC__STRIPACCESSORY:
- //case SC_SAVAGE_STEAK: case SC_COCKTAIL_WARG_BLOOD: case SC_MINOR_BBQ:
- //case SC_SIROMA_ICE_TEA: case SC_DROCERA_HERB_STEAMED: case SC_PUTTI_TAILS_NOODLES:
- case SC_NEUTRALBARRIER_MASTER: case SC_NEUTRALBARRIER: case SC_STEALTHFIELD_MASTER:
- case SC_STEALTHFIELD: case SC_GIANTGROWTH: case SC_MILLENNIUMSHIELD:
- case SC_REFRESH: case SC_STONEHARDSKIN: case SC_VITALITYACTIVATION:
- case SC_FIGHTINGSPIRIT: case SC_ABUNDANCE: case SC__SHADOWFORM:
- case SC_LEADERSHIP: case SC_GLORYWOUNDS: case SC_SOULCOLD:
- case SC_HAWKEYES: case SC_GUILDAURA: case SC_PUSH_CART:
- case SC_RAISINGDRAGON: case SC_GT_ENERGYGAIN: case SC_GT_CHANGE:
- case SC_GT_REVITALIZE: case SC_REFLECTDAMAGE: case SC_INSPIRATION:
- case SC_EXEEDBREAK: case SC_FORCEOFVANGUARD: case SC_BANDING:
- case SC_DUPLELIGHT: case SC_EXPIATIO: case SC_LAUDAAGNUS:
- case SC_LAUDARAMUS: case SC_GATLINGFEVER: case SC_INCREASING:
- case SC_ADJUSTMENT: case SC_MADNESSCANCEL:
-#ifdef RENEWAL
- case SC_EXTREMITYFIST2:
-#endif
- continue;
- /**
- * bugreport:4888 these songs may only be dispelled if you're not in their song area anymore
- **/
- case SC_WHISTLE:
- case SC_ASSNCROS:
- case SC_POEMBRAGI:
- case SC_APPLEIDUN:
- case SC_HUMMING:
- case SC_DONTFORGETME:
- case SC_FORTUNE:
- case SC_SERVICE4U:
- if( tsc->data[i]->val4 ) //val4 = out-of-song-area
- continue;
- break;
- case SC_ASSUMPTIO:
- if( bl->type == BL_MOB )
- continue;
- break;
- }
- if(i==SC_BERSERK || i==SC_SATURDAYNIGHTFEVER) tsc->data[i]->val2=0; //Mark a dispelled berserk to avoid setting hp to 100 by setting hp penalty to 0.
- status_change_end(bl, (sc_type)i, INVALID_TIMER);
- }
- break;
- }
- //Affect all targets on splash area.
- map_foreachinrange(skill_area_sub, bl, i, BL_CHAR,
- src, skill_id, skill_lv, tick, flag|1,
- skill_castend_damage_id);
- break;
-
- case TF_BACKSLIDING: //This is the correct implementation as per packet logging information. [Skotlex]
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),unit_getdir(bl),0);
- break;
-
- case TK_HIGHJUMP:
- {
- int x,y, dir = unit_getdir(src);
-
- //Fails on noteleport maps, except for GvG and BG maps [Skotlex]
- if( map[src->m].flag.noteleport &&
- !(map[src->m].flag.battleground || map_flag_gvg2(src->m) )
- ) {
- x = src->x;
- y = src->y;
- } else {
- x = src->x + dirx[dir]*skill_lv*2;
- y = src->y + diry[dir]*skill_lv*2;
- }
-
- clif_skill_nodamage(src,bl,TK_HIGHJUMP,skill_lv,1);
- if(!map_count_oncell(src->m,x,y,BL_PC|BL_NPC|BL_MOB) && map_getcell(src->m,x,y,CELL_CHKREACH)) {
- clif_slide(src,x,y);
- unit_movepos(src, x, y, 1, 0);
- }
- }
- break;
-
- case SA_CASTCANCEL:
- case SO_SPELLFIST:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- unit_skillcastcancel(src,1);
- if(sd) {
- int sp = skill_get_sp(sd->skill_id_old,sd->skill_lv_old);
- if( skill_id == SO_SPELLFIST ){
- sc_start4(src,type,100,skill_lv+1,skill_lv,sd->skill_id_old,sd->skill_lv_old,skill_get_time(skill_id,skill_lv));
- sd->skill_id_old = sd->skill_lv_old = 0;
- break;
- }
- sp = sp * (90 - (skill_lv-1)*20) / 100;
- if(sp < 0) sp = 0;
- status_zap(src, 0, sp);
- }
- break;
- case SA_SPELLBREAKER:
- {
- int sp;
- if(tsc && tsc->data[SC_MAGICROD]) {
- sp = skill_get_sp(skill_id,skill_lv);
- sp = sp * tsc->data[SC_MAGICROD]->val2 / 100;
- if(sp < 1) sp = 1;
- status_heal(bl,0,sp,2);
- status_percent_damage(bl, src, 0, -20, false); //20% max SP damage.
- } else {
- struct unit_data *ud = unit_bl2ud(bl);
- int bl_skill_id=0,bl_skill_lv=0,hp = 0;
- if (!ud || ud->skilltimer == INVALID_TIMER)
- break; //Nothing to cancel.
- bl_skill_id = ud->skill_id;
- bl_skill_lv = ud->skill_lv;
- if (tstatus->mode & MD_BOSS)
- { //Only 10% success chance against bosses. [Skotlex]
- if (rnd()%100 < 90)
- {
- if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- } else if (!dstsd || map_flag_vs(bl->m)) //HP damage only on pvp-maps when against players.
- hp = tstatus->max_hp/50; //Recover 2% HP [Skotlex]
-
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- unit_skillcastcancel(bl,0);
- sp = skill_get_sp(bl_skill_id,bl_skill_lv);
- status_zap(bl, hp, sp);
-
- if (hp && skill_lv >= 5)
- hp>>=1; //Recover half damaged HP at level 5 [Skotlex]
- else
- hp = 0;
-
- if (sp) //Recover some of the SP used
- sp = sp*(25*(skill_lv-1))/100;
-
- if(hp || sp)
- status_heal(src, hp, sp, 2);
- }
- }
- break;
- case SA_MAGICROD:
- clif_skill_nodamage(src,src,SA_MAGICROD,skill_lv,1);
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
- break;
- case SA_AUTOSPELL:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if(sd)
- clif_autospell(sd,skill_lv);
- else {
- int maxlv=1,spellid=0;
- static const int spellarray[3] = { MG_COLDBOLT,MG_FIREBOLT,MG_LIGHTNINGBOLT };
- if(skill_lv >= 10) {
- spellid = MG_FROSTDIVER;
-// if (tsc && tsc->data[SC_SPIRIT] && tsc->data[SC_SPIRIT]->val2 == SA_SAGE)
-// maxlv = 10;
-// else
- maxlv = skill_lv - 9;
- }
- else if(skill_lv >=8) {
- spellid = MG_FIREBALL;
- maxlv = skill_lv - 7;
- }
- else if(skill_lv >=5) {
- spellid = MG_SOULSTRIKE;
- maxlv = skill_lv - 4;
- }
- else if(skill_lv >=2) {
- int i = rnd()%3;
- spellid = spellarray[i];
- maxlv = skill_lv - 1;
- }
- else if(skill_lv > 0) {
- spellid = MG_NAPALMBEAT;
- maxlv = 3;
- }
- if(spellid > 0)
- sc_start4(src,SC_AUTOSPELL,100,skill_lv,spellid,maxlv,0,
- skill_get_time(SA_AUTOSPELL,skill_lv));
- }
- break;
-
- case BS_GREED:
- if(sd){
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- map_foreachinrange(skill_greed,bl,
- skill_get_splash(skill_id, skill_lv),BL_ITEM,bl);
- }
- break;
-
- case SA_ELEMENTWATER:
- case SA_ELEMENTFIRE:
- case SA_ELEMENTGROUND:
- case SA_ELEMENTWIND:
- if(sd && !dstmd) //Only works on monsters.
- break;
- if(tstatus->mode&MD_BOSS)
- 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:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start2(bl, type, 100, skill_lv, skill_get_ele(skill_id,skill_lv),
- skill_get_time(skill_id, skill_lv)));
- break;
- case NPC_CHANGEUNDEAD:
- //This skill should fail if target is wearing bathory/evil druid card [Brainstorm]
- //TO-DO This is ugly, fix it
- if(tstatus->def_ele==ELE_UNDEAD || tstatus->def_ele==ELE_DARK) break;
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start2(bl, type, 100, skill_lv, skill_get_ele(skill_id,skill_lv),
- skill_get_time(skill_id, skill_lv)));
- break;
-
- case NPC_PROVOCATION:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if (md) mob_unlocktarget(md, tick);
- break;
-
- case NPC_KEEPING:
- case NPC_BARRIER:
- {
- int skill_time = skill_get_time(skill_id,skill_lv);
- struct unit_data *ud = unit_bl2ud(bl);
- if (clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_time))
- && ud) { //Disable attacking/acting/moving for skill's duration.
- ud->attackabletime =
- ud->canact_tick =
- ud->canmove_tick = tick + skill_time;
- }
- }
- break;
-
- case NPC_REBIRTH:
- if( md && md->state.rebirth )
- break; // only works once
- sc_start(bl,type,100,skill_lv,-1);
- break;
-
- case NPC_DARKBLESSING:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start2(bl,type,(50+skill_lv*5),skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv)));
- break;
-
- case NPC_LICK:
- status_zap(bl, 0, 100);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,(skill_lv*5),skill_lv,skill_get_time2(skill_id,skill_lv)));
- break;
-
- case NPC_SUICIDE:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- status_kill(src); //When suiciding, neither exp nor drops is given.
- break;
-
- case NPC_SUMMONSLAVE:
- case NPC_SUMMONMONSTER:
- if(md && md->skill_idx >= 0)
- mob_summonslave(md,md->db->skill[md->skill_idx].val,skill_lv,skill_id);
- break;
-
- case NPC_CALLSLAVE:
- mob_warpslave(src,MOB_SLAVEDISTANCE);
- break;
-
- case NPC_RANDOMMOVE:
- if (md) {
- md->next_walktime = tick - 1;
- mob_randomwalk(md,tick);
- }
- break;
-
- case NPC_SPEEDUP:
- {
- // or does it increase casting rate? just a guess xD
- int i = SC_ASPDPOTION0 + skill_lv - 1;
- if (i > SC_ASPDPOTION3)
- i = SC_ASPDPOTION3;
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,(sc_type)i,100,skill_lv,skill_lv * 60000));
- }
- break;
-
- case NPC_REVENGE:
- // not really needed... but adding here anyway ^^
- if (md && md->master_id > 0) {
- struct block_list *mbl, *tbl;
- if ((mbl = map_id2bl(md->master_id)) == NULL ||
- (tbl = battle_gettargeted(mbl)) == NULL)
- break;
- md->state.provoke_flag = tbl->id;
- mob_target(md, tbl, sstatus->rhw.range);
- }
- break;
-
- case NPC_RUN:
- {
- const int mask[8][2] = {{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1}};
- uint8 dir = (bl == src)?unit_getdir(src):map_calc_dir(src,bl->x,bl->y); //If cast on self, run forward, else run away.
- unit_stop_attack(src);
- //Run skillv tiles overriding the can-move check.
- if (unit_walktoxy(src, src->x + skill_lv * mask[dir][0], src->y + skill_lv * mask[dir][1], 2) && md)
- md->state.skillstate = MSS_WALK; //Otherwise it isn't updated in the ai.
- }
- break;
-
- case NPC_TRANSFORMATION:
- case NPC_METAMORPHOSIS:
- if(md && md->skill_idx >= 0) {
- int class_ = mob_random_class (md->db->skill[md->skill_idx].val,0);
- if (skill_lv > 1) //Multiply the rest of mobs. [Skotlex]
- mob_summonslave(md,md->db->skill[md->skill_idx].val,skill_lv-1,skill_id);
- if (class_) mob_class_change(md, class_);
- }
- break;
-
- case NPC_EMOTION_ON:
- case NPC_EMOTION:
- //va[0] is the emotion to use.
- //NPC_EMOTION & NPC_EMOTION_ON can change a mob's mode 'permanently' [Skotlex]
- //val[1] 'sets' the mode
- //val[2] adds to the current mode
- //val[3] removes from the current mode
- //val[4] if set, asks to delete the previous mode change.
- if(md && md->skill_idx >= 0 && tsc)
- {
- clif_emotion(bl, md->db->skill[md->skill_idx].val[0]);
- if(md->db->skill[md->skill_idx].val[4] && tsce)
- status_change_end(bl, type, INVALID_TIMER);
-
- if(md->db->skill[md->skill_idx].val[1] || md->db->skill[md->skill_idx].val[2])
- sc_start4(src, type, 100, skill_lv,
- md->db->skill[md->skill_idx].val[1],
- md->db->skill[md->skill_idx].val[2],
- md->db->skill[md->skill_idx].val[3],
- skill_get_time(skill_id, skill_lv));
- }
- break;
-
- case NPC_POWERUP:
- sc_start(bl,SC_INCATKRATE,100,200,skill_get_time(skill_id, skill_lv));
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,100,skill_get_time(skill_id, skill_lv)));
- break;
-
- case NPC_AGIUP:
- sc_start(bl,SC_SPEEDUP1,100,skill_lv,skill_get_time(skill_id, skill_lv));
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,100,skill_get_time(skill_id, skill_lv)));
- break;
-
- case NPC_INVISIBLE:
- //Have val4 passed as 6 is for "infinite cloak" (do not end on attack/skill use).
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start4(bl,type,100,skill_lv,0,0,6,skill_get_time(skill_id,skill_lv)));
- break;
-
- case NPC_SIEGEMODE:
- // not sure what it does
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
-
- case WE_MALE:
- {
- int hp_rate=(!skill_lv)? 0:skill_db[skill_id].hp_rate[skill_lv-1];
- int gain_hp= tstatus->max_hp*abs(hp_rate)/100; // The earned is the same % of the target HP than it costed the caster. [Skotlex]
- clif_skill_nodamage(src,bl,skill_id,status_heal(bl, gain_hp, 0, 0),1);
- }
- break;
- case WE_FEMALE:
- {
- int sp_rate=(!skill_lv)? 0:skill_db[skill_id].sp_rate[skill_lv-1];
- int gain_sp=tstatus->max_sp*abs(sp_rate)/100;// The earned is the same % of the target SP than it costed the caster. [Skotlex]
- clif_skill_nodamage(src,bl,skill_id,status_heal(bl, 0, gain_sp, 0),1);
- }
- break;
-
- // parent-baby skills
- case WE_BABY:
- if(sd){
- struct map_session_data *f_sd = pc_get_father(sd);
- struct map_session_data *m_sd = pc_get_mother(sd);
- // if neither was found
- if(!f_sd && !m_sd){
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock();
- return 0;
- }
- status_change_start(bl,SC_STUN,10000,skill_lv,0,0,0,skill_get_time2(skill_id,skill_lv),8);
- if (f_sd) sc_start(&f_sd->bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
- if (m_sd) sc_start(&m_sd->bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
- }
- break;
-
- case PF_HPCONVERSION:
- {
- int hp, sp;
- hp = sstatus->max_hp/10;
- sp = hp * 10 * skill_lv / 100;
- if (!status_charge(src,hp,0)) {
- if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- status_heal(bl,0,sp,2);
- }
- break;
-
- case MA_REMOVETRAP:
- case HT_REMOVETRAP:
- {
- struct skill_unit* su;
- struct skill_unit_group* sg;
- su = BL_CAST(BL_SKILL, bl);
-
- // Mercenaries can remove any trap
- // Players can only remove their own traps or traps on Vs maps.
- if( su && (sg = su->group) && (src->type == BL_MER || sg->src_id == src->id || map_flag_vs(bl->m)) && (skill_get_inf2(sg->skill_id)&INF2_TRAP) )
- {
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- if( sd && !(sg->unit_id == UNT_USED_TRAPS || (sg->unit_id == UNT_ANKLESNARE && sg->val2 != 0 )) )
- { // prevent picking up expired traps
- if( battle_config.skill_removetrap_type )
- { // get back all items used to deploy the trap
- for( i = 0; i < 10; i++ )
- {
- if( skill_db[su->group->skill_id].itemid[i] > 0 )
- {
- int flag;
- struct item item_tmp;
- memset(&item_tmp,0,sizeof(item_tmp));
- item_tmp.nameid = skill_db[su->group->skill_id].itemid[i];
- item_tmp.identify = 1;
- if( item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,skill_db[su->group->skill_id].amount[i],LOG_TYPE_OTHER)) )
- {
- clif_additem(sd,0,0,flag);
- map_addflooritem(&item_tmp,skill_db[su->group->skill_id].amount[i],sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
- }
- }
- }
- else
- { // get back 1 trap
- struct item item_tmp;
- memset(&item_tmp,0,sizeof(item_tmp));
- item_tmp.nameid = su->group->item_id?su->group->item_id:ITEMID_TRAP;
- item_tmp.identify = 1;
- if( item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_OTHER)) )
- {
- clif_additem(sd,0,0,flag);
- map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
- }
- }
- skill_delunit(su);
- }else if(sd)
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
-
- }
- break;
- case HT_SPRINGTRAP:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- {
- struct skill_unit *su=NULL;
- if((bl->type==BL_SKILL) && (su=(struct skill_unit *)bl) && (su->group) ){
- switch(su->group->unit_id){
- case UNT_ANKLESNARE: // ankle snare
- if (su->group->val2 != 0)
- // if it is already trapping something don't spring it,
- // remove trap should be used instead
- break;
- // otherwise fallthrough to below
- case UNT_BLASTMINE:
- case UNT_SKIDTRAP:
- case UNT_LANDMINE:
- case UNT_SHOCKWAVE:
- case UNT_SANDMAN:
- case UNT_FLASHER:
- case UNT_FREEZINGTRAP:
- case UNT_CLAYMORETRAP:
- case UNT_TALKIEBOX:
- su->group->unit_id = UNT_USED_TRAPS;
- clif_changetraplook(bl, UNT_USED_TRAPS);
- 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,skill_id,skill_lv,1);
- if(sd)
- unit_skilluse_id(src,src->id,sd->skill_id_dance,sd->skill_lv_dance);
- break;
-
- case AS_SPLASHER:
- if(tstatus->mode&MD_BOSS
- /**
- * Renewal dropped the 3/4 hp requirement
- **/
- #ifndef RENEWAL
- || tstatus-> hp > tstatus->max_hp*3/4
- #endif
- ) {
- if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock();
- return 1;
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start4(bl,type,100,skill_lv,skill_id,src->id,skill_get_time(skill_id,skill_lv),1000));
-#ifndef RENEWAL
- if (sd) skill_blockpc_start (sd, skill_id, skill_get_time(skill_id, skill_lv)+3000);
-#endif
- break;
-
- case PF_MINDBREAKER:
- {
- if(tstatus->mode&MD_BOSS || battle_check_undead(tstatus->race,tstatus->def_ele))
- {
- map_freeblock_unlock();
- return 1;
- }
-
- if (tsce)
- { //HelloKitty2 (?) explained that this silently fails when target is
- //already inflicted. [Skotlex]
- map_freeblock_unlock();
- return 1;
- }
-
- //Has a 55% + skill_lv*5% success chance.
- if (!clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,55+5*skill_lv,skill_lv,skill_get_time(skill_id,skill_lv))))
- {
- if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock();
- return 0;
- }
-
- unit_skillcastcancel(bl,0);
-
- if(tsc && tsc->count){
- status_change_end(bl, SC_FREEZE, INVALID_TIMER);
- if(tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE)
- status_change_end(bl, SC_STONE, INVALID_TIMER);
- status_change_end(bl, SC_SLEEP, INVALID_TIMER);
- }
-
- if(dstmd)
- mob_target(dstmd,src,skill_get_range2(src,skill_id,skill_lv));
- }
- break;
-
- case PF_SOULCHANGE:
- {
- unsigned int sp1 = 0, sp2 = 0;
- if (dstmd) {
- if (dstmd->state.soul_change_flag) {
- if(sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- dstmd->state.soul_change_flag = 1;
- sp2 = sstatus->max_sp * 3 /100;
- status_heal(src, 0, sp2, 2);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
- }
- sp1 = sstatus->sp;
- sp2 = tstatus->sp;
- #ifdef RENEWAL
- sp1 = sp1 / 2;
- sp2 = sp2 / 2;
- if( tsc && tsc->data[SC_EXTREMITYFIST2] )
- sp1 = tstatus->sp;
- #endif
- status_set_sp(src, sp2, 3);
- status_set_sp(bl, sp1, 3);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- // Slim Pitcher
- case CR_SLIMPITCHER:
- // Updated to block Slim Pitcher from working on barricades and guardian stones.
- if( dstmd && (dstmd->class_ == MOBID_EMPERIUM || (dstmd->class_ >= MOBID_BARRICADE1 && dstmd->class_ <= MOBID_GUARIDAN_STONE2)) )
- break;
- if (potion_hp || potion_sp) {
- int hp = potion_hp, sp = potion_sp;
- hp = hp * (100 + (tstatus->vit<<1))/100;
- sp = sp * (100 + (tstatus->int_<<1))/100;
- if (dstsd) {
- if (hp)
- hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10 + pc_skillheal2_bonus(dstsd, skill_id))/100;
- if (sp)
- sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10 + pc_skillheal2_bonus(dstsd, skill_id))/100;
- }
- if( tsc && tsc->count ) {
- if (tsc->data[SC_CRITICALWOUND]) {
- hp -= hp * tsc->data[SC_CRITICALWOUND]->val2 / 100;
- sp -= sp * tsc->data[SC_CRITICALWOUND]->val2 / 100;
- }
- if (tsc->data[SC_DEATHHURT]) {
- hp -= hp * 20 / 100;
- sp -= sp * 20 / 100;
- }
- if( tsc->data[SC_WATER_INSIGNIA] && tsc->data[SC_WATER_INSIGNIA]->val1 == 2) {
- hp += hp / 10;
- sp += sp / 10;
- }
- }
- if(hp > 0)
- clif_skill_nodamage(NULL,bl,AL_HEAL,hp,1);
- if(sp > 0)
- clif_skill_nodamage(NULL,bl,MG_SRECOVERY,sp,1);
- status_heal(bl,hp,sp,0);
- }
- break;
- // Full Chemical Protection
- case CR_FULLPROTECTION:
- {
- unsigned int equip[] = {EQP_WEAPON, EQP_SHIELD, EQP_ARMOR, EQP_HEAD_TOP};
- int i, s = 0, skilltime = skill_get_time(skill_id,skill_lv);
-
- for (i=0 ; i<4; i++) {
- if( bl->type != BL_PC || ( dstsd && pc_checkequip(dstsd,equip[i]) < 0 ) )
- continue;
- sc_start(bl,(sc_type)(SC_CP_WEAPON + i),100,skill_lv,skilltime);
- s++;
- }
- if( sd && !s ){
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock(); // Don't consume item requirements
- return 0;
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case RG_CLEANER: //AppleGirl
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
-
- case CG_LONGINGFREEDOM:
- {
- if (tsc && !tsce && (tsce=tsc->data[SC_DANCING]) && tsce->val4
- && (tsce->val1&0xFFFF) != CG_MOONLIT) //Can't use Longing for Freedom while under Moonlight Petals. [Skotlex]
- {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- }
- }
- break;
-
- case CG_TAROTCARD:
- {
- int eff, count = -1;
- if( rnd() % 100 > skill_lv * 8 || (dstmd && ((dstmd->guardian_data && dstmd->class_ == MOBID_EMPERIUM) || mob_is_battleground(dstmd))) )
- {
- if( sd )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
-
- map_freeblock_unlock();
- return 0;
- }
- status_zap(src,0,skill_db[skill_get_index(skill_id)].sp[skill_lv]); // consume sp only if succeeded [Inkfish]
- do {
- eff = rnd() % 14;
- clif_specialeffect(bl, 523 + eff, AREA);
- switch (eff)
- {
- case 0: // heals SP to 0
- status_percent_damage(src, bl, 0, 100, false);
- break;
- case 1: // matk halved
- sc_start(bl,SC_INCMATKRATE,100,-50,skill_get_time2(skill_id,skill_lv));
- break;
- case 2: // all buffs removed
- status_change_clear_buffs(bl,1);
- break;
- case 3: // 1000 damage, random armor destroyed
- {
- int where[] = { EQP_ARMOR, EQP_SHIELD, EQP_HELM, EQP_SHOES, EQP_GARMENT };
- status_fix_damage(src, bl, 1000, 0);
- clif_damage(src,bl,tick,0,0,1000,0,0,0);
- if( !status_isdead(bl) )
- skill_break_equip(bl, where[rnd()%5], 10000, BCT_ENEMY);
- }
- break;
- case 4: // atk halved
- sc_start(bl,SC_INCATKRATE,100,-50,skill_get_time2(skill_id,skill_lv));
- break;
- case 5: // 2000HP heal, random teleported
- status_heal(src, 2000, 0, 0);
- if( !map_flag_vs(bl->m) )
- unit_warp(bl, -1,-1,-1, CLR_TELEPORT);
- break;
- case 6: // random 2 other effects
- if (count == -1)
- count = 3;
- else
- count++; //Should not retrigger this one.
- break;
- case 7: // stop freeze or stoned
- {
- enum sc_type sc[] = { SC_STOP, SC_FREEZE, SC_STONE };
- sc_start(bl,sc[rnd()%3],100,skill_lv,skill_get_time2(skill_id,skill_lv));
- }
- break;
- case 8: // curse coma and poison
- sc_start(bl,SC_COMA,100,skill_lv,skill_get_time2(skill_id,skill_lv));
- sc_start(bl,SC_CURSE,100,skill_lv,skill_get_time2(skill_id,skill_lv));
- sc_start(bl,SC_POISON,100,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
- case 9: // confusion
- sc_start(bl,SC_CONFUSION,100,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
- case 10: // 6666 damage, atk matk halved, cursed
- status_fix_damage(src, bl, 6666, 0);
- clif_damage(src,bl,tick,0,0,6666,0,0,0);
- sc_start(bl,SC_INCATKRATE,100,-50,skill_get_time2(skill_id,skill_lv));
- sc_start(bl,SC_INCMATKRATE,100,-50,skill_get_time2(skill_id,skill_lv));
- sc_start(bl,SC_CURSE,skill_lv,100,skill_get_time2(skill_id,skill_lv));
- break;
- case 11: // 4444 damage
- status_fix_damage(src, bl, 4444, 0);
- clif_damage(src,bl,tick,0,0,4444,0,0,0);
- break;
- case 12: // stun
- sc_start(bl,SC_STUN,100,skill_lv,5000);
- break;
- case 13: // atk,matk,hit,flee,def reduced
- sc_start(bl,SC_INCATKRATE,100,-20,skill_get_time2(skill_id,skill_lv));
- sc_start(bl,SC_INCMATKRATE,100,-20,skill_get_time2(skill_id,skill_lv));
- sc_start(bl,SC_INCHITRATE,100,-20,skill_get_time2(skill_id,skill_lv));
- sc_start(bl,SC_INCFLEERATE,100,-20,skill_get_time2(skill_id,skill_lv));
- sc_start(bl,SC_INCDEFRATE,100,-20,skill_get_time2(skill_id,skill_lv));
- break;
- default:
- break;
- }
- } while ((--count) > 0);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case SL_ALCHEMIST:
- case SL_ASSASIN:
- case SL_BARDDANCER:
- case SL_BLACKSMITH:
- case SL_CRUSADER:
- case SL_HUNTER:
- case SL_KNIGHT:
- case SL_MONK:
- case SL_PRIEST:
- case SL_ROGUE:
- case SL_SAGE:
- case SL_SOULLINKER:
- case SL_STAR:
- case SL_SUPERNOVICE:
- case SL_WIZARD:
- //NOTE: here, 'type' has the value of the associated MAPID, not of the SC_SPIRIT constant.
- if (sd && !(dstsd && (dstsd->class_&MAPID_UPPERMASK) == type)) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- if (skill_id == SL_SUPERNOVICE && dstsd && dstsd->die_counter && !(rnd()%100))
- { //Erase death count 1% of the casts
- dstsd->die_counter = 0;
- pc_setglobalreg(dstsd,"PC_DIE_COUNTER", 0);
- clif_specialeffect(bl, 0x152, AREA);
- //SC_SPIRIT invokes status_calc_pc for us.
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start4(bl,SC_SPIRIT,100,skill_lv,skill_id,0,0,skill_get_time(skill_id,skill_lv)));
- sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA,skill_lv));
- break;
- case SL_HIGH:
- if (sd && !(dstsd && (dstsd->class_&JOBL_UPPER) && !(dstsd->class_&JOBL_2) && dstsd->status.base_level < 70)) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start4(bl,type,100,skill_lv,skill_id,0,0,skill_get_time(skill_id,skill_lv)));
- sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA,skill_lv));
- break;
-
- case SL_SWOO:
- if (tsce) {
- if(sd)
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,10000,8);
- status_change_end(bl, SC_SWOO, INVALID_TIMER);
- break;
- }
- case SL_SKA: // [marquis007]
- case SL_SKE:
- if (sd && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,500,10);
- break;
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- if (skill_id == SL_SKE)
- sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA,skill_lv));
- break;
-
- // New guild skills [Celest]
- case GD_BATTLEORDER:
- if(flag&1) {
- if (status_get_guild_id(src) == status_get_guild_id(bl))
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv));
- } else if (status_get_guild_id(src)) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- map_foreachinrange(skill_area_sub, src,
- skill_get_splash(skill_id, skill_lv), BL_PC,
- src,skill_id,skill_lv,tick, flag|BCT_GUILD|1,
- skill_castend_nodamage_id);
- if (sd)
- guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
- }
- break;
- case GD_REGENERATION:
- if(flag&1) {
- if (status_get_guild_id(src) == status_get_guild_id(bl))
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv));
- } else if (status_get_guild_id(src)) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- map_foreachinrange(skill_area_sub, src,
- skill_get_splash(skill_id, skill_lv), BL_PC,
- src,skill_id,skill_lv,tick, flag|BCT_GUILD|1,
- skill_castend_nodamage_id);
- if (sd)
- guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
- }
- break;
- case GD_RESTORE:
- if(flag&1) {
- if (status_get_guild_id(src) == status_get_guild_id(bl))
- clif_skill_nodamage(src,bl,AL_HEAL,status_percent_heal(bl,90,90),1);
- } else if (status_get_guild_id(src)) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- map_foreachinrange(skill_area_sub, src,
- skill_get_splash(skill_id, skill_lv), BL_PC,
- src,skill_id,skill_lv,tick, flag|BCT_GUILD|1,
- skill_castend_nodamage_id);
- if (sd)
- guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
- }
- break;
- case GD_EMERGENCYCALL:
- {
- int dx[9]={-1, 1, 0, 0,-1, 1,-1, 1, 0};
- int dy[9]={ 0, 0, 1,-1, 1,-1,-1, 1, 0};
- int j = 0;
- struct guild *g = NULL;
- // i don't know if it actually summons in a circle, but oh well. ;P
- g = sd?sd->state.gmaster_flag:guild_search(status_get_guild_id(src));
- if (!g)
- break;
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- for(i = 0; i < g->max_member; i++, j++) {
- if (j>8) j=0;
- if ((dstsd = g->member[i].sd) != NULL && sd != dstsd && !dstsd->state.autotrade && !pc_isdead(dstsd)) {
- if (map[dstsd->bl.m].flag.nowarp && !map_flag_gvg2(dstsd->bl.m))
- continue;
- if(map_getcell(src->m,src->x+dx[j],src->y+dy[j],CELL_CHKNOREACH))
- dx[j] = dy[j] = 0;
- pc_setpos(dstsd, map_id2index(src->m), src->x+dx[j], src->y+dy[j], CLR_RESPAWN);
- }
- }
- if (sd)
- guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
- }
- break;
-
- case SG_FEEL:
- //AuronX reported you CAN memorize the same map as all three. [Skotlex]
- if (sd) {
- if(!sd->feel_map[skill_lv-1].index)
- clif_feel_req(sd->fd,sd, skill_lv);
- else
- clif_feel_info(sd, skill_lv-1, 1);
- }
- break;
-
- case SG_HATE:
- if (sd) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if (!pc_set_hate_mob(sd, skill_lv-1, bl))
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- }
- break;
-
- case GS_GLITTERING:
- if(sd) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if(rnd()%100 < (20+10*skill_lv))
- pc_addspiritball(sd,skill_get_time(skill_id,skill_lv),10);
- else if(sd->spiritball > 0)
- pc_delspiritball(sd,1,0);
- }
- break;
-
- case GS_CRACKER:
- /* per official standards, this skill works on players and mobs. */
- if (sd && (dstsd || dstmd))
- {
- i =65 -5*distance_bl(src,bl); //Base rate
- if (i < 30) i = 30;
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- sc_start(bl,SC_STUN, i,skill_lv,skill_get_time2(skill_id,skill_lv));
- }
- break;
-
- case AM_CALLHOMUN: //[orn]
- if (sd && !merc_call_homunculus(sd))
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
-
- case AM_REST:
- if (sd) {
- if (merc_hom_vaporize(sd,1))
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- else
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- }
- break;
-
- case HAMI_CASTLE: //[orn]
- if(rnd()%100 < 20*skill_lv && src != bl)
- {
- int x,y;
- x = src->x;
- y = src->y;
- if (hd)
- skill_blockhomun_start(hd, skill_id, skill_get_time2(skill_id,skill_lv));
-
- if (unit_movepos(src,bl->x,bl->y,0,0)) {
- clif_skill_nodamage(src,src,skill_id,skill_lv,1); // Homunc
- clif_slide(src,bl->x,bl->y) ;
- if (unit_movepos(bl,x,y,0,0))
- {
- clif_skill_nodamage(bl,bl,skill_id,skill_lv,1); // Master
- clif_slide(bl,x,y) ;
- }
-
- //TODO: Shouldn't also players and the like switch targets?
- map_foreachinrange(skill_chastle_mob_changetarget,src,
- AREA_SIZE, BL_MOB, bl, src);
- }
- }
- // Failed
- else if (hd && hd->master)
- clif_skill_fail(hd->master, skill_id, USESKILL_FAIL_LEVEL, 0);
- else if (sd)
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
- break;
- case HVAN_CHAOTIC: //[orn]
- {
- static const int per[5][2]={{20,50},{50,60},{25,75},{60,64},{34,67}};
- int r = rnd()%100;
- i = (skill_lv-1)%5;
- if(r<per[i][0]) //Self
- bl = src;
- else if(r<per[i][1]) //Master
- bl = battle_get_master(src);
- else //Enemy
- bl = map_id2bl(battle_gettarget(src));
-
- if (!bl) bl = src;
- i = skill_calc_heal(src, bl, skill_id, 1+rnd()%skill_lv, true);
- //Eh? why double skill packet?
- clif_skill_nodamage(src,bl,AL_HEAL,i,1);
- clif_skill_nodamage(src,bl,skill_id,i,1);
- status_heal(bl, i, 0, 0);
- }
- break;
- //Homun single-target support skills [orn]
- case HAMI_BLOODLUST:
- case HFLI_FLEET:
- case HFLI_SPEED:
- case HLIF_CHANGE:
- case MH_ANGRIFFS_MODUS:
- case MH_GOLDENE_FERSE:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- if (hd)
- skill_blockhomun_start(hd, skill_id, skill_get_time2(skill_id,skill_lv));
- break;
-
- case NPC_DRAGONFEAR:
- if (flag&1) {
- const enum sc_type sc[] = { SC_STUN, SC_SILENCE, SC_CONFUSION, SC_BLEEDING };
- int j;
- j = i = rnd()%ARRAYLENGTH(sc);
- while ( !sc_start(bl,sc[i],100,skill_lv,skill_get_time2(skill_id,i+1)) ) {
- i++;
- if ( i == ARRAYLENGTH(sc) )
- i = 0;
- if (i == j)
- break;
- }
- break;
- }
- case NPC_WIDEBLEEDING:
- case NPC_WIDECONFUSE:
- case NPC_WIDECURSE:
- case NPC_WIDEFREEZE:
- case NPC_WIDESLEEP:
- case NPC_WIDESILENCE:
- case NPC_WIDESTONE:
- case NPC_WIDESTUN:
- case NPC_SLOWCAST:
- case NPC_WIDEHELLDIGNITY:
- if (flag&1)
- sc_start(bl,type,100,skill_lv,skill_get_time2(skill_id,skill_lv));
- else {
- skill_area_temp[2] = 0; //For SD_PREAMBLE
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- map_foreachinrange(skill_area_sub, bl,
- skill_get_splash(skill_id, skill_lv),BL_CHAR,
- src,skill_id,skill_lv,tick, flag|BCT_ENEMY|SD_PREAMBLE|1,
- skill_castend_nodamage_id);
- }
- break;
- case NPC_WIDESOULDRAIN:
- if (flag&1)
- status_percent_damage(src,bl,0,((skill_lv-1)%5+1)*20,false);
- else {
- skill_area_temp[2] = 0; //For SD_PREAMBLE
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- map_foreachinrange(skill_area_sub, bl,
- skill_get_splash(skill_id, skill_lv),BL_CHAR,
- src,skill_id,skill_lv,tick, flag|BCT_ENEMY|SD_PREAMBLE|1,
- skill_castend_nodamage_id);
- }
- break;
- case ALL_PARTYFLEE:
- if( sd && !(flag&1) )
- {
- if( !sd->status.party_id )
- {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
- }
- else
- clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- break;
- case NPC_TALK:
- case ALL_WEWISH:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
- case ALL_BUYING_STORE:
- if( sd )
- {// players only, skill allows 5 buying slots
- clif_skill_nodamage(src, bl, skill_id, skill_lv, buyingstore_setup(sd, MAX_BUYINGSTORE_SLOTS));
- }
- break;
- case RK_ENCHANTBLADE:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,// formula not confirmed
- sc_start2(bl,type,100,skill_lv,100+20*skill_lv/*+sstatus->int_/2+status_get_lv(bl)/10*/,skill_get_time(skill_id,skill_lv)));
- break;
- case RK_DRAGONHOWLING:
- if( flag&1)
- sc_start(bl,type,50 + 6 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
- else
- {
- skill_area_temp[2] = 0;
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- map_foreachinrange(skill_area_sub, src,
- skill_get_splash(skill_id,skill_lv),BL_CHAR,
- src,skill_id,skill_lv,tick,flag|BCT_ENEMY|SD_PREAMBLE|1,
- skill_castend_nodamage_id);
- }
- break;
- case RK_IGNITIONBREAK:
- case LG_EARTHDRIVE:
- clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- i = skill_get_splash(skill_id,skill_lv);
- if( skill_id == LG_EARTHDRIVE ) {
- int dummy = 1;
- map_foreachinarea(skill_cell_overlap, src->m, src->x-i, src->y-i, src->x+i, src->y+i, BL_SKILL, LG_EARTHDRIVE, &dummy, src);
- }
- map_foreachinrange(skill_area_sub, bl,i,BL_CHAR,
- src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
- break;
- case RK_STONEHARDSKIN:
- if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 4 )
- {
- int heal = sstatus->hp / 4; // 25% HP
- if( status_charge(bl,heal,0) )
- clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start2(bl,type,100,skill_lv,heal,skill_get_time(skill_id,skill_lv)));
- else
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- }
- break;
- case RK_REFRESH:
- if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 8 )
- {
- int heal = status_get_max_hp(bl) * 25 / 100;
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- status_heal(bl,heal,0,1);
- status_change_clear_buffs(bl,4);
- }
- break;
-
- case RK_MILLENNIUMSHIELD:
- if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 9 )
- {
- short shields = (rnd()%100<50) ? 4 : ((rnd()%100<80) ? 3 : 2);
- sc_start4(bl,type,100,skill_lv,shields,1000,0,skill_get_time(skill_id,skill_lv));
- clif_millenniumshield(sd,shields);
- clif_skill_nodamage(src,bl,skill_id,1,1);
- }
- break;
-
- case RK_GIANTGROWTH:
- case RK_VITALITYACTIVATION:
- case RK_ABUNDANCE:
- case RK_CRUSHSTRIKE:
- if( sd )
- {
- int lv = 1; // RK_GIANTGROWTH
- if( skill_id == RK_VITALITYACTIVATION )
- lv = 2;
- else if( skill_id == RK_ABUNDANCE )
- lv = 6;
- else if( skill_id == RK_CRUSHSTRIKE )
- lv = 7;
- if( pc_checkskill(sd,RK_RUNEMASTERY) >= lv )
- clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- }
- break;
-
- case RK_FIGHTINGSPIRIT:
- if( flag&1 ) {
- if( src == bl )
- sc_start2(bl,type,100,skill_area_temp[5],10*(sd?pc_checkskill(sd,RK_RUNEMASTERY):10),skill_get_time(skill_id,skill_lv));
- else
- sc_start(bl,type,100,skill_area_temp[5]/4,skill_get_time(skill_id,skill_lv));
- } else if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 5 ) {
- if( sd->status.party_id ) {
- i = party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,BCT_PARTY,skill_area_sub_count);
- skill_area_temp[5] = 7 * i; // ATK
- party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,flag|BCT_PARTY|1,skill_castend_nodamage_id);
- } else
- sc_start2(bl,type,100,7,5,skill_get_time(skill_id,skill_lv));
- }
- clif_skill_nodamage(src,bl,skill_id,1,1);
- break;
- /**
- * Guilotine Cross
- **/
- case GC_ROLLINGCUTTER:
- {
- short count = 1;
- skill_area_temp[2] = 0;
- map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|SD_PREAMBLE|SD_SPLASH|1,skill_castend_damage_id);
- if( tsc && tsc->data[SC_ROLLINGCUTTER] )
- { // Every time the skill is casted the status change is reseted adding a counter.
- count += (short)tsc->data[SC_ROLLINGCUTTER]->val1;
- if( count > 10 )
- count = 10; // Max coounter
- status_change_end(bl, SC_ROLLINGCUTTER, INVALID_TIMER);
- }
- sc_start(bl,SC_ROLLINGCUTTER,100,count,skill_get_time(skill_id,skill_lv));
- clif_skill_nodamage(src,src,skill_id,skill_lv,1);
- }
- break;
-
- case GC_WEAPONBLOCKING:
- if( tsc && tsc->data[SC_WEAPONBLOCKING] )
- status_change_end(bl, SC_WEAPONBLOCKING, INVALID_TIMER);
- else
- sc_start(bl,SC_WEAPONBLOCKING,100,skill_lv,skill_get_time(skill_id,skill_lv));
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
-
- case GC_CREATENEWPOISON:
- if( sd )
- {
- clif_skill_produce_mix_list(sd,skill_id,25);
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- }
- break;
-
- case GC_POISONINGWEAPON:
- if( sd ) {
- clif_poison_list(sd,skill_lv);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case GC_ANTIDOTE:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if( tsc )
- {
- status_change_end(bl, SC_PARALYSE, INVALID_TIMER);
- status_change_end(bl, SC_PYREXIA, INVALID_TIMER);
- status_change_end(bl, SC_DEATHHURT, INVALID_TIMER);
- status_change_end(bl, SC_LEECHESEND, INVALID_TIMER);
- status_change_end(bl, SC_VENOMBLEED, INVALID_TIMER);
- status_change_end(bl, SC_MAGICMUSHROOM, INVALID_TIMER);
- status_change_end(bl, SC_TOXIN, INVALID_TIMER);
- status_change_end(bl, SC_OBLIVIONCURSE, INVALID_TIMER);
- }
- break;
-
- case GC_PHANTOMMENACE:
- clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,
- src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
- break;
-
- case GC_HALLUCINATIONWALK:
- {
- int heal = status_get_max_hp(bl) / 10;
- if( status_get_hp(bl) < heal ) { // if you haven't enough HP skill fails.
- if( sd ) clif_skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0);
- break;
- }
- if( !status_charge(bl,heal,0) )
- {
- if( sd ) clif_skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0);
- break;
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- }
- break;
- /**
- * Arch Bishop
- **/
- case AB_ANCILLA:
- if( sd ) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- skill_produce_mix(sd, skill_id, ITEMID_ANCILLA, 0, 0, 0, 1);
- }
- break;
-
- case AB_CLEMENTIA:
- case AB_CANTO:
- {
- int bless_lv = pc_checkskill(sd,AL_BLESSING) + (sd->status.job_level / 10);
- int agi_lv = pc_checkskill(sd,AL_INCAGI) + (sd->status.job_level / 10);
- if( sd == NULL || sd->status.party_id == 0 || flag&1 )
- clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(bl,type,100,
- (skill_id == AB_CLEMENTIA)? bless_lv : (skill_id == AB_CANTO)? agi_lv : skill_lv, skill_get_time(skill_id,skill_lv)));
- else if( sd )
- party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
- }
- break;
-
- case AB_PRAEFATIO:
- if( sd == NULL || sd->status.party_id == 0 || flag&1 )
- clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start4(bl, type, 100, skill_lv, 0, 0, 1, skill_get_time(skill_id, skill_lv)));
- else if( sd )
- party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
- break;
-
- case AB_CHEAL:
- if( sd == NULL || sd->status.party_id == 0 || flag&1 )
- {
- if( sd && tstatus && !battle_check_undead(tstatus->race, tstatus->def_ele) )
- {
- i = skill_calc_heal(src, bl, AL_HEAL, pc_checkskill(sd, AL_HEAL), true);
-
- if( (dstsd && pc_ismadogear(dstsd)) || status_isimmune(bl))
- i = 0; // Should heal by 0 or won't do anything?? in iRO it breaks the healing to members.. [malufett]
-
- clif_skill_nodamage(bl, bl, skill_id, i, 1);
- if( tsc && tsc->data[SC_AKAITSUKI] && i )
- i = ~i + 1;
- status_heal(bl, i, 0, 0);
- }
- }
- else if( sd )
- party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
- break;
-
- case AB_ORATIO:
- if( flag&1 )
- sc_start(bl, type, 40 + 5 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
- else
- {
- map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv), BL_CHAR,
- src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- }
- break;
-
- case AB_LAUDAAGNUS:
- if( flag&1 || sd == NULL ) {
- if( tsc && (tsc->data[SC_FREEZE] || tsc->data[SC_STONE] || tsc->data[SC_BLIND] ||
- tsc->data[SC_BURNING] || tsc->data[SC_FREEZING] || tsc->data[SC_CRYSTALIZE])) {
- // Success Chance: (40 + 10 * Skill Level) %
- if( rnd()%100 > 40+10*skill_lv ) break;
- status_change_end(bl, SC_FREEZE, INVALID_TIMER);
- status_change_end(bl, SC_STONE, INVALID_TIMER);
- status_change_end(bl, SC_BLIND, INVALID_TIMER);
- status_change_end(bl, SC_BURNING, INVALID_TIMER);
- status_change_end(bl, SC_FREEZING, INVALID_TIMER);
- status_change_end(bl, SC_CRYSTALIZE, INVALID_TIMER);
- }else //Success rate only applies to the curing effect and not stat bonus. Bonus status only applies to non infected targets
- clif_skill_nodamage(bl, bl, skill_id, skill_lv,
- sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)));
- } else if( sd )
- party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv),
- src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
- break;
-
- case AB_LAUDARAMUS:
- if( flag&1 || sd == NULL ) {
- if( tsc && (tsc->data[SC_SLEEP] || tsc->data[SC_STUN] || tsc->data[SC_MANDRAGORA] || tsc->data[SC_SILENCE]) ){
- // Success Chance: (40 + 10 * Skill Level) %
- if( rnd()%100 > 40+10*skill_lv ) break;
- status_change_end(bl, SC_SLEEP, INVALID_TIMER);
- status_change_end(bl, SC_STUN, INVALID_TIMER);
- status_change_end(bl, SC_MANDRAGORA, INVALID_TIMER);
- status_change_end(bl, SC_SILENCE, INVALID_TIMER);
- }else // Success rate only applies to the curing effect and not stat bonus. Bonus status only applies to non infected targets
- clif_skill_nodamage(bl, bl, skill_id, skill_lv,
- sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)));
- } else if( sd )
- party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv),
- src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
- break;
-
- case AB_CLEARANCE:
- if( flag&1 || (i = skill_get_splash(skill_id, skill_lv)) < 1 )
- { //As of the behavior in official server Clearance is just a super version of Dispell skill. [Jobbie]
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if((dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) || rnd()%100 >= 30 + 10 * skill_lv)
- {
- if (sd)
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- if(status_isimmune(bl) || !tsc || !tsc->count)
- break;
- for(i=0;i<SC_MAX;i++)
- {
- if (!tsc->data[i])
- continue;
- switch (i) {
- case SC_WEIGHT50: case SC_WEIGHT90: case SC_HALLUCINATION:
- 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: case SC_COMBO:
- case SC_STRFOOD: case SC_AGIFOOD: case SC_VITFOOD:
- case SC_INTFOOD: case SC_DEXFOOD: case SC_LUKFOOD:
- case SC_HITFOOD: case SC_FLEEFOOD: case SC_BATKFOOD:
- case SC_WATKFOOD: case SC_MATKFOOD: case SC_DANCING:
- case SC_SPIRIT: case SC_AUTOBERSERK:
- case SC_CARTBOOST: case SC_MELTDOWN: case SC_SAFETYWALL:
- case SC_SMA: case SC_SPEEDUP0: case SC_NOCHAT:
- case SC_ANKLE: case SC_SPIDERWEB: case SC_JAILED:
- case SC_ITEMBOOST: case SC_EXPBOOST: case SC_LIFEINSURANCE:
- case SC_BOSSMAPINFO: case SC_PNEUMA: case SC_AUTOSPELL:
- case SC_INCHITRATE: case SC_INCATKRATE: case SC_NEN:
- case SC_READYSTORM: case SC_READYDOWN: case SC_READYTURN:
- case SC_READYCOUNTER:case SC_DODGE: case SC_WARM:
- case SC_SPEEDUP1: case SC_AUTOTRADE: case SC_CRITICALWOUND:
- case SC_JEXPBOOST: case SC_INVINCIBLE: case SC_INVINCIBLEOFF:
- case SC_HELLPOWER: case SC_MANU_ATK: case SC_MANU_DEF:
- case SC_SPL_ATK: case SC_SPL_DEF: case SC_MANU_MATK:
- case SC_SPL_MATK: case SC_RICHMANKIM: case SC_ETERNALCHAOS:
- case SC_DRUMBATTLE: case SC_NIBELUNGEN: case SC_ROKISWEIL:
- case SC_INTOABYSS: case SC_SIEGFRIED: case SC_WHISTLE:
- case SC_ASSNCROS: case SC_POEMBRAGI: case SC_APPLEIDUN:
- case SC_HUMMING: case SC_DONTFORGETME: case SC_FORTUNE:
- case SC_SERVICE4U: case SC_FOOD_STR_CASH: case SC_FOOD_AGI_CASH:
- case SC_FOOD_VIT_CASH: case SC_FOOD_DEX_CASH: case SC_FOOD_INT_CASH:
- case SC_FOOD_LUK_CASH: case SC_ELECTRICSHOCKER: case SC_BITE:
- case SC__STRIPACCESSORY: case SC__ENERVATION: case SC__GROOMY:
- case SC__IGNORANCE: case SC__LAZINESS: case SC__UNLUCKY:
- case SC__WEAKNESS: //case SC_SAVAGE_STEAK: case SC_COCKTAIL_WARG_BLOOD:
- case SC_MAGNETICFIELD://case SC_MINOR_BBQ: case SC_SIROMA_ICE_TEA:
- //case SC_DROCERA_HERB_STEAMED: case SC_PUTTI_TAILS_NOODLES:
- case SC_NEUTRALBARRIER_MASTER: case SC_NEUTRALBARRIER:
- case SC_STEALTHFIELD_MASTER: case SC_STEALTHFIELD:
- case SC_LEADERSHIP: case SC_GLORYWOUNDS: case SC_SOULCOLD:
- case SC_HAWKEYES: case SC_GUILDAURA: case SC_PUSH_CART:
- case SC_PARTYFLEE: case SC_GT_REVITALIZE:
- case SC_RAISINGDRAGON: case SC_GT_ENERGYGAIN: case SC_GT_CHANGE:
-#ifdef RENEWAL
- case SC_EXTREMITYFIST2:
-#endif
- continue;
- case SC_ASSUMPTIO:
- if( bl->type == BL_MOB )
- continue;
- break;
- }
- if(i==SC_BERSERK || i==SC_SATURDAYNIGHTFEVER) tsc->data[i]->val2=0; //Mark a dispelled berserk to avoid setting hp to 100 by setting hp penalty to 0.
- status_change_end(bl,(sc_type)i,INVALID_TIMER);
- }
- break;
- }
- map_foreachinrange(skill_area_sub, bl, i, BL_CHAR, src, skill_id, skill_lv, tick, flag|1, skill_castend_damage_id);
- break;
-
- case AB_SILENTIUM:
- // Should the level of Lex Divina be equivalent to the level of Silentium or should the highest level learned be used? [LimitLine]
- map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv), BL_CHAR,
- src, PR_LEXDIVINA, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- break;
- /**
- * Warlock
- **/
- case WL_STASIS:
- if( flag&1 )
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
- else
- {
- map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id, skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,(map_flag_vs(src->m)?BCT_ALL:BCT_ENEMY|BCT_SELF)|flag|1,skill_castend_nodamage_id);
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- }
- break;
-
- case WL_WHITEIMPRISON:
- if( (src == bl || battle_check_target(src, bl, BCT_ENEMY)) && !is_boss(bl) )// Should not work with bosses.
- {
- int rate = ( sd? sd->status.job_level : 50 ) / 4;
-
- if( src == bl ) rate = 100; // Success Chance: On self, 100%
- else if(bl->type == BL_PC) rate += 20 + 10 * skill_lv; // On Players, (20 + 10 * Skill Level) %
- else rate += 40 + 10 * skill_lv; // On Monsters, (40 + 10 * Skill Level) %
-
- if( sd )
- skill_blockpc_start(sd,skill_id,4000);
-
- if( !(tsc && tsc->data[type]) ){
- i = sc_start2(bl,type,rate,skill_lv,src->id,(src == bl)?5000:(bl->type == BL_PC)?skill_get_time(skill_id,skill_lv):skill_get_time2(skill_id, skill_lv));
- clif_skill_nodamage(src,bl,skill_id,skill_lv,i);
- if( !i )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- }
- }else
- if( sd )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_TOTARGET,0);
- break;
-
- case WL_FROSTMISTY:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- map_foreachinrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_CHAR|BL_SKILL,src,skill_id,skill_lv,tick,flag|BCT_ENEMY,skill_castend_damage_id);
- break;
-
- case WL_JACKFROST:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- map_foreachinshootrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_CHAR|BL_SKILL,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
- break;
-
- case WL_MARSHOFABYSS:
- // Should marsh of abyss still apply half reduction to players after the 28/10 patch? [LimitLine]
- clif_skill_nodamage(src, bl, skill_id, skill_lv,
- sc_start4(bl, type, 100, skill_lv, status_get_int(src), sd ? sd->status.job_level : 50, 0,
- skill_get_time(skill_id, skill_lv)));
- break;
-
- case WL_SIENNAEXECRATE:
- if( status_isimmune(bl) || !tsc )
- break;
-
- if( flag&1 ) {
- if( bl->id == skill_area_temp[1] )
- break; // Already work on this target
-
- if( tsc && tsc->data[SC_STONE] )
- status_change_end(bl,SC_STONE,INVALID_TIMER);
- else
- status_change_start(bl,SC_STONE,10000,skill_lv,0,0,1000,skill_get_time(skill_id, skill_lv),2);
- } else {
- int rate = 40 + 8 * skill_lv + ( sd? sd->status.job_level : 50 ) / 4;
- // IroWiki says Rate should be reduced by target stats, but currently unknown
- if( rnd()%100 < rate ) { // Success on First Target
- if( !tsc->data[SC_STONE] )
- rate = status_change_start(bl,SC_STONE,10000,skill_lv,0,0,1000,skill_get_time(skill_id, skill_lv),2);
- else {
- rate = 1;
- status_change_end(bl,SC_STONE,INVALID_TIMER);
- }
-
- if( rate ) {
- skill_area_temp[1] = bl->id;
- map_foreachinrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_nodamage_id);
- }
- // Doesn't send failure packet if it fails on defense.
- }
- else if( sd ) // Failure on Rate
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- }
- break;
-
- case WL_SUMMONFB:
- case WL_SUMMONBL:
- case WL_SUMMONWB:
- case WL_SUMMONSTONE:
- {
- short element = 0, sctype = 0, pos = -1;
- struct status_change *sc = status_get_sc(src);
- if( !sc ) break;
-
- for( i = SC_SPHERE_1; i <= SC_SPHERE_5; i++ )
- {
- if( !sctype && !sc->data[i] )
- sctype = i; // Take the free SC
- if( sc->data[i] )
- pos = max(sc->data[i]->val2,pos);
- }
-
- if( !sctype )
- {
- if( sd ) // No free slots to put SC
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_SUMMON,0);
- break;
- }
-
- pos++; // Used in val2 for SC. Indicates the order of this ball
- switch( skill_id )
- { // Set val1. The SC element for this ball
- case WL_SUMMONFB: element = WLS_FIRE; break;
- case WL_SUMMONBL: element = WLS_WIND; break;
- case WL_SUMMONWB: element = WLS_WATER; break;
- case WL_SUMMONSTONE: element = WLS_STONE; break;
- }
-
- sc_start4(src,sctype,100,element,pos,skill_lv,0,skill_get_time(skill_id,skill_lv));
- clif_skill_nodamage(src,bl,skill_id,0,0);
- }
- break;
-
- case WL_READING_SB:
- if( sd ) {
- struct status_change *sc = status_get_sc(bl);
-
- for( i = SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++)
- if( sc && !sc->data[i] )
- break;
- if( i == SC_MAXSPELLBOOK ) {
- clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_READING, 0);
- break;
- }
-
- sc_start(bl, SC_STOP, 100, skill_lv, INVALID_TIMER); //Can't move while selecting a spellbook.
- clif_spellbook_list(sd);
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- }
- break;
- /**
- * Ranger
- **/
- case RA_FEARBREEZE:
- clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- clif_skill_nodamage(src, bl, skill_id, skill_lv, sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)));
- break;
-
- case RA_WUGMASTERY:
- if( sd ) {
- if( !pc_iswug(sd) )
- pc_setoption(sd,sd->sc.option|OPTION_WUG);
- else
- pc_setoption(sd,sd->sc.option&~OPTION_WUG);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case RA_WUGRIDER:
- if( sd ) {
- if( !pc_isridingwug(sd) && pc_iswug(sd) ) {
- pc_setoption(sd,sd->sc.option&~OPTION_WUG);
- pc_setoption(sd,sd->sc.option|OPTION_WUGRIDER);
- } else if( pc_isridingwug(sd) ) {
- pc_setoption(sd,sd->sc.option&~OPTION_WUGRIDER);
- pc_setoption(sd,sd->sc.option|OPTION_WUG);
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case RA_WUGDASH:
- if( tsce ) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,status_change_end(bl, type, INVALID_TIMER));
- map_freeblock_unlock();
- return 0;
- }
- if( sd && pc_isridingwug(sd) ) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start4(bl,type,100,skill_lv,unit_getdir(bl),0,0,1));
- clif_walkok(sd);
- }
- break;
-
- case RA_SENSITIVEKEEN:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR|BL_SKILL,src,skill_id,skill_lv,tick,flag|BCT_ENEMY,skill_castend_damage_id);
- break;
- /**
- * Mechanic
- **/
- case NC_F_SIDESLIDE:
- case NC_B_SIDESLIDE:
- {
- uint8 dir = (skill_id == NC_F_SIDESLIDE) ? (unit_getdir(src)+4)%8 : unit_getdir(src);
- skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),dir,0x1);
- clif_slide(src,src->x,src->y);
- clif_fixpos(src); //Aegis sent this packet
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case NC_SELFDESTRUCTION:
- if( sd ) {
- if( pc_ismadogear(sd) )
- pc_setmadogear(sd, 0);
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- skill_castend_damage_id(src, src, skill_id, skill_lv, tick, flag);
- status_set_sp(src, 0, 0);
- }
- break;
-
- case NC_ANALYZE:
- clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- clif_skill_nodamage(src, bl, skill_id, skill_lv,
- sc_start(bl,type, 30 + 12 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv)));
- if( sd ) pc_overheat(sd,1);
- break;
-
- case NC_MAGNETICFIELD:
- if( (i = sc_start2(bl,type,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv))) )
- {
- map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),splash_target(src),src,skill_id,skill_lv,tick,flag|BCT_ENEMY|SD_SPLASH|1,skill_castend_damage_id);;
- clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,6);
- if (sd) pc_overheat(sd,1);
- }
- clif_skill_nodamage(src,src,skill_id,skill_lv,i);
- break;
-
- case NC_REPAIR:
- if( sd )
- {
- int heal;
- if( dstsd && pc_ismadogear(dstsd) )
- {
- heal = dstsd->status.max_hp * (3+3*skill_lv) / 100;
- status_heal(bl,heal,0,2);
- } else {
- heal = sd->status.max_hp * (3+3*skill_lv) / 100;
- status_heal(src,heal,0,2);
- }
-
- clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- clif_skill_nodamage(src, bl, skill_id, skill_lv, heal);
- }
- break;
-
- case NC_DISJOINT:
- {
- if( bl->type != BL_MOB ) break;
- md = map_id2md(bl->id);
- if( md && md->class_ >= MOBID_SILVERSNIPER && md->class_ <= MOBID_MAGICDECOY_WIND )
- status_kill(bl);
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- }
- break;
- case SC_AUTOSHADOWSPELL:
- if( sd ) {
- if( sd->status.skill[sd->reproduceskill_id].id || sd->status.skill[sd->cloneskill_id].id ) {
- sc_start(src,SC_STOP,100,skill_lv,-1);// The skill_lv is stored in val1 used in skill_select_menu to determine the used skill lvl [Xazax]
- clif_autoshadowspell_list(sd);
- clif_skill_nodamage(src,bl,skill_id,1,1);
- }
- else
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_IMITATION_SKILL_NONE,0);
- }
- break;
-
- case SC_SHADOWFORM:
- if( sd && dstsd && src != bl && !dstsd->shadowform_id ) {
- if( clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start4(src,type,100,skill_lv,bl->id,4+skill_lv,0,skill_get_time(skill_id, skill_lv))) )
- dstsd->shadowform_id = src->id;
- }
- else if( sd )
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
- break;
-
- case SC_BODYPAINT:
- if( flag&1 ) {
- if( tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING] ||
- tsc->data[SC_CHASEWALK] || tsc->data[SC_CLOAKINGEXCEED] ||
- tsc->data[SC__INVISIBILITY]) ) {
- status_change_end(bl, SC_HIDING, INVALID_TIMER);
- status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
- status_change_end(bl, SC_CHASEWALK, INVALID_TIMER);
- status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
- status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER);
-
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
- sc_start(bl,SC_BLIND,53 + 2 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
- }
- } else {
- clif_skill_nodamage(src, bl, skill_id, 0, 1);
- map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR,
- src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
- }
- break;
-
- case SC_ENERVATION:
- case SC_GROOMY:
- case SC_LAZINESS:
- case SC_UNLUCKY:
- case SC_WEAKNESS:
- if( !(tsc && tsc->data[type]) ) {
- //((rand(myDEX / 12, myDEX / 4) + myJobLevel + 10 * skLevel) + myLevel / 10) - (targetLevel / 10 + targetLUK / 10 + (targetMaxWeight - targetWeight) / 1000 + rand(targetAGI / 6, targetAGI / 3))
- int rate = rnd_value(sstatus->dex/12,sstatus->dex/4) + 10*skill_lv + (sd?sd->status.job_level:0) + status_get_lv(src)/10
- - status_get_lv(bl)/10 - tstatus->luk/10 - (dstsd?(dstsd->max_weight-dstsd->weight)/10000:0) - rnd_value(tstatus->agi/6,tstatus->agi/3);
- rate = cap_value(rate, skill_lv+sstatus->dex/20, 100);
- clif_skill_nodamage(src,bl,skill_id,0,sc_start(bl,type,rate,skill_lv,skill_get_time(skill_id,skill_lv)));
- } else if( sd )
- clif_skill_fail(sd,skill_id,0,0);
- break;
-
- case SC_IGNORANCE:
- if( !(tsc && tsc->data[type]) ) {
- int rate = rnd_value(sstatus->dex/12,sstatus->dex/4) + 10*skill_lv + (sd?sd->status.job_level:0) + status_get_lv(src)/10
- - status_get_lv(bl)/10 - tstatus->luk/10 - (dstsd?(dstsd->max_weight-dstsd->weight)/10000:0) - rnd_value(tstatus->agi/6,tstatus->agi/3);
- rate = cap_value(rate, skill_lv+sstatus->dex/20, 100);
- if (clif_skill_nodamage(src,bl,skill_id,0,sc_start(bl,type,rate,skill_lv,skill_get_time(skill_id,skill_lv)))) {
- int sp = 200 * skill_lv;
- if( dstmd ) sp = dstmd->level * 2;
- if( status_zap(bl,0,sp) )
- status_heal(src,0,sp/2,3);
- }
- else if( sd ) clif_skill_fail(sd,skill_id,0,0);
- } else if( sd )
- clif_skill_fail(sd,skill_id,0,0);
- break;
-
- case LG_TRAMPLE:
- clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- map_foreachinrange(skill_destroy_trap,bl,skill_get_splash(skill_id,skill_lv),BL_SKILL,tick);
- break;
-
- case LG_REFLECTDAMAGE:
- if( tsc && tsc->data[type] )
- status_change_end(bl,type,INVALID_TIMER);
- else
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- break;
-
- case LG_SHIELDSPELL:
- if( flag&1 ) {
- int duration = (sd) ? sd->bonus.shieldmdef * 2000 : 10000;
- sc_start(bl,SC_SILENCE,100,skill_lv,duration);
- } else if( sd ) {
- int opt = skill_lv;
- int rate = rnd()%100;
- int val, brate;
- switch( skill_lv ) {
- case 1:
- {
- struct item_data *shield_data = sd->inventory_data[sd->equip_index[EQI_HAND_L]];
- if( !shield_data || shield_data->type != IT_ARMOR ) { // No shield?
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
- break;
- }
- brate = shield_data->def * 10;
- if( rate < 50 )
- opt = 1;
- else if( rate < 75 )
- opt = 2;
- else
- opt = 3;
-
- switch( opt ) {
- case 1:
- sc_start(bl,SC_SHIELDSPELL_DEF,100,opt,-1);
- clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- if( rate < brate )
- map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
- status_change_end(bl,SC_SHIELDSPELL_DEF,INVALID_TIMER);
- break;
- case 2:
- val = shield_data->def / 10; // % Reflected damage.
- sc_start2(bl,SC_SHIELDSPELL_DEF,brate,opt,val,shield_data->def * 1000);
- break;
- case 3:
- val = shield_data->def; // Attack increase.
- sc_start2(bl,SC_SHIELDSPELL_DEF,brate,opt,val,shield_data->def * 3000);
- break;
- }
- }
- break;
-
- case 2:
- brate = sd->bonus.shieldmdef * 20;
- if( rate < 30 )
- opt = 1;
- else if( rate < 60 )
- opt = 2;
- else
- opt = 3;
- switch( opt ) {
- case 1:
- sc_start(bl,SC_SHIELDSPELL_MDEF,100,opt,-1);
- clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- if( rate < brate )
- map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|2,skill_castend_damage_id);
- status_change_end(bl,SC_SHIELDSPELL_MDEF,INVALID_TIMER);
- break;
- case 2:
- sc_start(bl,SC_SHIELDSPELL_MDEF,100,opt,-1);
- clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- if( rate < brate )
- map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_nodamage_id);
- break;
- case 3:
- if( sc_start(bl,SC_SHIELDSPELL_MDEF,brate,opt,sd->bonus.shieldmdef * 30000) )
- clif_skill_nodamage(src,bl,PR_MAGNIFICAT,skill_lv,
- sc_start(bl,SC_MAGNIFICAT,100,1,sd->bonus.shieldmdef * 30000));
- break;
- }
- break;
-
- case 3:
- {
- struct item *it = &sd->status.inventory[sd->equip_index[EQI_HAND_L]];
- if( !it ) { // No shield?
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- brate = it->refine * 5;
- if( rate < 25 )
- opt = 1;
- else if( rate < 50 )
- opt = 2;
- else
- opt = 3;
- switch( opt ) {
- case 1:
- val = 105 * it->refine / 10;
- sc_start2(bl,SC_SHIELDSPELL_REF,brate,opt,val,skill_get_time(skill_id,skill_lv));
- break;
- case 2: case 3:
- if( rate < brate )
- {
- val = sstatus->max_hp * (11 + it->refine) / 100;
- status_heal(bl, val, 0, 3);
- }
- break;
- /*case 3:
- // Full protection. I need confirm what effect should be here. Moved to case 2 to until we got it.
- break;*/
- }
- }
- break;
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case LG_PIETY:
- if( flag&1 )
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
- else {
- skill_area_temp[2] = 0;
- map_foreachinrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_PC,src,skill_id,skill_lv,tick,flag|SD_PREAMBLE|BCT_PARTY|BCT_SELF|1,skill_castend_nodamage_id);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case LG_INSPIRATION:
- if( sd && !map[sd->bl.m].flag.noexppenalty && sd->status.base_level != MAX_LEVEL ) {
- sd->status.base_exp -= min(sd->status.base_exp, pc_nextbaseexp(sd) * 1 / 100); // 1% penalty.
- sd->status.job_exp -= min(sd->status.job_exp, pc_nextjobexp(sd) * 1 / 100);
- clif_updatestatus(sd,SP_BASEEXP);
- clif_updatestatus(sd,SP_JOBEXP);
- }
- clif_skill_nodamage(bl,src,skill_id,skill_lv,
- sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)));
- break;
- case SR_CURSEDCIRCLE:
- if( flag&1 ) {
- if( is_boss(bl) ) break;
- if( sc_start2(bl, type, 100, skill_lv, src->id, skill_get_time(skill_id, skill_lv))) {
- if( bl->type == BL_MOB )
- mob_unlocktarget((TBL_MOB*)bl,gettick());
- unit_stop_attack(bl);
- clif_bladestop(src, bl->id, 1);
- map_freeblock_unlock();
- return 1;
- }
- } else {
- int count = 0;
- clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- count = map_forcountinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv), (sd)?sd->spiritball_old:15, // Assume 15 spiritballs in non-charactors
- BL_CHAR, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
- if( sd ) pc_delspiritball(sd, count, 0);
- clif_skill_nodamage(src, src, skill_id, skill_lv,
- sc_start2(src, SC_CURSEDCIRCLE_ATKER, 100, skill_lv, count, skill_get_time(skill_id,skill_lv)));
- }
- break;
-
- case SR_RAISINGDRAGON:
- if( sd ) {
- short max = 5 + skill_lv;
- sc_start(bl, SC_EXPLOSIONSPIRITS, 100, skill_lv, skill_get_time(skill_id, skill_lv));
- for( i = 0; i < max; i++ ) // Don't call more than max available spheres.
- pc_addspiritball(sd, skill_get_time(skill_id, skill_lv), max);
- clif_skill_nodamage(src, bl, skill_id, skill_lv, sc_start(bl, type, 100, skill_lv,skill_get_time(skill_id, skill_lv)));
- }
- break;
-
- case SR_ASSIMILATEPOWER:
- if( flag&1 ) {
- i = 0;
- if( dstsd && dstsd->spiritball && (sd == dstsd || map_flag_vs(src->m)) && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER )
- {
- i = dstsd->spiritball; //1%sp per spiritball.
- pc_delspiritball(dstsd, dstsd->spiritball, 0);
- }
- if( i ) status_percent_heal(src, 0, i);
- clif_skill_nodamage(src, bl, skill_id, skill_lv, i ? 1:0);
- } else {
- clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|BCT_SELF|SD_SPLASH|1, skill_castend_nodamage_id);
- }
- break;
-
- case SR_POWERVELOCITY:
- if( !dstsd )
- break;
- if( sd && dstsd->spiritball <= 5 ) {
- for(i = 0; i <= 5; i++) {
- pc_addspiritball(dstsd, skill_get_time(MO_CALLSPIRITS, pc_checkskill(sd,MO_CALLSPIRITS)), i);
- pc_delspiritball(sd, sd->spiritball, 0);
- }
- }
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- break;
-
- case SR_GENTLETOUCH_CURE:
- {
- int heal;
-
- if( status_isimmune(bl) )
- {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
- break;
- }
-
- heal = 120 * skill_lv + status_get_max_hp(bl) * (2 + skill_lv) / 100;
- status_heal(bl, heal, 0, 0);
-
- if( (tsc && tsc->opt1) && (rnd()%100 < ((skill_lv * 5) + (status_get_dex(src) + status_get_lv(src)) / 4) - (1 + (rnd() % 10))) )
- {
- status_change_end(bl, SC_STONE, INVALID_TIMER);
- status_change_end(bl, SC_FREEZE, INVALID_TIMER);
- status_change_end(bl, SC_STUN, INVALID_TIMER);
- status_change_end(bl, SC_POISON, INVALID_TIMER);
- status_change_end(bl, SC_SILENCE, INVALID_TIMER);
- status_change_end(bl, SC_BLIND, INVALID_TIMER);
- status_change_end(bl, SC_HALLUCINATION, INVALID_TIMER);
- status_change_end(bl, SC_BURNING, INVALID_TIMER);
- status_change_end(bl, SC_FREEZING, INVALID_TIMER);
- }
-
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
- case SR_GENTLETOUCH_CHANGE:
- case SR_GENTLETOUCH_REVITALIZE:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start2(bl,type,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv)));
- break;
- case WA_SWING_DANCE:
- case WA_MOONLIT_SERENADE:
- if( sd == NULL || sd->status.party_id == 0 || (flag & 1) )
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
- else if( sd ) { // Only shows effects on caster.
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
- }
- break;
-
- case WA_SYMPHONY_OF_LOVER:
- case MI_RUSH_WINDMILL:
- case MI_ECHOSONG:
- if( sd == NULL || sd->status.party_id == 0 || (flag & 1) )
- sc_start4(bl,type,100,skill_lv,6*skill_lv,(sd?pc_checkskill(sd,WM_LESSON):0),(sd?sd->status.job_level:0),skill_get_time(skill_id,skill_lv));
- else if( sd ) { // Only shows effects on caster.
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
- }
- break;
-
- case MI_HARMONIZE:
- if( src != bl )
- clif_skill_nodamage(src, src, skill_id, skill_lv, sc_start(src, type, 100, skill_lv, skill_get_time(skill_id,skill_lv)));
- clif_skill_nodamage(src, bl, skill_id, skill_lv, sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id,skill_lv)));
- break;
-
- case WM_DEADHILLHERE:
- if( bl->type == BL_PC ) {
- if( !status_isdead(bl) )
- break;
-
- if( rnd()%100 < 88 + 2 * skill_lv ) {
- int heal = tstatus->sp;
- if( heal <= 0 )
- heal = 1;
- tstatus->hp = heal;
- tstatus->sp -= tstatus->sp * ( 120 - 20 * skill_lv ) / 100;
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- pc_revive((TBL_PC*)bl,heal,0);
- clif_resurrection(bl,1);
- }
- }
- break;
-
- case WM_SIRCLEOFNATURE:
- flag |= BCT_SELF|BCT_PARTY|BCT_GUILD;
- case WM_VOICEOFSIREN:
- if( skill_id != WM_SIRCLEOFNATURE )
- flag &= ~BCT_SELF;
- if( flag&1 ) {
- sc_start2(bl,type,(skill_id==WM_VOICEOFSIREN)?20+10*skill_lv:100,skill_lv,(skill_id==WM_VOICEOFSIREN)?src->id:0,skill_get_time(skill_id,skill_lv));
- } else {
- map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),(skill_id==WM_VOICEOFSIREN)?BL_CHAR|BL_SKILL:BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case WM_GLOOMYDAY:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- if( dstsd && ( pc_checkskill(dstsd,KN_BRANDISHSPEAR) || pc_checkskill(dstsd,LK_SPIRALPIERCE) ||
- pc_checkskill(dstsd,CR_SHIELDCHARGE) || pc_checkskill(dstsd,CR_SHIELDBOOMERANG) ||
- pc_checkskill(dstsd,PA_SHIELDCHAIN) || pc_checkskill(dstsd,LG_SHIELDPRESS) ) )
- {
- sc_start(bl,SC_GLOOMYDAY_SK,100,skill_lv,skill_get_time(skill_id,skill_lv));
- break;
- }
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
- break;
-
- case WM_SATURDAY_NIGHT_FEVER:
- if( flag&1 ) { // Affect to all targets arround the caster and caster too.
- if( !(tsc && tsc->data[type]) )
- sc_start(bl, type, 100, skill_lv,skill_get_time(skill_id, skill_lv));
- } else if( flag&2 ) {
- if( src->id != bl->id && battle_check_target(src,bl,BCT_ENEMY) > 0 )
- status_fix_damage(src,bl,9999,clif_damage(src,bl,tick,0,0,9999,0,0,0));
- } else if( sd ) {
- short chance = sstatus->int_/6 + sd->status.job_level/5 + skill_lv*4;
- if( !sd->status.party_id || (rnd()%100 > chance)) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_HELPER,0);
- break;
- }
- if( map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id,skill_lv),
- BL_PC, src, skill_id, skill_lv, tick, BCT_ENEMY, skill_area_sub_count) > 7 )
- flag |= 2;
- else
- flag |= 1;
- map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|BCT_SELF, skill_castend_nodamage_id);
- clif_skill_nodamage(src, bl, skill_id, skill_lv,
- sc_start(src,SC_STOP,100,skill_lv,skill_get_time2(skill_id,skill_lv)));
- if( flag&2 ) // Dealed here to prevent conflicts
- status_fix_damage(src,bl,9999,clif_damage(src,bl,tick,0,0,9999,0,0,0));
- }
- break;
-
- case WM_SONG_OF_MANA:
- case WM_DANCE_WITH_WUG:
- case WM_LERADS_DEW:
- if( flag&1 ) { // These affect to to all party members near the caster.
- struct status_change *sc = status_get_sc(src);
- if( sc && sc->data[type] ) {
- sc_start2(bl,type,100,skill_lv,sc->data[type]->val2,skill_get_time(skill_id,skill_lv));
- }
- } else if( sd ) {
- short lv = (short)skill_lv;
- int count = skill_check_pc_partner(sd,skill_id,&lv,skill_get_splash(skill_id,skill_lv),1);
- if( sc_start2(bl,type,100,skill_lv,count,skill_get_time(skill_id,skill_lv)) )
- party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,flag|BCT_PARTY|1,skill_castend_nodamage_id);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
-
- }
- break;
-
- case WM_MELODYOFSINK:
- case WM_BEYOND_OF_WARCRY:
- case WM_UNLIMITED_HUMMING_VOICE:
- if( flag&1 ) {
- sc_start2(bl,type,100,skill_lv,skill_area_temp[0],skill_get_time(skill_id,skill_lv));
- } else { // These affect to all targets arround the caster.
- short lv = (short)skill_lv;
- skill_area_temp[0] = (sd) ? skill_check_pc_partner(sd,skill_id,&lv,skill_get_splash(skill_id,skill_lv),1) : 50; // 50% chance in non BL_PC (clones).
- map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case WM_RANDOMIZESPELL: {
- int improv_skill_id = 0, improv_skill_lv;
- do {
- i = rnd() % MAX_SKILL_IMPROVISE_DB;
- improv_skill_id = skill_improvise_db[i].skill_id;
- } while( improv_skill_id == 0 || rnd()%10000 >= skill_improvise_db[i].per );
- improv_skill_lv = 4 + skill_lv;
- clif_skill_nodamage (src, bl, skill_id, skill_lv, 1);
-
- if( sd ) {
- sd->state.abra_flag = 2;
- sd->skillitem = improv_skill_id;
- sd->skillitemlv = improv_skill_lv;
- clif_item_skill(sd, improv_skill_id, improv_skill_lv);
- } else {
- struct unit_data *ud = unit_bl2ud(src);
- int inf = skill_get_inf(improv_skill_id);
- int target_id = 0;
- if (!ud) break;
- if (inf&INF_SELF_SKILL || inf&INF_SUPPORT_SKILL) {
- if (src->type == BL_PET)
- bl = (struct block_list*)((TBL_PET*)src)->msd;
- if (!bl) bl = src;
- unit_skilluse_id(src, bl->id, improv_skill_id, improv_skill_lv);
- } else {
- if (ud->target)
- target_id = ud->target;
- else switch (src->type) {
- case BL_MOB: target_id = ((TBL_MOB*)src)->target_id; break;
- case BL_PET: target_id = ((TBL_PET*)src)->target_id; break;
- }
- if (!target_id)
- break;
- if (skill_get_casttype(improv_skill_id) == CAST_GROUND) {
- bl = map_id2bl(target_id);
- if (!bl) bl = src;
- unit_skilluse_pos(src, bl->x, bl->y, improv_skill_id, improv_skill_lv);
- } else
- unit_skilluse_id(src, target_id, improv_skill_id, improv_skill_lv);
- }
- }
- }
- break;
-
-
- case RETURN_TO_ELDICASTES:
- case ALL_GUARDIAN_RECALL:
- if( sd )
- {
- short x, y; // Destiny position.
- unsigned short mapindex;
-
- if( skill_id == RETURN_TO_ELDICASTES)
- {
- x = 198;
- y = 187;
- mapindex = mapindex_name2id(MAP_DICASTES);
- }
- else
- {
- x = 44;
- y = 151;
- mapindex = mapindex_name2id(MAP_MORA);
- }
-
- if(!mapindex)
- { //Given map not found?
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- map_freeblock_unlock();
- return 0;
- }
- pc_setpos(sd, mapindex, x, y, CLR_TELEPORT);
- }
- break;
-
- case GM_SANDMAN:
- if( tsc ) {
- if( tsc->opt1 == OPT1_SLEEP )
- tsc->opt1 = 0;
- else
- tsc->opt1 = OPT1_SLEEP;
- clif_changeoption(bl);
- clif_skill_nodamage (src, bl, skill_id, skill_lv, 1);
- }
- break;
-
- case SO_ARRULLO:
- if( flag&1 )
- sc_start2(bl, type, 88 + 2 * skill_lv, skill_lv, 1, skill_get_time(skill_id, skill_lv));
- else {
- clif_skill_nodamage(src, bl, skill_id, 0, 1);
- map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR,
- src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
- }
- break;
-
- case SO_SUMMON_AGNI:
- case SO_SUMMON_AQUA:
- case SO_SUMMON_VENTUS:
- case SO_SUMMON_TERA:
- if( sd ) {
- int elemental_class = skill_get_elemental_type(skill_id,skill_lv);
-
- // Remove previous elemental fisrt.
- if( sd->ed )
- elemental_delete(sd->ed,0);
-
- // Summoning the new one.
- if( !elemental_create(sd,elemental_class,skill_get_time(skill_id,skill_lv)) ) {
- clif_skill_fail(sd,skill_id,0,0);
- break;
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case SO_EL_CONTROL:
- if( sd ) {
- int mode = EL_MODE_PASSIVE; // Standard mode.
-
- if( !sd->ed ) break;
-
- if( skill_lv == 4 ) {// At level 4 delete elementals.
- elemental_delete(sd->ed, 0);
- break;
- }
- switch( skill_lv ) {// Select mode bassed on skill level used.
- case 2: mode = EL_MODE_ASSIST; break;
- case 3: mode = EL_MODE_AGGRESSIVE; break;
- }
- if( !elemental_change_mode(sd->ed,mode) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
-
- case SO_EL_ACTION:
- if( sd ) {
- int duration = 3000;
- if( !sd->ed ) break;
- sd->skill_id_old = skill_id;
- elemental_action(sd->ed, bl, tick);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- switch(sd->ed->db->class_){
- case 2115:case 2124:
- case 2118:case 2121:
- duration = 6000;
- break;
- case 2116:case 2119:
- case 2122:case 2125:
- duration = 9000;
- break;
- }
- skill_blockpc_start(sd, skill_id, duration);
- }
- break;
-
- case SO_EL_CURE:
- if( sd ) {
- struct elemental_data *ed = sd->ed;
- int s_hp = sd->battle_status.hp * 10 / 100, s_sp = sd->battle_status.sp * 10 / 100;
- int e_hp, e_sp;
-
- if( !ed ) break;
- if( !status_charge(&sd->bl,s_hp,s_sp) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- e_hp = ed->battle_status.max_hp * 10 / 100;
- e_sp = ed->battle_status.max_sp * 10 / 100;
- status_heal(&ed->bl,e_hp,e_sp,3);
- clif_skill_nodamage(src,&ed->bl,skill_id,skill_lv,1);
- }
- break;
-
- case GN_CHANGEMATERIAL:
- case SO_EL_ANALYSIS:
- if( sd ) {
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- clif_skill_itemlistwindow(sd,skill_id,skill_lv);
- }
- break;
-
- case GN_BLOOD_SUCKER:
- {
- struct status_change *sc = status_get_sc(src);
-
- if( sc && sc->bs_counter < skill_get_maxcount( skill_id , skill_lv) ) {
- if( tsc && tsc->data[type] ){
- (sc->bs_counter)--;
- status_change_end(src, type, INVALID_TIMER); // the first one cancels and the last one will take effect resetting the timer
- }
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- sc_start2(bl, type, 100, skill_lv, src->id, skill_get_time(skill_id,skill_lv));
- (sc->bs_counter)++;
- } else if( sd ) {
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
- break;
- }
- }
- break;
-
- case GN_MANDRAGORA:
- if( flag&1 ) {
- if ( clif_skill_nodamage(bl, src, skill_id, skill_lv,
- sc_start(bl, type, 25 + 10 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv))) )
- status_zap(bl, 0, status_get_max_sp(bl) * (25 + 5 * skill_lv) / 100);
- } else
- map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR,
- src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
- break;
-
- case GN_SLINGITEM:
- if( sd ) {
- short ammo_id;
- i = sd->equip_index[EQI_AMMO];
- if( i <= 0 )
- break; // No ammo.
- ammo_id = sd->inventory_data[i]->nameid;
- if( ammo_id <= 0 )
- break;
- sd->itemid = ammo_id;
- if( itemdb_is_GNbomb(ammo_id) ) {
- if(battle_check_target(src,bl,BCT_ENEMY) > 0) {// Only attack if the target is an enemy.
- if( ammo_id == 13263 )
- map_foreachincell(skill_area_sub,bl->m,bl->x,bl->y,BL_CHAR,src,GN_SLINGITEM_RANGEMELEEATK,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
- else
- skill_attack(BF_WEAPON,src,src,bl,GN_SLINGITEM_RANGEMELEEATK,skill_lv,tick,flag);
- } else //Otherwise, it fails, shows animation and removes items.
- clif_skill_fail(sd,GN_SLINGITEM_RANGEMELEEATK,0xa,0);
- } else if( itemdb_is_GNthrowable(ammo_id) ){
- struct script_code *script = sd->inventory_data[i]->script;
- if( !script )
- break;
- if( dstsd )
- run_script(script,0,dstsd->bl.id,fake_nd->bl.id);
- else
- run_script(script,0,src->id,0);
- }
- }
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);// This packet is received twice actually, I think it is to show the animation.
- break;
-
- case GN_MIX_COOKING:
- case GN_MAKEBOMB:
- case GN_S_PHARMACY:
- if( sd ) {
- int qty = 1;
- sd->skill_id_old = skill_id;
- sd->skill_lv_old = skill_lv;
- if( skill_id != GN_S_PHARMACY && skill_lv > 1 )
- qty = 10;
- clif_cooking_list(sd,(skill_id - GN_MIX_COOKING) + 27,skill_id,qty,skill_id==GN_MAKEBOMB?5:6);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- }
- break;
- case EL_CIRCLE_OF_FIRE:
- case EL_PYROTECHNIC:
- case EL_HEATER:
- case EL_TROPIC:
- case EL_AQUAPLAY:
- case EL_COOLER:
- case EL_CHILLY_AIR:
- case EL_GUST:
- case EL_BLAST:
- case EL_WILD_STORM:
- case EL_PETROLOGY:
- case EL_CURSED_SOIL:
- case EL_UPHEAVAL:
- case EL_FIRE_CLOAK:
- case EL_WATER_DROP:
- case EL_WIND_CURTAIN:
- case EL_SOLID_SKIN:
- case EL_STONE_SHIELD:
- case EL_WIND_STEP: {
- struct elemental_data *ele = BL_CAST(BL_ELEM, src);
- if( ele ) {
- sc_type type2 = type-1;
- struct status_change *sc = status_get_sc(&ele->bl);
-
- if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
- elemental_clean_single_effect(ele, skill_id);
- } else {
- clif_skill_nodamage(src,src,skill_id,skill_lv,1);
- clif_skill_damage(src, ( skill_id == EL_GUST || skill_id == EL_BLAST || skill_id == EL_WILD_STORM )?src:bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- if( skill_id == EL_WIND_STEP ) // There aren't teleport, just push the master away.
- skill_blown(src,bl,(rnd()%skill_get_blewcount(skill_id,skill_lv))+1,rand()%8,0);
- sc_start(src,type2,100,skill_lv,skill_get_time(skill_id,skill_lv));
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
- }
- }
- }
- break;
-
- case EL_FIRE_MANTLE:
- case EL_WATER_BARRIER:
- case EL_ZEPHYR:
- case EL_POWER_OF_GAIA:
- clif_skill_nodamage(src,src,skill_id,skill_lv,1);
- clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- skill_unitsetting(src,skill_id,skill_lv,bl->x,bl->y,0);
- break;
-
- case EL_WATER_SCREEN: {
- struct elemental_data *ele = BL_CAST(BL_ELEM, src);
- if( ele ) {
- struct status_change *sc = status_get_sc(&ele->bl);
- sc_type type2 = type-1;
-
- clif_skill_nodamage(src,src,skill_id,skill_lv,1);
- if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
- elemental_clean_single_effect(ele, skill_id);
- } else {
- // This not heals at the end.
- clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- sc_start(src,type2,100,skill_lv,skill_get_time(skill_id,skill_lv));
- sc_start(bl,type,100,src->id,skill_get_time(skill_id,skill_lv));
- }
- }
- }
- break;
-
- case KO_KAHU_ENTEN:
- case KO_HYOUHU_HUBUKI:
- case KO_KAZEHU_SEIRAN:
- case KO_DOHU_KOUKAI:
- if(sd) {
- int ttype = skill_get_ele(skill_id, skill_lv);
- clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
- pc_add_talisman(sd, skill_get_time(skill_id, skill_lv), 10, ttype);
- }
- break;
-
- case KO_ZANZOU:
- if(sd){
- struct mob_data *md;
-
- md = mob_once_spawn_sub(src, src->m, src->x, src->y, status_get_name(src), 2308, "", SZ_SMALL, AI_NONE);
- if( md )
- {
- md->master_id = src->id;
- md->special_state.ai = AI_ZANZOU;
- if( md->deletetimer != INVALID_TIMER )
- delete_timer(md->deletetimer, mob_timer_delete);
- md->deletetimer = add_timer (gettick() + skill_get_time(skill_id, skill_lv), mob_timer_delete, md->bl.id, 0);
- mob_spawn( md );
- pc_setinvincibletimer(sd,500);// unlock target lock
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),unit_getdir(bl),0);
- }
- }
- break;
-
- case KO_KYOUGAKU:
- if( dstsd && tsc && !tsc->data[type] && rand()%100 < tstatus->int_/2 ){
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- }else if( sd )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
-
- case KO_JYUSATSU:
- if( dstsd && tsc && !tsc->data[type] &&
- rand()%100 < ((45+5*skill_lv) + skill_lv*5 - status_get_int(bl)/2) ){//[(Base chance of success) + (Skill Level x 5) - (int / 2)]%.
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- status_change_start(bl,type,10000,skill_lv,0,0,0,skill_get_time(skill_id,skill_lv),1));
- status_zap(bl, tstatus->max_hp*skill_lv*5/100 , 0);
- if( status_get_lv(bl) <= status_get_lv(src) )
- status_change_start(bl,SC_COMA,10,skill_lv,0,src->id,0,0,0);
- }else if( sd )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
-
- case KO_GENWAKU:
- if ( !map_flag_gvg(src->m) && ( dstsd || dstmd ) && battle_check_target(src,bl,BCT_ENEMY) > 0 ) {
- int x = src->x, y = src->y;
-
- if( sd && rnd()%100 > ((45+5*skill_lv) - status_get_int(bl)/10) ){//[(Base chance of success) - (Intelligence Objectives / 10)]%.
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
-
- if (unit_movepos(src,bl->x,bl->y,0,0)) {
- clif_skill_nodamage(src,src,skill_id,skill_lv,1);
- clif_slide(src,bl->x,bl->y) ;
- sc_start(src,SC_CONFUSION,80,skill_lv,skill_get_time(skill_id,skill_lv));
- if (unit_movepos(bl,x,y,0,0))
- {
- clif_skill_damage(bl,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, -1, 6);
- if( bl->type == BL_PC && pc_issit((TBL_PC*)bl))
- clif_sitting(bl); //Avoid sitting sync problem
- clif_slide(bl,x,y) ;
- sc_start(bl,SC_CONFUSION,80,skill_lv,skill_get_time(skill_id,skill_lv));
- }
- }
- }
- break;
-
- case OB_AKAITSUKI:
- case OB_OBOROGENSOU:
- if( sd && ( (skill_id == OB_OBOROGENSOU && bl->type == BL_MOB) // This skill does not work on monsters.
- || is_boss(bl) ) ){ // Does not work on Boss monsters.
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- case KO_IZAYOI:
- case OB_ZANGETSU:
- case KG_KYOMU:
- case KG_KAGEMUSYA:
- clif_skill_nodamage(src,bl,skill_id,skill_lv,
- sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
- clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- break;
-
- case KG_KAGEHUMI:
- if( flag&1 ){
- if(tsc && ( tsc->option&(OPTION_CLOAK|OPTION_HIDE) ||
- tsc->data[SC_CAMOUFLAGE] || tsc->data[SC__SHADOWFORM] ||
- tsc->data[SC_MARIONETTE] || tsc->data[SC_HARMONIZE])){
- sc_start(src, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
- sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
- status_change_end(bl, SC_HIDING, INVALID_TIMER);
- status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
- status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
- status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER);
- status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER);
- status_change_end(bl, SC_MARIONETTE, INVALID_TIMER);
- status_change_end(bl, SC_HARMONIZE, INVALID_TIMER);
- }
- if( skill_area_temp[2] == 1 ){
- clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- sc_start(src, SC_STOP, 100, skill_lv, skill_get_time(skill_id, skill_lv));
- }
- }else{
- skill_area_temp[2] = 0;
- map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_nodamage_id);
- }
- break;
-
- case MH_SILENT_BREEZE: {
- struct status_change *ssc = status_get_sc(src);
- struct block_list *m_bl = battle_get_master(src);
- const enum sc_type scs[] = {
- SC_MANDRAGORA, SC_HARMONIZE, SC_DEEPSLEEP, SC_VOICEOFSIREN, SC_SLEEP, SC_CONFUSION, SC_HALLUCINATION
- };
- int heal;
- if(tsc){
- for (i = 0; i < ARRAYLENGTH(scs); i++) {
- if (tsc->data[scs[i]]) status_change_end(bl, scs[i], INVALID_TIMER);
- }
- if (!tsc->data[SC_SILENCE]) //put inavoidable silence on target
- status_change_start(bl, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
- }
- heal = status_get_matk_min(src)*4;
- status_heal(bl, heal, 0, 7);
-
- //now inflict silence on everyone
- if(ssc && !ssc->data[SC_SILENCE]) //put inavoidable silence on homun
- status_change_start(src, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
- if(m_bl){
- struct status_change *msc = status_get_sc(m_bl);
- if(msc && !msc->data[SC_SILENCE]) //put inavoidable silence on master
- status_change_start(m_bl, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
- }
- if (hd)
- skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
- }
- break;
- case MH_OVERED_BOOST:
- if (hd){
- struct block_list *s_bl = battle_get_master(src);
- if(hd->homunculus.hunger>50) //reduce hunger
- hd->homunculus.hunger = hd->homunculus.hunger/2;
- else
- hd->homunculus.hunger = min(1,hd->homunculus.hunger);
- if(s_bl && s_bl->type==BL_PC){
- status_set_sp(s_bl,status_get_max_sp(s_bl)/2,0); //master drain 50% sp
- clif_send_homdata(((TBL_PC *)s_bl), SP_HUNGRY, hd->homunculus.hunger); //refresh hunger info
- sc_start(s_bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)); //gene bonus
- }
- sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
- skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
- }
- break;
- case MH_GRANITIC_ARMOR:
- case MH_PYROCLASTIC: {
- struct block_list *s_bl = battle_get_master(src);
- if(s_bl) sc_start2(s_bl, type, 100, skill_lv, hd->homunculus.level, skill_get_time(skill_id, skill_lv)); //start on master
- sc_start2(bl, type, 100, skill_lv, hd->homunculus.level, skill_get_time(skill_id, skill_lv));
- if (hd) skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
- }
- break;
-
- case MH_LIGHT_OF_REGENE:
- if(hd){
- hd->homunculus.intimacy = 251; //change to neutral (can't be cast if < 750)
- if(sd) clif_send_homdata(sd, SP_INTIMATE, hd->homunculus.intimacy); //refresh intimacy info
- }
- //don't break need to start status and start block timer
- case MH_STYLE_CHANGE:
- case MH_MAGMA_FLOW:
- case MH_PAIN_KILLER:
- sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
- if (hd)
- skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
- break;
- case MH_SUMMON_LEGION:
- {
- int summons[5] = {1004, 1303, 1303, 1994, 1994};
- int qty[5] = {3 , 3 , 4 , 4 , 5};
- struct mob_data *md;
- int i;
-
- for(i=0; i<qty[skill_lv - 1]; i++){ //easy way
- md = mob_once_spawn_sub(src, src->m, src->x, src->y, status_get_name(src), summons[skill_lv - 1], "", SZ_SMALL, AI_ATTACK);
- if (md) {
- md->master_id = src->id;
- if (md->deletetimer != INVALID_TIMER)
- delete_timer(md->deletetimer, mob_timer_delete);
- md->deletetimer = add_timer(gettick() + skill_get_time(skill_id, skill_lv), mob_timer_delete, md->bl.id, 0);
- mob_spawn(md); //Now it is ready for spawning.
- sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_ASSIST, 0, 60000);
- }
- }
- if (hd)
- skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
- }
- break;
- default:
- ShowWarning("skill_castend_nodamage_id: Unknown skill used:%d\n",skill_id);
- clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
- map_freeblock_unlock();
- return 1;
- }
-
- if(skill_id != SR_CURSEDCIRCLE){
- struct status_change *sc = status_get_sc(src);
- if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] )//Should only remove after the skill had been casted.
- status_change_end(src,SC_CURSEDCIRCLE_ATKER,INVALID_TIMER);
- }
-
- if (dstmd) { //Mob skill event for no damage skills (damage ones are handled in battle_calc_damage) [Skotlex]
- mob_log_damage(dstmd, src, 0); //Log interaction (counts as 'attacker' for the exp bonus)
- mobskill_event(dstmd, src, tick, MSC_SKILLUSED|(skill_id<<16));
- }
-
- if( sd && !(flag&1) )
- {// ensure that the skill last-cast tick is recorded
- sd->canskill_tick = gettick();
-
- if( sd->state.arrow_atk )
- {// consume arrow on last invocation to this skill.
- battle_consume_ammo(sd, skill_id, skill_lv);
- }
- skill_onskillusage(sd, bl, skill_id, tick);
- // perform skill requirement consumption
- skill_consume_requirement(sd,skill_id,skill_lv,2);
- }
-
- map_freeblock_unlock();
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data)
-{
- struct block_list *target, *src;
- struct map_session_data *sd;
- struct mob_data *md;
- struct unit_data *ud;
- struct status_change *sc = NULL;
- int inf,inf2,flag = 0;
-
- src = map_id2bl(id);
- if( src == NULL )
- {
- ShowDebug("skill_castend_id: src == NULL (tid=%d, id=%d)\n", tid, id);
- return 0;// not found
- }
-
- ud = unit_bl2ud(src);
- if( ud == NULL )
- {
- ShowDebug("skill_castend_id: ud == NULL (tid=%d, id=%d)\n", tid, id);
- return 0;// ???
- }
-
- sd = BL_CAST(BL_PC, src);
- md = BL_CAST(BL_MOB, src);
-
- if( src->prev == NULL ) {
- ud->skilltimer = INVALID_TIMER;
- return 0;
- }
-
- if(ud->skill_id != SA_CASTCANCEL && ud->skill_id != SO_SPELLFIST) {// otherwise handled in unit_skillcastcancel()
- if( ud->skilltimer != tid ) {
- ShowError("skill_castend_id: Timer mismatch %d!=%d!\n", ud->skilltimer, tid);
- ud->skilltimer = INVALID_TIMER;
- return 0;
- }
-
- if( sd && ud->skilltimer != INVALID_TIMER && (pc_checkskill(sd,SA_FREECAST) > 0 || ud->skill_id == LG_EXEEDBREAK) )
- {// restore original walk speed
- ud->skilltimer = INVALID_TIMER;
- status_calc_bl(&sd->bl, SCB_SPEED);
- }
-
- ud->skilltimer = INVALID_TIMER;
- }
-
- if (ud->skilltarget == id)
- target = src;
- else
- target = map_id2bl(ud->skilltarget);
-
- // Use a do so that you can break out of it when the skill fails.
- do {
- if(!target || target->prev==NULL) break;
-
- if(src->m != target->m || status_isdead(src)) break;
-
- switch (ud->skill_id) {
- //These should become skill_castend_pos
- case WE_CALLPARTNER:
- if(sd) clif_callpartner(sd);
- case WE_CALLPARENT:
- case WE_CALLBABY:
- case AM_RESURRECTHOMUN:
- case PF_SPIDERWEB:
- //Find a random spot to place the skill. [Skotlex]
- inf2 = skill_get_splash(ud->skill_id, ud->skill_lv);
- ud->skillx = target->x + inf2;
- ud->skilly = target->y + inf2;
- if (inf2 && !map_random_dir(target, &ud->skillx, &ud->skilly)) {
- ud->skillx = target->x;
- ud->skilly = target->y;
- }
- ud->skilltimer=tid;
- return skill_castend_pos(tid,tick,id,data);
- case GN_WALLOFTHORN:
- ud->skillx = target->x;
- ud->skilly = target->y;
- ud->skilltimer = tid;
- return skill_castend_pos(tid,tick,id,data);
- }
-
- if(ud->skill_id == RG_BACKSTAP) {
- uint8 dir = map_calc_dir(src,target->x,target->y),t_dir = unit_getdir(target);
- if(check_distance_bl(src, target, 0) || map_check_dir(dir,t_dir)) {
- break;
- }
- }
-
- if( ud->skill_id == PR_TURNUNDEAD )
- {
- struct status_data *tstatus = status_get_status_data(target);
- if( !battle_check_undead(tstatus->race, tstatus->def_ele) )
- break;
- }
-
- if( ud->skill_id == RA_WUGSTRIKE ){
- if( !path_search(NULL,src->m,src->x,src->y,target->x,target->y,1,CELL_CHKNOREACH))
- break;
- }
-
- if( ud->skill_id == PR_LEXDIVINA || ud->skill_id == MER_LEXDIVINA )
- {
- sc = status_get_sc(target);
- if( battle_check_target(src,target, BCT_ENEMY) <= 0 && (!sc || !sc->data[SC_SILENCE]) )
- { //If it's not an enemy, and not silenced, you can't use the skill on them. [Skotlex]
- clif_skill_nodamage (src, target, ud->skill_id, ud->skill_lv, 0);
- break;
- }
- }
- else
- { // Check target validity.
- inf = skill_get_inf(ud->skill_id);
- inf2 = skill_get_inf2(ud->skill_id);
-
- if(inf&INF_ATTACK_SKILL ||
- (inf&INF_SELF_SKILL && inf2&INF2_NO_TARGET_SELF) //Combo skills
- ) // Casted through combo.
- inf = BCT_ENEMY; //Offensive skill.
- else if(inf2&INF2_NO_ENEMY)
- inf = BCT_NOENEMY;
- else
- inf = 0;
-
- if(inf2 & (INF2_PARTY_ONLY|INF2_GUILD_ONLY) && src != target)
- {
- inf |=
- (inf2&INF2_PARTY_ONLY?BCT_PARTY:0)|
- (inf2&INF2_GUILD_ONLY?BCT_GUILD:0);
- //Remove neutral targets (but allow enemy if skill is designed to be so)
- inf &= ~BCT_NEUTRAL;
- }
-
- if( ud->skill_id >= SL_SKE && ud->skill_id <= SL_SKA && target->type == BL_MOB )
- {
- if( ((TBL_MOB*)target)->class_ == MOBID_EMPERIUM )
- break;
- }
- else if (inf && battle_check_target(src, target, inf) <= 0){
- if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
-
- if(inf&BCT_ENEMY && (sc = status_get_sc(target)) &&
- sc->data[SC_FOGWALL] &&
- rnd() % 100 < 75) { //Fogwall makes all offensive-type targetted skills fail at 75%
- if (sd) clif_skill_fail(sd, ud->skill_id, USESKILL_FAIL_LEVEL, 0);
- break;
- }
- }
-
- //Avoid doing double checks for instant-cast skills.
- if (tid != INVALID_TIMER && !status_check_skilluse(src, target, ud->skill_id, 1))
- break;
-
- if(md) {
- md->last_thinktime=tick +MIN_MOBTHINKTIME;
- if(md->skill_idx >= 0 && md->db->skill[md->skill_idx].emotion >= 0)
- clif_emotion(src, md->db->skill[md->skill_idx].emotion);
- }
-
- if(src != target && battle_config.skill_add_range &&
- !check_distance_bl(src, target, skill_get_range2(src,ud->skill_id,ud->skill_lv)+battle_config.skill_add_range))
- {
- if (sd) {
- clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
- if(battle_config.skill_out_range_consume) //Consume items anyway. [Skotlex]
- skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,3);
- }
- break;
- }
-
- if( sd )
- {
- if( !skill_check_condition_castend(sd, ud->skill_id, ud->skill_lv) )
- break;
- else
- skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,1);
- }
-#ifdef OFFICIAL_WALKPATH
- if( !path_search_long(NULL, src->m, src->x, src->y, target->x, target->y, CELL_CHKWALL) )
- break;
-#endif
- if( (src->type == BL_MER || src->type == BL_HOM) && !skill_check_condition_mercenary(src, ud->skill_id, ud->skill_lv, 1) )
- break;
-
- if (ud->state.running && ud->skill_id == TK_JUMPKICK)
- {
- ud->state.running = 0;
- status_change_end(src, SC_RUN, INVALID_TIMER);
- flag = 1;
- }
-
- if (ud->walktimer != INVALID_TIMER && ud->skill_id != TK_RUN && ud->skill_id != RA_WUGDASH)
- unit_stop_walking(src,1);
-
- if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
- ud->canact_tick = tick + skill_delayfix(src, ud->skill_id, ud->skill_lv); //Tests show wings don't overwrite the delay but skill scrolls do. [Inkfish]
- if (sd) { //Cooldown application
- int i, cooldown = skill_get_cooldown(ud->skill_id, ud->skill_lv);
- for (i = 0; i < ARRAYLENGTH(sd->skillcooldown) && sd->skillcooldown[i].id; i++) { // Increases/Decreases cooldown of a skill by item/card bonuses.
- if (sd->skillcooldown[i].id == ud->skill_id){
- cooldown += sd->skillcooldown[i].val;
- break;
- }
- }
- if(cooldown)
- skill_blockpc_start(sd, ud->skill_id, cooldown);
- }
- if( battle_config.display_status_timers && sd )
- clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skill_id, ud->skill_lv), 0, 0, 0);
- if( sd )
- {
- switch( ud->skill_id )
- {
- case GS_DESPERADO:
- sd->canequip_tick = tick + skill_get_time(ud->skill_id, ud->skill_lv);
- break;
- case CR_GRANDCROSS:
- case NPC_GRANDDARKNESS:
- if( (sc = status_get_sc(src)) && sc->data[SC_STRIPSHIELD] )
- {
- const struct TimerData *timer = get_timer(sc->data[SC_STRIPSHIELD]->timer);
- if( timer && timer->func == status_change_timer && DIFF_TICK(timer->tick,gettick()+skill_get_time(ud->skill_id, ud->skill_lv)) > 0 )
- break;
- }
- sc_start2(src, SC_STRIPSHIELD, 100, 0, 1, skill_get_time(ud->skill_id, ud->skill_lv));
- break;
- }
- }
- if (skill_get_state(ud->skill_id) != ST_MOVE_ENABLE)
- unit_set_walkdelay(src, tick, battle_config.default_walk_delay+skill_get_walkdelay(ud->skill_id, ud->skill_lv), 1);
-
- if(battle_config.skill_log && battle_config.skill_log&src->type)
- ShowInfo("Type %d, ID %d skill castend id [id =%d, lv=%d, target ID %d]\n",
- src->type, src->id, ud->skill_id, ud->skill_lv, target->id);
-
- map_freeblock_lock();
-
- // SC_MAGICPOWER needs to switch states before any damage is actually dealt
- skill_toggle_magicpower(src, ud->skill_id);
- if( ud->skill_id != RA_CAMOUFLAGE ) // only normal attack and auto cast skills benefit from its bonuses
- status_change_end(src,SC_CAMOUFLAGE, INVALID_TIMER);
-
- if (skill_get_casttype(ud->skill_id) == CAST_NODAMAGE)
- skill_castend_nodamage_id(src,target,ud->skill_id,ud->skill_lv,tick,flag);
- else
- skill_castend_damage_id(src,target,ud->skill_id,ud->skill_lv,tick,flag);
-
- sc = status_get_sc(src);
- if(sc && sc->count) {
- if(sc->data[SC_SPIRIT] &&
- sc->data[SC_SPIRIT]->val2 == SL_WIZARD &&
- sc->data[SC_SPIRIT]->val3 == ud->skill_id &&
- ud->skill_id != WZ_WATERBALL)
- sc->data[SC_SPIRIT]->val3 = 0; //Clear bounced spell check.
-
- if( sc->data[SC_DANCING] && skill_get_inf2(ud->skill_id)&INF2_SONG_DANCE && sd )
- skill_blockpc_start(sd,BD_ADAPTATION,3000);
- }
-
- if( sd && ud->skill_id != SA_ABRACADABRA && ud->skill_id != WM_RANDOMIZESPELL ) // they just set the data so leave it as it is.[Inkfish]
- sd->skillitem = sd->skillitemlv = 0;
-
- if (ud->skilltimer == INVALID_TIMER) {
- if(md) md->skill_idx = -1;
- else ud->skill_id = 0; //mobs can't clear this one as it is used for skill condition 'afterskill'
- ud->skill_lv = ud->skilltarget = 0;
- }
- map_freeblock_unlock();
- return 1;
- } while(0);
-
- //Skill failed.
- if (ud->skill_id == MO_EXTREMITYFIST && sd && !(sc && sc->data[SC_FOGWALL]))
- { //When Asura fails... (except when it fails from Fog of Wall)
- //Consume SP/spheres
- skill_consume_requirement(sd,ud->skill_id, ud->skill_lv,1);
- status_set_sp(src, 0, 0);
- sc = &sd->sc;
- if (sc->count)
- { //End states
- status_change_end(src, SC_EXPLOSIONSPIRITS, INVALID_TIMER);
- status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
-#ifdef RENEWAL
- sc_start(src, SC_EXTREMITYFIST2, 100, ud->skill_lv, skill_get_time(ud->skill_id, ud->skill_lv));
-#endif
- }
- if (target && target->m == src->m)
- { //Move character to target anyway.
- if (unit_movepos(src, src->x+3, src->y+3, 1, 1))
- { //Display movement + animation.
- clif_slide(src,src->x,src->y);
- clif_skill_damage(src,target,tick,sd->battle_status.amotion,0,0,1,ud->skill_id, ud->skill_lv, 5);
- }
- clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
- }
- }
-
- ud->skill_id = ud->skill_lv = ud->skilltarget = 0;
- if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
- ud->canact_tick = tick;
- //You can't place a skill failed packet here because it would be
- //sent in ALL cases, even cases where skill_check_condition fails
- //which would lead to double 'skill failed' messages u.u [Skotlex]
- if(sd)
- sd->skillitem = sd->skillitemlv = 0;
- else if(md)
- md->skill_idx = -1;
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_castend_pos(int tid, unsigned int tick, int id, intptr_t data)
-{
- struct block_list* src = map_id2bl(id);
- int maxcount;
- struct map_session_data *sd;
- struct unit_data *ud = unit_bl2ud(src);
- struct mob_data *md;
-
- nullpo_ret(ud);
-
- sd = BL_CAST(BL_PC , src);
- md = BL_CAST(BL_MOB, src);
-
- if( src->prev == NULL ) {
- ud->skilltimer = INVALID_TIMER;
- return 0;
- }
-
- if( ud->skilltimer != tid )
- {
- ShowError("skill_castend_pos: Timer mismatch %d!=%d\n", ud->skilltimer, tid);
- ud->skilltimer = INVALID_TIMER;
- return 0;
- }
-
- if( sd && ud->skilltimer != INVALID_TIMER && ( pc_checkskill(sd,SA_FREECAST) > 0 || ud->skill_id == LG_EXEEDBREAK ) )
- {// restore original walk speed
- ud->skilltimer = INVALID_TIMER;
- status_calc_bl(&sd->bl, SCB_SPEED);
- }
- ud->skilltimer = INVALID_TIMER;
-
- do {
- if( status_isdead(src) )
- break;
-
- if( !(src->type&battle_config.skill_reiteration) &&
- skill_get_unit_flag(ud->skill_id)&UF_NOREITERATION &&
- skill_check_unit_range(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv)
- )
- {
- if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- if( src->type&battle_config.skill_nofootset &&
- skill_get_unit_flag(ud->skill_id)&UF_NOFOOTSET &&
- skill_check_unit_range2(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv)
- )
- {
- if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- if( src->type&battle_config.land_skill_limit &&
- (maxcount = skill_get_maxcount(ud->skill_id, ud->skill_lv)) > 0
- ) {
- int i;
- for(i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i] && maxcount;i++) {
- if(ud->skillunit[i]->skill_id == ud->skill_id)
- maxcount--;
- }
- if( maxcount == 0 )
- {
- if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- }
-
- if(tid != INVALID_TIMER)
- { //Avoid double checks on instant cast skills. [Skotlex]
- if (!status_check_skilluse(src, NULL, ud->skill_id, 1))
- break;
- if(battle_config.skill_add_range &&
- !check_distance_blxy(src, ud->skillx, ud->skilly, skill_get_range2(src,ud->skill_id,ud->skill_lv)+battle_config.skill_add_range)) {
- if (sd && battle_config.skill_out_range_consume) //Consume items anyway.
- skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,3);
- break;
- }
- }
-
- if( sd )
- {
- if( !skill_check_condition_castend(sd, ud->skill_id, ud->skill_lv) )
- break;
- else
- skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,1);
- }
-
- if( (src->type == BL_MER || src->type == BL_HOM) && !skill_check_condition_mercenary(src, ud->skill_id, ud->skill_lv, 1) )
- break;
-
- if(md) {
- md->last_thinktime=tick +MIN_MOBTHINKTIME;
- if(md->skill_idx >= 0 && md->db->skill[md->skill_idx].emotion >= 0)
- clif_emotion(src, md->db->skill[md->skill_idx].emotion);
- }
-
- if(battle_config.skill_log && battle_config.skill_log&src->type)
- ShowInfo("Type %d, ID %d skill castend pos [id =%d, lv=%d, (%d,%d)]\n",
- src->type, src->id, ud->skill_id, ud->skill_lv, ud->skillx, ud->skilly);
-
- if (ud->walktimer != INVALID_TIMER)
- unit_stop_walking(src,1);
-
- if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
- ud->canact_tick = tick + skill_delayfix(src, ud->skill_id, ud->skill_lv);
- if (sd) { //Cooldown application
- int i, cooldown = skill_get_cooldown(ud->skill_id, ud->skill_lv);
- for (i = 0; i < ARRAYLENGTH(sd->skillcooldown) && sd->skillcooldown[i].id; i++) { // Increases/Decreases cooldown of a skill by item/card bonuses.
- if (sd->skillcooldown[i].id == ud->skill_id){
- cooldown += sd->skillcooldown[i].val;
- break;
- }
- }
- if(cooldown)
- skill_blockpc_start(sd, ud->skill_id, cooldown);
- }
- if( battle_config.display_status_timers && sd )
- clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skill_id, ud->skill_lv), 0, 0, 0);
-// if( sd )
-// {
-// switch( ud->skill_id )
-// {
-// case ????:
-// sd->canequip_tick = tick + ????;
-// break;
-// }
-// }
- unit_set_walkdelay(src, tick, battle_config.default_walk_delay+skill_get_walkdelay(ud->skill_id, ud->skill_lv), 1);
- status_change_end(src,SC_CAMOUFLAGE, INVALID_TIMER);// only normal attack and auto cast skills benefit from its bonuses
- map_freeblock_lock();
- skill_castend_pos2(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv,tick,0);
-
- if( sd && sd->skillitem != AL_WARP ) // Warp-Portal thru items will clear data in skill_castend_map. [Inkfish]
- sd->skillitem = sd->skillitemlv = 0;
-
- if (ud->skilltimer == INVALID_TIMER) {
- if (md) md->skill_idx = -1;
- else ud->skill_id = 0; //Non mobs can't clear this one as it is used for skill condition 'afterskill'
- ud->skill_lv = ud->skillx = ud->skilly = 0;
- }
-
- map_freeblock_unlock();
- return 1;
- } while(0);
-
- if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
- ud->canact_tick = tick;
- ud->skill_id = ud->skill_lv = 0;
- if(sd)
- sd->skillitem = sd->skillitemlv = 0;
- else if(md)
- md->skill_idx = -1;
- return 0;
-
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
-{
- struct map_session_data* sd;
- struct status_change* sc;
- struct status_change_entry *sce;
- struct skill_unit_group* sg;
- enum sc_type type;
- int i;
-
- //if(skill_lv <= 0) return 0;
- if(skill_id > 0 && !skill_lv) return 0; // celest
-
- nullpo_ret(src);
-
- if(status_isdead(src))
- return 0;
-
- sd = BL_CAST(BL_PC, src);
-
- sc = status_get_sc(src);
- type = status_skill2sc(skill_id);
- sce = (sc && type != -1)?sc->data[type]:NULL;
-
- switch (skill_id) { //Skill effect.
- case WZ_METEOR:
- case MO_BODYRELOCATION:
- case CR_CULTIVATION:
- case HW_GANBANTEIN:
- case LG_EARTHDRIVE:
- break; //Effect is displayed on respective switch case.
- default:
- if(skill_get_inf(skill_id)&INF_SELF_SKILL)
- clif_skill_nodamage(src,src,skill_id,skill_lv,1);
- else
- clif_skill_poseffect(src,skill_id,skill_lv,x,y,tick);
- }
-
- // SC_MAGICPOWER needs to switch states before any damage is actually dealt
- skill_toggle_magicpower(src, skill_id);
-
- switch(skill_id)
- {
- case PR_BENEDICTIO:
- skill_area_temp[1] = src->id;
- i = skill_get_splash(skill_id, skill_lv);
- map_foreachinarea(skill_area_sub,
- src->m, x-i, y-i, x+i, y+i, BL_PC,
- src, skill_id, skill_lv, tick, flag|BCT_ALL|1,
- skill_castend_nodamage_id);
- map_foreachinarea(skill_area_sub,
- src->m, x-i, y-i, x+i, y+i, BL_CHAR,
- src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1,
- skill_castend_damage_id);
- break;
-
- case BS_HAMMERFALL:
- i = skill_get_splash(skill_id, skill_lv);
- map_foreachinarea (skill_area_sub,
- src->m, x-i, y-i, x+i, y+i, BL_CHAR,
- src, skill_id, skill_lv, tick, flag|BCT_ENEMY|2,
- skill_castend_nodamage_id);
- break;
-
- case HT_DETECTING:
- i = skill_get_splash(skill_id, skill_lv);
- map_foreachinarea( status_change_timer_sub,
- src->m, x-i, y-i, x+i,y+i,BL_CHAR,
- src,NULL,SC_SIGHT,tick);
- if(battle_config.traps_setting&1)
- map_foreachinarea( skill_reveal_trap,
- src->m, x-i, y-i, x+i,y+i,BL_SKILL);
- break;
-
- case SR_RIDEINLIGHTNING:
- i = skill_get_splash(skill_id, skill_lv);
- map_foreachinarea(skill_area_sub, src->m, x-i, y-i, x+i, y+i, BL_CHAR,
- src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_damage_id);
- break;
-
- case SA_VOLCANO:
- case SA_DELUGE:
- case SA_VIOLENTGALE:
- { //Does not consumes if the skill is already active. [Skotlex]
- struct skill_unit_group *sg;
- if ((sg= skill_locate_element_field(src)) != NULL && ( sg->skill_id == SA_VOLCANO || sg->skill_id == SA_DELUGE || sg->skill_id == SA_VIOLENTGALE ))
- {
- if (sg->limit - DIFF_TICK(gettick(), sg->tick) > 0)
- {
- skill_unitsetting(src,skill_id,skill_lv,x,y,0);
- return 0; // not to consume items
- }
- else
- sg->limit = 0; //Disable it.
- }
- skill_unitsetting(src,skill_id,skill_lv,x,y,0);
- break;
- }
- case MG_SAFETYWALL:
- case MG_FIREWALL:
- case MG_THUNDERSTORM:
-
- case AL_PNEUMA:
- case WZ_ICEWALL:
- case WZ_FIREPILLAR:
- case WZ_QUAGMIRE:
- case WZ_VERMILION:
- case WZ_STORMGUST:
- case WZ_HEAVENDRIVE:
- case PR_SANCTUARY:
- case PR_MAGNUS:
- case CR_GRANDCROSS:
- case NPC_GRANDDARKNESS:
- case HT_SKIDTRAP:
- case MA_SKIDTRAP:
- case HT_LANDMINE:
- case MA_LANDMINE:
- case HT_ANKLESNARE:
- case HT_SHOCKWAVE:
- case HT_SANDMAN:
- case MA_SANDMAN:
- case HT_FLASHER:
- case HT_FREEZINGTRAP:
- case MA_FREEZINGTRAP:
- case HT_BLASTMINE:
- case HT_CLAYMORETRAP:
- case AS_VENOMDUST:
- case AM_DEMONSTRATION:
- case PF_FOGWALL:
- case PF_SPIDERWEB:
- case HT_TALKIEBOX:
- case WE_CALLPARTNER:
- case WE_CALLPARENT:
- case WE_CALLBABY:
- case AC_SHOWER: //Ground-placed skill implementation.
- case MA_SHOWER:
- case SA_LANDPROTECTOR:
- 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:
- case GS_DESPERADO:
- case NJ_KAENSIN:
- case NJ_BAKUENRYU:
- case NJ_SUITON:
- case NJ_HYOUSYOURAKU:
- case NJ_RAIGEKISAI:
- case NJ_KAMAITACHI:
-#ifdef RENEWAL
- case NJ_HUUMA:
-#endif
- case NPC_EVILLAND:
- case RA_ELECTRICSHOCKER:
- case RA_CLUSTERBOMB:
- case RA_MAGENTATRAP:
- case RA_COBALTTRAP:
- case RA_MAIZETRAP:
- case RA_VERDURETRAP:
- case RA_FIRINGTRAP:
- case RA_ICEBOUNDTRAP:
- case SC_MANHOLE:
- case SC_DIMENSIONDOOR:
- case SC_CHAOSPANIC:
- case SC_MAELSTROM:
- case WM_REVERBERATION:
- case WM_SEVERE_RAINSTORM:
- case WM_POEMOFNETHERWORLD:
- case SO_PSYCHIC_WAVE:
- case SO_VACUUM_EXTREME:
- case GN_WALLOFTHORN:
- case GN_THORNS_TRAP:
- case GN_DEMONIC_FIRE:
- case GN_HELLS_PLANT:
- case SO_EARTHGRAVE:
- case SO_DIAMONDDUST:
- case SO_FIRE_INSIGNIA:
- case SO_WATER_INSIGNIA:
- case SO_WIND_INSIGNIA:
- case SO_EARTH_INSIGNIA:
- case KO_HUUMARANKA:
- case KO_MUCHANAGE:
- case KO_BAKURETSU:
- case KO_ZENKAI:
- case MH_LAVA_SLIDE:
- case MH_VOLCANIC_ASH:
- case MH_POISON_MIST:
- case MH_STEINWAND:
- case MH_XENO_SLASHER:
- flag|=1;//Set flag to 1 to prevent deleting ammo (it will be deleted on group-delete).
- case GS_GROUNDDRIFT: //Ammo should be deleted right away.
- skill_unitsetting(src,skill_id,skill_lv,x,y,0);
- break;
- case RG_GRAFFITI: /* Graffiti [Valaris] */
- skill_clear_unitgroup(src);
- skill_unitsetting(src,skill_id,skill_lv,x,y,0);
- flag|=1;
- break;
- case HP_BASILICA:
- if( sc->data[SC_BASILICA] )
- status_change_end(src, SC_BASILICA, INVALID_TIMER); // Cancel Basilica
- else
- { // Create Basilica. Start SC on caster. Unit timer start SC on others.
- skill_clear_unitgroup(src);
- if( skill_unitsetting(src,skill_id,skill_lv,x,y,0) )
- sc_start4(src,type,100,skill_lv,0,0,src->id,skill_get_time(skill_id,skill_lv));
- flag|=1;
- }
- break;
- case CG_HERMODE:
- skill_clear_unitgroup(src);
- if ((sg = skill_unitsetting(src,skill_id,skill_lv,x,y,0)))
- sc_start4(src,SC_DANCING,100,
- skill_id,0,skill_lv,sg->group_id,skill_get_time(skill_id,skill_lv));
- flag|=1;
- break;
- case RG_CLEANER: // [Valaris]
- i = skill_get_splash(skill_id, skill_lv);
- map_foreachinarea(skill_graffitiremover,src->m,x-i,y-i,x+i,y+i,BL_SKILL);
- break;
-
- case SO_WARMER:
- flag|= 8;
- case SO_CLOUD_KILL:
- skill_unitsetting(src,skill_id,skill_lv,x,y,0);
- break;
-
- case WZ_METEOR: {
- int area = skill_get_splash(skill_id, skill_lv);
- short tmpx = 0, tmpy = 0, x1 = 0, y1 = 0;
-
- for( i = 0; i < 2 + (skill_lv>>1); i++ ) {
- // Creates a random Cell in the Splash Area
- tmpx = x - area + rnd()%(area * 2 + 1);
- tmpy = y - area + rnd()%(area * 2 + 1);
-
- if( i == 0 && path_search_long(NULL, src->m, src->x, src->y, tmpx, tmpy, CELL_CHKWALL) )
- clif_skill_poseffect(src,skill_id,skill_lv,tmpx,tmpy,tick);
-
- if( i > 0 )
- skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skill_id,skill_lv,(x1<<16)|y1,0);
-
- x1 = tmpx;
- y1 = tmpy;
- }
-
- skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skill_id,skill_lv,-1,0);
- }
- break;
-
- case AL_WARP:
- if(sd)
- {
- clif_skill_warppoint(sd, skill_id, skill_lv, sd->status.save_point.map,
- (skill_lv >= 2) ? sd->status.memo_point[0].map : 0,
- (skill_lv >= 3) ? sd->status.memo_point[1].map : 0,
- (skill_lv >= 4) ? sd->status.memo_point[2].map : 0
- );
- }
- return 0; // not to consume item.
-
- case MO_BODYRELOCATION:
- if (unit_movepos(src, x, y, 1, 1)) {
-#if PACKETVER >= 20111005
- clif_snap(src, src->x, src->y);
-#else
- clif_skill_poseffect(src,skill_id,skill_lv,src->x,src->y,tick);
-#endif
- if (sd)
- skill_blockpc_start (sd, MO_EXTREMITYFIST, 2000);
- }
- break;
- case NJ_SHADOWJUMP:
- if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground ) { //You don't move on GVG grounds.
- unit_movepos(src, x, y, 1, 0);
- clif_slide(src,x,y);
- }
- status_change_end(src, SC_HIDING, INVALID_TIMER);
- break;
- case AM_SPHEREMINE:
- case AM_CANNIBALIZE:
- {
- int summons[5] = { 1589, 1579, 1575, 1555, 1590 };
- //int summons[5] = { 1020, 1068, 1118, 1500, 1368 };
- int class_ = skill_id==AM_SPHEREMINE?1142:summons[skill_lv-1];
- struct mob_data *md;
-
- // Correct info, don't change any of this! [celest]
- md = mob_once_spawn_sub(src, src->m, x, y, status_get_name(src), class_, "", SZ_SMALL, AI_NONE);
- if (md) {
- md->master_id = src->id;
- md->special_state.ai = (skill_id == AM_SPHEREMINE) ? AI_SPHERE : AI_FLORA;
- if( md->deletetimer != INVALID_TIMER )
- delete_timer(md->deletetimer, mob_timer_delete);
- md->deletetimer = add_timer (gettick() + skill_get_time(skill_id,skill_lv), mob_timer_delete, md->bl.id, 0);
- mob_spawn (md); //Now it is ready for spawning.
- }
- }
- break;
-
- // Slim Pitcher [Celest]
- case CR_SLIMPITCHER:
- if (sd) {
- int i = skill_lv%11 - 1;
- int j = pc_search_inventory(sd,skill_db[skill_id].itemid[i]);
- if( j < 0 || skill_db[skill_id].itemid[i] <= 0 || sd->inventory_data[j] == NULL || sd->status.inventory[j].amount < skill_db[skill_id].amount[i] )
- {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 1;
- }
- potion_flag = 1;
- potion_hp = 0;
- potion_sp = 0;
- run_script(sd->inventory_data[j]->script,0,sd->bl.id,0);
- potion_flag = 0;
- //Apply skill bonuses
- i = pc_checkskill(sd,CR_SLIMPITCHER)*10
- + pc_checkskill(sd,AM_POTIONPITCHER)*10
- + pc_checkskill(sd,AM_LEARNINGPOTION)*5
- + pc_skillheal_bonus(sd, skill_id);
-
- potion_hp = potion_hp * (100+i)/100;
- potion_sp = potion_sp * (100+i)/100;
-
- if(potion_hp > 0 || potion_sp > 0) {
- i = skill_get_splash(skill_id, skill_lv);
- map_foreachinarea(skill_area_sub,
- src->m,x-i,y-i,x+i,y+i,BL_CHAR,
- src,skill_id,skill_lv,tick,flag|BCT_PARTY|BCT_GUILD|1,
- skill_castend_nodamage_id);
- }
- } else {
- int i = skill_lv%11 - 1;
- struct item_data *item;
- i = skill_db[skill_id].itemid[i];
- item = itemdb_search(i);
- potion_flag = 1;
- potion_hp = 0;
- potion_sp = 0;
- run_script(item->script,0,src->id,0);
- potion_flag = 0;
- i = skill_get_max(CR_SLIMPITCHER)*10;
-
- potion_hp = potion_hp * (100+i)/100;
- potion_sp = potion_sp * (100+i)/100;
-
- if(potion_hp > 0 || potion_sp > 0) {
- i = skill_get_splash(skill_id, skill_lv);
- map_foreachinarea(skill_area_sub,
- src->m,x-i,y-i,x+i,y+i,BL_CHAR,
- src,skill_id,skill_lv,tick,flag|BCT_PARTY|BCT_GUILD|1,
- skill_castend_nodamage_id);
- }
- }
- break;
-
- case HW_GANBANTEIN:
- if (rnd()%100 < 80) {
- int dummy = 1;
- clif_skill_poseffect(src,skill_id,skill_lv,x,y,tick);
- i = skill_get_splash(skill_id, skill_lv);
- map_foreachinarea(skill_cell_overlap, src->m, x-i, y-i, x+i, y+i, BL_SKILL, HW_GANBANTEIN, &dummy, src);
- } else {
- if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 1;
- }
- break;
-
- case HW_GRAVITATION:
- if ((sg = skill_unitsetting(src,skill_id,skill_lv,x,y,0)))
- sc_start4(src,type,100,skill_lv,0,BCT_SELF,sg->group_id,skill_get_time(skill_id,skill_lv));
- flag|=1;
- break;
-
- // Plant Cultivation [Celest]
- case CR_CULTIVATION:
- if (sd) {
- if( map_count_oncell(src->m,x,y,BL_CHAR) > 0 )
- {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 1;
- }
- clif_skill_poseffect(src,skill_id,skill_lv,x,y,tick);
- if (rnd()%100 < 50) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- } else {
- TBL_MOB* md = mob_once_spawn_sub(src, src->m, x, y, "--ja--",(skill_lv < 2 ? 1084+rnd()%2 : 1078+rnd()%6),"", SZ_SMALL, AI_NONE);
- int i;
- if (!md) break;
- if ((i = skill_get_time(skill_id, skill_lv)) > 0)
- {
- if( md->deletetimer != INVALID_TIMER )
- delete_timer(md->deletetimer, mob_timer_delete);
- md->deletetimer = add_timer (tick + i, mob_timer_delete, md->bl.id, 0);
- }
- mob_spawn (md);
- }
- }
- break;
-
- case SG_SUN_WARM:
- case SG_MOON_WARM:
- case SG_STAR_WARM:
- skill_clear_unitgroup(src);
- if ((sg = skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0)))
- sc_start4(src,type,100,skill_lv,0,0,sg->group_id,skill_get_time(skill_id,skill_lv));
- flag|=1;
- break;
-
- case PA_GOSPEL:
- if (sce && sce->val4 == BCT_SELF)
- {
- status_change_end(src, SC_GOSPEL, INVALID_TIMER);
- return 0;
- }
- else
- {
- sg = skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0);
- if (!sg) break;
- if (sce)
- status_change_end(src, type, INVALID_TIMER); //Was under someone else's Gospel. [Skotlex]
- sc_start4(src,type,100,skill_lv,0,sg->group_id,BCT_SELF,skill_get_time(skill_id,skill_lv));
- clif_skill_poseffect(src, skill_id, skill_lv, 0, 0, tick); // PA_GOSPEL music packet
- }
- break;
- case NJ_TATAMIGAESHI:
- if (skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0))
- sc_start(src,type,100,skill_lv,skill_get_time2(skill_id,skill_lv));
- break;
-
- case AM_RESURRECTHOMUN: //[orn]
- if (sd)
- {
- if (!merc_resurrect_homunculus(sd, 20*skill_lv, x, y))
- {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- break;
- }
- }
- break;
-
- case RK_WINDCUTTER:
- clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- case NC_COLDSLOWER:
- case NC_ARMSCANNON:
- case RK_DRAGONBREATH:
- case WM_LULLABY_DEEPSLEEP:
- i = skill_get_splash(skill_id,skill_lv);
- map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,splash_target(src),
- src,skill_id,skill_lv,tick,flag|(skill_id==WM_LULLABY_DEEPSLEEP?BCT_ALL:BCT_ENEMY)|1,skill_castend_damage_id);
- break;
- /**
- * Guilotine Cross
- **/
- case GC_POISONSMOKE:
- if( !(sc && sc->data[SC_POISONINGWEAPON]) ) {
- if( sd )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_GC_POISONINGWEAPON,0);
- return 0;
- }
- clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,6);
- skill_unitsetting(src, skill_id, skill_lv, x, y, flag);
- //status_change_end(src,SC_POISONINGWEAPON,INVALID_TIMER); // 08/31/2011 - When using poison smoke, you no longer lose the poisoning weapon effect.
- break;
- /**
- * Arch Bishop
- **/
- case AB_EPICLESIS:
- if( (sg = skill_unitsetting(src, skill_id, skill_lv, x, y, 0)) ) {
- i = sg->unit->range;
- map_foreachinarea(skill_area_sub, src->m, x - i, y - i, x + i, y + i, BL_CHAR, src, ALL_RESURRECTION, 1, tick, flag|BCT_NOENEMY|1,skill_castend_nodamage_id);
- }
- break;
- /**
- * Warlock
- **/
- case WL_COMET:
- if( sc ) {
- sc->comet_x = x;
- sc->comet_y = y;
- }
- i = skill_get_splash(skill_id,skill_lv);
- map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,splash_target(src),src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
- break;
-
- case WL_EARTHSTRAIN:
- {
- int i, wave = skill_lv + 4, dir = map_calc_dir(src,x,y);
- int sx = x = src->x, sy = y = src->y; // Store first caster's location to avoid glitch on unit setting
-
- for( i = 1; i <= wave; i++ )
- {
- switch( dir ){
- case 0: case 1: case 7: sy = y + i; break;
- case 3: case 4: case 5: sy = y - i; break;
- case 2: sx = x - i; break;
- case 6: sx = x + i; break;
- }
- skill_addtimerskill(src,gettick() + (150 * i),0,sx,sy,skill_id,skill_lv,dir,flag&2);
- }
- }
- break;
- /**
- * Ranger
- **/
- case RA_DETONATOR:
- i = skill_get_splash(skill_id, skill_lv);
- map_foreachinarea(skill_detonator, src->m, x-i, y-i, x+i, y+i, BL_SKILL, src);
- clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
- break;
- /**
- * Mechanic
- **/
- case NC_NEUTRALBARRIER:
- case NC_STEALTHFIELD:
- skill_clear_unitgroup(src); // To remove previous skills - cannot used combined
- if( (sg = skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0)) != NULL ) {
- sc_start2(src,skill_id == NC_NEUTRALBARRIER ? SC_NEUTRALBARRIER_MASTER : SC_STEALTHFIELD_MASTER,100,skill_lv,sg->group_id,skill_get_time(skill_id,skill_lv));
- if( sd ) pc_overheat(sd,1);
- }
- break;
-
- case NC_SILVERSNIPER:
- {
- int class_ = 2042;
- struct mob_data *md;
-
- md = mob_once_spawn_sub(src, src->m, x, y, status_get_name(src), class_, "", SZ_SMALL, AI_NONE);
- if( md )
- {
- md->master_id = src->id;
- md->special_state.ai = AI_FLORA;
- if( md->deletetimer != INVALID_TIMER )
- delete_timer(md->deletetimer, mob_timer_delete);
- md->deletetimer = add_timer (gettick() + skill_get_time(skill_id, skill_lv), mob_timer_delete, md->bl.id, 0);
- mob_spawn( md );
- }
- }
- break;
-
- case NC_MAGICDECOY:
- if( sd ) clif_magicdecoy_list(sd,skill_lv,x,y);
- break;
-
- case SC_FEINTBOMB:
- clif_skill_nodamage(src,src,skill_id,skill_lv,1);
- skill_unitsetting(src,skill_id,skill_lv,x,y,0); // Set bomb on current Position
- if( skill_blown(src,src,6,unit_getdir(src),0) )
- skill_castend_nodamage_id(src,src,TF_HIDING,1,tick,0);
- break;
-
- case LG_OVERBRAND:
- {
- int width;//according to data from irowiki it actually is a square
- for( width = 0; width < 7; width++ )
- for( i = 0; i < 7; i++ )
- map_foreachincell(skill_area_sub, src->m, x-2+i, y-2+width, splash_target(src), src, LG_OVERBRAND_BRANDISH, skill_lv, tick, flag|BCT_ENEMY,skill_castend_damage_id);
- for( width = 0; width < 7; width++ )
- for( i = 0; i < 7; i++ )
- map_foreachincell(skill_area_sub, src->m, x-2+i, y-2+width, splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY,skill_castend_damage_id);
- }
- break;
-
- case LG_BANDING:
- if( sc && sc->data[SC_BANDING] )
- status_change_end(src,SC_BANDING,INVALID_TIMER);
- else if( (sg = skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0)) != NULL ) {
- sc_start4(src,SC_BANDING,100,skill_lv,0,0,sg->group_id,skill_get_time(skill_id,skill_lv));
- if( sd ) pc_banding(sd,skill_lv);
- }
- clif_skill_nodamage(src,src,skill_id,skill_lv,1);
- break;
-
- case LG_RAYOFGENESIS:
- if( status_charge(src,status_get_max_hp(src)*3*skill_lv / 100,0) ) {
- i = skill_get_splash(skill_id,skill_lv);
- map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,splash_target(src),
- src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
- } else if( sd )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL,0);
- break;
-
- case WM_DOMINION_IMPULSE:
- i = skill_get_splash(skill_id, skill_lv);
- map_foreachinarea( skill_ative_reverberation,
- src->m, x-i, y-i, x+i,y+i,BL_SKILL);
- break;
-
- case WM_GREAT_ECHO:
- flag|=1; // Should counsume 1 item per skill usage.
- map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY, skill_castend_damage_id);
- break;
- case GN_CRAZYWEED: {
- int area = skill_get_splash(GN_CRAZYWEED_ATK, skill_lv);
- short x1 = 0, y1 = 0;
-
- for( i = 0; i < 3 + (skill_lv/2); i++ ) {
- x1 = x - area + rnd()%(area * 2 + 1);
- y1 = y - area + rnd()%(area * 2 + 1);
- skill_addtimerskill(src,tick+i*150,0,x1,y1,GN_CRAZYWEED_ATK,skill_lv,-1,0);
- }
- }
- break;
- case GN_FIRE_EXPANSION: {
- int i;
- struct unit_data *ud = unit_bl2ud(src);
-
- if( !ud ) break;
-
- for( i = 0; i < MAX_SKILLUNITGROUP && ud->skillunit[i]; i ++ ) {
- if( ud->skillunit[i]->skill_id == GN_DEMONIC_FIRE &&
- distance_xy(x, y, ud->skillunit[i]->unit->bl.x, ud->skillunit[i]->unit->bl.y) < 4 ) {
- switch( skill_lv ) {
- case 3:
- ud->skillunit[i]->unit_id = UNT_FIRE_EXPANSION_SMOKE_POWDER;
- clif_changetraplook(&ud->skillunit[i]->unit->bl, UNT_FIRE_EXPANSION_SMOKE_POWDER);
- break;
- case 4:
- ud->skillunit[i]->unit_id = UNT_FIRE_EXPANSION_TEAR_GAS;
- clif_changetraplook(&ud->skillunit[i]->unit->bl, UNT_FIRE_EXPANSION_TEAR_GAS);
- break;
- case 5:
- map_foreachinarea(skill_area_sub, src->m,
- ud->skillunit[i]->unit->bl.x - 3, ud->skillunit[i]->unit->bl.y - 3,
- ud->skillunit[i]->unit->bl.x + 3, ud->skillunit[i]->unit->bl.y + 3, BL_CHAR,
- src, CR_ACIDDEMONSTRATION, sd ? pc_checkskill(sd, CR_ACIDDEMONSTRATION) : skill_lv, tick, flag|BCT_ENEMY|1|SD_LEVEL, skill_castend_damage_id);
- skill_delunit(ud->skillunit[i]->unit);
- break;
- default:
- ud->skillunit[i]->unit->val2 = skill_lv;
- ud->skillunit[i]->unit->group->val2 = skill_lv;
- break;
- }
- }
- }
- }
- break;
-
- case SO_FIREWALK:
- case SO_ELECTRICWALK:
- if( sc && sc->data[type] )
- status_change_end(src,type,INVALID_TIMER);
- clif_skill_nodamage(src, src ,skill_id, skill_lv,
- sc_start2(src, type, 100, skill_id, skill_lv, skill_get_time(skill_id, skill_lv)));
- break;
-
- case SC_BLOODYLUST: //set in another group so instance will move if recasted
- flag |= 33;
- skill_unitsetting(src, skill_id, skill_lv, x, y, 0);
- break;
-
- case KO_MAKIBISHI:
- for( i = 0; i < (skill_lv+2); i++ ) {
- x = src->x - 1 + rnd()%3;
- y = src->y - 1 + rnd()%3;
- skill_unitsetting(src,skill_id,skill_lv,x,y,0);
- }
- break;
-
- default:
- ShowWarning("skill_castend_pos2: Unknown skill used:%d\n",skill_id);
- return 1;
- }
-
- if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] ) //Should only remove after the skill has been casted.
- status_change_end(src,SC_CURSEDCIRCLE_ATKER,INVALID_TIMER);
-
- if( sd )
- {// ensure that the skill last-cast tick is recorded
- sd->canskill_tick = gettick();
-
- if( sd->state.arrow_atk && !(flag&1) )
- {// consume arrow if this is a ground skill
- battle_consume_ammo(sd, skill_id, skill_lv);
- }
-
- // perform skill requirement consumption
- skill_consume_requirement(sd,skill_id,skill_lv,2);
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_castend_map (struct map_session_data *sd, uint16 skill_id, const char *map)
-{
- nullpo_ret(sd);
-
-//Simplify skill_failed code.
-#define skill_failed(sd) { sd->menuskill_id = sd->menuskill_val = 0; }
- if(skill_id != sd->menuskill_id)
- return 0;
-
- if( sd->bl.prev == NULL || pc_isdead(sd) ) {
- skill_failed(sd);
- return 0;
- }
-
- if( ( sd->sc.opt1 && sd->sc.opt1 != OPT1_BURNING ) || sd->sc.option&OPTION_HIDE ) {
- skill_failed(sd);
- return 0;
- }
- if(sd->sc.count && (
- sd->sc.data[SC_SILENCE] ||
- sd->sc.data[SC_ROKISWEIL] ||
- sd->sc.data[SC_AUTOCOUNTER] ||
- sd->sc.data[SC_STEELBODY] ||
- (sd->sc.data[SC_DANCING] && skill_id < RK_ENCHANTBLADE && !pc_checkskill(sd, WM_LESSON)) ||
- sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] ||
- sd->sc.data[SC_BASILICA] ||
- sd->sc.data[SC_MARIONETTE] ||
- sd->sc.data[SC_WHITEIMPRISON] ||
- (sd->sc.data[SC_STASIS] && skill_block_check(&sd->bl, SC_STASIS, skill_id)) ||
- (sd->sc.data[SC_KAGEHUMI] && skill_block_check(&sd->bl, SC_KAGEHUMI, skill_id)) ||
- sd->sc.data[SC_OBLIVIONCURSE] ||
- sd->sc.data[SC__MANHOLE] ||
- (sd->sc.data[SC_ASH] && rnd()%2) //50% fail chance under ASH
- )) {
- skill_failed(sd);
- return 0;
- }
-
- pc_stop_attack(sd);
- pc_stop_walking(sd,0);
-
- if(battle_config.skill_log && battle_config.skill_log&BL_PC)
- ShowInfo("PC %d skill castend skill =%d map=%s\n",sd->bl.id,skill_id,map);
-
- if(strcmp(map,"cancel")==0) {
- skill_failed(sd);
- return 0;
- }
-
- switch(skill_id)
- {
- case AL_TELEPORT:
- if(strcmp(map,"Random")==0)
- pc_randomwarp(sd,CLR_TELEPORT);
- else if (sd->menuskill_val > 1) //Need lv2 to be able to warp here.
- pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
- break;
-
- case AL_WARP:
- {
- const struct point *p[4];
- struct skill_unit_group *group;
- int i, lv, wx, wy;
- int maxcount=0;
- int x,y;
- unsigned short mapindex;
-
- mapindex = mapindex_name2id((char*)map);
- if(!mapindex) { //Given map not found?
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- skill_failed(sd);
- return 0;
- }
- p[0] = &sd->status.save_point;
- p[1] = &sd->status.memo_point[0];
- p[2] = &sd->status.memo_point[1];
- p[3] = &sd->status.memo_point[2];
-
- if((maxcount = skill_get_maxcount(skill_id, sd->menuskill_val)) > 0) {
- for(i=0;i<MAX_SKILLUNITGROUP && sd->ud.skillunit[i] && maxcount;i++) {
- if(sd->ud.skillunit[i]->skill_id == skill_id)
- maxcount--;
- }
- if(!maxcount) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- skill_failed(sd);
- return 0;
- }
- }
-
- lv = sd->skillitem==skill_id?sd->skillitemlv:pc_checkskill(sd,skill_id);
- wx = sd->menuskill_val>>16;
- wy = sd->menuskill_val&0xffff;
-
- if( lv <= 0 ) return 0;
- if( lv > 4 ) lv = 4; // crash prevention
-
- // check if the chosen map exists in the memo list
- ARR_FIND( 0, lv, i, mapindex == p[i]->map );
- if( i < lv ) {
- x=p[i]->x;
- y=p[i]->y;
- } else {
- skill_failed(sd);
- return 0;
- }
-
- if(!skill_check_condition_castend(sd, sd->menuskill_id, lv))
- { // This checks versus skill_id/skill_lv...
- skill_failed(sd);
- return 0;
- }
-
- skill_consume_requirement(sd,sd->menuskill_id,lv,2);
- sd->skillitem = sd->skillitemlv = 0; // Clear data that's skipped in 'skill_castend_pos' [Inkfish]
-
- if((group=skill_unitsetting(&sd->bl,skill_id,lv,wx,wy,0))==NULL) {
- skill_failed(sd);
- return 0;
- }
-
- group->val1 = (group->val1<<16)|(short)0;
- // record the destination coordinates
- group->val2 = (x<<16)|y;
- group->val3 = mapindex;
- }
- break;
- }
-
- sd->menuskill_id = sd->menuskill_val = 0;
- return 0;
-#undef skill_failed
-}
-
-/// transforms 'target' skill unit into dissonance (if conditions are met)
-static int skill_dance_overlap_sub(struct block_list* bl, va_list ap)
-{
- struct skill_unit* target = (struct skill_unit*)bl;
- struct skill_unit* src = va_arg(ap, struct skill_unit*);
- int flag = va_arg(ap, int);
-
- if (src == target)
- return 0;
- if (!target->group || !(target->group->state.song_dance&0x1))
- return 0;
- if (!(target->val2 & src->val2 & ~UF_ENSEMBLE)) //They don't match (song + dance) is valid.
- return 0;
-
- if (flag) //Set dissonance
- target->val2 |= UF_ENSEMBLE; //Add ensemble to signal this unit is overlapping.
- else //Remove dissonance
- target->val2 &= ~UF_ENSEMBLE;
-
- clif_skill_setunit(target); //Update look of affected cell.
-
- return 1;
-}
-
-//Does the song/dance overlapping -> dissonance check. [Skotlex]
-//When flag is 0, this unit is about to be removed, cancel the dissonance effect
-//When 1, this unit has been positioned, so start the cancel effect.
-int skill_dance_overlap(struct skill_unit* unit, int flag)
-{
- if (!unit || !unit->group || !(unit->group->state.song_dance&0x1))
- return 0;
- if (!flag && !(unit->val2&UF_ENSEMBLE))
- return 0; //Nothing to remove, this unit is not overlapped.
-
- if (unit->val1 != unit->group->skill_id)
- { //Reset state
- unit->val1 = unit->group->skill_id;
- unit->val2 &= ~UF_ENSEMBLE;
- }
-
- return map_foreachincell(skill_dance_overlap_sub, unit->bl.m,unit->bl.x,unit->bl.y,BL_SKILL, unit,flag);
-}
-
-/*==========================================
- * Converts this group information so that it is handled as a Dissonance or Ugly Dance cell.
- * Flag: 0 - Convert, 1 - Revert.
- *------------------------------------------*/
-static bool skill_dance_switch(struct skill_unit* unit, int flag)
-{
- static int prevflag = 1; // by default the backup is empty
- static struct skill_unit_group backup;
- struct skill_unit_group* group = unit->group;
-
- // val2&UF_ENSEMBLE is a hack to indicate dissonance
- if ( !(group->state.song_dance&0x1 && unit->val2&UF_ENSEMBLE) )
- return false;
-
- if( flag == prevflag )
- {// protection against attempts to read an empty backup / write to a full backup
- ShowError("skill_dance_switch: Attempted to %s (skill_id=%d, skill_lv=%d, src_id=%d).\n",
- flag ? "read an empty backup" : "write to a full backup",
- group->skill_id, group->skill_lv, group->src_id);
- return false;
- }
- prevflag = flag;
-
- if( !flag )
- { //Transform
- uint16 skill_id = unit->val2&UF_SONG ? BA_DISSONANCE : DC_UGLYDANCE;
-
- // backup
- backup.skill_id = group->skill_id;
- backup.skill_lv = group->skill_lv;
- backup.unit_id = group->unit_id;
- backup.target_flag = group->target_flag;
- backup.bl_flag = group->bl_flag;
- backup.interval = group->interval;
-
- // replace
- group->skill_id = skill_id;
- group->skill_lv = 1;
- group->unit_id = skill_get_unit_id(skill_id,0);
- group->target_flag = skill_get_unit_target(skill_id);
- group->bl_flag = skill_get_unit_bl_target(skill_id);
- group->interval = skill_get_unit_interval(skill_id);
- }
- else
- { //Restore
- group->skill_id = backup.skill_id;
- group->skill_lv = backup.skill_lv;
- group->unit_id = backup.unit_id;
- group->target_flag = backup.target_flag;
- group->bl_flag = backup.bl_flag;
- group->interval = backup.interval;
- }
-
- return true;
-}
-/**
- * Upon Ice Wall cast it checks all nearby mobs to find any who may be blocked by the IW
- **/
-static int skill_icewall_block(struct block_list *bl,va_list ap) {
- struct block_list *target = NULL;
- struct mob_data *md = ((TBL_MOB*)bl);
-
- nullpo_ret(bl);
- nullpo_ret(md);
- if( !md->target_id || ( target = map_id2bl(md->target_id) ) == NULL )
- return 0;
-
- if( path_search_long(NULL,bl->m,bl->x,bl->y,target->x,target->y,CELL_CHKICEWALL) )
- return 0;
-
- if( !check_distance_bl(bl, target, status_get_range(bl) ) ) {
- mob_unlocktarget(md,gettick());
- mob_stop_walking(md,1);
- }
-
- return 0;
-}
-/*==========================================
- * Initializes and sets a ground skill.
- * flag&1 is used to determine when the skill 'morphs' (Warp portal becomes active, or Fire Pillar becomes active)
- *------------------------------------------*/
-struct skill_unit_group* skill_unitsetting (struct block_list *src, uint16 skill_id, uint16 skill_lv, int16 x, int16 y, int flag)
-{
- struct skill_unit_group *group;
- int i,limit,val1=0,val2=0,val3=0;
- int target,interval,range,unit_flag,req_item=0;
- struct s_skill_unit_layout *layout;
- struct map_session_data *sd;
- struct status_data *status;
- struct status_change *sc;
- int active_flag=1;
- int subunt=0;
-
- nullpo_retr(NULL, src);
-
- limit = skill_get_time(skill_id,skill_lv);
- range = skill_get_unit_range(skill_id,skill_lv);
- interval = skill_get_unit_interval(skill_id);
- target = skill_get_unit_target(skill_id);
- unit_flag = skill_get_unit_flag(skill_id);
- layout = skill_get_unit_layout(skill_id,skill_lv,src,x,y);
-
- sd = BL_CAST(BL_PC, src);
- status = status_get_status_data(src);
- sc = status_get_sc(src); // for traps, firewall and fogwall - celest
-
- switch( skill_id ) {
- case MH_STEINWAND:
- val2 = 4 + skill_lv; //nb of attack blocked
- break;
- case MG_SAFETYWALL:
- #ifdef RENEWAL
- /**
- * According to data provided in RE, SW life is equal to 3 times caster's health
- **/
- val2 = status_get_max_hp(src) * 3;
- #else
- val2 = skill_lv+1;
- #endif
- break;
- case MG_FIREWALL:
- if(sc && sc->data[SC_VIOLENTGALE])
- limit = limit*3/2;
- val2=4+skill_lv;
- break;
-
- case AL_WARP:
- val1=skill_lv+6;
- if(!(flag&1))
- limit=2000;
- else // previous implementation (not used anymore)
- { //Warp Portal morphing to active mode, extract relevant data from src. [Skotlex]
- if( src->type != BL_SKILL ) return NULL;
- group = ((TBL_SKILL*)src)->group;
- src = map_id2bl(group->src_id);
- if( !src ) return NULL;
- val2 = group->val2; //Copy the (x,y) position you warp to
- val3 = group->val3; //as well as the mapindex to warp to.
- }
- break;
- case HP_BASILICA:
- val1 = src->id; // Store caster id.
- break;
-
- case PR_SANCTUARY:
- case NPC_EVILLAND:
- val1=(skill_lv+3)*2;
- break;
-
- case WZ_FIREPILLAR:
- if((flag&1)!=0)
- limit=1000;
- val1=skill_lv+2;
- break;
- case WZ_QUAGMIRE: //The target changes to "all" if used in a gvg map. [Skotlex]
- case AM_DEMONSTRATION:
- case GN_HELLS_PLANT:
- if (map_flag_vs(src->m) && battle_config.vs_traps_bctall
- && (src->type&battle_config.vs_traps_bctall))
- target = BCT_ALL;
- break;
- case HT_SHOCKWAVE:
- val1=skill_lv*15+10;
- case HT_SANDMAN:
- case MA_SANDMAN:
- case HT_CLAYMORETRAP:
- case HT_SKIDTRAP:
- case MA_SKIDTRAP:
- case HT_LANDMINE:
- case MA_LANDMINE:
- case HT_ANKLESNARE:
- case HT_FLASHER:
- case HT_FREEZINGTRAP:
- case MA_FREEZINGTRAP:
- case HT_BLASTMINE:
- /**
- * Ranger
- **/
- case RA_ELECTRICSHOCKER:
- case RA_CLUSTERBOMB:
- case RA_MAGENTATRAP:
- case RA_COBALTTRAP:
- case RA_MAIZETRAP:
- case RA_VERDURETRAP:
- case RA_FIRINGTRAP:
- case RA_ICEBOUNDTRAP:
- {
- struct skill_condition req = skill_get_requirement(sd,skill_id,skill_lv);
- ARR_FIND(0, MAX_SKILL_ITEM_REQUIRE, i, req.itemid[i] && (req.itemid[i] == ITEMID_TRAP || req.itemid[i] == ITEMID_TRAP_ALLOY));
- if( req.itemid[i] )
- req_item = req.itemid[i];
- if( map_flag_gvg(src->m) || map[src->m].flag.battleground )
- limit *= 4; // longer trap times in WOE [celest]
- if( battle_config.vs_traps_bctall && map_flag_vs(src->m) && (src->type&battle_config.vs_traps_bctall) )
- target = BCT_ALL;
- }
- break;
-
- case SA_LANDPROTECTOR:
- case SA_VOLCANO:
- case SA_DELUGE:
- case SA_VIOLENTGALE:
- {
- struct skill_unit_group *old_sg;
- if ((old_sg = skill_locate_element_field(src)) != NULL)
- { //HelloKitty confirmed that these are interchangeable,
- //so you can change element and not consume gemstones.
- if ((
- old_sg->skill_id == SA_VOLCANO ||
- old_sg->skill_id == SA_DELUGE ||
- old_sg->skill_id == SA_VIOLENTGALE
- ) && old_sg->limit > 0)
- { //Use the previous limit (minus the elapsed time) [Skotlex]
- limit = old_sg->limit - DIFF_TICK(gettick(), old_sg->tick);
- if (limit < 0) //This can happen...
- limit = skill_get_time(skill_id,skill_lv);
- }
- skill_clear_group(src,1);
- }
- break;
- }
-
- case BA_DISSONANCE:
- case DC_UGLYDANCE:
- val1 = 10; //FIXME: This value is not used anywhere, what is it for? [Skotlex]
- break;
- case BA_WHISTLE:
- val1 = skill_lv +status->agi/10; // Flee increase
- val2 = ((skill_lv+1)/2)+status->luk/10; // Perfect dodge increase
- if(sd){
- val1 += pc_checkskill(sd,BA_MUSICALLESSON);
- val2 += pc_checkskill(sd,BA_MUSICALLESSON);
- }
- break;
- case DC_HUMMING:
- val1 = 2*skill_lv+status->dex/10; // Hit increase
- #ifdef RENEWAL
- val1 *= 2;
- #endif
- if(sd)
- val1 += pc_checkskill(sd,DC_DANCINGLESSON);
- break;
- case BA_POEMBRAGI:
- val1 = 3*skill_lv+status->dex/10; // Casting time reduction
- //For some reason at level 10 the base delay reduction is 50%.
- val2 = (skill_lv<10?3*skill_lv:50)+status->int_/5; // After-cast delay reduction
- if(sd){
- val1 += 2*pc_checkskill(sd,BA_MUSICALLESSON);
- val2 += 2*pc_checkskill(sd,BA_MUSICALLESSON);
- }
- break;
- case DC_DONTFORGETME:
- val1 = status->dex/10 + 3*skill_lv + 5; // ASPD decrease
- val2 = status->agi/10 + 3*skill_lv + 5; // Movement speed adjustment.
- if(sd){
- val1 += pc_checkskill(sd,DC_DANCINGLESSON);
- val2 += pc_checkskill(sd,DC_DANCINGLESSON);
- }
- break;
- case BA_APPLEIDUN:
- val1 = 5+2*skill_lv+status->vit/10; // MaxHP percent increase
- if(sd)
- val1 += pc_checkskill(sd,BA_MUSICALLESSON);
- break;
- case DC_SERVICEFORYOU:
- val1 = 15+skill_lv+(status->int_/10); // MaxSP percent increase TO-DO: this INT bonus value is guessed
- val2 = 20+3*skill_lv+(status->int_/10); // SP cost reduction
- if(sd){
- val1 += pc_checkskill(sd,DC_DANCINGLESSON); //TO-DO This bonus value is guessed
- val2 += pc_checkskill(sd,DC_DANCINGLESSON); //TO-DO Should be half this value
- }
- break;
- case BA_ASSASSINCROSS:
- val1 = 100+(10*skill_lv)+(status->agi/10); // ASPD increase
- if(sd)
- val1 += 5*pc_checkskill(sd,BA_MUSICALLESSON);
- break;
- case DC_FORTUNEKISS:
- val1 = 10+skill_lv+(status->luk/10); // Critical increase
- if(sd)
- val1 += pc_checkskill(sd,DC_DANCINGLESSON);
- val1*=10; //Because every 10 crit is an actual cri point.
- break;
- case BD_DRUMBATTLEFIELD:
- #ifdef RENEWAL
- val1 = (skill_lv+5)*25; //Watk increase
- val2 = skill_lv*10; //Def increase
- #else
- val1 = (skill_lv+1)*25; //Watk increase
- val2 = (skill_lv+1)*2; //Def increase
- #endif
- break;
- case BD_RINGNIBELUNGEN:
- val1 = (skill_lv+2)*25; //Watk increase
- break;
- case BD_RICHMANKIM:
- val1 = 25 + 11*skill_lv; //Exp increase bonus.
- break;
- case BD_SIEGFRIED:
- val1 = 55 + skill_lv*5; //Elemental Resistance
- val2 = skill_lv*10; //Status ailment resistance
- break;
- case WE_CALLPARTNER:
- if (sd) val1 = sd->status.partner_id;
- break;
- case WE_CALLPARENT:
- if (sd) {
- val1 = sd->status.father;
- val2 = sd->status.mother;
- }
- break;
- case WE_CALLBABY:
- if (sd) val1 = sd->status.child;
- break;
- case NJ_KAENSIN:
- skill_clear_group(src, 1); //Delete previous Kaensins/Suitons
- val2 = (skill_lv+1)/2 + 4;
- break;
- case NJ_SUITON:
- skill_clear_group(src, 1);
- break;
-
- case GS_GROUNDDRIFT:
- {
- int element[5]={ELE_WIND,ELE_DARK,ELE_POISON,ELE_WATER,ELE_FIRE};
-
- val1 = status->rhw.ele;
- if (!val1)
- val1=element[rnd()%5];
-
- switch (val1)
- {
- case ELE_FIRE:
- subunt++;
- case ELE_WATER:
- subunt++;
- case ELE_POISON:
- subunt++;
- case ELE_DARK:
- subunt++;
- case ELE_WIND:
- break;
- default:
- subunt=rnd()%5;
- break;
- }
-
- break;
- }
- case GC_POISONSMOKE:
- if( !(sc && sc->data[SC_POISONINGWEAPON]) )
- return NULL;
- val2 = sc->data[SC_POISONINGWEAPON]->val2; // Type of Poison
- val3 = sc->data[SC_POISONINGWEAPON]->val1;
- limit = 4000 + 2000 * skill_lv;
- break;
- case GD_LEADERSHIP:
- case GD_GLORYWOUNDS:
- case GD_SOULCOLD:
- case GD_HAWKEYES:
- limit = 1000000;//it doesn't matter
- break;
- case LG_BANDING:
- limit = -1;
- break;
- case WM_REVERBERATION:
- interval = limit;
- val2 = 1;
- case WM_POEMOFNETHERWORLD: // Can't be placed on top of Land Protector.
- if( map_getcell(src->m, x, y, CELL_CHKLANDPROTECTOR) )
- return NULL;
- break;
- case SO_CLOUD_KILL:
- skill_clear_group(src, 4);
- break;
- case SO_WARMER:
- skill_clear_group(src, 8);
- break;
- case SO_VACUUM_EXTREME:
- range++;
-
- break;
- case SC_BLOODYLUST:
- skill_clear_group(src, 32);
- break;
- case GN_WALLOFTHORN:
- if( flag&1 )
- limit = 3000;
- val3 = (x<<16)|y;
- break;
- case KO_ZENKAI:
- if( sd ){
- ARR_FIND(1, 6, i, sd->talisman[i] > 0);
- if( i < 5 ){
- val1 = sd->talisman[i]; // no. of aura
- val2 = i; // aura type
- limit += val1 * 1000;
- subunt = i - 1;
- pc_del_talisman(sd, sd->talisman[i], i);
- }
- }
- break;
- }
-
- nullpo_retr(NULL, group=skill_initunitgroup(src,layout->count,skill_id,skill_lv,skill_get_unit_id(skill_id,flag&1)+subunt, limit, interval));
- group->val1=val1;
- group->val2=val2;
- group->val3=val3;
- group->target_flag=target;
- group->bl_flag= skill_get_unit_bl_target(skill_id);
- group->state.ammo_consume = (sd && sd->state.arrow_atk && skill_id != GS_GROUNDDRIFT); //Store if this skill needs to consume ammo.
- group->state.song_dance = (unit_flag&(UF_DANCE|UF_SONG)?1:0)|(unit_flag&UF_ENSEMBLE?2:0); //Signals if this is a song/dance/duet
- group->state.guildaura = ( skill_id >= GD_LEADERSHIP && skill_id <= GD_HAWKEYES )?1:0;
- group->item_id = req_item;
- //if tick is greater than current, do not invoke onplace function just yet. [Skotlex]
- if (DIFF_TICK(group->tick, gettick()) > SKILLUNITTIMER_INTERVAL)
- active_flag = 0;
-
- if(skill_id==HT_TALKIEBOX || skill_id==RG_GRAFFITI){
- group->valstr=(char *) aMalloc(MESSAGE_SIZE*sizeof(char));
- if (sd)
- safestrncpy(group->valstr, sd->message, MESSAGE_SIZE);
- else //Eh... we have to write something here... even though mobs shouldn't use this. [Skotlex]
- safestrncpy(group->valstr, "Boo!", MESSAGE_SIZE);
- }
-
- if (group->state.song_dance) {
- if(sd){
- sd->skill_id_dance = skill_id;
- sd->skill_lv_dance = skill_lv;
- }
- if (
- sc_start4(src, SC_DANCING, 100, skill_id, group->group_id, skill_lv,
- (group->state.song_dance&2?BCT_SELF:0), limit+1000) &&
- sd && group->state.song_dance&2 && skill_id != CG_HERMODE //Hermod is a encore with a warp!
- )
- skill_check_pc_partner(sd, skill_id, &skill_lv, 1, 1);
- }
-
- limit = group->limit;
- for( i = 0; i < layout->count; i++ )
- {
- struct skill_unit *unit;
- int ux = x + layout->dx[i];
- int uy = y + layout->dy[i];
- int val1 = skill_lv;
- int val2 = 0;
- int alive = 1;
-
- if( !group->state.song_dance && !map_getcell(src->m,ux,uy,CELL_CHKREACH) )
- continue; // don't place skill units on walls (except for songs/dances/encores)
- if( battle_config.skill_wall_check && skill_get_unit_flag(skill_id)&UF_PATHCHECK && !path_search_long(NULL,src->m,ux,uy,x,y,CELL_CHKWALL) )
- continue; // no path between cell and center of casting.
-
- switch( skill_id )
- {
- case MG_FIREWALL:
- case NJ_KAENSIN:
- val2=group->val2;
- break;
- case WZ_ICEWALL:
- val1 = (skill_lv <= 1) ? 500 : 200 + 200*skill_lv;
- val2 = map_getcell(src->m, ux, uy, CELL_GETTYPE);
- break;
- case HT_LANDMINE:
- case MA_LANDMINE:
- case HT_ANKLESNARE:
- case HT_SHOCKWAVE:
- case HT_SANDMAN:
- case MA_SANDMAN:
- case HT_FLASHER:
- case HT_FREEZINGTRAP:
- case MA_FREEZINGTRAP:
- case HT_TALKIEBOX:
- case HT_SKIDTRAP:
- case MA_SKIDTRAP:
- case HT_CLAYMORETRAP:
- case HT_BLASTMINE:
- /**
- * Ranger
- **/
- case RA_ELECTRICSHOCKER:
- case RA_CLUSTERBOMB:
- case RA_MAGENTATRAP:
- case RA_COBALTTRAP:
- case RA_MAIZETRAP:
- case RA_VERDURETRAP:
- case RA_FIRINGTRAP:
- case RA_ICEBOUNDTRAP:
- val1 = 3500;
- break;
- case GS_DESPERADO:
- val1 = abs(layout->dx[i]);
- val2 = abs(layout->dy[i]);
- if (val1 < 2 || val2 < 2) { //Nearby cross, linear decrease with no diagonals
- if (val2 > val1) val1 = val2;
- if (val1) val1--;
- val1 = 36 -12*val1;
- } else //Diagonal edges
- val1 = 28 -4*val1 -4*val2;
- if (val1 < 1) val1 = 1;
- val2 = 0;
- break;
- case WM_REVERBERATION:
- val1 = 1 + skill_lv;
- break;
- case GN_WALLOFTHORN:
- val1 = 1000 * skill_lv; // Need official value. [LimitLine]
- break;
- default:
- if (group->state.song_dance&0x1)
- val2 = unit_flag&(UF_DANCE|UF_SONG); //Store whether this is a song/dance
- break;
- }
- if (skill_get_unit_flag(skill_id) & UF_RANGEDSINGLEUNIT && i == (layout->count / 2))
- val2 |= UF_RANGEDSINGLEUNIT; // center.
-
- if( range <= 0 )
- map_foreachincell(skill_cell_overlap,src->m,ux,uy,BL_SKILL,skill_id, &alive, src);
- if( !alive )
- continue;
-
- nullpo_retr(NULL, unit=skill_initunit(group,i,ux,uy,val1,val2));
- unit->limit=limit;
- unit->range=range;
-
- if (skill_id == PF_FOGWALL && alive == 2)
- { //Double duration of cells on top of Deluge/Suiton
- unit->limit *= 2;
- group->limit = unit->limit;
- }
-
- // execute on all targets standing on this cell
- if (range==0 && active_flag)
- map_foreachincell(skill_unit_effect,unit->bl.m,unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),1);
- }
-
- if (!group->alive_count)
- { //No cells? Something that was blocked completely by Land Protector?
- skill_delunitgroup(group);
- return NULL;
- }
-
- //success, unit created.
- switch( skill_id ) {
- case WZ_ICEWALL:
- map_foreachinrange(skill_icewall_block, src, AREA_SIZE, BL_MOB);
- break;
- case NJ_TATAMIGAESHI: //Store number of tiles.
- group->val1 = group->alive_count;
- break;
- }
-
- return group;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-void ext_skill_unit_onplace(struct skill_unit *src, struct block_list *bl, unsigned int tick){skill_unit_onplace(src, bl, tick);}
-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 status_change *sc;
- struct status_change_entry *sce;
- enum sc_type type;
- uint16 skill_id;
-
- nullpo_ret(src);
- nullpo_ret(bl);
-
- if(bl->prev==NULL || !src->alive || status_isdead(bl))
- return 0;
-
- nullpo_ret(sg=src->group);
- nullpo_ret(ss=map_id2bl(sg->src_id));
-
- if( skill_get_type(sg->skill_id) == BF_MAGIC && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR) && sg->skill_id != SA_LANDPROTECTOR )
- return 0; //AoE skills are ineffective. [Skotlex]
-
- sc = status_get_sc(bl);
-
- if (sc && sc->option&OPTION_HIDE && sg->skill_id != WZ_HEAVENDRIVE && sg->skill_id != WL_EARTHSTRAIN )
- return 0; //Hidden characters are immune to AoE skills except to these. [Skotlex]
-
- type = status_skill2sc(sg->skill_id);
- sce = (sc && type != -1)?sc->data[type]:NULL;
- skill_id = sg->skill_id; //In case the group is deleted, we need to return the correct skill id, still.
- switch (sg->unit_id)
- {
- case UNT_SPIDERWEB:
- if( sc && sc->data[SC_SPIDERWEB] && sc->data[SC_SPIDERWEB]->val1 > 0 )
- { // If you are fiberlocked and can't move, it will only increase your fireweakness level. [Inkfish]
- sc->data[SC_SPIDERWEB]->val2++;
- break;
- }
- else if( sc )
- {
- int sec = skill_get_time2(sg->skill_id,sg->skill_lv);
- if( status_change_start(bl,type,10000,sg->skill_lv,1,sg->group_id,0,sec,8) )
- {
- const struct TimerData* td = sc->data[type]?get_timer(sc->data[type]->timer):NULL;
- if( td )
- sec = DIFF_TICK(td->tick, tick);
- map_moveblock(bl, src->bl.x, src->bl.y, tick);
- clif_fixpos(bl);
- sg->val2 = bl->id;
- }
- else
- sec = 3000; //Couldn't trap it?
- sg->limit = DIFF_TICK(tick,sg->tick)+sec;
- }
- break;
- case UNT_SAFETYWALL:
- if (!sce)
- sc_start4(bl,type,100,sg->skill_lv,sg->skill_id,sg->group_id,0,sg->limit);
- break;
-
- case UNT_PNEUMA:
- case UNT_CHAOSPANIC:
- case UNT_MAELSTROM:
- if (!sce)
- sc_start4(bl,type,100,sg->skill_lv,sg->group_id,0,0,sg->limit);
- break;
- case UNT_BLOODYLUST:
- if (sg->src_id == bl->id)
- break; //Does not affect the caster.
- if (!sce) {
- TBL_PC *sd = BL_CAST(BL_PC, bl); //prevent fullheal exploit
- if (sd && sd->bloodylust_tick && DIFF_TICK(gettick(), sd->bloodylust_tick) < skill_get_time2(SC_BLOODYLUST, 1))
- clif_skill_nodamage(&src->bl,bl,sg->skill_id,sg->skill_lv,
- sc_start4(bl, type, 100, sg->skill_lv, 1, 0, 0, skill_get_time(LK_BERSERK, sg->skill_lv)));
- else {
- if (sd) sd->bloodylust_tick = gettick();
- clif_skill_nodamage(&src->bl,bl,sg->skill_id,sg->skill_lv,
- sc_start4(bl, type, 100, sg->skill_lv, 0, 0, 0, skill_get_time(LK_BERSERK, sg->skill_lv)));
- }
- }
- break;
-
- case UNT_WARP_WAITING: {
- int working = sg->val1&0xffff;
-
- if(bl->type==BL_PC && !working){
- struct map_session_data *sd = (struct map_session_data *)bl;
- if((!sd->chatID || battle_config.chat_warpportal)
- && sd->ud.to_x == src->bl.x && sd->ud.to_y == src->bl.y)
- {
- int x = sg->val2>>16;
- int y = sg->val2&0xffff;
- int count = sg->val1>>16;
- unsigned short m = sg->val3;
-
- if( --count <= 0 )
- skill_delunitgroup(sg);
-
- if ( map_mapindex2mapid(sg->val3) == sd->bl.m && x == sd->bl.x && y == sd->bl.y )
- working = 1;/* we break it because officials break it, lovely stuff. */
-
- sg->val1 = (count<<16)|working;
-
- pc_setpos(sd,m,x,y,CLR_TELEPORT);
- }
- } else if(bl->type == BL_MOB && battle_config.mob_warp&2) {
- int16 m = map_mapindex2mapid(sg->val3);
- if (m < 0) break; //Map not available on this map-server.
- unit_warp(bl,m,sg->val2>>16,sg->val2&0xffff,CLR_TELEPORT);
- }
- }
- break;
-
- case UNT_QUAGMIRE:
- if( !sce && battle_check_target(&sg->unit->bl,bl,sg->target_flag) > 0 )
- sc_start4(bl,type,100,sg->skill_lv,sg->group_id,0,0,sg->limit);
- break;
-
- case UNT_VOLCANO:
- case UNT_DELUGE:
- case UNT_VIOLENTGALE:
- if(!sce)
- sc_start(bl,type,100,sg->skill_lv,sg->limit);
- break;
-
- case UNT_SUITON:
- if(!sce)
- sc_start4(bl,type,100,sg->skill_lv,
- map_flag_vs(bl->m) || battle_check_target(&src->bl,bl,BCT_ENEMY)>0?1:0, //Send val3 =1 to reduce agi.
- 0,0,sg->limit);
- break;
-
- case UNT_HERMODE:
- if (sg->src_id!=bl->id && battle_check_target(&src->bl,bl,BCT_PARTY|BCT_GUILD) > 0)
- status_change_clear_buffs(bl,1); //Should dispell only allies.
- case UNT_RICHMANKIM:
- case UNT_ETERNALCHAOS:
- case UNT_DRUMBATTLEFIELD:
- case UNT_RINGNIBELUNGEN:
- case UNT_ROKISWEIL:
- case UNT_INTOABYSS:
- case UNT_SIEGFRIED:
- //Needed to check when a dancer/bard leaves their ensemble area.
- if (sg->src_id==bl->id && !(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_BARDDANCER))
- return skill_id;
- if (!sce)
- sc_start4(bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit);
- break;
- case UNT_WHISTLE:
- case UNT_ASSASSINCROSS:
- case UNT_POEMBRAGI:
- case UNT_APPLEIDUN:
- case UNT_HUMMING:
- case UNT_DONTFORGETME:
- case UNT_FORTUNEKISS:
- case UNT_SERVICEFORYOU:
- if (sg->src_id==bl->id && !(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_BARDDANCER))
- return 0;
-
- if (!sc) return 0;
- if (!sce)
- sc_start4(bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit);
- else if (sce->val4 == 1) {
- //Readjust timers since the effect will not last long.
- sce->val4 = 0;
- delete_timer(sce->timer, status_change_timer);
- sce->timer = add_timer(tick+sg->limit, status_change_timer, bl->id, type);
- }
- break;
-
- case UNT_FOGWALL:
- if (!sce)
- {
- sc_start4(bl, type, 100, sg->skill_lv, sg->val1, sg->val2, sg->group_id, sg->limit);
- if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0)
- skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, BF_MISC, ATK_DEF, tick);
- }
- break;
-
- case UNT_GRAVITATION:
- if (!sce)
- sc_start4(bl,type,100,sg->skill_lv,0,BCT_ENEMY,sg->group_id,sg->limit);
- break;
-
-// officially, icewall has no problems existing on occupied cells [ultramage]
-// case UNT_ICEWALL: //Destroy the cell. [Skotlex]
-// src->val1 = 0;
-// if(src->limit + sg->tick > tick + 700)
-// src->limit = DIFF_TICK(tick+700,sg->tick);
-// break;
-
- case UNT_MOONLIT:
- //Knockback out of area if affected char isn't in Moonlit effect
- if (sc && sc->data[SC_DANCING] && (sc->data[SC_DANCING]->val1&0xFFFF) == CG_MOONLIT)
- break;
- if (ss == bl) //Also needed to prevent infinite loop crash.
- break;
- skill_blown(ss,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),unit_getdir(bl),0);
- break;
-
- case UNT_WALLOFTHORN:
- if( status_get_mode(bl)&MD_BOSS )
- break; // iRO Wiki says that this skill don't affect to Boss monsters.
- if( map_flag_vs(bl->m) || bl->id == src->bl.id || battle_check_target(&src->bl,bl, BCT_ENEMY) == 1 )
- skill_attack(skill_get_type(sg->skill_id), ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
- break;
-
- case UNT_VOLCANIC_ASH:
- if (!sce)
- sc_start(bl, SC_ASH, 100, sg->skill_lv, skill_get_time(MH_VOLCANIC_ASH, sg->skill_lv));
- break;
-
- case UNT_GD_LEADERSHIP:
- case UNT_GD_GLORYWOUNDS:
- case UNT_GD_SOULCOLD:
- case UNT_GD_HAWKEYES:
- if ( !sce )
- sc_start4(bl,type,100,sg->skill_lv,0,0,0,1000);
- break;
- }
- return skill_id;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, unsigned int tick)
-{
- struct skill_unit_group *sg;
- struct block_list *ss;
- TBL_PC* tsd;
- struct status_data *tstatus;
- struct status_change *tsc;
- struct skill_unit_group_tickset *ts;
- enum sc_type type;
- uint16 skill_id;
- int diff=0;
-
- nullpo_ret(src);
- nullpo_ret(bl);
-
- if (bl->prev==NULL || !src->alive || status_isdead(bl))
- return 0;
-
- nullpo_ret(sg=src->group);
- nullpo_ret(ss=map_id2bl(sg->src_id));
- tsd = BL_CAST(BL_PC, bl);
- tsc = status_get_sc(bl);
-
- if ( tsc && tsc->data[SC_HOVERING] )
- return 0; //Under hovering characters are immune to trap and ground target skills.
-
- tstatus = status_get_status_data(bl);
- type = status_skill2sc(sg->skill_id);
- skill_id = sg->skill_id;
-
- if (sg->interval == -1) {
- switch (sg->unit_id) {
- case UNT_ANKLESNARE: //These happen when a trap is splash-triggered by multiple targets on the same cell.
- case UNT_FIREPILLAR_ACTIVE:
- case UNT_ELECTRICSHOCKER:
- case UNT_MANHOLE:
- return 0;
- default:
- ShowError("skill_unit_onplace_timer: interval error (unit id %x)\n", sg->unit_id);
- return 0;
- }
- }
-
- if ((ts = skill_unitgrouptickset_search(bl,sg,tick)))
- { //Not all have it, eg: Traps don't have it even though they can be hit by Heaven's Drive [Skotlex]
- diff = DIFF_TICK(tick,ts->tick);
- if (diff < 0)
- return 0;
- ts->tick = tick+sg->interval;
-
- if ((skill_id==CR_GRANDCROSS || skill_id==NPC_GRANDDARKNESS) && !battle_config.gx_allhit)
- ts->tick += sg->interval*(map_count_oncell(bl->m,bl->x,bl->y,BL_CHAR)-1);
- }
-
- switch (sg->unit_id)
- {
- case UNT_FIREWALL:
- case UNT_KAEN:
- {
- int count=0;
- const int x = bl->x, y = bl->y;
-
- if( sg->skill_id == GN_WALLOFTHORN && !map_flag_vs(bl->m) )
- break;
-
- //Take into account these hit more times than the timer interval can handle.
- do
- skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0);
- while(--src->val2 && x == bl->x && y == bl->y &&
- ++count < SKILLUNITTIMER_INTERVAL/sg->interval && !status_isdead(bl));
-
- if (src->val2<=0)
- skill_delunit(src);
- }
- break;
-
- case UNT_SANCTUARY:
- if( battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race==RC_DEMON )
- { //Only damage enemies with offensive Sanctuary. [Skotlex]
- if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 && skill_attack(BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0) )
- sg->val1 -= 2; // reduce healing count if this was meant for damaging [hekate]
- }
- else
- {
- int heal = skill_calc_heal(ss,bl,sg->skill_id,sg->skill_lv,true);
- struct mob_data *md = BL_CAST(BL_MOB, bl);
-#ifdef RENEWAL
- if( md && md->class_ == MOBID_EMPERIUM )
- break;
-#endif
- if( md && mob_is_battleground(md) )
- break;
- if( tstatus->hp >= tstatus->max_hp )
- break;
- if( status_isimmune(bl) )
- heal = 0;
- clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
- if( tsc && tsc->data[SC_AKAITSUKI] && heal )
- heal = ~heal + 1;
- status_heal(bl, heal, 0, 0);
- if( diff >= 500 )
- sg->val1--;
- }
- if( sg->val1 <= 0 )
- skill_delunitgroup(sg);
- break;
-
- case UNT_EVILLAND:
- //Will heal demon and undead element monsters, but not players.
- if ((bl->type == BL_PC) || (!battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race!=RC_DEMON))
- { //Damage enemies
- if(battle_check_target(&src->bl,bl,BCT_ENEMY)>0)
- skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
- } else {
- int heal = skill_calc_heal(ss,bl,sg->skill_id,sg->skill_lv,true);
- if (tstatus->hp >= tstatus->max_hp)
- break;
- if (status_isimmune(bl))
- heal = 0;
- clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
- status_heal(bl, heal, 0, 0);
- }
- break;
-
- case UNT_MAGNUS:
- if (!battle_check_undead(tstatus->race,tstatus->def_ele) && tstatus->race!=RC_DEMON)
- break;
- skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
- break;
-
- case UNT_DUMMYSKILL:
- switch (sg->skill_id)
- {
- case SG_SUN_WARM: //SG skills [Komurka]
- case SG_MOON_WARM:
- case SG_STAR_WARM:
- {
- int count = 0;
- const int x = bl->x, y = bl->y;
-
- //If target isn't knocked back it should hit every "interval" ms [Playtester]
- do
- {
- if( bl->type == BL_PC )
- status_zap(bl, 0, 15); // sp damage to players
- else // mobs
- if( status_charge(ss, 0, 2) ) // costs 2 SP per hit
- {
- if( !skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0) )
- status_charge(ss, 0, 8); //costs additional 8 SP if miss
- }
- else
- { //should end when out of sp.
- sg->limit = DIFF_TICK(tick,sg->tick);
- break;
- }
- } while( x == bl->x && y == bl->y &&
- ++count < SKILLUNITTIMER_INTERVAL/sg->interval && !status_isdead(bl) );
- }
- break;
- /**
- * The storm gust counter was dropped in renewal
- **/
- #ifndef RENEWAL
- case WZ_STORMGUST: //SG counter does not reset per stormgust. IE: One hit from a SG and two hits from another will freeze you.
- if (tsc)
- tsc->sg_counter++; //SG hit counter.
- if (skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0) <= 0 && tsc)
- tsc->sg_counter=0; //Attack absorbed.
- break;
- #endif
- case GS_DESPERADO:
- if (rnd()%100 < src->val1)
- skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
- break;
- case GN_CRAZYWEED_ATK:
- if( bl->type == BL_SKILL ){
- struct skill_unit *su = (struct skill_unit *)bl;
- if( su && !(skill_get_inf2(su->group->skill_id)&INF2_TRAP) )
- break;
- }
- default:
- skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
- }
- break;
-
- case UNT_FIREPILLAR_WAITING:
- skill_unitsetting(ss,sg->skill_id,sg->skill_lv,src->bl.x,src->bl.y,1);
- skill_delunit(src);
- break;
-
- case UNT_SKIDTRAP:
- {
- skill_blown(&src->bl,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),unit_getdir(bl),0);
- sg->unit_id = UNT_USED_TRAPS;
- clif_changetraplook(&src->bl, UNT_USED_TRAPS);
- sg->limit=DIFF_TICK(tick,sg->tick)+1500;
- }
- break;
-
- case UNT_ANKLESNARE:
- case UNT_MANHOLE:
- if( sg->val2 == 0 && tsc && (sg->unit_id == UNT_ANKLESNARE || bl->id != sg->src_id) ) {
- int sec = skill_get_time2(sg->skill_id,sg->skill_lv);
- if( status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,sec, 8) ) {
- const struct TimerData* td = tsc->data[type]?get_timer(tsc->data[type]->timer):NULL;
- if( td )
- sec = DIFF_TICK(td->tick, tick);
- unit_movepos(bl, src->bl.x, src->bl.y, 0, 0);
- clif_fixpos(bl);
- sg->val2 = bl->id;
- } else
- sec = 3000; //Couldn't trap it?
- if( sg->unit_id == UNT_ANKLESNARE ) {
- clif_skillunit_update(&src->bl);
- /**
- * If you're snared from a trap that was invisible this makes the trap be
- * visible again -- being you stepped on it (w/o this the trap remains invisible and you go "WTF WHY I CANT MOVE")
- * bugreport:3961
- **/
- clif_changetraplook(&src->bl, UNT_ANKLESNARE);
- }
- sg->limit = DIFF_TICK(tick,sg->tick)+sec;
- sg->interval = -1;
- src->range = 0;
- }
- break;
-
- case UNT_ELECTRICSHOCKER:
- if( bl->id != ss->id ) {
- if( status_get_mode(bl)&MD_BOSS )
- break;
- if( status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id, sg->skill_lv), 8) ) {
-
- map_moveblock(bl, src->bl.x, src->bl.y, tick);
- clif_fixpos(bl);
-
- }
-
- map_foreachinrange(skill_trap_splash, &src->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl, tick);
- sg->unit_id = UNT_USED_TRAPS; //Changed ID so it does not invoke a for each in area again.
- }
- break;
-
- case UNT_VENOMDUST:
- if(tsc && !tsc->data[type])
- status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),0);
- break;
-
-
- case UNT_MAGENTATRAP:
- case UNT_COBALTTRAP:
- case UNT_MAIZETRAP:
- case UNT_VERDURETRAP:
- if( bl->type == BL_PC )// it won't work on players
- break;
- case UNT_FIRINGTRAP:
- case UNT_ICEBOUNDTRAP:
- case UNT_CLUSTERBOMB:
- if( bl->id == ss->id )// it won't trigger on caster
- break;
- case UNT_LANDMINE:
- case UNT_CLAYMORETRAP:
- case UNT_BLASTMINE:
- case UNT_SHOCKWAVE:
- case UNT_SANDMAN:
- case UNT_FLASHER:
- case UNT_FREEZINGTRAP:
- case UNT_FIREPILLAR_ACTIVE:
- map_foreachinrange(skill_trap_splash,&src->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl,tick);
- if (sg->unit_id != UNT_FIREPILLAR_ACTIVE)
- clif_changetraplook(&src->bl, sg->unit_id==UNT_LANDMINE?UNT_FIREPILLAR_ACTIVE:UNT_USED_TRAPS);
- sg->limit=DIFF_TICK(tick,sg->tick)+1500 +
- (sg->unit_id== UNT_CLUSTERBOMB || sg->unit_id== UNT_ICEBOUNDTRAP?1000:0);// Cluster Bomb/Icebound has 1s to disappear once activated.
- sg->unit_id = UNT_USED_TRAPS; //Changed ID so it does not invoke a for each in area again.
- break;
-
- case UNT_TALKIEBOX:
- if (sg->src_id == bl->id)
- break;
- if (sg->val2 == 0){
- clif_talkiebox(&src->bl, sg->valstr);
- sg->unit_id = UNT_USED_TRAPS;
- clif_changetraplook(&src->bl, UNT_USED_TRAPS);
- sg->limit = DIFF_TICK(tick, sg->tick) + 5000;
- sg->val2 = -1;
- }
- break;
-
- case UNT_LULLABY:
- if (ss->id == bl->id)
- break;
- skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, ATK_DEF, tick);
- break;
-
- case UNT_UGLYDANCE: //Ugly Dance [Skotlex]
- if (ss->id != bl->id)
- skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, ATK_DEF, tick);
- break;
-
- case UNT_DISSONANCE:
- skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
- break;
-
- case UNT_APPLEIDUN: //Apple of Idun [Skotlex]
- {
- int heal;
-#ifdef RENEWAL
- struct mob_data *md = BL_CAST(BL_MOB, bl);
- if( md && md->class_ == MOBID_EMPERIUM )
- break;
-#endif
- if( sg->src_id == bl->id && !(tsc && tsc->data[SC_SPIRIT] && tsc->data[SC_SPIRIT]->val2 == SL_BARDDANCER) )
- break; // affects self only when soullinked
- heal = skill_calc_heal(ss,bl,sg->skill_id, sg->skill_lv, true);
- if( tsc->data[SC_AKAITSUKI] && heal )
- heal = ~heal + 1;
- clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
- status_heal(bl, heal, 0, 0);
- break;
- }
-
- case UNT_TATAMIGAESHI:
- case UNT_DEMONSTRATION:
- skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
- break;
-
- case UNT_GOSPEL:
- if (rnd()%100 > sg->skill_lv*10 || ss == bl)
- break;
- if (battle_check_target(ss,bl,BCT_PARTY)>0)
- { // Support Effect only on party, not guild
- int heal;
- int i = rnd()%13; // Positive buff count
- int time = skill_get_time2(sg->skill_id, sg->skill_lv); //Duration
- switch (i)
- {
- case 0: // Heal 1~9999 HP
- heal = rnd() %9999+1;
- clif_skill_nodamage(ss,bl,AL_HEAL,heal,1);
- status_heal(bl,heal,0,0);
- break;
- case 1: // End all negative status
- status_change_clear_buffs(bl,6);
- if (tsd) clif_gospel_info(tsd, 0x15);
- break;
- case 2: // Immunity to all status
- sc_start(bl,SC_SCRESIST,100,100,time);
- if (tsd) clif_gospel_info(tsd, 0x16);
- break;
- case 3: // MaxHP +100%
- sc_start(bl,SC_INCMHPRATE,100,100,time);
- if (tsd) clif_gospel_info(tsd, 0x17);
- break;
- case 4: // MaxSP +100%
- sc_start(bl,SC_INCMSPRATE,100,100,time);
- if (tsd) clif_gospel_info(tsd, 0x18);
- break;
- case 5: // All stats +20
- sc_start(bl,SC_INCALLSTATUS,100,20,time);
- if (tsd) clif_gospel_info(tsd, 0x19);
- break;
- case 6: // Level 10 Blessing
- sc_start(bl,SC_BLESSING,100,10,time);
- break;
- case 7: // Level 10 Increase AGI
- sc_start(bl,SC_INCREASEAGI,100,10,time);
- break;
- case 8: // Enchant weapon with Holy element
- sc_start(bl,SC_ASPERSIO,100,1,time);
- if (tsd) clif_gospel_info(tsd, 0x1c);
- break;
- case 9: // Enchant armor with Holy element
- sc_start(bl,SC_BENEDICTIO,100,1,time);
- if (tsd) clif_gospel_info(tsd, 0x1d);
- break;
- case 10: // DEF +25%
- sc_start(bl,SC_INCDEFRATE,100,25,time);
- if (tsd) clif_gospel_info(tsd, 0x1e);
- break;
- case 11: // ATK +100%
- sc_start(bl,SC_INCATKRATE,100,100,time);
- if (tsd) clif_gospel_info(tsd, 0x1f);
- break;
- case 12: // HIT/Flee +50
- sc_start(bl,SC_INCHIT,100,50,time);
- sc_start(bl,SC_INCFLEE,100,50,time);
- if (tsd) clif_gospel_info(tsd, 0x20);
- break;
- }
- }
- else if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0)
- { // Offensive Effect
- int i = rnd()%9; // Negative buff count
- int time = skill_get_time2(sg->skill_id, sg->skill_lv);
- switch (i)
- {
- case 0: // Deal 1~9999 damage
- skill_attack(BF_MISC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
- break;
- case 1: // Curse
- sc_start(bl,SC_CURSE,100,1,time);
- break;
- case 2: // Blind
- sc_start(bl,SC_BLIND,100,1,time);
- break;
- case 3: // Poison
- sc_start(bl,SC_POISON,100,1,time);
- break;
- case 4: // Level 10 Provoke
- sc_start(bl,SC_PROVOKE,100,10,time);
- break;
- case 5: // DEF -100%
- sc_start(bl,SC_INCDEFRATE,100,-100,time);
- break;
- case 6: // ATK -100%
- sc_start(bl,SC_INCATKRATE,100,-100,time);
- break;
- case 7: // Flee -100%
- sc_start(bl,SC_INCFLEERATE,100,-100,time);
- break;
- case 8: // Speed/ASPD -25%
- sc_start4(bl,SC_GOSPEL,100,1,0,0,BCT_ENEMY,time);
- break;
- }
- }
- break;
-
- case UNT_BASILICA:
- {
- int i = battle_check_target(&src->bl, bl, BCT_ENEMY);
- if( i > 0 && !(status_get_mode(bl)&MD_BOSS) )
- { // knock-back any enemy except Boss
- skill_blown(&src->bl, bl, 2, unit_getdir(bl), 0);
- clif_fixpos(bl);
- }
-
- if( sg->src_id != bl->id && i <= 0 )
- sc_start4(bl, type, 100, 0, 0, 0, src->bl.id, sg->interval + 100);
- }
- break;
-
- case UNT_GRAVITATION:
- case UNT_EARTHSTRAIN:
- case UNT_FIREWALK:
- case UNT_ELECTRICWALK:
- case UNT_PSYCHIC_WAVE:
- skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
- break;
-
- case UNT_GROUNDDRIFT_WIND:
- case UNT_GROUNDDRIFT_DARK:
- case UNT_GROUNDDRIFT_POISON:
- case UNT_GROUNDDRIFT_WATER:
- case UNT_GROUNDDRIFT_FIRE:
- map_foreachinrange(skill_trap_splash,&src->bl,
- skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag,
- &src->bl,tick);
- sg->unit_id = UNT_USED_TRAPS;
- //clif_changetraplook(&src->bl, UNT_FIREPILLAR_ACTIVE);
- sg->limit=DIFF_TICK(tick,sg->tick)+1500;
- break;
- /**
- * 3rd stuff
- **/
- case UNT_POISONSMOKE:
- if( battle_check_target(ss,bl,BCT_ENEMY) > 0 && !(tsc && tsc->data[sg->val2]) && rnd()%100 < 20 )
- sc_start(bl,sg->val2,100,sg->val3,skill_get_time2(GC_POISONINGWEAPON, 1));
- break;
-
- case UNT_EPICLESIS:
- if( bl->type == BL_PC && !battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race != RC_DEMON )
- {
- if( ++sg->val2 % 3 == 0 ) {
- int hp, sp;
- switch( sg->skill_lv )
- {
- case 1: case 2: hp = 3; sp = 2; break;
- case 3: case 4: hp = 4; sp = 3; break;
- case 5: default: hp = 5; sp = 4; break;
- }
- hp = tstatus->max_hp * hp / 100;
- sp = tstatus->max_sp * sp / 100;
- status_heal(bl, hp, sp, 2);
- sc_start(bl, type, 100, sg->skill_lv, (sg->interval * 3) + 100);
- }
- // Reveal hidden players every 5 seconds.
- if( sg->val2 % 5 == 0 ) {
- // TODO: check if other hidden status can be removed.
- status_change_end(bl,SC_HIDING,INVALID_TIMER);
- status_change_end(bl,SC_CLOAKING,INVALID_TIMER);
- }
- }
- /* Enable this if kRO fix the current skill. Currently no damage on undead and demon monster. [Jobbie]
- else if( battle_check_target(ss, bl, BCT_ENEMY) > 0 && battle_check_undead(tstatus->race, tstatus->def_ele) )
- skill_castend_damage_id(&src->bl, bl, sg->skill_id, sg->skill_lv, 0, 0);*/
- break;
-
- case UNT_STEALTHFIELD:
- if( bl->id == sg->src_id )
- break; // Dont work on Self (video shows that)
- case UNT_NEUTRALBARRIER:
- sc_start(bl,type,100,sg->skill_lv,sg->interval + 100);
- break;
-
- case UNT_DIMENSIONDOOR:
- if( tsd && !map[bl->m].flag.noteleport )
- pc_randomwarp(tsd,3);
- else if( bl->type == BL_MOB && battle_config.mob_warp&8 )
- unit_warp(bl,-1,-1,-1,3);
- break;
-
- case UNT_REVERBERATION:
- clif_changetraplook(&src->bl,UNT_USED_TRAPS);
- map_foreachinrange(skill_trap_splash,&src->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl,tick);
- sg->limit = DIFF_TICK(tick,sg->tick)+1000;
- sg->unit_id = UNT_USED_TRAPS;
- break;
-
- case UNT_SEVERE_RAINSTORM:
- if( battle_check_target(&src->bl, bl, BCT_ENEMY) )
- skill_attack(BF_WEAPON,ss,&src->bl,bl,WM_SEVERE_RAINSTORM_MELEE,sg->skill_lv,tick,0);
- break;
- case UNT_NETHERWORLD:
- if( !(status_get_mode(bl)&MD_BOSS) && ss != bl && battle_check_target(&src->bl, bl, BCT_PARTY) ) {
- if( !(tsc && tsc->data[type]) ){
- sc_start(bl, type, 100, sg->skill_lv, skill_get_time2(sg->skill_id,sg->skill_lv));
- sg->limit = DIFF_TICK(tick,sg->tick);
- sg->unit_id = UNT_USED_TRAPS;
- }
- }
- break;
- case UNT_THORNS_TRAP:
- if( tsc ) {
- if( !sg->val2 ) {
- int sec = skill_get_time2(sg->skill_id, sg->skill_lv);
- if( sc_start(bl, type, 100, sg->skill_lv, sec) ) {
- const struct TimerData* td = tsc->data[type]?get_timer(tsc->data[type]->timer):NULL;
- if( td )
- sec = DIFF_TICK(td->tick, tick);
- ///map_moveblock(bl, src->bl.x, src->bl.y, tick); // in official server it doesn't behave like this. [malufett]
- clif_fixpos(bl);
- sg->val2 = bl->id;
- } else
- sec = 3000; // Couldn't trap it?
- sg->limit = DIFF_TICK(tick, sg->tick) + sec;
- } else if( tsc->data[SC_THORNSTRAP] && bl->id == sg->val2 )
- skill_attack(skill_get_type(GN_THORNS_TRAP), ss, ss, bl, sg->skill_id, sg->skill_lv, tick, SD_LEVEL|SD_ANIMATION);
- }
- break;
-
- case UNT_DEMONIC_FIRE: {
- TBL_PC* sd = BL_CAST(BL_PC, ss);
- switch( sg->val2 ) {
- case 1:
- case 2:
- default:
- sc_start(bl, SC_BURNING, 4 + 4 * sg->skill_lv, sg->skill_lv,
- skill_get_time2(sg->skill_id, sg->skill_lv));
- skill_attack(skill_get_type(sg->skill_id), ss, &src->bl, bl,
- sg->skill_id, sg->skill_lv + 10 * sg->val2, tick, 0);
- break;
- case 3:
- skill_attack(skill_get_type(CR_ACIDDEMONSTRATION), ss, &src->bl, bl,
- CR_ACIDDEMONSTRATION, sd ? pc_checkskill(sd, CR_ACIDDEMONSTRATION) : sg->skill_lv, tick, 0);
- break;
-
- }
- }
- break;
-
- case UNT_FIRE_EXPANSION_SMOKE_POWDER:
- sc_start(bl, status_skill2sc(GN_FIRE_EXPANSION_SMOKE_POWDER), 100, sg->skill_lv, 1000);
- break;
-
- case UNT_FIRE_EXPANSION_TEAR_GAS:
- sc_start(bl, status_skill2sc(GN_FIRE_EXPANSION_TEAR_GAS), 100, sg->skill_lv, 1000);
- break;
-
- case UNT_HELLS_PLANT:
- if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 )
- skill_attack(skill_get_type(GN_HELLS_PLANT_ATK), ss, &src->bl, bl, GN_HELLS_PLANT_ATK, sg->skill_lv, tick, 0);
- if( ss != bl) //The caster is the only one who can step on the Plants, without destroying them
- sg->limit = DIFF_TICK(tick, sg->tick) + 100;
- break;
-
- case UNT_CLOUD_KILL:
- if(tsc && !tsc->data[type])
- status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),8);
- skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
- break;
-
- case UNT_WARMER:
- if( bl->type == BL_PC && !battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race != RC_DEMON ) {
- int hp = 125 * sg->skill_lv; // Officially is 125 * skill_lv.
- struct status_change *ssc = status_get_sc(ss);
- if( ssc && ssc->data[SC_HEATER_OPTION] )
- hp += hp * ssc->data[SC_HEATER_OPTION]->val3 / 100;
- if( tstatus->hp != tstatus->max_hp )
- clif_skill_nodamage(&src->bl, bl, AL_HEAL, hp, 0);
- if( tsc && tsc->data[SC_AKAITSUKI] && hp )
- hp = ~hp + 1;
- status_heal(bl, hp, 0, 0);
- sc_start(bl, SC_WARMER, 100, sg->skill_lv, skill_get_time2(sg->skill_id,sg->skill_lv));
- }
- break;
-
- case UNT_FIRE_INSIGNIA:
- case UNT_WATER_INSIGNIA:
- case UNT_WIND_INSIGNIA:
- case UNT_EARTH_INSIGNIA:
- case UNT_ZEPHYR:
- sc_start(bl,type, 100, sg->skill_lv, sg->interval);
- if (sg->unit_id != UNT_ZEPHYR && !battle_check_undead(tstatus->race, tstatus->def_ele)) {
- int hp = tstatus->max_hp / 100; //+1% each 5s
- if ((sg->val3) % 5) { //each 5s
- if (tstatus->def_ele == skill_get_ele(sg->skill_id,sg->skill_lv)){
- status_heal(bl, hp, 0, 2);
- } else if((sg->unit_id == UNT_FIRE_INSIGNIA && tstatus->def_ele == ELE_EARTH)
- ||(sg->unit_id == UNT_WATER_INSIGNIA && tstatus->def_ele == ELE_FIRE)
- ||(sg->unit_id == UNT_WIND_INSIGNIA && tstatus->def_ele == ELE_WATER)
- ||(sg->unit_id == UNT_EARTH_INSIGNIA && tstatus->def_ele == ELE_WIND)
- ){
- status_heal(bl, -hp, 0, 0);
- }
- }
- sg->val3++; //timer
- if (sg->val3 > 5) sg->val3 = 0;
- }
- break;
-
- case UNT_VACUUM_EXTREME:
- {// TODO: official behavior in gvg area. [malufett]
- int sec = sg->limit - DIFF_TICK(tick, sg->tick);
- int range = skill_get_unit_range(sg->skill_id, sg->skill_lv);
-
- if( tsc && !tsc->data[type] &&
- distance_xy(src->bl.x, src->bl.y, bl->x, bl->y) <= range)// don't consider outer bounderies
- sc_start(bl, type, 100, sg->skill_lv, sec);
-
- if( unit_is_walking(bl) && // wait until target stop walking
- ( tsc && tsc->data[type] && tsc->data[type]->val4 >= tsc->data[type]->val3-range ))
- break;
-
- if( tsc && ( !tsc->data[type] || (tsc->data[type] && tsc->data[type]->val4 < 1 ) ) )
- break;
-
- if( unit_is_walking(bl) &&
- distance_xy(src->bl.x, src->bl.y, bl->x, bl->y) > range )// going outside of boundaries? then force it to stop
- unit_stop_walking(bl,1);
-
- if( !unit_is_walking(bl) &&
- distance_xy(src->bl.x, src->bl.y, bl->x, bl->y) <= range && // only snap if the target is inside the range or
- src->bl.x != bl->x && src->bl.y != bl->y){// diagonal position parallel to VE's center
- unit_movepos(bl, src->bl.x, src->bl.y, 0, 0);
- clif_fixpos(bl);
- }
- }
- break;
-
- case UNT_FIRE_MANTLE:
- if( battle_check_target(&src->bl, bl, BCT_ENEMY) )
- skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
- break;
-
- case UNT_ZENKAI_WATER:
- case UNT_ZENKAI_LAND:
- case UNT_ZENKAI_FIRE:
- case UNT_ZENKAI_WIND:
- if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 ){
- switch( sg->unit_id ){
- case UNT_ZENKAI_WATER:
- sc_start(bl, SC_CRYSTALIZE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
- sc_start(bl, SC_FREEZE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
- sc_start(bl, SC_FREEZING, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
- break;
- case UNT_ZENKAI_LAND:
- sc_start(bl, SC_STONE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
- sc_start(bl, SC_POISON, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
- break;
- case UNT_ZENKAI_FIRE:
- sc_start(bl, SC_BURNING, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
- break;
- case UNT_ZENKAI_WIND:
- sc_start(bl, SC_SILENCE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
- sc_start(bl, SC_SLEEP, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
- sc_start(bl, SC_DEEPSLEEP, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
- break;
- }
- }else
- sc_start2(bl,type,100,sg->val1,sg->val2,skill_get_time2(sg->skill_id, sg->skill_lv));
- break;
-
- case UNT_MAKIBISHI:
- skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
- sg->limit = DIFF_TICK(tick, sg->tick);
- sg->unit_id = UNT_USED_TRAPS;
- break;
-
- case UNT_LAVA_SLIDE:
- skill_attack(BF_WEAPON, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
- if(++sg->val1 > 4) //after 5 stop hit and destroy me
- sg->limit = DIFF_TICK(tick, sg->tick);
- break;
- case UNT_POISON_MIST:
- skill_attack(BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
- status_change_start(bl, SC_BLIND, rnd() % 100 > sg->skill_lv * 10, sg->skill_lv, sg->skill_id, 0, 0, skill_get_time2(sg->skill_id, sg->skill_lv), 2|8);
- break;
- }
-
- if (bl->type == BL_MOB && ss != bl)
- mobskill_event((TBL_MOB*)bl, ss, tick, MSC_SKILLUSED|(skill_id<<16));
-
- return skill_id;
-}
-/*==========================================
- * Triggered when a char steps out of a skill cell
- *------------------------------------------*/
-int skill_unit_onout (struct skill_unit *src, struct block_list *bl, unsigned int tick)
-{
- struct skill_unit_group *sg;
- struct status_change *sc;
- struct status_change_entry *sce;
- enum sc_type type;
-
- nullpo_ret(src);
- nullpo_ret(bl);
- nullpo_ret(sg=src->group);
- sc = status_get_sc(bl);
- type = status_skill2sc(sg->skill_id);
- sce = (sc && type != -1)?sc->data[type]:NULL;
-
- if( bl->prev==NULL ||
- (status_isdead(bl) && sg->unit_id != UNT_ANKLESNARE && sg->unit_id != UNT_SPIDERWEB) ) //Need to delete the trap if the source died.
- return 0;
-
- switch(sg->unit_id){
- case UNT_SAFETYWALL:
- case UNT_PNEUMA:
- case UNT_EPICLESIS://Arch Bishop
- case UNT_NEUTRALBARRIER:
- case UNT_STEALTHFIELD:
- if (sce)
- status_change_end(bl, type, INVALID_TIMER);
- break;
-
- case UNT_BASILICA:
- if( sce && sce->val4 == src->bl.id )
- status_change_end(bl, type, INVALID_TIMER);
- break;
- case UNT_HERMODE: //Clear Hermode if the owner moved.
- if (sce && sce->val3 == BCT_SELF && sce->val4 == sg->group_id)
- status_change_end(bl, type, INVALID_TIMER);
- break;
-
- case UNT_SPIDERWEB:
- {
- struct block_list *target = map_id2bl(sg->val2);
- if (target && target==bl)
- {
- if (sce && sce->val3 == sg->group_id)
- status_change_end(bl, type, INVALID_TIMER);
- sg->limit = DIFF_TICK(tick,sg->tick)+1000;
- }
- break;
- }
- }
- return sg->skill_id;
-}
-
-/*==========================================
- * Triggered when a char steps out of a skill group (entirely) [Skotlex]
- *------------------------------------------*/
-static int skill_unit_onleft (uint16 skill_id, struct block_list *bl, unsigned int tick)
-{
- struct status_change *sc;
- struct status_change_entry *sce;
- enum sc_type type;
-
- sc = status_get_sc(bl);
- if (sc && !sc->count)
- sc = NULL;
-
- type = status_skill2sc(skill_id);
- sce = (sc && type != -1)?sc->data[type]:NULL;
-
- switch (skill_id)
- {
- case WZ_QUAGMIRE:
- if (bl->type==BL_MOB)
- break;
- if (sce)
- status_change_end(bl, type, INVALID_TIMER);
- 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:
- if(sc && sc->data[SC_DANCING] && (sc->data[SC_DANCING]->val1&0xFFFF) == skill_id)
- { //Check if you just stepped out of your ensemble skill to cancel dancing. [Skotlex]
- //We don't check for SC_LONGING because someone could always have knocked you back and out of the song/dance.
- //FIXME: This code is not perfect, it doesn't checks for the real ensemble's owner,
- //it only checks if you are doing the same ensemble. So if there's two chars doing an ensemble
- //which overlaps, by stepping outside of the other parther's ensemble will cause you to cancel
- //your own. Let's pray that scenario is pretty unlikely and noone will complain too much about it.
- status_change_end(bl, SC_DANCING, INVALID_TIMER);
- }
- case MH_STEINWAND:
- case MG_SAFETYWALL:
- case AL_PNEUMA:
- case SA_VOLCANO:
- case SA_DELUGE:
- case SA_VIOLENTGALE:
- case CG_HERMODE:
- case HW_GRAVITATION:
- case NJ_SUITON:
- case SC_MAELSTROM:
- case EL_WATER_BARRIER:
- case EL_ZEPHYR:
- case EL_POWER_OF_GAIA:
- case SO_FIRE_INSIGNIA:
- case SO_WATER_INSIGNIA:
- case SO_WIND_INSIGNIA:
- case SO_EARTH_INSIGNIA:
- if (sce)
- status_change_end(bl, type, INVALID_TIMER);
- break;
- case SC_BLOODYLUST:
- if (sce) {
- status_change_end(bl, type, INVALID_TIMER);
- status_set_sp(bl, 0, 0); //set sp to 0 when quitting zone
- }
- break;
-
- case BA_POEMBRAGI:
- case BA_WHISTLE:
- case BA_ASSASSINCROSS:
- case BA_APPLEIDUN:
- case DC_HUMMING:
- case DC_DONTFORGETME:
- case DC_FORTUNEKISS:
- case DC_SERVICEFORYOU:
- if (sce)
- {
- delete_timer(sce->timer, status_change_timer);
- //NOTE: It'd be nice if we could get the skill_lv for a more accurate extra time, but alas...
- //not possible on our current implementation.
- sce->val4 = 1; //Store the fact that this is a "reduced" duration effect.
- sce->timer = add_timer(tick+skill_get_time2(skill_id,1), status_change_timer, bl->id, type);
- }
- break;
- case PF_FOGWALL:
- if (sce)
- {
- status_change_end(bl, type, INVALID_TIMER);
- if ((sce=sc->data[SC_BLIND]))
- {
- if (bl->type == BL_PC) //Players get blind ended inmediately, others have it still for 30 secs. [Skotlex]
- status_change_end(bl, SC_BLIND, INVALID_TIMER);
- else {
- delete_timer(sce->timer, status_change_timer);
- sce->timer = add_timer(30000+tick, status_change_timer, bl->id, SC_BLIND);
- }
- }
- }
- break;
- case GD_LEADERSHIP:
- case GD_GLORYWOUNDS:
- case GD_SOULCOLD:
- case GD_HAWKEYES:
- if( !(sce && sce->val4) )
- status_change_end(bl, type, INVALID_TIMER);
- break;
- }
-
- return skill_id;
-}
-
-/*==========================================
- * Invoked when a unit cell has been placed/removed/deleted.
- * flag values:
- * flag&1: Invoke onplace function (otherwise invoke onout)
- * flag&4: Invoke a onleft call (the unit might be scheduled for deletion)
- *------------------------------------------*/
-static int skill_unit_effect (struct block_list* bl, va_list ap)
-{
- struct skill_unit* unit = va_arg(ap,struct skill_unit*);
- struct skill_unit_group* group = unit->group;
- unsigned int tick = va_arg(ap,unsigned int);
- unsigned int flag = va_arg(ap,unsigned int);
- uint16 skill_id;
- bool dissonance;
-
- if( (!unit->alive && !(flag&4)) || bl->prev == NULL )
- return 0;
-
- nullpo_ret(group);
-
- dissonance = skill_dance_switch(unit, 0);
-
- //Necessary in case the group is deleted after calling on_place/on_out [Skotlex]
- skill_id = group->skill_id;
- //Target-type check.
- if( !(group->bl_flag&bl->type && battle_check_target(&unit->bl,bl,group->target_flag)>0) && (flag&4) ) {
- if( group->state.song_dance&0x1 || (group->src_id == bl->id && group->state.song_dance&0x2) )
- skill_unit_onleft(skill_id, bl, tick);//Ensemble check to terminate it.
- } else {
- if( flag&1 )
- skill_unit_onplace(unit,bl,tick);
- else
- skill_unit_onout(unit,bl,tick);
-
- if( flag&4 )
- skill_unit_onleft(skill_id, bl, tick);
- }
-
- if( dissonance ) skill_dance_switch(unit, 1);
-
- 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_ret(src);
- nullpo_ret(sg=src->group);
-
- switch( sg->unit_id ) {
- case UNT_BLASTMINE:
- case UNT_SKIDTRAP:
- case UNT_LANDMINE:
- case UNT_SHOCKWAVE:
- case UNT_SANDMAN:
- case UNT_FLASHER:
- case UNT_CLAYMORETRAP:
- case UNT_FREEZINGTRAP:
- case UNT_TALKIEBOX:
- case UNT_ANKLESNARE:
- case UNT_ICEWALL:
- case UNT_REVERBERATION:
- case UNT_WALLOFTHORN:
- src->val1-=damage;
- break;
- default:
- damage = 0;
- break;
- }
- return damage;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-static int skill_check_condition_char_sub (struct block_list *bl, va_list ap)
-{
- int *c, skill_id;
- struct block_list *src;
- struct map_session_data *sd;
- struct map_session_data *tsd;
- int *p_sd; //Contains the list of characters found.
-
- nullpo_ret(bl);
- nullpo_ret(tsd=(struct map_session_data*)bl);
- nullpo_ret(src=va_arg(ap,struct block_list *));
- nullpo_ret(sd=(struct map_session_data*)src);
-
- c=va_arg(ap,int *);
- p_sd = va_arg(ap, int *);
- skill_id = va_arg(ap,int);
-
- if ( ((skill_id != PR_BENEDICTIO && *c >=1) || *c >=2) && !(skill_get_inf2(skill_id)&INF2_CHORUS_SKILL) )
- return 0; //Partner found for ensembles, or the two companions for Benedictio. [Skotlex]
-
- if (bl == src)
- return 0;
-
- if(pc_isdead(tsd))
- return 0;
-
- if (tsd->sc.data[SC_SILENCE] || ( tsd->sc.opt1 && tsd->sc.opt1 != OPT1_BURNING ))
- return 0;
-
- if( skill_get_inf2(skill_id)&INF2_CHORUS_SKILL ) {
- if( tsd->status.party_id == sd->status.party_id && (tsd->class_&MAPID_THIRDMASK) == MAPID_MINSTRELWANDERER )
- p_sd[(*c)++] = tsd->bl.id;
- return 1;
- } else {
-
- switch(skill_id) {
- case PR_BENEDICTIO: {
- uint8 dir = map_calc_dir(&sd->bl,tsd->bl.x,tsd->bl.y);
- dir = (unit_getdir(&sd->bl) + dir)%8; //This adjusts dir to account for the direction the sd is facing.
- if ((tsd->class_&MAPID_BASEMASK) == MAPID_ACOLYTE && (dir == 2 || dir == 6) //Must be standing to the left/right of Priest.
- && sd->status.sp >= 10)
- p_sd[(*c)++]=tsd->bl.id;
- return 1;
- }
- case AB_ADORAMUS:
- // Adoramus does not consume Blue Gemstone when there is at least 1 Priest class next to the caster
- if( (tsd->class_&MAPID_UPPERMASK) == MAPID_PRIEST )
- p_sd[(*c)++] = tsd->bl.id;
- return 1;
- case WL_COMET:
- // Comet does not consume Red Gemstones when there is at least 1 Warlock class next to the caster
- if( ( sd->class_&MAPID_THIRDMASK ) == MAPID_WARLOCK )
- p_sd[(*c)++] = tsd->bl.id;
- return 1;
- case LG_RAYOFGENESIS:
- if( tsd->status.party_id == sd->status.party_id && (tsd->class_&MAPID_THIRDMASK) == MAPID_ROYAL_GUARD &&
- tsd->sc.data[SC_BANDING] )
- p_sd[(*c)++] = tsd->bl.id;
- return 1;
- default: //Warning: Assuming Ensemble Dance/Songs for code speed. [Skotlex]
- {
- uint16 skill_lv;
- if(pc_issit(tsd) || !unit_can_move(&tsd->bl))
- return 0;
- if (sd->status.sex != tsd->status.sex &&
- (tsd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER &&
- (skill_lv = pc_checkskill(tsd, skill_id)) > 0 &&
- (tsd->weapontype1==W_MUSICAL || tsd->weapontype1==W_WHIP) &&
- sd->status.party_id && tsd->status.party_id &&
- sd->status.party_id == tsd->status.party_id &&
- !tsd->sc.data[SC_DANCING])
- {
- p_sd[(*c)++]=tsd->bl.id;
- return skill_lv;
- } else {
- return 0;
- }
- }
- break;
- }
-
- }
- return 0;
-}
-
-/*==========================================
- * Checks and stores partners for ensemble skills [Skotlex]
- *------------------------------------------*/
-int skill_check_pc_partner (struct map_session_data *sd, uint16 skill_id, short* skill_lv, int range, int cast_flag)
-{
- static int c=0;
- static int p_sd[2] = { 0, 0 };
- int i;
- bool is_chorus = ( skill_get_inf2(skill_id)&INF2_CHORUS_SKILL );
-
- if (!battle_config.player_skill_partner_check || pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL))
- return is_chorus ? MAX_PARTY : 99; //As if there were infinite partners.
-
- if (cast_flag) { //Execute the skill on the partners.
- struct map_session_data* tsd;
- switch (skill_id) {
- case PR_BENEDICTIO:
- for (i = 0; i < c; i++) {
- if ((tsd = map_id2sd(p_sd[i])) != NULL)
- status_charge(&tsd->bl, 0, 10);
- }
- return c;
- case AB_ADORAMUS:
- if( c > 0 && (tsd = map_id2sd(p_sd[0])) != NULL ) {
- i = 2 * (*skill_lv);
- status_charge(&tsd->bl, 0, i);
- }
- break;
- case WM_GREAT_ECHO:
- for( i = 0; i < c; i++ ) {
- if( (tsd = map_id2sd(p_sd[i])) != NULL )
- status_zap(&tsd->bl,0,skill_get_sp(skill_id,*skill_lv)/c);
- }
- break;
- default: //Warning: Assuming Ensemble skills here (for speed)
- if( is_chorus )
- break;//Chorus skills are not to be parsed as ensambles
- if (c > 0 && sd->sc.data[SC_DANCING] && (tsd = map_id2sd(p_sd[0])) != NULL) {
- sd->sc.data[SC_DANCING]->val4 = tsd->bl.id;
- sc_start4(&tsd->bl,SC_DANCING,100,skill_id,sd->sc.data[SC_DANCING]->val2,*skill_lv,sd->bl.id,skill_get_time(skill_id,*skill_lv)+1000);
- clif_skill_nodamage(&tsd->bl, &sd->bl, skill_id, *skill_lv, 1);
- tsd->skill_id_dance = skill_id;
- tsd->skill_lv_dance = *skill_lv;
- }
- return c;
- }
- }
-
- //Else: new search for partners.
- c = 0;
- memset (p_sd, 0, sizeof(p_sd));
- if( is_chorus )
- i = party_foreachsamemap(skill_check_condition_char_sub,sd,AREA_SIZE,&sd->bl, &c, &p_sd, skill_id, *skill_lv);
- else
- i = map_foreachinrange(skill_check_condition_char_sub, &sd->bl, range, BL_PC, &sd->bl, &c, &p_sd, skill_id);
-
- if ( skill_id != PR_BENEDICTIO && skill_id != AB_ADORAMUS && skill_id != WL_COMET ) //Apply the average lv to encore skills.
- *skill_lv = (i+(*skill_lv))/(c+1); //I know c should be one, but this shows how it could be used for the average of n partners.
- return c;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-static int skill_check_condition_mob_master_sub (struct block_list *bl, va_list ap)
-{
- int *c,src_id,mob_class,skill;
- struct mob_data *md;
-
- md=(struct mob_data*)bl;
- src_id=va_arg(ap,int);
- mob_class=va_arg(ap,int);
- skill=va_arg(ap,int);
- c=va_arg(ap,int *);
-
- if( md->master_id != src_id || md->special_state.ai != (unsigned)(skill == AM_SPHEREMINE?2:skill == KO_ZANZOU?4:3) )
- return 0; //Non alchemist summoned mobs have nothing to do here.
-
- if(md->class_==mob_class)
- (*c)++;
-
- return 1;
-}
-
-/*==========================================
- * Determines if a given skill should be made to consume ammo
- * when used by the player. [Skotlex]
- *------------------------------------------*/
-int skill_isammotype (struct map_session_data *sd, int skill)
-{
- return (
- battle_config.arrow_decrement==2 &&
- (sd->status.weapon == W_BOW || (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)) &&
- skill != HT_PHANTASMIC &&
- skill_get_type(skill) == BF_WEAPON &&
- !(skill_get_nk(skill)&NK_NO_DAMAGE) &&
- !skill_get_spiritball(skill,1) //Assume spirit spheres are used as ammo instead.
- );
-}
-
-int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv) {
- struct status_data *status;
- struct status_change *sc;
- struct skill_condition require;
- int i;
-
- nullpo_ret(sd);
-
- if (sd->chatID) return 0;
-
- if( pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->skillitem != skill_id )
- { //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex]
- sd->state.arrow_atk = skill_get_ammotype(skill_id)?1:0; //Need to do arrow state check.
- sd->spiritball_old = sd->spiritball; //Need to do Spiritball check.
- return 1;
- }
-
- switch( sd->menuskill_id ) {
- case AM_PHARMACY:
- switch( skill_id ) {
- case AM_PHARMACY:
- case AC_MAKINGARROW:
- case BS_REPAIRWEAPON:
- case AM_TWILIGHT1:
- case AM_TWILIGHT2:
- case AM_TWILIGHT3:
- return 0;
- }
- break;
- case GN_MIX_COOKING:
- case GN_MAKEBOMB:
- case GN_S_PHARMACY:
- case GN_CHANGEMATERIAL:
- if( sd->menuskill_id != skill_id )
- return 0;
- break;
- }
- status = &sd->battle_status;
- sc = &sd->sc;
- if( !sc->count )
- sc = NULL;
-
- if( sd->skillitem == skill_id )
- {
- if( sd->state.abra_flag ) // Hocus-Pocus was used. [Inkfish]
- sd->state.abra_flag = 0;
- else
- { // When a target was selected, consume items that were skipped in pc_use_item [Skotlex]
- if( (i = sd->itemindex) == -1 ||
- sd->status.inventory[i].nameid != sd->itemid ||
- sd->inventory_data[i] == NULL ||
- !sd->inventory_data[i]->flag.delay_consume ||
- sd->status.inventory[i].amount < 1
- )
- { //Something went wrong, item exploit?
- sd->itemid = sd->itemindex = -1;
- return 0;
- }
- //Consume
- sd->itemid = sd->itemindex = -1;
- if( skill_id == WZ_EARTHSPIKE && sc && sc->data[SC_EARTHSCROLL] && rnd()%100 > sc->data[SC_EARTHSCROLL]->val2 ) // [marquis007]
- ; //Do not consume item.
- else if( sd->status.inventory[i].expire_time == 0 )
- pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME); // Rental usable items are not consumed until expiration
- }
- return 1;
- }
-
- if( pc_is90overweight(sd) )
- {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_WEIGHTOVER,0);
- return 0;
- }
-
- if( sc && ( sc->data[SC__SHADOWFORM] || sc->data[SC__IGNORANCE] ) )
- return 0;
-
- switch( skill_id ) { // Turn off check.
- case BS_MAXIMIZE: case NV_TRICKDEAD: case TF_HIDING: case AS_CLOAKING: case CR_AUTOGUARD:
- case ML_AUTOGUARD: case CR_DEFENDER: case ML_DEFENDER: case ST_CHASEWALK: case PA_GOSPEL:
- case CR_SHRINK: case TK_RUN: case GS_GATLINGFEVER: case TK_READYCOUNTER: case TK_READYDOWN:
- case TK_READYSTORM: case TK_READYTURN: case SG_FUSION: case RA_WUGDASH: case KO_YAMIKUMO:
- if( sc && sc->data[status_skill2sc(skill_id)] )
- return 1;
- }
-
- // Check the skills that can be used while mounted on a warg
- if( pc_isridingwug(sd) ) {
- switch( skill_id ) {
- 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_SPRINGTRAP: case RA_DETONATOR: case RA_CLUSTERBOMB:
- case HT_TALKIEBOX: case RA_FIRINGTRAP: case RA_ICEBOUNDTRAP:
- case RA_WUGDASH: case RA_WUGRIDER: case RA_WUGSTRIKE:
- break;
- default: // in official there is no message.
- return 0;
- }
-
- }
- if( pc_ismadogear(sd) ) {
- switch( skill_id ) { //None Mado skills are unusable when Mado is equipped. [Jobbie]
- case BS_REPAIRWEAPON: case WS_MELTDOWN:
- case BS_HAMMERFALL: case WS_CARTBOOST:
- case BS_ADRENALINE: case WS_WEAPONREFINE:
- case BS_WEAPONPERFECT: case WS_CARTTERMINATION:
- case BS_OVERTHRUST: case WS_OVERTHRUSTMAX:
- case BS_MAXIMIZE: case NC_AXEBOOMERANG:
- case BS_ADRENALINE2: case NC_POWERSWING:
- case BS_UNFAIRLYTRICK: case NC_AXETORNADO:
- case BS_GREED:
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- default: //Only Mechanic exlcusive skill can be used.
- break;
- }
- }
- if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL )
- return 0;
-
- require = skill_get_requirement(sd,skill_id,skill_lv);
-
- //Can only update state when weapon/arrow info is checked.
- sd->state.arrow_atk = require.ammo?1:0;
-
- // perform skill-specific checks (and actions)
- switch( skill_id ) {
- case SO_SPELLFIST:
- if(sd->skill_id_old != MG_FIREBOLT && sd->skill_id_old != MG_COLDBOLT && sd->skill_id_old != MG_LIGHTNINGBOLT){
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- case SA_CASTCANCEL:
- if(sd->ud.skilltimer == INVALID_TIMER) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case AL_WARP:
- if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza]
- char output[128]; sprintf(output, msg_txt(365), skill_get_name(AL_WARP));
- clif_displaymessage(sd->fd, output); //"Duel: Can't use %s in duel."
- return 0;
- }
- break;
- case MO_CALLSPIRITS:
- if(sc && sc->data[SC_RAISINGDRAGON])
- skill_lv += sc->data[SC_RAISINGDRAGON]->val1;
- if(sd->spiritball >= skill_lv) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case MO_FINGEROFFENSIVE:
- case GS_FLING:
- case SR_RAMPAGEBLASTER:
- case SR_RIDEINLIGHTNING:
- if( sd->spiritball > 0 && sd->spiritball < require.spiritball )
- sd->spiritball_old = require.spiritball = sd->spiritball;
- else
- sd->spiritball_old = require.spiritball;
- break;
- case MO_CHAINCOMBO:
- if(!sc)
- return 0;
- if(sc->data[SC_BLADESTOP])
- break;
- if(sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_TRIPLEATTACK)
- break;
- return 0;
- case MO_COMBOFINISH:
- if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_CHAINCOMBO))
- return 0;
- break;
- case CH_TIGERFIST:
- if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_COMBOFINISH))
- return 0;
- break;
- case CH_CHAINCRUSH:
- if(!(sc && sc->data[SC_COMBO]))
- return 0;
- if(sc->data[SC_COMBO]->val1 != MO_COMBOFINISH && sc->data[SC_COMBO]->val1 != CH_TIGERFIST)
- return 0;
- break;
- case MO_EXTREMITYFIST:
- // if(sc && sc->data[SC_EXTREMITYFIST]) //To disable Asura during the 5 min skill block uncomment this...
- // return 0;
- if( sc && (sc->data[SC_BLADESTOP] || sc->data[SC_CURSEDCIRCLE_ATKER]) )
- break;
- if( sc && sc->data[SC_COMBO] )
- {
- switch(sc->data[SC_COMBO]->val1) {
- case MO_COMBOFINISH:
- case CH_TIGERFIST:
- case CH_CHAINCRUSH:
- break;
- default:
- return 0;
- }
- }
- else if( !unit_can_move(&sd->bl) )
- { //Placed here as ST_MOVE_ENABLE should not apply if rooted or on a combo. [Skotlex]
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
-
- case TK_MISSION:
- if( (sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON )
- {// Cannot be used by Non-Taekwon classes
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
-
- case TK_READYCOUNTER:
- case TK_READYDOWN:
- case TK_READYSTORM:
- case TK_READYTURN:
- case TK_JUMPKICK:
- if( (sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER )
- {// Soul Linkers cannot use this skill
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
-
- case TK_TURNKICK:
- case TK_STORMKICK:
- case TK_DOWNKICK:
- case TK_COUNTER:
- if ((sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER)
- return 0; //Anti-Soul Linker check in case you job-changed with Stances active.
- if(!(sc && sc->data[SC_COMBO]) || sc->data[SC_COMBO]->val1 == TK_JUMPKICK)
- return 0; //Combo needs to be ready
-
- if (sc->data[SC_COMBO]->val3) { //Kick chain
- //Do not repeat a kick.
- if (sc->data[SC_COMBO]->val3 != skill_id)
- break;
- status_change_end(&sd->bl, SC_COMBO, INVALID_TIMER);
- return 0;
- }
- if(sc->data[SC_COMBO]->val1 != skill_id && !( sd && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON) )) { //Cancel combo wait.
- unit_cancel_combo(&sd->bl);
- return 0;
- }
- break; //Combo ready.
- case BD_ADAPTATION:
- {
- int time;
- if(!(sc && sc->data[SC_DANCING]))
- {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- time = 1000*(sc->data[SC_DANCING]->val3>>16);
- if (skill_get_time(
- (sc->data[SC_DANCING]->val1&0xFFFF), //Dance Skill ID
- (sc->data[SC_DANCING]->val1>>16)) //Dance Skill LV
- - time < skill_get_time2(skill_id,skill_lv))
- {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- }
- break;
-
- case PR_BENEDICTIO:
- if (skill_check_pc_partner(sd, skill_id, &skill_lv, 1, 0) < 2)
- {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
-
- case SL_SMA:
- if(!(sc && sc->data[SC_SMA]))
- return 0;
- break;
-
- case HT_POWER:
- if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_id))
- return 0;
- break;
-
- case CG_HERMODE:
- if(!npc_check_areanpc(1,sd->bl.m,sd->bl.x,sd->bl.y,skill_get_splash(skill_id, skill_lv)))
- {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case CG_MOONLIT: //Check there's no wall in the range+1 area around the caster. [Skotlex]
- {
- int i,x,y,range = skill_get_splash(skill_id, skill_lv)+1;
- int size = range*2+1;
- for (i=0;i<size*size;i++) {
- x = sd->bl.x+(i%size-range);
- y = sd->bl.y+(i/size-range);
- if (map_getcell(sd->bl.m,x,y,CELL_CHKWALL)) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- }
- }
- break;
- case PR_REDEMPTIO:
- {
- int exp;
- if( ((exp = pc_nextbaseexp(sd)) > 0 && get_percentage(sd->status.base_exp, exp) < 1) ||
- ((exp = pc_nextjobexp(sd)) > 0 && get_percentage(sd->status.job_exp, exp) < 1)) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); //Not enough exp.
- return 0;
- }
- break;
- }
- case AM_TWILIGHT2:
- case AM_TWILIGHT3:
- if (!party_skill_check(sd, sd->status.party_id, skill_id, skill_lv))
- {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case SG_SUN_WARM:
- case SG_MOON_WARM:
- case SG_STAR_WARM:
- if (sc && sc->data[SC_MIRACLE])
- break;
- i = skill_id-SG_SUN_WARM;
- if (sd->bl.m == sd->feel_map[i].m)
- break;
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- break;
- case SG_SUN_COMFORT:
- case SG_MOON_COMFORT:
- case SG_STAR_COMFORT:
- if (sc && sc->data[SC_MIRACLE])
- break;
- i = skill_id-SG_SUN_COMFORT;
- if (sd->bl.m == sd->feel_map[i].m &&
- (battle_config.allow_skill_without_day || sg_info[i].day_func()))
- break;
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- case SG_FUSION:
- if (sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_STAR)
- break;
- //Auron insists we should implement SP consumption when you are not Soul Linked. [Skotlex]
- //Only invoke on skill begin cast (instant cast skill). [Kevin]
- if( require.sp > 0 )
- {
- if (status->sp < (unsigned int)require.sp)
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_SP_INSUFFICIENT,0);
- else
- status_zap(&sd->bl, 0, require.sp);
- }
- return 0;
- case GD_BATTLEORDER:
- case GD_REGENERATION:
- case GD_RESTORE:
- if (!map_flag_gvg2(sd->bl.m)) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- case GD_EMERGENCYCALL:
- // other checks were already done in skillnotok()
- if (!sd->status.guild_id || !sd->state.gmaster_flag)
- return 0;
- break;
-
- case GS_GLITTERING:
- if(sd->spiritball >= 10) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
-
- case NJ_ISSEN:
-#ifdef RENEWAL
- if (status->hp < (status->hp/100)) {
-#else
- if (status->hp < 2) {
-#endif
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- case NJ_BUNSINJYUTSU:
- if (!(sc && sc->data[SC_NEN])) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
-
- case NJ_ZENYNAGE:
- case KO_MUCHANAGE:
- if(sd->status.zeny < require.zeny) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_MONEY,0);
- return 0;
- }
- break;
- case PF_HPCONVERSION:
- if (status->sp == status->max_sp)
- return 0; //Unusable when at full SP.
- break;
- case AM_CALLHOMUN: //Can't summon if a hom is already out
- if (sd->status.hom_id && sd->hd && !sd->hd->homunculus.vaporize) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case AM_REST: //Can't vapo homun if you don't have an active homunc or it's hp is < 80%
- if (!merc_is_hom_active(sd->hd) || sd->hd->battle_status.hp < (sd->hd->battle_status.max_hp*80/100))
- {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- /**
- * Arch Bishop
- **/
- case AB_ANCILLA:
- {
- int count = 0;
- for( i = 0; i < MAX_INVENTORY; i ++ )
- if( sd->status.inventory[i].nameid == ITEMID_ANCILLA )
- count += sd->status.inventory[i].amount;
- if( count >= 3 ) {
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_ANCILLA_NUMOVER, 0);
- return 0;
- }
- }
- break;
- /**
- * Keeping as a note:
- * Bug Report #17 provides a link to a sep-2011 changelog that shows this requirement was removed
- **/
- //case AB_LAUDAAGNUS:
- //case AB_LAUDARAMUS:
- // if( !sd->status.party_id ) {
- // clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- // return 0;
- // }
- // break;
-
- case AB_ADORAMUS:
- /**
- * Warlock
- **/
- case WL_COMET:
- if( skill_check_pc_partner(sd,skill_id,&skill_lv,1,0) <= 0 && ((i = pc_search_inventory(sd,require.itemid[0])) < 0 || sd->status.inventory[i].amount < require.amount[0]) )
- {
- //clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_ITEM,require.amount[0],require.itemid[0]);
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case WL_SUMMONFB:
- case WL_SUMMONBL:
- case WL_SUMMONWB:
- case WL_SUMMONSTONE:
- if( sc )
- {
- ARR_FIND(SC_SPHERE_1,SC_SPHERE_5+1,i,!sc->data[i]);
- if( i == SC_SPHERE_5+1 )
- { // No more free slots
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_SUMMON,0);
- return 0;
- }
- }
- break;
- /**
- * Guilotine Cross
- **/
- case GC_HALLUCINATIONWALK:
- if( sc && (sc->data[SC_HALLUCINATIONWALK] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY]) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case GC_COUNTERSLASH:
- case GC_WEAPONCRUSH:
- if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == GC_WEAPONBLOCKING) ) {
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_GC_WEAPONBLOCKING, 0);
- return 0;
- }
- break;
- /**
- * Ranger
- **/
- case RA_WUGMASTERY:
- if( pc_isfalcon(sd) || pc_isridingwug(sd) || sd->sc.data[SC__GROOMY]) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case RA_WUGSTRIKE:
- if( !pc_iswug(sd) && !pc_isridingwug(sd) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case RA_WUGRIDER:
- if( pc_isfalcon(sd) || ( !pc_isridingwug(sd) && !pc_iswug(sd) ) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case RA_WUGDASH:
- if(!pc_isridingwug(sd)) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- /**
- * Royal Guard
- **/
- case LG_BANDING:
- if( sc && sc->data[SC_INSPIRATION] ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case LG_PRESTIGE:
- if( sc && (sc->data[SC_BANDING] || sc->data[SC_INSPIRATION]) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case LG_RAGEBURST:
- if( sd->spiritball == 0 ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_SKILLINTERVAL,0);
- return 0;
- }
- sd->spiritball_old = require.spiritball = sd->spiritball;
- break;
- case LG_RAYOFGENESIS:
- if( sc && sc->data[SC_INSPIRATION] )
- return 1; // Don't check for partner.
- if( !(sc && sc->data[SC_BANDING]) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL,0);
- return 0;
- } else if( skill_check_pc_partner(sd,skill_id,&skill_lv,skill_get_range(skill_id,skill_lv),0) < 1 )
- return 0; // Just fails, no msg here.
- break;
- case LG_HESPERUSLIT:
- if( !sc || !sc->data[SC_BANDING] ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case SR_FALLENEMPIRE:
- if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == SR_DRAGONCOMBO) )
- return 0;
- break;
-
- case SR_CRESCENTELBOW:
- if( sc && sc->data[SC_CRESCENTELBOW] ) {
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_DUPLICATE, 0);
- return 0;
- }
- break;
- case SR_CURSEDCIRCLE:
- if (map_flag_gvg(sd->bl.m)) {
- if (map_foreachinrange(mob_count_sub, &sd->bl, skill_get_splash(skill_id, skill_lv), BL_MOB,
- MOBID_EMPERIUM, MOBID_GUARIDAN_STONE1, MOBID_GUARIDAN_STONE2)) {
- char output[128];
- sprintf(output, "You're too close to a stone or emperium to do this skill");
- clif_colormes(sd, COLOR_RED, output);
- return 0;
- }
- }
- if( sd->spiritball > 0 )
- sd->spiritball_old = require.spiritball = sd->spiritball;
- else {
- clif_skill_fail(sd,skill_id,0,0);
- return 0;
- }
- break;
- case SR_GATEOFHELL:
- if( sd->spiritball > 0 )
- sd->spiritball_old = require.spiritball;
- break;
- case SC_MANHOLE:
- case SC_DIMENSIONDOOR:
- if( sc && sc->data[SC_MAGNETICFIELD] ) {
- clif_skill_fail(sd,skill_id,0,0);
- return 0;
- }
- break;
- case WM_GREAT_ECHO: {
- int count;
- count = skill_check_pc_partner(sd, skill_id, &skill_lv, skill_get_splash(skill_id,skill_lv), 0);
- if( count < 1 ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_HELPER,0);
- return 0;
- } else
- require.sp -= require.sp * 20 * count / 100; // -20% each W/M in the party.
- }
- break;
- case SO_FIREWALK:
- case SO_ELECTRICWALK: // Can't be casted until you've walked all cells.
- if( sc && sc->data[SC_PROPERTYWALK] &&
- sc->data[SC_PROPERTYWALK]->val3 < skill_get_maxcount(sc->data[SC_PROPERTYWALK]->val1,sc->data[SC_PROPERTYWALK]->val2) ) {
- clif_skill_fail(sd,skill_id,0x0,0);
- return 0;
- }
- break;
- case SO_EL_CONTROL:
- if( !sd->status.ele_id || !sd->ed ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case RETURN_TO_ELDICASTES:
- if( pc_ismadogear(sd) ) { //Cannot be used if Mado is equipped.
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case LG_REFLECTDAMAGE:
- case CR_REFLECTSHIELD:
- if( sc && sc->data[SC_KYOMU] && rand()%100 < 30){
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case KO_KAHU_ENTEN:
- case KO_HYOUHU_HUBUKI:
- case KO_KAZEHU_SEIRAN:
- case KO_DOHU_KOUKAI:
- {
- int ttype = skill_get_ele(skill_id, skill_lv);
- ARR_FIND(1, 5, i, sd->talisman[i] > 0 && i != ttype);
- if( (i < 5 && i != ttype) || sd->talisman[ttype] >= 10 ){
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
- return 0;
- }
- }
- break;
- case KO_KAIHOU:
- case KO_ZENKAI:
- ARR_FIND(1, 6, i, sd->talisman[i] > 0);
- if( i > 4 ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- }
-
- switch(require.state) {
- case ST_HIDING:
- if(!(sc && sc->option&OPTION_HIDE)) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case ST_CLOAKING:
- if(!pc_iscloaking(sd)) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case ST_HIDDEN:
- if(!pc_ishiding(sd)) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case ST_RIDING:
- if(!pc_isriding(sd)) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case ST_FALCON:
- if(!pc_isfalcon(sd)) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case ST_CARTBOOST:
- if(!(sc && sc->data[SC_CARTBOOST])) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- case ST_CART:
- if(!pc_iscarton(sd)) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case ST_SHIELD:
- if(sd->status.shield <= 0) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case ST_SIGHT:
- if(!(sc && sc->data[SC_SIGHT])) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case ST_EXPLOSIONSPIRITS:
- if(!(sc && sc->data[SC_EXPLOSIONSPIRITS])) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case ST_RECOV_WEIGHT_RATE:
- if(battle_config.natural_heal_weight_rate <= 100 && sd->weight*100/sd->max_weight >= (unsigned int)battle_config.natural_heal_weight_rate) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case ST_MOVE_ENABLE:
- if (sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_id)
- sd->ud.canmove_tick = gettick(); //When using a combo, cancel the can't move delay to enable the skill. [Skotlex]
-
- if (!unit_can_move(&sd->bl)) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case ST_WATER:
- if (sc && (sc->data[SC_DELUGE] || sc->data[SC_SUITON]))
- break;
- if (map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKWATER))
- break;
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- /**
- * Rune Knight
- **/
- case ST_RIDINGDRAGON:
- if( !pc_isridingdragon(sd) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- /**
- * Wug
- **/
- case ST_WUG:
- if( !pc_iswug(sd) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- /**
- * Riding Wug
- **/
- case ST_RIDINGWUG:
- if( !pc_isridingwug(sd) ){
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- /**
- * Mechanic
- **/
- case ST_MADO:
- if( !pc_ismadogear(sd) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- /**
- * Sorcerer
- **/
- case ST_ELEMENTALSPIRIT:
- if(!sd->ed) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_EL_SUMMON,0);
- return 0;
- }
- break;
- case ST_POISONINGWEAPON:
- if (!(sc && sc->data[SC_POISONINGWEAPON])) {
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_GC_POISONINGWEAPON, 0);
- return 0;
- }
- break;
- case ST_ROLLINGCUTTER:
- if (!(sc && sc->data[SC_ROLLINGCUTTER])) {
- clif_skill_fail(sd, skill_id, USESKILL_FAIL_CONDITION, 0);
- return 0;
- }
- break;
- case ST_MH_FIGHTING:
- if (!(sc && sc->data[SC_STYLE_CHANGE] && sc->data[SC_STYLE_CHANGE]->val2 == MH_MD_FIGHTING)){
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- case ST_MH_GRAPPLING:
- if (!(sc && sc->data[SC_STYLE_CHANGE] && sc->data[SC_STYLE_CHANGE]->val2 == MH_MD_GRAPPLING)){
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- }
-
- if(require.mhp > 0 && get_percentage(status->hp, status->max_hp) > require.mhp) {
- //mhp is the max-hp-requirement, that is,
- //you must have this % or less of HP to cast it.
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0);
- return 0;
- }
-
- if( require.weapon && !pc_check_weapontype(sd,require.weapon) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0);
- return 0;
- }
-
- if( require.sp > 0 && status->sp < (unsigned int)require.sp) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_SP_INSUFFICIENT,0);
- return 0;
- }
-
- if( require.zeny > 0 && sd->status.zeny < require.zeny ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_MONEY,0);
- return 0;
- }
-
- if( require.spiritball > 0 && sd->spiritball < require.spiritball) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_SPIRITS,require.spiritball);
- return 0;
- }
-
- return 1;
-}
-
-int skill_check_condition_castend(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv)
-{
- struct skill_condition require;
- struct status_data *status;
- int i;
- int index[MAX_SKILL_ITEM_REQUIRE];
-
- nullpo_ret(sd);
-
- if( sd->chatID )
- return 0;
-
- if( pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->skillitem != skill_id ) {
- //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex]
- sd->state.arrow_atk = skill_get_ammotype(skill_id)?1:0; //Need to do arrow state check.
- sd->spiritball_old = sd->spiritball; //Need to do Spiritball check.
- return 1;
- }
-
- switch( sd->menuskill_id ) { // Cast start or cast end??
- case AM_PHARMACY:
- switch( skill_id ) {
- case AM_PHARMACY:
- case AC_MAKINGARROW:
- case BS_REPAIRWEAPON:
- case AM_TWILIGHT1:
- case AM_TWILIGHT2:
- case AM_TWILIGHT3:
- return 0;
- }
- break;
- case GN_MIX_COOKING:
- case GN_MAKEBOMB:
- case GN_S_PHARMACY:
- case GN_CHANGEMATERIAL:
- if( sd->menuskill_id != skill_id )
- return 0;
- break;
- }
-
- if( sd->skillitem == skill_id ) // Casting finished (Item skill or Hocus-Pocus)
- return 1;
-
- if( pc_is90overweight(sd) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_WEIGHTOVER,0);
- return 0;
- }
-
- // perform skill-specific checks (and actions)
- switch( skill_id ) {
- case PR_BENEDICTIO:
- skill_check_pc_partner(sd, skill_id, &skill_lv, 1, 1);
- break;
- case AM_CANNIBALIZE:
- case AM_SPHEREMINE: {
- int c=0;
- int summons[5] = { 1589, 1579, 1575, 1555, 1590 };
- //int summons[5] = { 1020, 1068, 1118, 1500, 1368 };
- int maxcount = (skill_id==AM_CANNIBALIZE)? 6-skill_lv : skill_get_maxcount(skill_id,skill_lv);
- int mob_class = (skill_id==AM_CANNIBALIZE)? summons[skill_lv-1] :1142;
- if(battle_config.land_skill_limit && maxcount>0 && (battle_config.land_skill_limit&BL_PC)) {
- i = map_foreachinmap(skill_check_condition_mob_master_sub ,sd->bl.m, BL_MOB, sd->bl.id, mob_class, skill_id, &c);
- if(c >= maxcount ||
- (skill_id==AM_CANNIBALIZE && c != i && battle_config.summon_flora&2))
- { //Fails when: exceed max limit. There are other plant types already out.
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- }
- break;
- }
- case NC_SILVERSNIPER:
- case NC_MAGICDECOY: {
- int c = 0, j;
- int maxcount = skill_get_maxcount(skill_id,skill_lv);
- int mob_class = 2042;
- if( skill_id == NC_MAGICDECOY )
- mob_class = 2043;
-
- if( battle_config.land_skill_limit && maxcount > 0 && ( battle_config.land_skill_limit&BL_PC ) ) {
- if( skill_id == NC_MAGICDECOY ) {
- for( j = mob_class; j <= 2046; j++ )
- map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, j, skill_id, &c);
- } else
- map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, mob_class, skill_id, &c);
- if( c >= maxcount ) {
- clif_skill_fail(sd , skill_id, USESKILL_FAIL_LEVEL, 0);
- return 0;
- }
- }
- }
- break;
- case KO_ZANZOU: {
- int c = 0;
- i = map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, 2308, skill_id, &c);
- if( c >= skill_get_maxcount(skill_id,skill_lv) || c != i)
- {
- clif_skill_fail(sd , skill_id, USESKILL_FAIL_LEVEL, 0);
- return 0;
- }
- }
- break;
- }
-
- status = &sd->battle_status;
-
- require = skill_get_requirement(sd,skill_id,skill_lv);
-
- if( require.hp > 0 && status->hp <= (unsigned int)require.hp) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0);
- return 0;
- }
-
- if( require.weapon && !pc_check_weapontype(sd,require.weapon) ) {
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0);
- return 0;
- }
-
- if( require.ammo ) { //Skill requires stuff equipped in the arrow slot.
- if((i=sd->equip_index[EQI_AMMO]) < 0 || !sd->inventory_data[i] ) {
- clif_arrow_fail(sd,0);
- return 0;
- } else if( sd->status.inventory[i].amount < require.ammo_qty ) {
- char e_msg[100];
- sprintf(e_msg,"Skill Failed. [%s] requires %dx %s.",
- skill_get_desc(skill_id),
- require.ammo_qty,
- itemdb_jname(sd->status.inventory[i].nameid));
- clif_colormes(sd,COLOR_RED,e_msg);
- return 0;
- }
- if (!(require.ammo&1<<sd->inventory_data[i]->look)) { //Ammo type check. Send the "wrong weapon type" message
- //which is the closest we have to wrong ammo type. [Skotlex]
- clif_arrow_fail(sd,0); //Haplo suggested we just send the equip-arrows message instead. [Skotlex]
- //clif_skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0);
- return 0;
- }
- }
-
- for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; ++i ) {
- if( !require.itemid[i] )
- continue;
- index[i] = pc_search_inventory(sd,require.itemid[i]);
- if( index[i] < 0 || sd->status.inventory[index[i]].amount < require.amount[i] ) {
- if( require.itemid[i] == ITEMID_RED_GEMSTONE )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_REDJAMSTONE,0);// red gemstone required
- else if( require.itemid[i] == ITEMID_BLUE_GEMSTONE )
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_BLUEJAMSTONE,0);// blue gemstone required
- else
- clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- }
-
- return 1;
-}
-
-// type&2: consume items (after skill was used)
-// type&1: consume the others (before skill was used)
-int skill_consume_requirement( struct map_session_data *sd, uint16 skill_id, uint16 skill_lv, short type)
-{
- struct skill_condition req;
-
- nullpo_ret(sd);
-
- req = skill_get_requirement(sd,skill_id,skill_lv);
-
- if( type&1 )
- {
- if( skill_id == CG_TAROTCARD || sd->state.autocast )
- req.sp = 0; // TarotCard will consume sp in skill_cast_nodamage_id [Inkfish]
- if(req.hp || req.sp)
- status_zap(&sd->bl, req.hp, req.sp);
-
- if(req.spiritball > 0)
- pc_delspiritball(sd,req.spiritball,0);
-
- if(req.zeny > 0)
- {
- if( skill_id == NJ_ZENYNAGE )
- req.zeny = 0; //Zeny is reduced on skill_attack.
- if( sd->status.zeny < req.zeny )
- req.zeny = sd->status.zeny;
- pc_payzeny(sd,req.zeny,LOG_TYPE_CONSUME,NULL);
- }
- }
-
- if( type&2 )
- {
- struct status_change *sc = &sd->sc;
- int n,i;
-
- if( !sc->count )
- sc = NULL;
-
- for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; ++i )
- {
- if( !req.itemid[i] )
- continue;
-
- if( itemid_isgemstone(req.itemid[i]) && skill_id != HW_GANBANTEIN && sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_WIZARD )
- continue; //Gemstones are checked, but not substracted from inventory.
-
- switch( skill_id ){
- case SA_SEISMICWEAPON:
- if( sc && sc->data[SC_UPHEAVAL_OPTION] && rnd()%100 < 50 )
- continue;
- break;
- case SA_FLAMELAUNCHER:
- case SA_VOLCANO:
- if( sc && sc->data[SC_TROPIC_OPTION] && rnd()%100 < 50 )
- continue;
- break;
- case SA_FROSTWEAPON:
- case SA_DELUGE:
- if( sc && sc->data[SC_CHILLY_AIR_OPTION] && rnd()%100 < 50 )
- continue;
- break;
- case SA_LIGHTNINGLOADER:
- case SA_VIOLENTGALE:
- if( sc && sc->data[SC_WILD_STORM_OPTION] && rnd()%100 < 50 )
- continue;
- break;
- }
-
- if( (n = pc_search_inventory(sd,req.itemid[i])) >= 0 )
- pc_delitem(sd,n,req.amount[i],0,1,LOG_TYPE_CONSUME);
- }
- }
-
- return 1;
-}
-
-struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv)
-{
- struct skill_condition req;
- struct status_data *status;
- struct status_change *sc;
- int i,hp_rate,sp_rate, sp_skill_rate_bonus = 100;
- uint16 idx;
-
- memset(&req,0,sizeof(req));
-
- if( !sd )
- return req;
-
- if( sd->skillitem == skill_id )
- return req; // Item skills and Hocus-Pocus don't have requirements.[Inkfish]
-
- sc = &sd->sc;
- if( !sc->count )
- sc = NULL;
-
- switch( skill_id )
- { // Turn off check.
- case BS_MAXIMIZE: case NV_TRICKDEAD: case TF_HIDING: case AS_CLOAKING: case CR_AUTOGUARD:
- case ML_AUTOGUARD: case CR_DEFENDER: case ML_DEFENDER: case ST_CHASEWALK: case PA_GOSPEL:
- case CR_SHRINK: case TK_RUN: case GS_GATLINGFEVER: case TK_READYCOUNTER: case TK_READYDOWN:
- case TK_READYSTORM: case TK_READYTURN: case SG_FUSION: case KO_YAMIKUMO:
- if( sc && sc->data[status_skill2sc(skill_id)] )
- return req;
- }
-
- idx = skill_get_index(skill_id);
- if( idx == 0 ) // invalid skill id
- return req;
- if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL )
- return req;
-
- status = &sd->battle_status;
-
- req.hp = skill_db[idx].hp[skill_lv-1];
- hp_rate = skill_db[idx].hp_rate[skill_lv-1];
- if(hp_rate > 0)
- req.hp += (status->hp * hp_rate)/100;
- else
- req.hp += (status->max_hp * (-hp_rate))/100;
-
- req.sp = skill_db[idx].sp[skill_lv-1];
- if((sd->skill_id_old == BD_ENCORE) && skill_id == sd->skill_id_dance)
- req.sp /= 2;
- sp_rate = skill_db[idx].sp_rate[skill_lv-1];
- if(sp_rate > 0)
- req.sp += (status->sp * sp_rate)/100;
- else
- req.sp += (status->max_sp * (-sp_rate))/100;
- if( sd->dsprate != 100 )
- req.sp = req.sp * sd->dsprate / 100;
-
- ARR_FIND(0, ARRAYLENGTH(sd->skillusesprate), i, sd->skillusesprate[i].id == skill_id);
- if( i < ARRAYLENGTH(sd->skillusesprate) )
- sp_skill_rate_bonus += sd->skillusesprate[i].val;
- ARR_FIND(0, ARRAYLENGTH(sd->skillusesp), i, sd->skillusesp[i].id == skill_id);
- if( i < ARRAYLENGTH(sd->skillusesp) )
- req.sp -= sd->skillusesp[i].val;
-
- req.sp = cap_value(req.sp * sp_skill_rate_bonus / 100, 0, SHRT_MAX);
-
- if( sc ) {
- if( sc->data[SC__LAZINESS] )
- req.sp += req.sp + sc->data[SC__LAZINESS]->val1 * 10;
- if (sc->data[SC_UNLIMITEDHUMMINGVOICE])
- req.sp += req.sp * sc->data[SC_UNLIMITEDHUMMINGVOICE]->val2 / 100;
- if( sc->data[SC_RECOGNIZEDSPELL] )
- req.sp += req.sp / 4;
- }
-
- req.zeny = skill_db[idx].zeny[skill_lv-1];
-
- if( sc && sc->data[SC__UNLUCKY] )
- req.zeny += sc->data[SC__UNLUCKY]->val1 * 500;
-
- req.spiritball = skill_db[idx].spiritball[skill_lv-1];
-
- req.state = skill_db[idx].state;
-
- req.mhp = skill_db[idx].mhp[skill_lv-1];
-
- req.weapon = skill_db[idx].weapon;
-
- req.ammo_qty = skill_db[idx].ammo_qty[skill_lv-1];
- if (req.ammo_qty)
- req.ammo = skill_db[idx].ammo;
-
- if (!req.ammo && skill_id && skill_isammotype(sd, skill_id))
- { //Assume this skill is using the weapon, therefore it requires arrows.
- req.ammo = 0xFFFFFFFF; //Enable use on all ammo types.
- req.ammo_qty = 1;
- }
-
- for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++ ) {
- if( (skill_id == AM_POTIONPITCHER || skill_id == CR_SLIMPITCHER || skill_id == CR_CULTIVATION) && i != skill_lv%11 - 1 )
- continue;
-
- switch( skill_id ) {
- case AM_CALLHOMUN:
- if (sd->status.hom_id) //Don't delete items when hom is already out.
- continue;
- break;
- case NC_SHAPESHIFT:
- if( i < 4 )
- continue;
- break;
- case WZ_FIREPILLAR: // celest
- if (skill_lv <= 5) // no gems required at level 1-5
- continue;
- break;
- case AB_ADORAMUS:
- if( itemid_isgemstone(skill_db[idx].itemid[i]) && skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 2) )
- continue;
- break;
- case WL_COMET:
- if( itemid_isgemstone(skill_db[idx].itemid[i]) && skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 0) )
- continue;
- break;
- case GN_FIRE_EXPANSION:
- if( i < 5 )
- continue;
- break;
- case SO_SUMMON_AGNI:
- case SO_SUMMON_AQUA:
- case SO_SUMMON_VENTUS:
- case SO_SUMMON_TERA:
- case SO_WATER_INSIGNIA:
- case SO_FIRE_INSIGNIA:
- case SO_WIND_INSIGNIA:
- case SO_EARTH_INSIGNIA:
- if( i < 3 )
- continue;
- break;
- }
-
- req.itemid[i] = skill_db[idx].itemid[i];
- req.amount[i] = skill_db[idx].amount[i];
-
- if( itemid_isgemstone(req.itemid[i]) && skill_id != HW_GANBANTEIN )
- {
- if( sd->special_state.no_gemstone )
- { //Make it substract 1 gem rather than skipping the cost.
- if( --req.amount[i] < 1 )
- req.itemid[i] = 0;
- }
- if(sc && sc->data[SC_INTOABYSS])
- {
- if( skill_id != SA_ABRACADABRA )
- req.itemid[i] = req.amount[i] = 0;
- else if( --req.amount[i] < 1 )
- req.amount[i] = 1; // Hocus Pocus allways use at least 1 gem
- }
- }
- if( skill_id >= HT_SKIDTRAP && skill_id <= HT_TALKIEBOX && pc_checkskill(sd, RA_RESEARCHTRAP) > 0){
- int16 itIndex;
- if( (itIndex = pc_search_inventory(sd,req.itemid[i])) < 0 || ( itIndex >= 0 && sd->status.inventory[itIndex].amount < req.amount[i] ) ){
- req.itemid[i] = ITEMID_TRAP_ALLOY;
- req.amount[i] = 1;
- }
- break;
- }
- }
-
- /* requirements are level-dependent */
- switch( skill_id ) {
- case NC_SHAPESHIFT:
- case GN_FIRE_EXPANSION:
- case SO_SUMMON_AGNI:
- case SO_SUMMON_AQUA:
- case SO_SUMMON_VENTUS:
- case SO_SUMMON_TERA:
- case SO_WATER_INSIGNIA:
- case SO_FIRE_INSIGNIA:
- case SO_WIND_INSIGNIA:
- case SO_EARTH_INSIGNIA:
- req.itemid[skill_lv-1] = skill_db[idx].itemid[skill_lv-1];
- req.amount[skill_lv-1] = skill_db[idx].amount[skill_lv-1];
- break;
- }
-
- // Check for cost reductions due to skills & SCs
- switch(skill_id) {
- case MC_MAMMONITE:
- if(pc_checkskill(sd,BS_UNFAIRLYTRICK)>0)
- req.zeny -= req.zeny*10/100;
- break;
- case AL_HOLYLIGHT:
- if(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_PRIEST)
- req.sp *= 5;
- break;
- case SL_SMA:
- case SL_STUN:
- case SL_STIN:
- {
- int kaina_lv = pc_checkskill(sd,SL_KAINA);
-
- if(kaina_lv==0 || sd->status.base_level<70)
- break;
- if(sd->status.base_level>=90)
- req.sp -= req.sp*7*kaina_lv/100;
- else if(sd->status.base_level>=80)
- req.sp -= req.sp*5*kaina_lv/100;
- else if(sd->status.base_level>=70)
- req.sp -= req.sp*3*kaina_lv/100;
- }
- break;
- case MO_TRIPLEATTACK:
- case MO_CHAINCOMBO:
- case MO_COMBOFINISH:
- case CH_TIGERFIST:
- case CH_CHAINCRUSH:
- if(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_MONK)
- req.sp -= req.sp*25/100; //FIXME: Need real data. this is a custom value.
- break;
- case MO_BODYRELOCATION:
- if( sc && sc->data[SC_EXPLOSIONSPIRITS] )
- req.spiritball = 0;
- break;
- case MO_EXTREMITYFIST:
- if( sc )
- {
- if( sc->data[SC_BLADESTOP] )
- req.spiritball--;
- else if( sc->data[SC_COMBO] )
- {
- switch( sc->data[SC_COMBO]->val1 )
- {
- case MO_COMBOFINISH:
- req.spiritball = 4;
- break;
- case CH_TIGERFIST:
- req.spiritball = 3;
- break;
- case CH_CHAINCRUSH: //It should consume whatever is left as long as it's at least 1.
- req.spiritball = sd->spiritball?sd->spiritball:1;
- break;
- }
- }else if( sc->data[SC_RAISINGDRAGON] && sd->spiritball > 5)
- req.spiritball = sd->spiritball; // must consume all regardless of the amount required
- }
- break;
- case SR_RAMPAGEBLASTER:
- req.spiritball = sd->spiritball?sd->spiritball:15;
- break;
- case SR_GATEOFHELL:
- if( sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == SR_FALLENEMPIRE )
- req.sp -= req.sp * 10 / 100;
- break;
- case SO_SUMMON_AGNI:
- case SO_SUMMON_AQUA:
- case SO_SUMMON_VENTUS:
- case SO_SUMMON_TERA:
- req.sp -= req.sp * (5 + 5 * pc_checkskill(sd,SO_EL_SYMPATHY)) / 100;
- break;
- case SO_PSYCHIC_WAVE:
- if( sc && sc->data[SC_BLAST_OPTION] )
- req.sp += req.sp * 150 / 100;
- break;
- }
-
- return req;
-}
-
-/*==========================================
- * Does cast-time reductions based on dex, item bonuses and config setting
- *------------------------------------------*/
-int skill_castfix (struct block_list *bl, uint16 skill_id, uint16 skill_lv) {
- int time = skill_get_cast(skill_id, skill_lv);
-
- nullpo_ret(bl);
-#ifndef RENEWAL_CAST
- {
- struct map_session_data *sd;
-
- sd = BL_CAST(BL_PC, bl);
-
- // calculate base cast time (reduced by dex)
- if( !(skill_get_castnodex(skill_id, skill_lv)&1) ) {
- int scale = battle_config.castrate_dex_scale - status_get_dex(bl);
- if( scale > 0 ) // not instant cast
- time = time * scale / battle_config.castrate_dex_scale;
- else
- return 0; // instant cast
- }
-
- // calculate cast time reduced by item/card bonuses
- if( !(skill_get_castnodex(skill_id, skill_lv)&4) && sd )
- {
- int i;
- if( sd->castrate != 100 )
- time = time * sd->castrate / 100;
- for( i = 0; i < ARRAYLENGTH(sd->skillcast) && sd->skillcast[i].id; i++ )
- {
- if( sd->skillcast[i].id == skill_id )
- {
- time+= time * sd->skillcast[i].val / 100;
- break;
- }
- }
- }
-
- }
-#endif
- // config cast time multiplier
- if (battle_config.cast_rate != 100)
- time = time * battle_config.cast_rate / 100;
- // return final cast time
- time = max(time, 0);
-
-// ShowInfo("Castime castfix = %d\n",time);
- return time;
-}
-
-/*==========================================
- * Does cast-time reductions based on sc data.
- *------------------------------------------*/
-int skill_castfix_sc (struct block_list *bl, int time)
-{
- struct status_change *sc = status_get_sc(bl);
-
- if( time < 0 )
- return 0;
-
- if (sc && sc->count) {
- if (sc->data[SC_SLOWCAST])
- time += time * sc->data[SC_SLOWCAST]->val2 / 100;
- if (sc->data[SC_PARALYSIS])
- time += sc->data[SC_PARALYSIS]->val3;
- if (sc->data[SC_SUFFRAGIUM]) {
- time -= time * sc->data[SC_SUFFRAGIUM]->val2 / 100;
- status_change_end(bl, SC_SUFFRAGIUM, INVALID_TIMER);
- }
- if (sc->data[SC_MEMORIZE]) {
- time>>=1;
- if ((--sc->data[SC_MEMORIZE]->val2) <= 0)
- status_change_end(bl, SC_MEMORIZE, INVALID_TIMER);
- }
- if (sc->data[SC_POEMBRAGI])
- time -= time * sc->data[SC_POEMBRAGI]->val2 / 100;
- if (sc->data[SC_IZAYOI])
- time -= time * 50 / 100;
- }
- time = max(time, 0);
-
-// ShowInfo("Castime castfix_sc = %d\n",time);
- return time;
-}
-#ifdef RENEWAL_CAST
-int skill_vfcastfix (struct block_list *bl, double time, uint16 skill_id, uint16 skill_lv)
-{
- struct status_change *sc = status_get_sc(bl);
- struct map_session_data *sd = BL_CAST(BL_PC,bl);
- int fixed = skill_get_fixed_cast(skill_id, skill_lv), fixcast_r = 0, varcast_r = 0, i = 0;
-
- if( time < 0 )
- return 0;
-
- if( fixed == 0 ){
- fixed = (int)time * 20 / 100; // fixed time
- time = time * 80 / 100; // variable time
- }else if( fixed < 0 ) // no fixed cast time
- fixed = 0;
-
- if(sd && !(skill_get_castnodex(skill_id, skill_lv)&4) ){ // Increases/Decreases fixed/variable cast time of a skill by item/card bonuses.
- if( sd->bonus.varcastrate < 0 )
- VARCAST_REDUCTION(sd->bonus.varcastrate);
- for (i = 0; i < ARRAYLENGTH(sd->skillfixcast) && sd->skillfixcast[i].id; i++)
- if (sd->skillfixcast[i].id == skill_id){ // bonus2 bSkillFixedCast
- fixed += sd->skillfixcast[i].val;
- break;
- }
- for( i = 0; i < ARRAYLENGTH(sd->skillvarcast) && sd->skillvarcast[i].id; i++ )
- if( sd->skillvarcast[i].id == skill_id ){ // bonus2 bSkillVariableCast
- time += sd->skillvarcast[i].val;
- break;
- }
- for( i = 0; i < ARRAYLENGTH(sd->skillcast) && sd->skillcast[i].id; i++ )
- if( sd->skillcast[i].id == skill_id ){ // bonus2 bVariableCastrate
- if( (i=sd->skillcast[i].val) < 0)
- VARCAST_REDUCTION(i);
- break;
- }
- }
-
- if (sc && sc->count && !(skill_get_castnodex(skill_id, skill_lv)&2) ) {
- // All variable cast additive bonuses must come first
- if (sc->data[SC_SLOWCAST])
- VARCAST_REDUCTION(-sc->data[SC_SLOWCAST]->val2);
-
- // Variable cast reduction bonuses
- if (sc->data[SC_SUFFRAGIUM]) {
- VARCAST_REDUCTION(sc->data[SC_SUFFRAGIUM]->val2);
- status_change_end(bl, SC_SUFFRAGIUM, INVALID_TIMER);
- }
- if (sc->data[SC_MEMORIZE]) {
- VARCAST_REDUCTION(50);
- if ((--sc->data[SC_MEMORIZE]->val2) <= 0)
- status_change_end(bl, SC_MEMORIZE, INVALID_TIMER);
- }
- if (sc->data[SC_POEMBRAGI])
- VARCAST_REDUCTION(sc->data[SC_POEMBRAGI]->val2);
- if (sc->data[SC_IZAYOI])
- VARCAST_REDUCTION(50);
- if (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 3 && (skill_get_ele(skill_id, skill_lv) == ELE_WATER))
- VARCAST_REDUCTION(30); //Reduces 30% Variable Cast Time of Water spells.
- // Fixed cast reduction bonuses
- if( sc->data[SC__LAZINESS] )
- fixcast_r = max(fixcast_r, sc->data[SC__LAZINESS]->val2);
- if( sc->data[SC_SECRAMENT] )
- fixcast_r = max(fixcast_r, sc->data[SC_SECRAMENT]->val2);
- if( sd && ( skill_lv = pc_checkskill(sd, WL_RADIUS) ) && skill_id >= WL_WHITEIMPRISON && skill_id <= WL_FREEZE_SP )
- fixcast_r = max(fixcast_r, 5 + skill_lv * 5);
- // Fixed cast non percentage bonuses
- if( sc->data[SC_MANDRAGORA] && (skill_id >= SM_BASH && skill_id <= RETURN_TO_ELDICASTES) )
- fixed += sc->data[SC_MANDRAGORA]->val1 * 1000 / 2;
- if (sc->data[SC_IZAYOI] && (skill_id >= NJ_TOBIDOUGU && skill_id <= NJ_ISSEN))
- fixed = 0;
- if( sc->data[SC_GUST_OPTION] || sc->data[SC_BLAST_OPTION] || sc->data[SC_WILD_STORM_OPTION] )
- fixed -= 1000;
- }
-
- if( sd && !(skill_get_castnodex(skill_id, skill_lv)&4) ){
- VARCAST_REDUCTION( max(sd->bonus.varcastrate, 0) + max(i, 0) );
- fixcast_r = max(fixcast_r, sd->bonus.fixcastrate) + min(sd->bonus.fixcastrate,0);
- }
-
- if( varcast_r < 0 ) // now compute overall factors
- time = time * (1 - (float)varcast_r / 100);
- if( !(skill_get_castnodex(skill_id, skill_lv)&1) )// reduction from status point
- time = (1 - sqrt( ((float)(status_get_dex(bl)*2 + status_get_int(bl)) / battle_config.vcast_stat_scale) )) * time;
- // underflow checking/capping
- time = max(time, 0) + (1 - (float)min(fixcast_r, 100) / 100) * max(fixed,0);
-
- return (int)time;
-}
-#endif
-
-/*==========================================
- * Does delay reductions based on dex/agi, sc data, item bonuses, ...
- *------------------------------------------*/
-int skill_delayfix (struct block_list *bl, uint16 skill_id, uint16 skill_lv)
-{
- int delaynodex = skill_get_delaynodex(skill_id, skill_lv);
- int time = skill_get_delay(skill_id, skill_lv);
- struct map_session_data *sd;
- struct status_change *sc = status_get_sc(bl);
-
- nullpo_ret(bl);
- sd = BL_CAST(BL_PC, bl);
-
- if (skill_id == SA_ABRACADABRA || skill_id == WM_RANDOMIZESPELL)
- return 0; //Will use picked skill's delay.
-
- if (bl->type&battle_config.no_skill_delay)
- return battle_config.min_skill_delay_limit;
-
- if (time < 0)
- time = -time + status_get_amotion(bl); // If set to <0, add to attack motion.
-
- // Delay reductions
- switch (skill_id) { //Monk combo skills have their delay reduced by agi/dex.
- case MO_TRIPLEATTACK:
- case MO_CHAINCOMBO:
- case MO_COMBOFINISH:
- case CH_TIGERFIST:
- case CH_CHAINCRUSH:
- case SR_DRAGONCOMBO:
- case SR_FALLENEMPIRE:
- time -= 4*status_get_agi(bl) - 2*status_get_dex(bl);
- break;
- case HP_BASILICA:
- if( sc && !sc->data[SC_BASILICA] )
- time = 0; // There is no Delay on Basilica creation, only on cancel
- break;
- default:
- if (battle_config.delay_dependon_dex && !(delaynodex&1))
- { // if skill delay is allowed to be reduced by dex
- int scale = battle_config.castrate_dex_scale - status_get_dex(bl);
- if (scale > 0)
- time = time * scale / battle_config.castrate_dex_scale;
- else //To be capped later to minimum.
- time = 0;
- }
- if (battle_config.delay_dependon_agi && !(delaynodex&1))
- { // if skill delay is allowed to be reduced by agi
- int scale = battle_config.castrate_dex_scale - status_get_agi(bl);
- if (scale > 0)
- time = time * scale / battle_config.castrate_dex_scale;
- else //To be capped later to minimum.
- time = 0;
- }
- }
-
- if ( sc && sc->data[SC_SPIRIT] )
- {
- switch (skill_id) {
- case CR_SHIELDBOOMERANG:
- if (sc->data[SC_SPIRIT]->val2 == SL_CRUSADER)
- time /= 2;
- break;
- case AS_SONICBLOW:
- if (!map_flag_gvg(bl->m) && !map[bl->m].flag.battleground && sc->data[SC_SPIRIT]->val2 == SL_ASSASIN)
- time /= 2;
- break;
- }
- }
-
- if (!(delaynodex&2))
- {
- if (sc && sc->count) {
- if (sc->data[SC_POEMBRAGI])
- time -= time * sc->data[SC_POEMBRAGI]->val3 / 100;
- if (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 3 && (skill_get_ele(skill_id, skill_lv) == ELE_WIND))
- time /= 2; // After Delay of Wind element spells reduced by 50%.
- }
-
- }
-
- if( !(delaynodex&4) && sd && sd->delayrate != 100 )
- time = time * sd->delayrate / 100;
-
- if (battle_config.delay_rate != 100)
- time = time * battle_config.delay_rate / 100;
-
- //min delay
- time = max(time, status_get_amotion(bl)); // Delay can never be below amotion [Playtester]
- time = max(time, battle_config.min_skill_delay_limit);
-
-// ShowInfo("Delay delayfix = %d\n",time);
- return time;
-}
-
-/*=========================================
- *
- *-----------------------------------------*/
-struct square {
- int val1[5];
- int val2[5];
-};
-
-static void skill_brandishspear_first (struct square *tc, uint8 dir, int16 x, int16 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;
- }
-
-}
-
-static void skill_brandishspear_dir (struct square* tc, uint8 dir, int are)
-{
- int c;
- nullpo_retv(tc);
-
- for( c = 0; c < 5; c++ )
- {
- switch( dir )
- {
- case 0: tc->val2[c]+=are; break;
- case 1: tc->val1[c]-=are; tc->val2[c]+=are; break;
- case 2: tc->val1[c]-=are; break;
- case 3: tc->val1[c]-=are; tc->val2[c]-=are; break;
- case 4: tc->val2[c]-=are; break;
- case 5: tc->val1[c]+=are; tc->val2[c]-=are; break;
- case 6: tc->val1[c]+=are; break;
- case 7: tc->val1[c]+=are; tc->val2[c]+=are; break;
- }
- }
-}
-
-void skill_brandishspear(struct block_list* src, struct block_list* bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
-{
- int c,n=4;
- uint8 dir = map_calc_dir(src,bl->x,bl->y);
- struct square tc;
- int x=bl->x,y=bl->y;
- skill_brandishspear_first(&tc,dir,x,y);
- skill_brandishspear_dir(&tc,dir,4);
- skill_area_temp[1] = bl->id;
-
- if(skill_lv > 9){
- for(c=1;c<4;c++){
- map_foreachincell(skill_area_sub,
- bl->m,tc.val1[c],tc.val2[c],BL_CHAR,
- src,skill_id,skill_lv,tick, flag|BCT_ENEMY|n,
- skill_castend_damage_id);
- }
- }
- if(skill_lv > 6){
- skill_brandishspear_dir(&tc,dir,-1);
- n--;
- }else{
- skill_brandishspear_dir(&tc,dir,-2);
- n-=2;
- }
-
- if(skill_lv > 3){
- for(c=0;c<5;c++){
- map_foreachincell(skill_area_sub,
- bl->m,tc.val1[c],tc.val2[c],BL_CHAR,
- src,skill_id,skill_lv,tick, flag|BCT_ENEMY|n,
- skill_castend_damage_id);
- if(skill_lv > 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_foreachincell(skill_area_sub,
- bl->m,tc.val1[c%5],tc.val2[c%5],BL_CHAR,
- src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1,
- skill_castend_damage_id);
- }
-}
-
-/*==========================================
- * Weapon Repair [Celest/DracoRPG]
- *------------------------------------------*/
-void skill_repairweapon (struct map_session_data *sd, int idx) {
- int material;
- int materials[4] = { 1002, 998, 999, 756 };
- struct item *item;
- struct map_session_data *target_sd;
-
- nullpo_retv(sd);
-
- if ( !( target_sd = map_id2sd(sd->menuskill_val) ) ) //Failed....
- return;
-
- if( idx == 0xFFFF ) // No item selected ('Cancel' clicked)
- return;
- if( idx < 0 || idx >= MAX_INVENTORY )
- return; //Invalid index??
-
- item = &target_sd->status.inventory[idx];
- if( item->nameid <= 0 || item->attribute == 0 )
- return; //Again invalid item....
-
- if( sd != target_sd && !battle_check_range(&sd->bl,&target_sd->bl, skill_get_range2(&sd->bl, sd->menuskill_id,sd->menuskill_val2) ) ){
- clif_item_repaireffect(sd,idx,1);
- return;
- }
-
- if ( target_sd->inventory_data[idx]->type == IT_WEAPON )
- material = materials [ target_sd->inventory_data[idx]->wlv - 1 ]; // Lv1/2/3/4 weapons consume 1 Iron Ore/Iron/Steel/Rough Oridecon
- else
- material = materials [2]; // Armors consume 1 Steel
- if ( pc_search_inventory(sd,material) < 0 ) {
- clif_skill_fail(sd,sd->menuskill_id,USESKILL_FAIL_LEVEL,0);
- return;
- }
-
- clif_skill_nodamage(&sd->bl,&target_sd->bl,sd->menuskill_id,1,1);
-
- item->attribute = 0;/* clear broken state */
-
- clif_equiplist(target_sd);
-
- pc_delitem(sd,pc_search_inventory(sd,material),1,0,0,LOG_TYPE_CONSUME);
-
- clif_item_repaireffect(sd,idx,0);
-
- if( sd != target_sd )
- clif_item_repaireffect(target_sd,idx,0);
-}
-
-/*==========================================
- * Item Appraisal
- *------------------------------------------*/
-void skill_identify (struct map_session_data *sd, int idx)
-{
- int flag=1;
-
- nullpo_retv(sd);
-
- if(idx >= 0 && idx < MAX_INVENTORY) {
- if(sd->status.inventory[idx].nameid > 0 && sd->status.inventory[idx].identify == 0 ){
- flag=0;
- sd->status.inventory[idx].identify=1;
- }
- }
- clif_item_identified(sd,idx,flag);
-}
-
-/*==========================================
- * Weapon Refine [Celest]
- *------------------------------------------*/
-void skill_weaponrefine (struct map_session_data *sd, int idx)
-{
- nullpo_retv(sd);
-
- if (idx >= 0 && idx < MAX_INVENTORY)
- {
- int i = 0, ep = 0, per;
- int material[5] = { 0, 1010, 1011, 984, 984 };
- struct item *item;
- struct item_data *ditem = sd->inventory_data[idx];
- item = &sd->status.inventory[idx];
-
- if(item->nameid > 0 && ditem->type == IT_WEAPON)
- {
- if( item->refine >= sd->menuskill_val
- || item->refine >= 10 // if it's no longer refineable
- || ditem->flag.no_refine // if the item isn't refinable
- || (i = pc_search_inventory(sd, material [ditem->wlv])) < 0 )
- {
- clif_skill_fail(sd,sd->menuskill_id,USESKILL_FAIL_LEVEL,0);
- return;
- }
-
- per = status_get_refine_chance(ditem->wlv, (int)item->refine);
- per += (((signed int)sd->status.job_level)-50)/2; //Updated per the new kro descriptions. [Skotlex]
-
- pc_delitem(sd, i, 1, 0, 0, LOG_TYPE_OTHER);
- if (per > rnd() % 100) {
- log_pick_pc(sd, LOG_TYPE_OTHER, -1, item);
- item->refine++;
- log_pick_pc(sd, LOG_TYPE_OTHER, 1, item);
- if(item->equip) {
- ep = item->equip;
- pc_unequipitem(sd,idx,3);
- }
- clif_refine(sd->fd,0,idx,item->refine);
- clif_delitem(sd,idx,1,3);
- clif_additem(sd,idx,1,0);
- if (ep)
- pc_equipitem(sd,idx,ep);
- clif_misceffect(&sd->bl,3);
- if(item->refine == 10 &&
- item->card[0] == CARD0_FORGE &&
- (int)MakeDWord(item->card[2],item->card[3]) == sd->status.char_id)
- { // Fame point system [DracoRPG]
- switch(ditem->wlv){
- case 1:
- pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point
- break;
- case 2:
- pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point
- break;
- case 3:
- pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point
- break;
- }
- }
- } else {
- item->refine = 0;
- if(item->equip)
- pc_unequipitem(sd,idx,3);
- clif_refine(sd->fd,1,idx,item->refine);
- pc_delitem(sd,idx,1,0,2, LOG_TYPE_OTHER);
- clif_misceffect(&sd->bl,2);
- clif_emotion(&sd->bl, E_OMG);
- }
- }
- }
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_autospell (struct map_session_data *sd, uint16 skill_id)
-{
- uint16 skill_lv;
- int maxlv=1,lv;
-
- nullpo_ret(sd);
-
- skill_lv = sd->menuskill_val;
- lv=pc_checkskill(sd,skill_id);
-
- if(!skill_lv || !lv) return 0; // Player must learn the skill before doing auto-spell [Lance]
-
- if(skill_id==MG_NAPALMBEAT) maxlv=3;
- else if(skill_id==MG_COLDBOLT || skill_id==MG_FIREBOLT || skill_id==MG_LIGHTNINGBOLT){
- if (sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_SAGE)
- maxlv =10; //Soul Linker bonus. [Skotlex]
- else if(skill_lv==2) maxlv=1;
- else if(skill_lv==3) maxlv=2;
- else if(skill_lv>=4) maxlv=3;
- }
- else if(skill_id==MG_SOULSTRIKE){
- if(skill_lv==5) maxlv=1;
- else if(skill_lv==6) maxlv=2;
- else if(skill_lv>=7) maxlv=3;
- }
- else if(skill_id==MG_FIREBALL){
- if(skill_lv==8) maxlv=1;
- else if(skill_lv>=9) maxlv=2;
- }
- else if(skill_id==MG_FROSTDIVER) maxlv=1;
- else return 0;
-
- if(maxlv > lv)
- maxlv = lv;
-
- sc_start4(&sd->bl,SC_AUTOSPELL,100,skill_lv,skill_id,maxlv,0,
- skill_get_time(SA_AUTOSPELL,skill_lv));
- return 0;
-}
-
-/*==========================================
- * Sitting skills functions.
- *------------------------------------------*/
-static int skill_sit_count (struct block_list *bl, va_list ap)
-{
- struct map_session_data *sd;
- int type =va_arg(ap,int);
- sd=(struct map_session_data*)bl;
-
- if(!pc_issit(sd))
- return 0;
-
- if(type&1 && pc_checkskill(sd,RG_GANGSTER) > 0)
- return 1;
-
- if(type&2 && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0))
- return 1;
-
- return 0;
-}
-
-static int skill_sit_in (struct block_list *bl, va_list ap)
-{
- struct map_session_data *sd;
- int type =va_arg(ap,int);
-
- sd=(struct map_session_data*)bl;
-
- if(!pc_issit(sd))
- return 0;
-
- if(type&1 && pc_checkskill(sd,RG_GANGSTER) > 0)
- sd->state.gangsterparadise=1;
-
- if(type&2 && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0 ))
- {
- sd->state.rest=1;
- status_calc_regen(bl, &sd->battle_status, &sd->regen);
- status_calc_regen_rate(bl, &sd->regen, &sd->sc);
- }
-
- return 0;
-}
-
-static int skill_sit_out (struct block_list *bl, va_list ap)
-{
- struct map_session_data *sd;
- int type =va_arg(ap,int);
- sd=(struct map_session_data*)bl;
- if(sd->state.gangsterparadise && type&1)
- sd->state.gangsterparadise=0;
- if(sd->state.rest && type&2) {
- sd->state.rest=0;
- status_calc_regen(bl, &sd->battle_status, &sd->regen);
- status_calc_regen_rate(bl, &sd->regen, &sd->sc);
- }
- return 0;
-}
-
-int skill_sit (struct map_session_data *sd, int type)
-{
- int flag = 0;
- int range = 0, lv;
- nullpo_ret(sd);
-
-
- if((lv = pc_checkskill(sd,RG_GANGSTER)) > 0) {
- flag|=1;
- range = skill_get_splash(RG_GANGSTER, lv);
- }
- if((lv = pc_checkskill(sd,TK_HPTIME)) > 0) {
- flag|=2;
- range = skill_get_splash(TK_HPTIME, lv);
- }
- else if ((lv = pc_checkskill(sd,TK_SPTIME)) > 0) {
- flag|=2;
- range = skill_get_splash(TK_SPTIME, lv);
- }
-
- if( type ) {
- clif_status_load(&sd->bl,SI_SIT,1);
- } else {
- clif_status_load(&sd->bl,SI_SIT,0);
- }
-
- if (!flag) return 0;
-
- if(type) {
- if (map_foreachinrange(skill_sit_count,&sd->bl, range, BL_PC, flag) > 1)
- map_foreachinrange(skill_sit_in,&sd->bl, range, BL_PC, flag);
- } else {
- if (map_foreachinrange(skill_sit_count,&sd->bl, range, BL_PC, flag) < 2)
- map_foreachinrange(skill_sit_out,&sd->bl, range, BL_PC, flag);
- }
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_frostjoke_scream (struct block_list *bl, va_list ap)
-{
- struct block_list *src;
- uint16 skill_id,skill_lv;
- unsigned int tick;
-
- nullpo_ret(bl);
- nullpo_ret(src=va_arg(ap,struct block_list*));
-
- skill_id=va_arg(ap,int);
- skill_lv=va_arg(ap,int);
- if(!skill_lv) return 0;
- tick=va_arg(ap,unsigned int);
-
- if (src == bl || status_isdead(bl))
- return 0;
- if (bl->type == BL_PC) {
- struct map_session_data *sd = (struct map_session_data *)bl;
- if ( sd && sd->sc.option&(OPTION_INVISIBLE|OPTION_MADOGEAR) )
- return 0;//Frost Joke / Scream cannot target invisible or MADO Gear characters [Ind]
- }
- //It has been reported that Scream/Joke works the same regardless of woe-setting. [Skotlex]
- if(battle_check_target(src,bl,BCT_ENEMY) > 0)
- skill_additional_effect(src,bl,skill_id,skill_lv,BF_MISC,ATK_DEF,tick);
- else if(battle_check_target(src,bl,BCT_PARTY) > 0 && rnd()%100 < 10)
- skill_additional_effect(src,bl,skill_id,skill_lv,BF_MISC,ATK_DEF,tick);
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-static void skill_unitsetmapcell (struct skill_unit *src, uint16 skill_id, uint16 skill_lv, cell_t cell, bool flag)
-{
- int range = skill_get_unit_range(skill_id,skill_lv);
- int x,y;
-
- for( y = src->bl.y - range; y <= src->bl.y + range; ++y )
- for( x = src->bl.x - range; x <= src->bl.x + range; ++x )
- map_setcell(src->bl.m, x, y, cell, flag);
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_attack_area (struct block_list *bl, va_list ap)
-{
- struct block_list *src,*dsrc;
- int atk_type,skill_id,skill_lv,flag,type;
- unsigned int tick;
-
- if(status_isdead(bl))
- return 0;
-
- atk_type = va_arg(ap,int);
- src=va_arg(ap,struct block_list*);
- dsrc=va_arg(ap,struct block_list*);
- skill_id=va_arg(ap,int);
- skill_lv=va_arg(ap,int);
- tick=va_arg(ap,unsigned int);
- flag=va_arg(ap,int);
- type=va_arg(ap,int);
-
-
- if (skill_area_temp[1] == bl->id) //This is the target of the skill, do a full attack and skip target checks.
- return skill_attack(atk_type,src,dsrc,bl,skill_id,skill_lv,tick,flag);
-
- if(battle_check_target(dsrc,bl,type) <= 0 ||
- !status_check_skilluse(NULL, bl, skill_id, 2))
- return 0;
-
-
- switch (skill_id) {
- case WZ_FROSTNOVA: //Skills that don't require the animation to be removed
- case NPC_ACIDBREATH:
- case NPC_DARKNESSBREATH:
- case NPC_FIREBREATH:
- case NPC_ICEBREATH:
- case NPC_THUNDERBREATH:
- return skill_attack(atk_type,src,dsrc,bl,skill_id,skill_lv,tick,flag);
- default:
- //Area-splash, disable skill animation.
- return skill_attack(atk_type,src,dsrc,bl,skill_id,skill_lv,tick,flag|SD_ANIMATION);
- }
-}
-/*==========================================
- *
- *------------------------------------------*/
-int skill_clear_group (struct block_list *bl, int flag)
-{
- struct unit_data *ud = unit_bl2ud(bl);
- struct skill_unit_group *group[MAX_SKILLUNITGROUP];
- int i, count=0;
-
- nullpo_ret(bl);
- if (!ud) return 0;
-
- //All groups to be deleted are first stored on an array since the array elements shift around when you delete them. [Skotlex]
- for (i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i];i++)
- {
- switch (ud->skillunit[i]->skill_id) {
- case SA_DELUGE:
- case SA_VOLCANO:
- case SA_VIOLENTGALE:
- case SA_LANDPROTECTOR:
- case NJ_SUITON:
- case NJ_KAENSIN:
- if (flag&1)
- group[count++]= ud->skillunit[i];
- break;
- case SO_WARMER:
- if( flag&8 )
- group[count++]= ud->skillunit[i];
- break;
- case SC_BLOODYLUST:
- if (flag & 32)
- group[count++] = ud->skillunit[i];
- break;
- default:
- if (flag&2 && skill_get_inf2(ud->skillunit[i]->skill_id)&INF2_TRAP)
- group[count++]= ud->skillunit[i];
- break;
- }
-
- }
- for (i=0;i<count;i++)
- skill_delunitgroup(group[i]);
- return count;
-}
-
-/*==========================================
- * Returns the first element field found [Skotlex]
- *------------------------------------------*/
-struct skill_unit_group *skill_locate_element_field(struct block_list *bl)
-{
- struct unit_data *ud = unit_bl2ud(bl);
- int i;
- nullpo_ret(bl);
- if (!ud) return NULL;
-
- for (i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i];i++) {
- switch (ud->skillunit[i]->skill_id) {
- case SA_DELUGE:
- case SA_VOLCANO:
- case SA_VIOLENTGALE:
- case SA_LANDPROTECTOR:
- case NJ_SUITON:
- case SO_WARMER:
- case SC_BLOODYLUST:
- return ud->skillunit[i];
- }
- }
- return NULL;
-}
-
-// for graffiti cleaner [Valaris]
-int skill_graffitiremover (struct block_list *bl, va_list ap)
-{
- struct skill_unit *unit=NULL;
-
- nullpo_ret(bl);
- nullpo_ret(ap);
-
- if(bl->type!=BL_SKILL || (unit=(struct skill_unit *)bl) == NULL)
- return 0;
-
- if((unit->group) && (unit->group->unit_id == UNT_GRAFFITI))
- skill_delunit(unit);
-
- return 0;
-}
-
-int skill_greed (struct block_list *bl, va_list ap)
-{
- struct block_list *src;
- struct map_session_data *sd=NULL;
- struct flooritem_data *fitem=NULL;
-
- nullpo_ret(bl);
- nullpo_ret(src = va_arg(ap, struct block_list *));
-
- if(src->type == BL_PC && (sd=(struct map_session_data *)src) && bl->type==BL_ITEM && (fitem=(struct flooritem_data *)bl))
- pc_takeitem(sd, fitem);
-
- return 0;
-}
-//For Ranger's Detonator [Jobbie/3CeAM]
-int skill_detonator(struct block_list *bl, va_list ap)
-{
- struct skill_unit *unit=NULL;
- struct block_list *src;
- int unit_id;
-
- nullpo_ret(bl);
- nullpo_ret(ap);
- src = va_arg(ap,struct block_list *);
-
- if( bl->type != BL_SKILL || (unit = (struct skill_unit *)bl) == NULL || !unit->group )
- return 0;
- if( unit->group->src_id != src->id )
- return 0;
-
- unit_id = unit->group->unit_id;
- switch( unit_id )
- { //List of Hunter and Ranger Traps that can be detonate.
- case UNT_BLASTMINE:
- case UNT_SANDMAN:
- case UNT_CLAYMORETRAP:
- case UNT_TALKIEBOX:
- case UNT_CLUSTERBOMB:
- case UNT_FIRINGTRAP:
- case UNT_ICEBOUNDTRAP:
- if( unit_id == UNT_TALKIEBOX )
- {
- clif_talkiebox(bl,unit->group->valstr);
- unit->group->val2 = -1;
- }
- else
- map_foreachinrange(skill_trap_splash,bl,skill_get_splash(unit->group->skill_id,unit->group->skill_lv),unit->group->bl_flag,bl,unit->group->tick);
-
- clif_changetraplook(bl,unit_id == UNT_FIRINGTRAP ? UNT_DUMMYSKILL : UNT_USED_TRAPS);
- unit->group->unit_id = UNT_USED_TRAPS;
- unit->group->limit = DIFF_TICK(gettick(),unit->group->tick) +
- (unit_id == UNT_TALKIEBOX ? 5000 : (unit_id == UNT_CLUSTERBOMB || unit_id == UNT_ICEBOUNDTRAP? 2500 : 1500) );
- break;
- }
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-static int skill_cell_overlap(struct block_list *bl, va_list ap)
-{
- uint16 skill_id;
- int *alive;
- struct skill_unit *unit;
-
- skill_id = va_arg(ap,int);
- alive = va_arg(ap,int *);
- unit = (struct skill_unit *)bl;
-
- if (unit == NULL || unit->group == NULL || (*alive) == 0)
- return 0;
-
- switch (skill_id) {
- case SA_LANDPROTECTOR:
- if( unit->group->skill_id == SA_LANDPROTECTOR ) {//Check for offensive Land Protector to delete both. [Skotlex]
- (*alive) = 0;
- skill_delunit(unit);
- return 1;
- }
- if( !(skill_get_inf2(unit->group->skill_id)&(INF2_SONG_DANCE|INF2_TRAP)) ) { //It deletes everything except songs/dances and traps
- skill_delunit(unit);
- return 1;
- }
- break;
- case HW_GANBANTEIN:
- case LG_EARTHDRIVE:
- if( !(unit->group->state.song_dance&0x1) ) {// Don't touch song/dance.
- skill_delunit(unit);
- return 1;
- }
- break;
- case SA_VOLCANO:
- case SA_DELUGE:
- case SA_VIOLENTGALE:
-// The official implementation makes them fail to appear when casted on top of ANYTHING
-// but I wonder if they didn't actually meant to fail when casted on top of each other?
-// hence, I leave the alternate implementation here, commented. [Skotlex]
- if (unit->range <= 0)
- {
- (*alive) = 0;
- return 1;
- }
-/*
- switch (unit->group->skill_id)
- { //These cannot override each other.
- case SA_VOLCANO:
- case SA_DELUGE:
- case SA_VIOLENTGALE:
- (*alive) = 0;
- return 1;
- }
-*/
- break;
- case PF_FOGWALL:
- switch(unit->group->skill_id) {
- case SA_VOLCANO: //Can't be placed on top of these
- case SA_VIOLENTGALE:
- (*alive) = 0;
- return 1;
- case SA_DELUGE:
- case NJ_SUITON:
- //Cheap 'hack' to notify the calling function that duration should be doubled [Skotlex]
- (*alive) = 2;
- break;
- }
- break;
- case HP_BASILICA:
- if (unit->group->skill_id == HP_BASILICA)
- { //Basilica can't be placed on top of itself to avoid map-cell stacking problems. [Skotlex]
- (*alive) = 0;
- return 1;
- }
- break;
- case GN_CRAZYWEED_ATK:
- switch(unit->group->unit_id){ //TODO: look for other ground skills that are affected.
- case UNT_WALLOFTHORN:
- case UNT_THORNS_TRAP:
- case UNT_BLOODYLUST:
- case UNT_CHAOSPANIC:
- case UNT_MAELSTROM:
- case UNT_FIREPILLAR_ACTIVE:
- case UNT_LANDPROTECTOR:
- case UNT_VOLCANO:
- case UNT_DELUGE:
- case UNT_VIOLENTGALE:
- case UNT_SAFETYWALL:
- case UNT_PNEUMA:
- skill_delunit(unit);
- return 1;
- }
- break;
- }
-
- if (unit->group->skill_id == SA_LANDPROTECTOR && !(skill_get_inf2(skill_id)&(INF2_SONG_DANCE|INF2_TRAP))) { //It deletes everything except songs/dances/traps
- (*alive) = 0;
- return 1;
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_chastle_mob_changetarget(struct block_list *bl,va_list ap)
-{
- struct mob_data* md;
- struct unit_data*ud = unit_bl2ud(bl);
- struct block_list *from_bl;
- struct block_list *to_bl;
- md = (struct mob_data*)bl;
- from_bl = va_arg(ap,struct block_list *);
- to_bl = va_arg(ap,struct block_list *);
-
- if(ud && ud->target == from_bl->id)
- ud->target = to_bl->id;
-
- if(md->bl.type == BL_MOB && md->target_id == from_bl->id)
- md->target_id = to_bl->id;
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-static int skill_trap_splash (struct block_list *bl, va_list ap)
-{
- struct block_list *src;
- int tick;
- struct skill_unit *unit;
- struct skill_unit_group *sg;
- struct block_list *ss;
- src = va_arg(ap,struct block_list *);
- unit = (struct skill_unit *)src;
- tick = va_arg(ap,int);
-
- if( !unit->alive || bl->prev == NULL )
- return 0;
-
- nullpo_ret(sg = unit->group);
- nullpo_ret(ss = map_id2bl(sg->src_id));
-
- if(battle_check_target(src,bl,sg->target_flag) <= 0)
- return 0;
-
- switch(sg->unit_id){
- case UNT_SHOCKWAVE:
- case UNT_SANDMAN:
- case UNT_FLASHER:
- skill_additional_effect(ss,bl,sg->skill_id,sg->skill_lv,BF_MISC,ATK_DEF,tick);
- break;
- case UNT_GROUNDDRIFT_WIND:
- if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1))
- sc_start(bl,SC_STUN,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv));
- break;
- case UNT_GROUNDDRIFT_DARK:
- if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1))
- sc_start(bl,SC_BLIND,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv));
- break;
- case UNT_GROUNDDRIFT_POISON:
- if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1))
- sc_start(bl,SC_POISON,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv));
- break;
- case UNT_GROUNDDRIFT_WATER:
- if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1))
- sc_start(bl,SC_FREEZE,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv));
- break;
- case UNT_GROUNDDRIFT_FIRE:
- if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1))
- skill_blown(src,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),-1,0);
- break;
- case UNT_ELECTRICSHOCKER:
- clif_skill_damage(src,bl,tick,0,0,-30000,1,sg->skill_id,sg->skill_lv,5);
- break;
- case UNT_FIRINGTRAP:
- case UNT_ICEBOUNDTRAP:
- case UNT_CLUSTERBOMB:
- if( ss != bl )
- skill_attack(BF_MISC,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1|SD_LEVEL);
- break;
- case UNT_MAGENTATRAP:
- case UNT_COBALTTRAP:
- case UNT_MAIZETRAP:
- case UNT_VERDURETRAP:
- if( bl->type != BL_PC && !is_boss(bl) )
- sc_start2(bl,SC_ELEMENTALCHANGE,100,sg->skill_lv,skill_get_ele(sg->skill_id,sg->skill_lv),skill_get_time2(sg->skill_id,sg->skill_lv));
- break;
- case UNT_REVERBERATION:
- skill_addtimerskill(ss,tick+50,bl->id,0,0,WM_REVERBERATION_MELEE,sg->skill_lv,BF_WEAPON,0); // for proper skill delay animation when use with Dominion Impulse
- skill_addtimerskill(ss,tick+250,bl->id,0,0,WM_REVERBERATION_MAGIC,sg->skill_lv,BF_MAGIC,0);
- break;
- default:
- skill_attack(skill_get_type(sg->skill_id),ss,src,bl,sg->skill_id,sg->skill_lv,tick,0);
- break;
- }
- return 1;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_enchant_elemental_end (struct block_list *bl, int type)
-{
- struct status_change *sc;
- const enum sc_type scs[] = { SC_ENCPOISON, SC_ASPERSIO, SC_FIREWEAPON, SC_WATERWEAPON, SC_WINDWEAPON, SC_EARTHWEAPON, SC_SHADOWWEAPON, SC_GHOSTWEAPON, SC_ENCHANTARMS, SC_EXEEDBREAK };
- int i;
- nullpo_ret(bl);
- nullpo_ret(sc= status_get_sc(bl));
-
- if (!sc->count) return 0;
-
- for (i = 0; i < ARRAYLENGTH(scs); i++)
- if (type != scs[i] && sc->data[scs[i]])
- status_change_end(bl, scs[i], INVALID_TIMER);
-
- return 0;
-}
-
-bool skill_check_cloaking(struct block_list *bl, struct status_change_entry *sce)
-{
- static int dx[] = { 0, 1, 0, -1, -1, 1, 1, -1};
- static int dy[] = {-1, 0, 1, 0, -1, -1, 1, 1};
- bool wall = true;
-
- if( (bl->type == BL_PC && battle_config.pc_cloak_check_type&1)
- || (bl->type != BL_PC && battle_config.monster_cloak_check_type&1) )
- { //Check for walls.
- int i;
- ARR_FIND( 0, 8, i, map_getcell(bl->m, bl->x+dx[i], bl->y+dy[i], CELL_CHKNOPASS) != 0 );
- if( i == 8 )
- wall = false;
- }
-
- if( sce )
- {
- if( !wall )
- {
- if( sce->val1 < 3 ) //End cloaking.
- status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
- else
- if( sce->val4&1 )
- { //Remove wall bonus
- sce->val4&=~1;
- status_calc_bl(bl,SCB_SPEED);
- }
- }
- else
- {
- if( !(sce->val4&1) )
- { //Add wall speed bonus
- sce->val4|=1;
- status_calc_bl(bl,SCB_SPEED);
- }
- }
- }
-
- return wall;
-}
-bool skill_check_camouflage(struct block_list *bl, struct status_change_entry *sce)
-{
- static int dx[] = { 0, 1, 0, -1, -1, 1, 1, -1};
- static int dy[] = {-1, 0, 1, 0, -1, -1, 1, 1};
- bool wall = true;
-
- if( bl->type == BL_PC )
- { //Check for walls.
- int i;
- ARR_FIND( 0, 8, i, map_getcell(bl->m, bl->x+dx[i], bl->y+dy[i], CELL_CHKNOPASS) != 0 );
- if( i == 8 )
- wall = false;
- }
-
- if( sce )
- {
- if( !wall )
- {
- if( sce->val1 < 3 ) //End camouflage.
- status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER);
- else
- if( sce->val3&1 )
- { //Remove wall bonus
- sce->val3&=~1;
- status_calc_bl(bl,SCB_SPEED);
- }
- }
- }
-
- return wall;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int x, int y, int val1, int val2)
-{
- struct skill_unit *unit;
-
- nullpo_retr(NULL, group);
- nullpo_retr(NULL, group->unit); // crash-protection against poor coding
- nullpo_retr(NULL, unit=&group->unit[idx]);
-
- if(!unit->alive)
- group->alive_count++;
-
- unit->bl.id=map_get_new_object_id();
- unit->bl.type=BL_SKILL;
- unit->bl.m=group->map;
- unit->bl.x=x;
- unit->bl.y=y;
- unit->group=group;
- unit->alive=1;
- unit->val1=val1;
- unit->val2=val2;
-
- idb_put(skillunit_db, unit->bl.id, unit);
- map_addiddb(&unit->bl);
- map_addblock(&unit->bl);
-
- // perform oninit actions
- switch (group->skill_id) {
- case WZ_ICEWALL:
- map_setgatcell(unit->bl.m,unit->bl.x,unit->bl.y,5);
- clif_changemapcell(0,unit->bl.m,unit->bl.x,unit->bl.y,5,AREA);
- skill_unitsetmapcell(unit,WZ_ICEWALL,group->skill_lv,CELL_ICEWALL,true);
- map[unit->bl.m].icewall_num++;
- break;
- case SA_LANDPROTECTOR:
- skill_unitsetmapcell(unit,SA_LANDPROTECTOR,group->skill_lv,CELL_LANDPROTECTOR,true);
- break;
- case HP_BASILICA:
- skill_unitsetmapcell(unit,HP_BASILICA,group->skill_lv,CELL_BASILICA,true);
- break;
- case SC_MAELSTROM:
- skill_unitsetmapcell(unit,SC_MAELSTROM,group->skill_lv,CELL_MAELSTROM,true);
- break;
- default:
- if (group->state.song_dance&0x1) //Check for dissonance.
- skill_dance_overlap(unit, 1);
- break;
- }
-
- clif_skill_setunit(unit);
-
- return unit;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_delunit (struct skill_unit* unit)
-{
- struct skill_unit_group *group;
-
- nullpo_ret(unit);
- if( !unit->alive )
- return 0;
- unit->alive=0;
-
- nullpo_ret(group=unit->group);
-
- if( group->state.song_dance&0x1 ) //Cancel dissonance effect.
- skill_dance_overlap(unit, 0);
-
- // invoke onout event
- if( !unit->range )
- map_foreachincell(skill_unit_effect,unit->bl.m,unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),4);
-
- // perform ondelete actions
- switch (group->skill_id) {
- case HT_ANKLESNARE: {
- struct block_list* target = map_id2bl(group->val2);
- if( target )
- status_change_end(target, SC_ANKLE, INVALID_TIMER);
- }
- break;
- case WZ_ICEWALL:
- map_setgatcell(unit->bl.m,unit->bl.x,unit->bl.y,unit->val2);
- clif_changemapcell(0,unit->bl.m,unit->bl.x,unit->bl.y,unit->val2,ALL_SAMEMAP); // hack to avoid clientside cell bug
- skill_unitsetmapcell(unit,WZ_ICEWALL,group->skill_lv,CELL_ICEWALL,false);
- map[unit->bl.m].icewall_num--;
- break;
- case SA_LANDPROTECTOR:
- skill_unitsetmapcell(unit,SA_LANDPROTECTOR,group->skill_lv,CELL_LANDPROTECTOR,false);
- break;
- case HP_BASILICA:
- skill_unitsetmapcell(unit,HP_BASILICA,group->skill_lv,CELL_BASILICA,false);
- break;
- case RA_ELECTRICSHOCKER: {
- struct block_list* target = map_id2bl(group->val2);
- if( target )
- status_change_end(target, SC_ELECTRICSHOCKER, INVALID_TIMER);
- }
- break;
- case SC_MAELSTROM:
- skill_unitsetmapcell(unit,SC_MAELSTROM,group->skill_lv,CELL_MAELSTROM,false);
- break;
- case SC_MANHOLE: // Note : Removing the unit don't remove the status (official info)
- if( group->val2 ) { // Someone Traped
- struct status_change *tsc = status_get_sc( map_id2bl(group->val2));
- if( tsc && tsc->data[SC__MANHOLE] )
- tsc->data[SC__MANHOLE]->val4 = 0; // Remove the Unit ID
- }
- break;
- }
-
- clif_skill_delunit(unit);
-
- unit->group=NULL;
- map_delblock(&unit->bl); // don't free yet
- map_deliddb(&unit->bl);
- idb_remove(skillunit_db, unit->bl.id);
- if(--group->alive_count==0)
- skill_delunitgroup(group);
-
- return 0;
-}
-/*==========================================
- *
- *------------------------------------------*/
-static DBMap* group_db = NULL;// int group_id -> struct skill_unit_group*
-
-/// Returns the target skill_unit_group or NULL if not found.
-struct skill_unit_group* skill_id2group(int group_id)
-{
- return (struct skill_unit_group*)idb_get(group_db, group_id);
-}
-
-
-static int skill_unit_group_newid = MAX_SKILL_DB;
-
-/// Returns a new group_id that isn't being used in group_db.
-/// Fatal error if nothing is available.
-static int skill_get_new_group_id(void)
-{
- if( skill_unit_group_newid >= MAX_SKILL_DB && skill_id2group(skill_unit_group_newid) == NULL )
- return skill_unit_group_newid++;// available
- {// find next id
- int base_id = skill_unit_group_newid;
- while( base_id != ++skill_unit_group_newid )
- {
- if( skill_unit_group_newid < MAX_SKILL_DB )
- skill_unit_group_newid = MAX_SKILL_DB;
- if( skill_id2group(skill_unit_group_newid) == NULL )
- return skill_unit_group_newid++;// available
- }
- // full loop, nothing available
- ShowFatalError("skill_get_new_group_id: All ids are taken. Exiting...");
- exit(1);
- }
-}
-
-struct skill_unit_group* skill_initunitgroup (struct block_list* src, int count, uint16 skill_id, uint16 skill_lv, int unit_id, int limit, int interval)
-{
- struct unit_data* ud = unit_bl2ud( src );
- struct skill_unit_group* group;
- int i;
-
- if(!(skill_id && skill_lv)) return 0;
-
- nullpo_retr(NULL, src);
- nullpo_retr(NULL, ud);
-
- // find a free spot to store the new unit group
- ARR_FIND( 0, MAX_SKILLUNITGROUP, i, ud->skillunit[i] == NULL );
- if(i == MAX_SKILLUNITGROUP)
- {
- // array is full, make room by discarding oldest group
- int j=0;
- unsigned maxdiff=0,x,tick=gettick();
- for(i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i];i++)
- if((x=DIFF_TICK(tick,ud->skillunit[i]->tick))>maxdiff){
- maxdiff=x;
- j=i;
- }
- skill_delunitgroup(ud->skillunit[j]);
- //Since elements must have shifted, we use the last slot.
- i = MAX_SKILLUNITGROUP-1;
- }
-
- group = ers_alloc(skill_unit_ers, struct skill_unit_group);
- group->src_id = src->id;
- group->party_id = status_get_party_id(src);
- group->guild_id = status_get_guild_id(src);
- group->bg_id = bg_team_get_id(src);
- group->group_id = skill_get_new_group_id();
- group->unit = (struct skill_unit *)aCalloc(count,sizeof(struct skill_unit));
- group->unit_count = count;
- group->alive_count = 0;
- group->val1 = 0;
- group->val2 = 0;
- group->val3 = 0;
- group->skill_id = skill_id;
- group->skill_lv = skill_lv;
- group->unit_id = unit_id;
- group->map = src->m;
- group->limit = limit;
- group->interval = interval;
- group->tick = gettick();
- group->valstr = NULL;
-
- ud->skillunit[i] = group;
-
- if (skill_id == PR_SANCTUARY) //Sanctuary starts healing +1500ms after casted. [Skotlex]
- group->tick += 1500;
-
- idb_put(group_db, group->group_id, group);
- return group;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int line, const char* func)
-{
- struct block_list* src;
- struct unit_data *ud;
- int i,j;
-
- if( group == NULL )
- {
- ShowDebug("skill_delunitgroup: group is NULL (source=%s:%d, %s)! Please report this! (#3504)\n", file, line, func);
- return 0;
- }
-
- src=map_id2bl(group->src_id);
- ud = unit_bl2ud(src);
- if(!src || !ud) {
- ShowError("skill_delunitgroup: Group's source not found! (src_id: %d skill_id: %d)\n", group->src_id, group->skill_id);
- return 0;
- }
-
- if( !status_isdead(src) && ((TBL_PC*)src)->state.warping && !((TBL_PC*)src)->state.changemap ) {
- switch( group->skill_id ) {
- 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:
- skill_usave_add(((TBL_PC*)src), group->skill_id, group->skill_lv);
- break;
- }
- }
-
- if (skill_get_unit_flag(group->skill_id)&(UF_DANCE|UF_SONG|UF_ENSEMBLE))
- {
- struct status_change* sc = status_get_sc(src);
- if (sc && sc->data[SC_DANCING])
- {
- sc->data[SC_DANCING]->val2 = 0 ; //This prevents status_change_end attempting to redelete the group. [Skotlex]
- status_change_end(src, SC_DANCING, INVALID_TIMER);
- }
- }
-
- // end Gospel's status change on 'src'
- // (needs to be done when the group is deleted by other means than skill deactivation)
- if (group->unit_id == UNT_GOSPEL) {
- struct status_change *sc = status_get_sc(src);
- if(sc && sc->data[SC_GOSPEL]) {
- sc->data[SC_GOSPEL]->val3 = 0; //Remove reference to this group. [Skotlex]
- status_change_end(src, SC_GOSPEL, INVALID_TIMER);
- }
- }
-
- switch( group->skill_id ) {
- case SG_SUN_WARM:
- case SG_MOON_WARM:
- case SG_STAR_WARM:
- {
- struct status_change *sc = NULL;
- if( (sc = status_get_sc(src)) != NULL && sc->data[SC_WARM] ) {
- sc->data[SC_WARM]->val4 = 0;
- status_change_end(src, SC_WARM, INVALID_TIMER);
- }
- }
- break;
- case NC_NEUTRALBARRIER:
- {
- struct status_change *sc = NULL;
- if( (sc = status_get_sc(src)) != NULL && sc->data[SC_NEUTRALBARRIER_MASTER] ) {
- sc->data[SC_NEUTRALBARRIER_MASTER]->val2 = 0;
- status_change_end(src,SC_NEUTRALBARRIER_MASTER,INVALID_TIMER);
- }
- }
- break;
- case NC_STEALTHFIELD:
- {
- struct status_change *sc = NULL;
- if( (sc = status_get_sc(src)) != NULL && sc->data[SC_STEALTHFIELD_MASTER] ) {
- sc->data[SC_STEALTHFIELD_MASTER]->val2 = 0;
- status_change_end(src,SC_STEALTHFIELD_MASTER,INVALID_TIMER);
- }
- }
- break;
- case LG_BANDING:
- {
- struct status_change *sc = NULL;
- if( (sc = status_get_sc(src)) && sc->data[SC_BANDING] ) {
- sc->data[SC_BANDING]->val4 = 0;
- status_change_end(src,SC_BANDING,INVALID_TIMER);
- }
- }
- break;
- }
-
- if (src->type==BL_PC && group->state.ammo_consume)
- battle_consume_ammo((TBL_PC*)src, group->skill_id, group->skill_lv);
-
- group->alive_count=0;
-
- // remove all unit cells
- if(group->unit != NULL)
- for( i = 0; i < group->unit_count; i++ )
- skill_delunit(&group->unit[i]);
-
- // clear Talkie-box string
- if( group->valstr != NULL )
- {
- aFree(group->valstr);
- group->valstr = NULL;
- }
-
- idb_remove(group_db, group->group_id);
- map_freeblock(&group->unit->bl); // schedules deallocation of whole array (HACK)
- group->unit=NULL;
- group->group_id=0;
- group->unit_count=0;
-
- // locate this group, swap with the last entry and delete it
- ARR_FIND( 0, MAX_SKILLUNITGROUP, i, ud->skillunit[i] == group );
- ARR_FIND( i, MAX_SKILLUNITGROUP, j, ud->skillunit[j] == NULL ); j--;
- if( i < MAX_SKILLUNITGROUP )
- {
- ud->skillunit[i] = ud->skillunit[j];
- ud->skillunit[j] = NULL;
- ers_free(skill_unit_ers, group);
- }
- else
- ShowError("skill_delunitgroup: Group not found! (src_id: %d skill_id: %d)\n", group->src_id, group->skill_id);
-
- return 1;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_clear_unitgroup (struct block_list *src)
-{
- struct unit_data *ud = unit_bl2ud(src);
-
- nullpo_ret(ud);
-
- while (ud->skillunit[0])
- skill_delunitgroup(ud->skillunit[0]);
-
- return 1;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-struct skill_unit_group_tickset *skill_unitgrouptickset_search (struct block_list *bl, struct skill_unit_group *group, int tick)
-{
- int i,j=-1,k,s,id;
- struct unit_data *ud;
- struct skill_unit_group_tickset *set;
-
- nullpo_ret(bl);
- if (group->interval==-1)
- return NULL;
-
- ud = unit_bl2ud(bl);
- if (!ud) return NULL;
-
- set = ud->skillunittick;
-
- if (skill_get_unit_flag(group->skill_id)&UF_NOOVERLAP)
- id = s = group->skill_id;
- else
- id = s = group->group_id;
-
- for (i=0; i<MAX_SKILLUNITGROUPTICKSET; i++) {
- k = (i+s) % MAX_SKILLUNITGROUPTICKSET;
- if (set[k].id == id)
- return &set[k];
- else if (j==-1 && (DIFF_TICK(tick,set[k].tick)>0 || set[k].id==0))
- j=k;
- }
-
- if (j == -1) {
- ShowWarning ("skill_unitgrouptickset_search: tickset is full\n");
- j = id % MAX_SKILLUNITGROUPTICKSET;
- }
-
- set[j].id = id;
- set[j].tick = tick;
- return &set[j];
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_unit_timer_sub_onplace (struct block_list* bl, va_list ap)
-{
- struct skill_unit* unit = va_arg(ap,struct skill_unit *);
- struct skill_unit_group* group = unit->group;
- unsigned int tick = va_arg(ap,unsigned int);
-
- if( !unit->alive || bl->prev == NULL )
- return 0;
-
- nullpo_ret(group);
-
- if( !(skill_get_inf2(group->skill_id)&(INF2_SONG_DANCE|INF2_TRAP|INF2_NOLP)) && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR) )
- return 0; //AoE skills are ineffective. [Skotlex]
-
- if( battle_check_target(&unit->bl,bl,group->target_flag) <= 0 )
- return 0;
-
- skill_unit_onplace_timer(unit,bl,tick);
-
- return 1;
-}
-
-/**
- * @see DBApply
- */
-static int skill_unit_timer_sub(DBKey key, DBData *data, va_list ap)
-{
- struct skill_unit* unit = db_data2ptr(data);
- struct skill_unit_group* group = unit->group;
- unsigned int tick = va_arg(ap,unsigned int);
- bool dissonance;
- struct block_list* bl = &unit->bl;
-
- if( !unit->alive )
- return 0;
-
- nullpo_ret(group);
-
- // check for expiration
- if( !group->state.guildaura && (DIFF_TICK(tick,group->tick) >= group->limit || DIFF_TICK(tick,group->tick) >= unit->limit) )
- {// skill unit expired (inlined from skill_unit_onlimit())
- switch( group->unit_id )
- {
- case UNT_BLASTMINE:
-#ifdef RENEWAL
- case UNT_CLAYMORETRAP:
-#endif
- case UNT_GROUNDDRIFT_WIND:
- case UNT_GROUNDDRIFT_DARK:
- case UNT_GROUNDDRIFT_POISON:
- case UNT_GROUNDDRIFT_WATER:
- case UNT_GROUNDDRIFT_FIRE:
- group->unit_id = UNT_USED_TRAPS;
- //clif_changetraplook(bl, UNT_FIREPILLAR_ACTIVE);
- group->limit=DIFF_TICK(tick+1500,group->tick);
- unit->limit=DIFF_TICK(tick+1500,group->tick);
- break;
-
- case UNT_ANKLESNARE:
- case UNT_ELECTRICSHOCKER:
- if( group->val2 > 0 ) {
- // Used Trap don't returns back to item
- skill_delunit(unit);
- break;
- }
- case UNT_SKIDTRAP:
- case UNT_LANDMINE:
- case UNT_SHOCKWAVE:
- case UNT_SANDMAN:
- case UNT_FLASHER:
- case UNT_FREEZINGTRAP:
-#ifndef RENEWAL
- case UNT_CLAYMORETRAP:
-#endif
- case UNT_TALKIEBOX:
- case UNT_CLUSTERBOMB:
- case UNT_MAGENTATRAP:
- case UNT_COBALTTRAP:
- case UNT_MAIZETRAP:
- case UNT_VERDURETRAP:
- case UNT_FIRINGTRAP:
- case UNT_ICEBOUNDTRAP:
-
- {
- struct block_list* src;
- if( unit->val1 > 0 && (src = map_id2bl(group->src_id)) != NULL && src->type == BL_PC )
- { // revert unit back into a trap
- struct item item_tmp;
- memset(&item_tmp,0,sizeof(item_tmp));
- item_tmp.nameid = group->item_id?group->item_id:ITEMID_TRAP;
- item_tmp.identify = 1;
- map_addflooritem(&item_tmp,1,bl->m,bl->x,bl->y,0,0,0,0);
- }
- skill_delunit(unit);
- }
- break;
-
- case UNT_WARP_ACTIVE:
- // warp portal opens (morph to a UNT_WARP_WAITING cell)
- group->unit_id = skill_get_unit_id(group->skill_id, 1); // UNT_WARP_WAITING
- clif_changelook(&unit->bl, LOOK_BASE, group->unit_id);
- // restart timers
- group->limit = skill_get_time(group->skill_id,group->skill_lv);
- unit->limit = skill_get_time(group->skill_id,group->skill_lv);
- // apply effect to all units standing on it
- map_foreachincell(skill_unit_effect,unit->bl.m,unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),1);
- break;
-
- case UNT_CALLFAMILY:
- {
- struct map_session_data *sd = NULL;
- if(group->val1) {
- sd = map_charid2sd(group->val1);
- group->val1 = 0;
- if (sd && !map[sd->bl.m].flag.nowarp)
- pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,CLR_TELEPORT);
- }
- if(group->val2) {
- sd = map_charid2sd(group->val2);
- group->val2 = 0;
- if (sd && !map[sd->bl.m].flag.nowarp)
- pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,CLR_TELEPORT);
- }
- skill_delunit(unit);
- }
- break;
-
- case UNT_REVERBERATION:
- if( unit->val1 <= 0 ) { // If it was deactivated.
- skill_delunit(unit);
- break;
- }
- clif_changetraplook(bl,UNT_USED_TRAPS);
- map_foreachinrange(skill_trap_splash, bl, skill_get_splash(group->skill_id, group->skill_lv), group->bl_flag, bl, tick);
- group->limit = DIFF_TICK(tick,group->tick)+1000;
- unit->limit = DIFF_TICK(tick,group->tick)+1000;
- group->unit_id = UNT_USED_TRAPS;
- break;
-
- case UNT_FEINTBOMB: {
- struct block_list *src = map_id2bl(group->src_id);
- if( src )
- map_foreachinrange(skill_area_sub, &group->unit->bl, unit->range, splash_target(src), src, SC_FEINTBOMB, group->skill_lv, tick, BCT_ENEMY|SD_ANIMATION|1, skill_castend_damage_id);
- skill_delunit(unit);
- break;
- }
-
- case UNT_BANDING:
- {
- struct block_list *src = map_id2bl(group->src_id);
- struct status_change *sc;
- if( !src || (sc = status_get_sc(src)) == NULL || !sc->data[SC_BANDING] )
- {
- skill_delunit(unit);
- break;
- }
- // This unit isn't removed while SC_BANDING is active.
- group->limit = DIFF_TICK(tick+group->interval,group->tick);
- unit->limit = DIFF_TICK(tick+group->interval,group->tick);
- }
- break;
-
- default:
- skill_delunit(unit);
- }
- }
- else
- {// skill unit is still active
- switch( group->unit_id )
- {
- case UNT_ICEWALL:
- // icewall loses 50 hp every second
- unit->val1 -= SKILLUNITTIMER_INTERVAL/20; // trap's hp
- if( unit->val1 <= 0 && unit->limit + group->tick > tick + 700 )
- unit->limit = DIFF_TICK(tick+700,group->tick);
- break;
- case UNT_BLASTMINE:
- case UNT_SKIDTRAP:
- case UNT_LANDMINE:
- case UNT_SHOCKWAVE:
- case UNT_SANDMAN:
- case UNT_FLASHER:
- case UNT_CLAYMORETRAP:
- case UNT_FREEZINGTRAP:
- case UNT_TALKIEBOX:
- case UNT_ANKLESNARE:
- if( unit->val1 <= 0 ) {
- if( group->unit_id == UNT_ANKLESNARE && group->val2 > 0 )
- skill_delunit(unit);
- else {
- clif_changetraplook(bl, group->unit_id==UNT_LANDMINE?UNT_FIREPILLAR_ACTIVE:UNT_USED_TRAPS);
- group->limit = DIFF_TICK(tick, group->tick) + 1500;
- group->unit_id = UNT_USED_TRAPS;
- }
- }
- break;
- case UNT_REVERBERATION:
- if( unit->val1 <= 0 ){
- clif_changetraplook(bl,UNT_USED_TRAPS);
- map_foreachinrange(skill_trap_splash, bl, skill_get_splash(group->skill_id, group->skill_lv), group->bl_flag, bl, tick);
- group->limit = DIFF_TICK(tick,group->tick)+1000;
- unit->limit = DIFF_TICK(tick,group->tick)+1000;
- group->unit_id = UNT_USED_TRAPS;
- }
- break;
- case UNT_WALLOFTHORN:
- if( unit->val1 <= 0 ) {
- group->unit_id = UNT_USED_TRAPS;
- group->limit = DIFF_TICK(tick, group->tick) + 1500;
- }
- break;
- }
- }
-
- //Don't continue if unit or even group is expired and has been deleted.
- if( !group || !unit->alive )
- return 0;
-
- dissonance = skill_dance_switch(unit, 0);
-
- if( unit->range >= 0 && group->interval != -1 )
- {
- if( battle_config.skill_wall_check )
- map_foreachinshootrange(skill_unit_timer_sub_onplace, bl, unit->range, group->bl_flag, bl,tick);
- else
- map_foreachinrange(skill_unit_timer_sub_onplace, bl, unit->range, group->bl_flag, bl,tick);
-
- if(unit->range == -1) //Unit disabled, but it should not be deleted yet.
- group->unit_id = UNT_USED_TRAPS;
-
- if( group->unit_id == UNT_TATAMIGAESHI )
- {
- unit->range = -1; //Disable processed cell.
- if (--group->val1 <= 0) // number of live cells
- { //All tiles were processed, disable skill.
- group->target_flag=BCT_NOONE;
- group->bl_flag= BL_NUL;
- }
- }
- }
-
- if( dissonance ) skill_dance_switch(unit, 1);
-
- return 0;
-}
-/*==========================================
- * Executes on all skill units every SKILLUNITTIMER_INTERVAL miliseconds.
- *------------------------------------------*/
-int skill_unit_timer(int tid, unsigned int tick, int id, intptr_t data)
-{
- map_freeblock_lock();
-
- skillunit_db->foreach(skillunit_db, skill_unit_timer_sub, tick);
-
- map_freeblock_unlock();
-
- return 0;
-}
-
-static int skill_unit_temp[20]; // temporary storage for tracking skill unit skill ids as players move in/out of them
-/*==========================================
- *
- *------------------------------------------*/
-int skill_unit_move_sub (struct block_list* bl, va_list ap)
-{
- struct skill_unit* unit = (struct skill_unit *)bl;
- struct skill_unit_group* group = unit->group;
-
- struct block_list* target = va_arg(ap,struct block_list*);
- unsigned int tick = va_arg(ap,unsigned int);
- int flag = va_arg(ap,int);
-
- bool dissonance;
- uint16 skill_id;
- int i;
-
- nullpo_ret(group);
-
- if( !unit->alive || target->prev == NULL )
- return 0;
-
- if( flag&1 && ( unit->group->skill_id == PF_SPIDERWEB || unit->group->skill_id == GN_THORNS_TRAP ) )
- return 0; // Fiberlock is never supposed to trigger on skill_unit_move. [Inkfish]
-
- dissonance = skill_dance_switch(unit, 0);
-
- //Necessary in case the group is deleted after calling on_place/on_out [Skotlex]
- skill_id = unit->group->skill_id;
-
- if( unit->group->interval != -1 && !(skill_get_unit_flag(skill_id)&UF_DUALMODE) && skill_id != BD_LULLABY ) //Lullaby is the exception, bugreport:411
- { //Non-dualmode unit skills with a timer don't trigger when walking, so just return
- if( dissonance ) skill_dance_switch(unit, 1);
- return 0;
- }
-
- //Target-type check.
- if( !(group->bl_flag&target->type && battle_check_target(&unit->bl,target,group->target_flag) > 0) )
- {
- if( group->src_id == target->id && group->state.song_dance&0x2 )
- { //Ensemble check to see if they went out/in of the area [Skotlex]
- if( flag&1 )
- {
- if( flag&2 )
- { //Clear this skill id.
- ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == skill_id );
- if( i < ARRAYLENGTH(skill_unit_temp) )
- skill_unit_temp[i] = 0;
- }
- }
- else
- {
- if( flag&2 )
- { //Store this skill id.
- ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == 0 );
- if( i < ARRAYLENGTH(skill_unit_temp) )
- skill_unit_temp[i] = skill_id;
- else
- ShowError("skill_unit_move_sub: Reached limit of unit objects per cell!\n");
- }
-
- }
-
- if( flag&4 )
- skill_unit_onleft(skill_id,target,tick);
- }
-
- if( dissonance ) skill_dance_switch(unit, 1);
-
- return 0;
- }
- else
- {
- if( flag&1 )
- {
- int result = skill_unit_onplace(unit,target,tick);
- if( flag&2 && result )
- { //Clear skill ids we have stored in onout.
- ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == result );
- if( i < ARRAYLENGTH(skill_unit_temp) )
- skill_unit_temp[i] = 0;
- }
- }
- else
- {
- int result = skill_unit_onout(unit,target,tick);
- if( flag&2 && result )
- { //Store this unit id.
- ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == 0 );
- if( i < ARRAYLENGTH(skill_unit_temp) )
- skill_unit_temp[i] = skill_id;
- else
- ShowError("skill_unit_move_sub: Reached limit of unit objects per cell!\n");
- }
- }
-
- //TODO: Normally, this is dangerous since the unit and group could be freed
- //inside the onout/onplace functions. Currently it is safe because we know song/dance
- //cells do not get deleted within them. [Skotlex]
- if( dissonance ) skill_dance_switch(unit, 1);
-
- if( flag&4 )
- skill_unit_onleft(skill_id,target,tick);
-
- return 1;
- }
-}
-
-/*==========================================
- * Invoked when a char has moved and unit cells must be invoked (onplace, onout, onleft)
- * Flag values:
- * flag&1: invoke skill_unit_onplace (otherwise invoke skill_unit_onout)
- * flag&2: this function is being invoked twice as a bl moves, store in memory the affected
- * units to figure out when they have left a group.
- * flag&4: Force a onleft event (triggered when the bl is killed, for example)
- *------------------------------------------*/
-int skill_unit_move (struct block_list *bl, unsigned int tick, int flag)
-{
- nullpo_ret(bl);
-
- if( bl->prev == NULL )
- return 0;
-
- if( flag&2 && !(flag&1) )
- { //Onout, clear data
- memset(skill_unit_temp, 0, sizeof(skill_unit_temp));
- }
-
- map_foreachincell(skill_unit_move_sub,bl->m,bl->x,bl->y,BL_SKILL,bl,tick,flag);
-
- if( flag&2 && flag&1 )
- { //Onplace, check any skill units you have left.
- int i;
- for( i = 0; i < ARRAYLENGTH(skill_unit_temp); i++ )
- if( skill_unit_temp[i] )
- skill_unit_onleft(skill_unit_temp[i], bl, tick);
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_unit_move_unit_group (struct skill_unit_group *group, int16 m, int16 dx, int16 dy)
-{
- int i,j;
- unsigned int tick = gettick();
- int *m_flag;
- struct skill_unit *unit1;
- struct skill_unit *unit2;
-
- if (group == NULL)
- return 0;
- if (group->unit_count<=0)
- return 0;
- if (group->unit==NULL)
- return 0;
-
- if (skill_get_unit_flag(group->skill_id)&UF_ENSEMBLE)
- return 0; //Ensembles may not be moved around.
-
- if( group->unit_id == UNT_ICEWALL || group->unit_id == UNT_WALLOFTHORN )
- return 0; //Icewalls and Wall of Thorns don't get knocked back
-
- m_flag = (int *) aCalloc(group->unit_count, sizeof(int));
- // m_flag
- // 0: Neither of the following (skill_unit_onplace & skill_unit_onout are needed)
- // 1: Unit will move to a slot that had another unit of the same group (skill_unit_onplace not needed)
- // 2: Another unit from same group will end up positioned on this unit (skill_unit_onout not needed)
- // 3: Both 1+2.
- for(i=0;i<group->unit_count;i++){
- unit1=&group->unit[i];
- if (!unit1->alive || unit1->bl.m!=m)
- continue;
- for(j=0;j<group->unit_count;j++){
- unit2=&group->unit[j];
- if (!unit2->alive)
- continue;
- if (unit1->bl.x+dx==unit2->bl.x && unit1->bl.y+dy==unit2->bl.y){
- m_flag[i] |= 0x1;
- }
- if (unit1->bl.x-dx==unit2->bl.x && unit1->bl.y-dy==unit2->bl.y){
- m_flag[i] |= 0x2;
- }
- }
- }
- j = 0;
- for (i=0;i<group->unit_count;i++) {
- unit1=&group->unit[i];
- if (!unit1->alive)
- continue;
- if (!(m_flag[i]&0x2)) {
- if (group->state.song_dance&0x1) //Cancel dissonance effect.
- skill_dance_overlap(unit1, 0);
- map_foreachincell(skill_unit_effect,unit1->bl.m,unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,4);
- }
- //Move Cell using "smart" criteria (avoid useless moving around)
- switch(m_flag[i])
- {
- case 0:
- //Cell moves independently, safely move it.
- map_moveblock(&unit1->bl, unit1->bl.x+dx, unit1->bl.y+dy, tick);
- break;
- case 1:
- //Cell moves unto another cell, look for a replacement cell that won't collide
- //and has no cell moving into it (flag == 2)
- for(;j<group->unit_count;j++)
- {
- if(m_flag[j]!=2 || !group->unit[j].alive)
- continue;
- //Move to where this cell would had moved.
- unit2 = &group->unit[j];
- map_moveblock(&unit1->bl, unit2->bl.x+dx, unit2->bl.y+dy, tick);
- j++; //Skip this cell as we have used it.
- break;
- }
- break;
- case 2:
- case 3:
- break; //Don't move the cell as a cell will end on this tile anyway.
- }
- if (!(m_flag[i]&0x2)) { //We only moved the cell in 0-1
- if (group->state.song_dance&0x1) //Check for dissonance effect.
- skill_dance_overlap(unit1, 1);
- clif_skill_setunit(unit1);
- map_foreachincell(skill_unit_effect,unit1->bl.m,unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,1);
- }
- }
- aFree(m_flag);
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_can_produce_mix (struct map_session_data *sd, int nameid, int trigger, int qty)
-{
- int i,j;
-
- nullpo_ret(sd);
-
- if(nameid<=0)
- return 0;
-
- for(i=0;i<MAX_SKILL_PRODUCE_DB;i++){
- if(skill_produce_db[i].nameid == nameid ){
- if((j=skill_produce_db[i].req_skill)>0 &&
- pc_checkskill(sd,j) < skill_produce_db[i].req_skill_lv)
- continue; // must iterate again to check other skills that produce it. [malufett]
- if( j > 0 && sd->menuskill_id > 0 && sd->menuskill_id != j )
- continue; // special case
- break;
- }
- }
-
- if( i >= MAX_SKILL_PRODUCE_DB )
- return 0;
-
- if( pc_checkadditem(sd, nameid, qty) == ADDITEM_OVERAMOUNT )
- {// cannot carry the produced stuff
- return 0;
- }
-
- if(trigger>=0){
- if(trigger>20) { // Non-weapon, non-food item (itemlv must match)
- if(skill_produce_db[i].itemlv!=trigger)
- return 0;
- } else if(trigger>10) { // Food (any item level between 10 and 20 will do)
- if(skill_produce_db[i].itemlv<=10 || skill_produce_db[i].itemlv>20)
- return 0;
- } else { // Weapon (itemlv must be higher or equal)
- if(skill_produce_db[i].itemlv>trigger)
- return 0;
- }
- }
-
- for(j=0;j<MAX_PRODUCE_RESOURCE;j++){
- int id,x,y;
- if( (id=skill_produce_db[i].mat_id[j]) <= 0 )
- continue;
- if(skill_produce_db[i].mat_amount[j] <= 0) {
- if(pc_search_inventory(sd,id) < 0)
- return 0;
- }
- else {
- for(y=0,x=0;y<MAX_INVENTORY;y++)
- if( sd->status.inventory[y].nameid == id )
- x+=sd->status.inventory[y].amount;
- if(x<qty*skill_produce_db[i].mat_amount[j])
- return 0;
- }
- }
- return i+1;
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int skill_produce_mix (struct map_session_data *sd, uint16 skill_id, int nameid, int slot1, int slot2, int slot3, int qty)
-{
- int slot[3];
- int i,sc,ele,idx,equip,wlv,make_per = 0,flag = 0,skill_lv = 0;
- int num = -1; // exclude the recipe
- struct status_data *status;
- struct item_data* data;
-
- nullpo_ret(sd);
- status = status_get_status_data(&sd->bl);
-
- if( sd->skill_id_old == skill_id )
- skill_lv = sd->skill_lv_old;
-
- if( !(idx=skill_can_produce_mix(sd,nameid,-1, qty)) )
- return 0;
- idx--;
-
- if (qty < 1)
- qty = 1;
-
- if (!skill_id) //A skill can be specified for some override cases.
- skill_id = skill_produce_db[idx].req_skill;
-
- if( skill_id == GC_RESEARCHNEWPOISON )
- skill_id = GC_CREATENEWPOISON;
-
- slot[0]=slot1;
- slot[1]=slot2;
- slot[2]=slot3;
-
- for(i=0,sc=0,ele=0;i<3;i++){ //Note that qty should always be one if you are using these!
- int j;
- if( slot[i]<=0 )
- continue;
- j = pc_search_inventory(sd,slot[i]);
- if(j < 0)
- continue;
- if(slot[i]==1000){ /* Star Crumb */
- pc_delitem(sd,j,1,1,0,LOG_TYPE_PRODUCE);
- sc++;
- }
- if(slot[i]>=994 && slot[i]<=997 && ele==0){ /* Flame Heart . . . Great Nature */
- static const int ele_table[4]={3,1,4,2};
- pc_delitem(sd,j,1,1,0,LOG_TYPE_PRODUCE);
- ele=ele_table[slot[i]-994];
- }
- }
-
- if( skill_id == RK_RUNEMASTERY ) {
- int temp_qty, skill_lv = pc_checkskill(sd,skill_id);
- data = itemdb_search(nameid);
-
- if( skill_lv == 10 ) temp_qty = 1 + rnd()%3;
- else if( skill_lv > 5 ) temp_qty = 1 + rnd()%2;
- else temp_qty = 1;
-
- if (data->stack.inventory) {
- for( i = 0; i < MAX_INVENTORY; i++ ) {
- if( sd->status.inventory[i].nameid == nameid ) {
- if( sd->status.inventory[i].amount >= data->stack.amount ) {
- clif_msgtable(sd->fd,0x61b);
- return 0;
- } else {
- /**
- * the amount fits, say we got temp_qty 4 and 19 runes, we trim temp_qty to 1.
- **/
- if( temp_qty + sd->status.inventory[i].amount >= data->stack.amount )
- temp_qty = data->stack.amount - sd->status.inventory[i].amount;
- }
- break;
- }
- }
- }
- qty = temp_qty;
- }
-
- for(i=0;i<MAX_PRODUCE_RESOURCE;i++){
- int j,id,x;
- if( (id=skill_produce_db[idx].mat_id[i]) <= 0 )
- continue;
- num++;
- x=( skill_id == RK_RUNEMASTERY ? 1 : qty)*skill_produce_db[idx].mat_amount[i];
- do{
- int y=0;
- j = pc_search_inventory(sd,id);
-
- if(j >= 0){
- y = sd->status.inventory[j].amount;
- if(y>x)y=x;
- pc_delitem(sd,j,y,0,0,LOG_TYPE_PRODUCE);
- } else
- ShowError("skill_produce_mix: material item error\n");
-
- x-=y;
- }while( j>=0 && x>0 );
- }
-
- if( (equip = (itemdb_isequip(nameid) && skill_id != GN_CHANGEMATERIAL && skill_id != GN_MAKEBOMB )) )
- wlv = itemdb_wlv(nameid);
- if(!equip) {
- switch(skill_id){
- case BS_IRON:
- case BS_STEEL:
- case BS_ENCHANTEDSTONE:
- // Ores & Metals Refining - skill bonuses are straight from kRO website [DracoRPG]
- i = pc_checkskill(sd,skill_id);
- make_per = sd->status.job_level*20 + status->dex*10 + status->luk*10; //Base chance
- switch(nameid){
- case 998: // Iron
- make_per += 4000+i*500; // Temper Iron bonus: +26/+32/+38/+44/+50
- break;
- case 999: // Steel
- make_per += 3000+i*500; // Temper Steel bonus: +35/+40/+45/+50/+55
- break;
- case 1000: //Star Crumb
- make_per = 100000; // Star Crumbs are 100% success crafting rate? (made 1000% so it succeeds even after penalties) [Skotlex]
- break;
- default: // Enchanted Stones
- make_per += 1000+i*500; // Enchantedstone Craft bonus: +15/+20/+25/+30/+35
- break;
- }
- break;
- case ASC_CDP:
- make_per = (2000 + 40*status->dex + 20*status->luk);
- break;
- case AL_HOLYWATER:
- /**
- * Arch Bishop
- **/
- case AB_ANCILLA:
- make_per = 100000; //100% success
- break;
- case AM_PHARMACY: // Potion Preparation - reviewed with the help of various Ragnainfo sources [DracoRPG]
- case AM_TWILIGHT1:
- case AM_TWILIGHT2:
- case AM_TWILIGHT3:
- make_per = pc_checkskill(sd,AM_LEARNINGPOTION)*50
- + pc_checkskill(sd,AM_PHARMACY)*300 + sd->status.job_level*20
- + (status->int_/2)*10 + status->dex*10+status->luk*10;
- if(merc_is_hom_active(sd->hd)) {//Player got a homun
- int skill;
- if((skill=merc_hom_checkskill(sd->hd,HVAN_INSTRUCT)) > 0) //His homun is a vanil with instruction change
- make_per += skill*100; //+1% bonus per level
- }
- switch(nameid){
- case 501: // Red Potion
- case 503: // Yellow Potion
- case 504: // White Potion
- make_per += (1+rnd()%100)*10 + 2000;
- break;
- case 970: // Alcohol
- make_per += (1+rnd()%100)*10 + 1000;
- break;
- case 7135: // Bottle Grenade
- case 7136: // Acid Bottle
- case 7137: // Plant Bottle
- case 7138: // Marine Sphere Bottle
- make_per += (1+rnd()%100)*10;
- break;
- case 546: // Condensed Yellow Potion
- make_per -= (1+rnd()%50)*10;
- break;
- case 547: // Condensed White Potion
- case 7139: // Glistening Coat
- make_per -= (1+rnd()%100)*10;
- break;
- //Common items, recieve no bonus or penalty, listed just because they are commonly produced
- case 505: // Blue Potion
- case 545: // Condensed Red Potion
- case 605: // Anodyne
- case 606: // Aloevera
- default:
- break;
- }
- if(battle_config.pp_rate != 100)
- make_per = make_per * battle_config.pp_rate / 100;
- break;
- case SA_CREATECON: // Elemental Converter Creation
- make_per = 100000; // should be 100% success rate
- break;
- /**
- * Rune Knight
- **/
- case RK_RUNEMASTERY:
- {
- int A = 100 * (51 + 2 * pc_checkskill(sd, skill_id));
- int B = 100 * status->dex / 30 + 10 * (status->luk + sd->status.job_level);
- int C = 100 * cap_value(sd->itemid,0,100); //itemid depend on makerune()
- int D = 0;
- switch (nameid) { //rune rank it_diff 9 craftable rune
- case ITEMID_BERKANA:
- D = -2000;
- break; //Rank S
- case ITEMID_NAUTHIZ:
- case ITEMID_URUZ:
- D = -1500;
- break; //Rank A
- case ITEMID_ISA:
- case ITEMID_WYRD:
- D = -1000;
- break; //Rank B
- case ITEMID_RAIDO:
- case ITEMID_THURISAZ:
- case ITEMID_HAGALAZ:
- case ITEMID_OTHILA:
- D = -500;
- break; //Rank C
- default: D = -1500;
- break; //not specified =-15%
- }
- make_per = A + B + C + D;
- break;
- }
- /**
- * Guilotine Cross
- **/
- case GC_CREATENEWPOISON:
- make_per = 3000 + 500 * pc_checkskill(sd,GC_RESEARCHNEWPOISON);
- qty = 1+rnd()%pc_checkskill(sd,GC_RESEARCHNEWPOISON);
- break;
- case GN_CHANGEMATERIAL:
- for(i=0; i<MAX_SKILL_PRODUCE_DB; i++)
- if( skill_changematerial_db[i].itemid == nameid ){
- make_per = skill_changematerial_db[i].rate * 10;
- break;
- }
- break;
- case GN_S_PHARMACY:
- {
- int difficulty = 0;
-
- difficulty = (620 - 20 * skill_lv);// (620 - 20 * Skill Level)
-
- make_per = status->int_ + status->dex/2 + status->luk + sd->status.job_level + (30+rnd()%120) + // (Caster?s INT) + (Caster?s DEX / 2) + (Caster?s LUK) + (Caster?s Job Level) + Random number between (30 ~ 150) +
- (sd->status.base_level-100) + pc_checkskill(sd, AM_LEARNINGPOTION) + pc_checkskill(sd, CR_FULLPROTECTION)*(4+rnd()%6); // (Caster?s Base Level - 100) + (Potion Research x 5) + (Full Chemical Protection Skill Level) x (Random number between 4 ~ 10)
-
- switch(nameid){// difficulty factor
- case 12422: case 12425:
- case 12428:
- difficulty += 10;
- break;
- case 6212: case 12426:
- difficulty += 15;
- break;
- case 13264: case 12423:
- case 12427: case 12436:
- difficulty += 20;
- break;
- case 6210: case 6211:
- case 12437:
- difficulty += 30;
- break;
- case 12424: case 12475:
- difficulty += 40;
- break;
- }
-
- if( make_per >= 400 && make_per > difficulty)
- qty = 10;
- else if( make_per >= 300 && make_per > difficulty)
- qty = 7;
- else if( make_per >= 100 && make_per > difficulty)
- qty = 6;
- else if( make_per >= 1 && make_per > difficulty)
- qty = 5;
- else
- qty = 4;
- make_per = 10000;
- }
- break;
- case GN_MAKEBOMB:
- case GN_MIX_COOKING:
- {
- int difficulty = 30 + rnd()%120; // Random number between (30 ~ 150)
-
- make_per = sd->status.job_level / 4 + status->luk / 2 + status->dex / 3; // (Caster?s Job Level / 4) + (Caster?s LUK / 2) + (Caster?s DEX / 3)
- qty = ~(5 + rnd()%5) + 1;
-
- switch(nameid){// difficulty factor
- case 13260:
- difficulty += 5;
- break;
- case 13261: case 13262:
- difficulty += 10;
- break;
- case 12429: case 12430: case 12431:
- case 12432: case 12433: case 12434:
- case 13263:
- difficulty += 15;
- break;
- case 13264:
- difficulty += 20;
- break;
- }
-
- if( make_per >= 30 && make_per > difficulty)
- qty = 10 + rnd()%2;
- else if( make_per >= 10 && make_per > difficulty)
- qty = 10;
- else if( make_per == 10 && make_per > difficulty)
- qty = 8;
- else if( (make_per >= 50 || make_per < 30) && make_per < difficulty)
- ;// Food/Bomb creation fails.
- else if( make_per >= 30 && make_per < difficulty)
- qty = 5;
-
- if( qty < 0 || (skill_lv == 1 && make_per < difficulty)){
- qty = ~qty + 1;
- make_per = 0;
- }else
- make_per = 10000;
- qty = (skill_lv > 1 ? qty : 1);
- }
- break;
- default:
- if (sd->menuskill_id == AM_PHARMACY &&
- sd->menuskill_val > 10 && sd->menuskill_val <= 20)
- { //Assume Cooking Dish
- if (sd->menuskill_val >= 15) //Legendary Cooking Set.
- make_per = 10000; //100% Success
- else
- make_per = 1200 * (sd->menuskill_val - 10)
- + 20 * (sd->status.base_level + 1)
- + 20 * (status->dex + 1)
- + 100 * (rnd()%(30+5*(sd->cook_mastery/400) - (6+sd->cook_mastery/80)) + (6+sd->cook_mastery/80))
- - 400 * (skill_produce_db[idx].itemlv - 11 + 1)
- - 10 * (100 - status->luk + 1)
- - 500 * (num - 1)
- - 100 * (rnd()%4 + 1);
- break;
- }
- make_per = 5000;
- break;
- }
- } else { // Weapon Forging - skill bonuses are straight from kRO website, other things from a jRO calculator [DracoRPG]
- make_per = 5000 + sd->status.job_level*20 + status->dex*10 + status->luk*10; // Base
- make_per += pc_checkskill(sd,skill_id)*500; // Smithing skills bonus: +5/+10/+15
- make_per += pc_checkskill(sd,BS_WEAPONRESEARCH)*100 +((wlv >= 3)? pc_checkskill(sd,BS_ORIDEOCON)*100:0); // Weaponry Research bonus: +1/+2/+3/+4/+5/+6/+7/+8/+9/+10, Oridecon Research bonus (custom): +1/+2/+3/+4/+5
- make_per -= (ele?2000:0) + sc*1500 + (wlv>1?wlv*1000:0); // Element Stone: -20%, Star Crumb: -15% each, Weapon level malus: -0/-20/-30
- if(pc_search_inventory(sd,989) > 0) make_per+= 1000; // Emperium Anvil: +10
- else if(pc_search_inventory(sd,988) > 0) make_per+= 500; // Golden Anvil: +5
- else if(pc_search_inventory(sd,987) > 0) make_per+= 300; // Oridecon Anvil: +3
- else if(pc_search_inventory(sd,986) > 0) make_per+= 0; // Anvil: +0?
- if(battle_config.wp_rate != 100)
- make_per = make_per * battle_config.wp_rate / 100;
- }
-
- if (sd->class_&JOBL_BABY) //if it's a Baby Class
- make_per = (make_per * 50) / 100; //Baby penalty is 50% (bugreport:4847)
-
- if(make_per < 1) make_per = 1;
-
-
- if(rnd()%10000 < make_per || qty > 1){ //Success, or crafting multiple items.
- struct item tmp_item;
- memset(&tmp_item,0,sizeof(tmp_item));
- tmp_item.nameid=nameid;
- tmp_item.amount=1;
- tmp_item.identify=1;
- if(equip){
- tmp_item.card[0]=CARD0_FORGE;
- tmp_item.card[1]=((sc*5)<<8)+ele;
- tmp_item.card[2]=GetWord(sd->status.char_id,0); // CharId
- tmp_item.card[3]=GetWord(sd->status.char_id,1);
- } else {
- //Flag is only used on the end, so it can be used here. [Skotlex]
- switch (skill_id) {
- case BS_DAGGER:
- case BS_SWORD:
- case BS_TWOHANDSWORD:
- case BS_AXE:
- case BS_MACE:
- case BS_KNUCKLE:
- case BS_SPEAR:
- flag = battle_config.produce_item_name_input&0x1;
- break;
- case AM_PHARMACY:
- case AM_TWILIGHT1:
- case AM_TWILIGHT2:
- case AM_TWILIGHT3:
- flag = battle_config.produce_item_name_input&0x2;
- break;
- case AL_HOLYWATER:
- /**
- * Arch Bishop
- **/
- case AB_ANCILLA:
- flag = battle_config.produce_item_name_input&0x8;
- break;
- case ASC_CDP:
- flag = battle_config.produce_item_name_input&0x10;
- break;
- default:
- flag = battle_config.produce_item_name_input&0x80;
- break;
- }
- if (flag) {
- tmp_item.card[0]=CARD0_CREATE;
- tmp_item.card[1]=0;
- tmp_item.card[2]=GetWord(sd->status.char_id,0); // CharId
- tmp_item.card[3]=GetWord(sd->status.char_id,1);
- }
- }
-
-// if(log_config.produce > 0)
-// log_produce(sd,nameid,slot1,slot2,slot3,1);
-//TODO update PICKLOG
-
- if(equip){
- clif_produceeffect(sd,0,nameid);
- clif_misceffect(&sd->bl,3);
- if(itemdb_wlv(nameid) >= 3 && ((ele? 1 : 0) + sc) >= 3) // Fame point system [DracoRPG]
- pc_addfame(sd,10); // Success to forge a lv3 weapon with 3 additional ingredients = +10 fame point
- } else {
- int fame = 0;
- tmp_item.amount = 0;
-
- for (i=0; i< qty; i++) { //Apply quantity modifiers.
- if( (skill_id == GN_MIX_COOKING || skill_id == GN_MAKEBOMB || skill_id == GN_S_PHARMACY) && make_per > 1){
- tmp_item.amount = qty;
- break;
- }
- if (rnd()%10000 < make_per || qty == 1) { //Success
- tmp_item.amount++;
- if(nameid < 545 || nameid > 547)
- continue;
- if( skill_id != AM_PHARMACY &&
- skill_id != AM_TWILIGHT1 &&
- skill_id != AM_TWILIGHT2 &&
- skill_id != AM_TWILIGHT3 )
- continue;
- //Add fame as needed.
- switch(++sd->potion_success_counter) {
- case 3:
- fame+=1; // Success to prepare 3 Condensed Potions in a row
- break;
- case 5:
- fame+=3; // Success to prepare 5 Condensed Potions in a row
- break;
- case 7:
- fame+=10; // Success to prepare 7 Condensed Potions in a row
- break;
- case 10:
- fame+=50; // Success to prepare 10 Condensed Potions in a row
- sd->potion_success_counter = 0;
- break;
- }
- } else //Failure
- sd->potion_success_counter = 0;
- }
-
- if (fame)
- pc_addfame(sd,fame);
- //Visual effects and the like.
- switch (skill_id) {
- case AM_PHARMACY:
- case AM_TWILIGHT1:
- case AM_TWILIGHT2:
- case AM_TWILIGHT3:
- case ASC_CDP:
- clif_produceeffect(sd,2,nameid);
- clif_misceffect(&sd->bl,5);
- break;
- case BS_IRON:
- case BS_STEEL:
- case BS_ENCHANTEDSTONE:
- clif_produceeffect(sd,0,nameid);
- clif_misceffect(&sd->bl,3);
- break;
- case RK_RUNEMASTERY:
- case GC_CREATENEWPOISON:
- clif_produceeffect(sd,2,nameid);
- clif_misceffect(&sd->bl,5);
- break;
- default: //Those that don't require a skill?
- if( skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20)
- { //Cooking items.
- clif_specialeffect(&sd->bl, 608, AREA);
- if( sd->cook_mastery < 1999 )
- pc_setglobalreg(sd, "COOK_MASTERY",sd->cook_mastery + ( 1 << ( (skill_produce_db[idx].itemlv - 11) / 2 ) ) * 5);
- }
- break;
- }
- }
- if ( skill_id == GN_CHANGEMATERIAL && tmp_item.amount) { //Success
- int j, k = 0;
- for(i=0; i<MAX_SKILL_PRODUCE_DB; i++)
- if( skill_changematerial_db[i].itemid == nameid ){
- for(j=0; j<5; j++){
- if( rnd()%1000 < skill_changematerial_db[i].qty_rate[j] ){
- tmp_item.amount = qty * skill_changematerial_db[i].qty[j];
- if((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
- clif_additem(sd,0,0,flag);
- map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
- k++;
- }
- }
- break;
- }
- if( k ){
- clif_msg_skill(sd,skill_id,0x627);
- return 1;
- }
- } else if (tmp_item.amount) { //Success
- if((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
- clif_additem(sd,0,0,flag);
- map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
- if( skill_id == GN_MIX_COOKING || skill_id == GN_MAKEBOMB || skill_id == GN_S_PHARMACY )
- clif_msg_skill(sd,skill_id,0x627);
- return 1;
- }
- }
- //Failure
-// if(log_config.produce)
-// log_produce(sd,nameid,slot1,slot2,slot3,0);
-//TODO update PICKLOG
-
- if(equip){
- clif_produceeffect(sd,1,nameid);
- clif_misceffect(&sd->bl,2);
- } else {
- switch (skill_id) {
- case ASC_CDP: //25% Damage yourself, and display same effect as failed potion.
- status_percent_damage(NULL, &sd->bl, -25, 0, true);
- case AM_PHARMACY:
- case AM_TWILIGHT1:
- case AM_TWILIGHT2:
- case AM_TWILIGHT3:
- clif_produceeffect(sd,3,nameid);
- clif_misceffect(&sd->bl,6);
- sd->potion_success_counter = 0; // Fame point system [DracoRPG]
- break;
- case BS_IRON:
- case BS_STEEL:
- case BS_ENCHANTEDSTONE:
- clif_produceeffect(sd,1,nameid);
- clif_misceffect(&sd->bl,2);
- break;
- case RK_RUNEMASTERY:
- case GC_CREATENEWPOISON:
- clif_produceeffect(sd,3,nameid);
- clif_misceffect(&sd->bl,6);
- break;
- case GN_MIX_COOKING: {
- struct item tmp_item;
- const int compensation[5] = {13265, 13266, 13267, 12435, 13268};
- int rate = rnd()%500;
- memset(&tmp_item,0,sizeof(tmp_item));
- if( rate < 50) i = 4;
- else if( rate < 100) i = 2+rnd()%1;
- else if( rate < 250 ) i = 1;
- else if( rate < 500 ) i = 0;
- tmp_item.nameid = compensation[i];
- tmp_item.amount = qty;
- tmp_item.identify = 1;
- if( pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE) ) {
- clif_additem(sd,0,0,flag);
- map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
- clif_msg_skill(sd,skill_id,0x628);
- }
- break;
- case GN_MAKEBOMB:
- case GN_S_PHARMACY:
- case GN_CHANGEMATERIAL:
- clif_msg_skill(sd,skill_id,0x628);
- break;
- default:
- if( skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20 )
- { //Cooking items.
- clif_specialeffect(&sd->bl, 609, AREA);
- if( sd->cook_mastery > 0 )
- pc_setglobalreg(sd, "COOK_MASTERY", sd->cook_mastery - ( 1 << ((skill_produce_db[idx].itemlv - 11) / 2) ) - ( ( ( 1 << ((skill_produce_db[idx].itemlv - 11) / 2) ) >> 1 ) * 3 ));
- }
- }
- }
- return 0;
-}
-
-int skill_arrow_create (struct map_session_data *sd, int nameid)
-{
- int i,j,flag,index=-1;
- struct item tmp_item;
-
- nullpo_ret(sd);
-
- if(nameid <= 0)
- return 1;
-
- for(i=0;i<MAX_SKILL_ARROW_DB;i++)
- if(nameid == skill_arrow_db[i].nameid) {
- index = i;
- break;
- }
-
- if(index < 0 || (j = pc_search_inventory(sd,nameid)) < 0)
- return 1;
-
- pc_delitem(sd,j,1,0,0,LOG_TYPE_PRODUCE);
- for(i=0;i<MAX_ARROW_RESOURCE;i++) {
- memset(&tmp_item,0,sizeof(tmp_item));
- tmp_item.identify = 1;
- tmp_item.nameid = skill_arrow_db[index].cre_id[i];
- tmp_item.amount = skill_arrow_db[index].cre_amount[i];
- if(battle_config.produce_item_name_input&0x4) {
- tmp_item.card[0]=CARD0_CREATE;
- tmp_item.card[1]=0;
- tmp_item.card[2]=GetWord(sd->status.char_id,0); // CharId
- tmp_item.card[3]=GetWord(sd->status.char_id,1);
- }
- if(tmp_item.nameid <= 0 || tmp_item.amount <= 0)
- continue;
- if((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
- clif_additem(sd,0,0,flag);
- map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
- }
-
- return 0;
-}
-int skill_poisoningweapon( struct map_session_data *sd, int nameid) {
- sc_type type;
- int chance, i;
- nullpo_ret(sd);
- if( nameid <= 0 || (i = pc_search_inventory(sd,nameid)) < 0 || pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME) ) {
- clif_skill_fail(sd,GC_POISONINGWEAPON,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- switch( nameid )
- { // t_lv used to take duration from skill_get_time2
- case PO_PARALYSE: type = SC_PARALYSE; break;
- case PO_PYREXIA: type = SC_PYREXIA; break;
- case PO_DEATHHURT: type = SC_DEATHHURT; break;
- case PO_LEECHESEND: type = SC_LEECHESEND; break;
- case PO_VENOMBLEED: type = SC_VENOMBLEED; break;
- case PO_TOXIN: type = SC_TOXIN; break;
- case PO_MAGICMUSHROOM: type = SC_MAGICMUSHROOM; break;
- case PO_OBLIVIONCURSE: type = SC_OBLIVIONCURSE; break;
- default:
- clif_skill_fail(sd,GC_POISONINGWEAPON,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
-
- chance = 2 + 2 * sd->menuskill_val; // 2 + 2 * skill_lv
- sc_start4(&sd->bl, SC_POISONINGWEAPON, 100, pc_checkskill(sd, GC_RESEARCHNEWPOISON), //in Aegis it store the level of GC_RESEARCHNEWPOISON in val1
- type, chance, 0, skill_get_time(GC_POISONINGWEAPON, sd->menuskill_val));
-
- return 0;
-}
-
-static void skill_toggle_magicpower(struct block_list *bl, uint16 skill_id)
-{
- struct status_change *sc = status_get_sc(bl);
-
- // non-offensive and non-magic skills do not affect the status
- if (skill_get_nk(skill_id)&NK_NO_DAMAGE || !(skill_get_type(skill_id)&BF_MAGIC))
- return;
-
- if (sc && sc->count && sc->data[SC_MAGICPOWER])
- {
- if (sc->data[SC_MAGICPOWER]->val4)
- {
- status_change_end(bl, SC_MAGICPOWER, INVALID_TIMER);
- }
- else
- {
- sc->data[SC_MAGICPOWER]->val4 = 1;
- status_calc_bl(bl, status_sc2scb_flag(SC_MAGICPOWER));
-#ifndef RENEWAL
- if(bl->type == BL_PC){// update current display.
- clif_updatestatus(((TBL_PC *)bl),SP_MATK1);
- clif_updatestatus(((TBL_PC *)bl),SP_MATK2);
- }
-#endif
- }
- }
-}
-
-
-int skill_magicdecoy(struct map_session_data *sd, int nameid) {
- int x, y, i, class_, skill;
- struct mob_data *md;
- nullpo_ret(sd);
- skill = sd->menuskill_val;
-
- if( nameid <= 0 || !itemdb_is_element(nameid) || (i = pc_search_inventory(sd,nameid)) < 0 || !skill || pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME) )
- {
- clif_skill_fail(sd,NC_MAGICDECOY,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
-
- // Spawn Position
- pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME);
- x = sd->sc.comet_x;
- y = sd->sc.comet_y;
- sd->sc.comet_x = sd->sc.comet_y = 0;
- sd->menuskill_val = 0;
-
- class_ = (nameid == 990 || nameid == 991) ? 2043 + nameid - 990 : (nameid == 992) ? 2046 : 2045;
-
-
- md = mob_once_spawn_sub(&sd->bl, sd->bl.m, x, y, sd->status.name, class_, "", SZ_SMALL, AI_NONE);
- if( md ) {
- md->master_id = sd->bl.id;
- md->special_state.ai = AI_FLORA;
- if( md->deletetimer != INVALID_TIMER )
- delete_timer(md->deletetimer, mob_timer_delete);
- md->deletetimer = add_timer (gettick() + skill_get_time(NC_MAGICDECOY,skill), mob_timer_delete, md->bl.id, 0);
- mob_spawn(md);
- md->status.matk_min = md->status.matk_max = 250 + (50 * skill);
- }
-
- return 0;
-}
-
-// Warlock Spellbooks. [LimitLine/3CeAM]
-int skill_spellbook (struct map_session_data *sd, int nameid) {
- int i, max_preserve, skill_id, point;
- struct status_change *sc;
-
- nullpo_ret(sd);
-
- sc = status_get_sc(&sd->bl);
- status_change_end(&sd->bl, SC_STOP, INVALID_TIMER);
-
- for(i=SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++) if( sc && !sc->data[i] ) break;
- if( i > SC_MAXSPELLBOOK )
- {
- clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_READING, 0);
- return 0;
- }
-
- ARR_FIND(0,MAX_SKILL_SPELLBOOK_DB,i,skill_spellbook_db[i].nameid == nameid); // Search for information of this item
- if( i == MAX_SKILL_SPELLBOOK_DB ) return 0;
-
- if( !pc_checkskill(sd, (skill_id = skill_spellbook_db[i].skill_id)) )
- { // User don't know the skill
- sc_start(&sd->bl, SC_SLEEP, 100, 1, skill_get_time(WL_READING_SB, pc_checkskill(sd,WL_READING_SB)));
- clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_DIFFICULT_SLEEP, 0);
- return 0;
- }
-
- max_preserve = 4 * pc_checkskill(sd, WL_FREEZE_SP) + status_get_int(&sd->bl) / 10 + sd->status.base_level / 10;
- point = skill_spellbook_db[i].point;
-
- if( sc && sc->data[SC_READING_SB] ){
- if( (sc->data[SC_READING_SB]->val2 + point) > max_preserve )
- {
- clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_PRESERVATION_POINT, 0);
- return 0;
- }
- for(i = SC_MAXSPELLBOOK; i >= SC_SPELLBOOK1; i--){ // This is how official saves spellbook. [malufett]
- if( !sc->data[i] ){
- sc->data[SC_READING_SB]->val2 += point; // increase points
- sc_start4(&sd->bl, (sc_type)i, 100, skill_id, pc_checkskill(sd,skill_id), point, 0, INVALID_TIMER);
- break;
- }
- }
- }else{
- sc_start2(&sd->bl, SC_READING_SB, 100, 0, point, INVALID_TIMER);
- sc_start4(&sd->bl, SC_MAXSPELLBOOK, 100, skill_id, pc_checkskill(sd,skill_id), point, 0, INVALID_TIMER);
- }
-
- return 1;
-}
-int skill_select_menu(struct map_session_data *sd,uint16 skill_id) {
- int id, lv, prob, aslvl = 0;
- nullpo_ret(sd);
-
- if (sd->sc.data[SC_STOP]) {
- aslvl = sd->sc.data[SC_STOP]->val1;
- status_change_end(&sd->bl,SC_STOP,INVALID_TIMER);
- }
-
- if( skill_id >= GS_GLITTERING || skill_get_type(skill_id) != BF_MAGIC ||
- (id = sd->status.skill[skill_id].id) == 0 || sd->status.skill[skill_id].flag != SKILL_FLAG_PLAGIARIZED ) {
- clif_skill_fail(sd,SC_AUTOSHADOWSPELL,0,0);
- return 0;
- }
-
- lv = (aslvl + 1) / 2; // The level the skill will be autocasted
- lv = min(lv,sd->status.skill[skill_id].lv);
- prob = (aslvl == 10) ? 15 : (32 - 2 * aslvl); // Probability at level 10 was increased to 15.
- sc_start4(&sd->bl,SC__AUTOSHADOWSPELL,100,id,lv,prob,0,skill_get_time(SC_AUTOSHADOWSPELL,aslvl));
- return 0;
-}
-int skill_elementalanalysis(struct map_session_data* sd, int n, uint16 skill_lv, unsigned short* item_list) {
- int i;
-
- nullpo_ret(sd);
- nullpo_ret(item_list);
-
- if( n <= 0 )
- return 1;
-
- for( i = 0; i < n; i++ ) {
- int nameid, add_amount, del_amount, idx, product, flag;
- struct item tmp_item;
-
- idx = item_list[i*2+0]-2;
- del_amount = item_list[i*2+1];
-
- if( skill_lv == 2 )
- del_amount -= (del_amount % 10);
- add_amount = (skill_lv == 1) ? del_amount * (5 + rnd()%5) : del_amount / 10 ;
-
- if( (nameid = sd->status.inventory[idx].nameid) <= 0 || del_amount > sd->status.inventory[idx].amount ) {
- clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0);
- return 1;
- }
-
- switch( nameid ) {
- // Level 1
- case 994: product = 990; break; // Flame Heart -> Red Blood.
- case 995: product = 991; break; // Mystic Frozen -> Crystal Blue.
- case 996: product = 992; break; // Rough Wind -> Wind of Verdure.
- case 997: product = 993; break; // Great Nature -> Green Live.
- // Level 2
- case 990: product = 994; break; // Red Blood -> Flame Heart.
- case 991: product = 995; break; // Crystal Blue -> Mystic Frozen.
- case 992: product = 996; break; // Wind of Verdure -> Rough Wind.
- case 993: product = 997; break; // Green Live -> Great Nature.
- default:
- clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0);
- return 1;
- }
-
- if( pc_delitem(sd,idx,del_amount,0,1,LOG_TYPE_CONSUME) ) {
- clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0);
- return 1;
- }
-
- if( skill_lv == 2 && rnd()%100 < 25 ) { // At level 2 have a fail chance. You loose your items if it fails.
- clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0);
- return 1;
- }
-
-
- memset(&tmp_item,0,sizeof(tmp_item));
- tmp_item.nameid = product;
- tmp_item.amount = add_amount;
- tmp_item.identify = 1;
-
- if( tmp_item.amount ) {
- if( (flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_CONSUME)) ) {
- clif_additem(sd,0,0,flag);
- map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
- }
- }
-
- }
-
- return 0;
-}
-
-int skill_changematerial(struct map_session_data *sd, int n, unsigned short *item_list) {
- int i, j, k, c, p = 0, nameid, amount;
-
- nullpo_ret(sd);
- nullpo_ret(item_list);
-
- // Search for objects that can be created.
- for( i = 0; i < MAX_SKILL_PRODUCE_DB; i++ ) {
- if( skill_produce_db[i].itemlv == 26 ) {
- p = 0;
- do {
- c = 0;
- // Verification of overlap between the objects required and the list submitted.
- for( j = 0; j < MAX_PRODUCE_RESOURCE; j++ ) {
- if( skill_produce_db[i].mat_id[j] > 0 ) {
- for( k = 0; k < n; k++ ) {
- int idx = item_list[k*2+0]-2;
- nameid = sd->status.inventory[idx].nameid;
- amount = item_list[k*2+1];
- if( nameid > 0 && sd->status.inventory[idx].identify == 0 ){
- clif_msg_skill(sd,GN_CHANGEMATERIAL,0x62D);
- return 0;
- }
- if( nameid == skill_produce_db[i].mat_id[j] && (amount-p*skill_produce_db[i].mat_amount[j]) >= skill_produce_db[i].mat_amount[j]
- && (amount-p*skill_produce_db[i].mat_amount[j])%skill_produce_db[i].mat_amount[j] == 0 ) // must be in exact amount
- c++; // match
- }
- }
- else
- break; // No more items required
- }
- p++;
- } while(n == j && c == n);
- p--;
- if ( p > 0 ) {
- skill_produce_mix(sd,GN_CHANGEMATERIAL,skill_produce_db[i].nameid,0,0,0,p);
- return 1;
- }
- }
- }
-
- if( p == 0)
- clif_msg_skill(sd,GN_CHANGEMATERIAL,0x623);
-
- return 0;
-}
-/**
- * for Royal Guard's LG_TRAMPLE
- **/
-static int skill_destroy_trap( struct block_list *bl, va_list ap ) {
- struct skill_unit *su = (struct skill_unit *)bl;
- struct skill_unit_group *sg;
- unsigned int tick;
-
- nullpo_ret(su);
- tick = va_arg(ap, unsigned int);
-
- if (su->alive && (sg = su->group) && skill_get_inf2(sg->skill_id)&INF2_TRAP) {
- switch( sg->unit_id ) {
- case UNT_LANDMINE:
- case UNT_CLAYMORETRAP:
- case UNT_BLASTMINE:
- case UNT_SHOCKWAVE:
- case UNT_SANDMAN:
- case UNT_FLASHER:
- case UNT_FREEZINGTRAP:
- case UNT_CLUSTERBOMB:
- case UNT_FIRINGTRAP:
- case UNT_ICEBOUNDTRAP:
- map_foreachinrange(skill_trap_splash,&su->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &su->bl,tick);
- break;
- }
- // Traps aren't recovered.
- skill_delunit(su);
- }
- return 0;
-}
-/*==========================================
- *
- *------------------------------------------*/
-int skill_blockpc_end(int tid, unsigned int tick, int id, intptr_t data)
-{
- struct map_session_data *sd = map_id2sd(id);
- struct skill_cd * cd = NULL;
-
- if (data <= 0 || data >= MAX_SKILL)
- return 0;
- if (!sd) return 0;
- if (sd->blockskill[data] != (0x1|(tid&0xFE))) return 0;
-
- if( ( cd = idb_get(skillcd_db,sd->status.char_id) ) ) {
- int i,cursor;
- ARR_FIND( 0, cd->cursor+1, cursor, cd->skidx[cursor] == data );
- cd->duration[cursor] = 0;
- cd->skidx[cursor] = 0;
- cd->nameid[cursor] = 0;
- // compact the cool down list
- for( i = 0, cursor = 0; i < cd->cursor; i++ ) {
- if( cd->duration[i] == 0 )
- continue;
- if( cursor != i ) {
- cd->duration[cursor] = cd->duration[i];
- cd->skidx[cursor] = cd->skidx[i];
- cd->nameid[cursor] = cd->nameid[i];
- }
- cursor++;
- }
- if( cursor == 0 )
- idb_remove(skillcd_db,sd->status.char_id);
- else
- cd->cursor = cursor;
- }
-
- sd->blockskill[data] = 0;
- return 1;
-}
-
-/**
- * flags a singular skill as being blocked from persistent usage.
- * @param sd the player the skill delay affects
- * @param skill_id the skill which should be delayed
- * @param tick the length of time the delay should last
- * @param load whether this assignment is being loaded upon player login
- * @return 0 if successful, -1 otherwise
- */
-int skill_blockpc_start_(struct map_session_data *sd, uint16 skill_id, int tick, bool load)
-{
- int oskill_id = skill_id;
- struct skill_cd* cd = NULL;
- uint16 idx = skill_get_index(skill_id);
-
- nullpo_retr (-1, sd);
-
- if (idx == 0)
- return -1;
-
- if (tick < 1) {
- sd->blockskill[idx] = 0;
- return -1;
- }
-
- if( battle_config.display_status_timers )
- clif_skill_cooldown(sd, idx, tick);
-
- if( !load )
- {// not being loaded initially so ensure the skill delay is recorded
- if( !(cd = idb_get(skillcd_db,sd->status.char_id)) )
- {// create a new skill cooldown object for map storage
- CREATE( cd, struct skill_cd, 1 );
- idb_put( skillcd_db, sd->status.char_id, cd );
- }
-
- // record the skill duration in the database map
- cd->duration[cd->cursor] = tick;
- cd->skidx[cd->cursor] = idx;
- cd->nameid[cd->cursor] = oskill_id;
- cd->cursor++;
- }
-
- sd->blockskill[idx] = 0x1|(0xFE&add_timer(gettick()+tick,skill_blockpc_end,sd->bl.id,idx));
- return 0;
-}
-
-int skill_blockhomun_end(int tid, unsigned int tick, int id, intptr_t data) //[orn]
-{
- struct homun_data *hd = (TBL_HOM*) map_id2bl(id);
- if (data <= 0 || data >= MAX_SKILL)
- return 0;
- if (hd) hd->blockskill[data] = 0;
-
- return 1;
-}
-
-int skill_blockhomun_start(struct homun_data *hd, uint16 skill_id, int tick) //[orn]
-{
- uint16 idx = skill_get_index(skill_id);
- nullpo_retr (-1, hd);
-
-
- if (idx == 0)
- return -1;
-
- if (tick < 1) {
- hd->blockskill[idx] = 0;
- return -1;
- }
- hd->blockskill[idx] = 1;
- return add_timer(gettick() + tick, skill_blockhomun_end, hd->bl.id, idx);
-}
-
-int skill_blockmerc_end(int tid, unsigned int tick, int id, intptr_t data) //[orn]
-{
- struct mercenary_data *md = (TBL_MER*)map_id2bl(id);
- if( data <= 0 || data >= MAX_SKILL )
- return 0;
- if( md ) md->blockskill[data] = 0;
-
- return 1;
-}
-
-int skill_blockmerc_start(struct mercenary_data *md, uint16 skill_id, int tick)
-{
- uint16 idx = skill_get_index(skill_id);
- nullpo_retr (-1, md);
-
- if (idx == 0)
- return -1;
- if( tick < 1 )
- {
- md->blockskill[idx] = 0;
- return -1;
- }
- md->blockskill[idx] = 1;
- return add_timer(gettick() + tick, skill_blockmerc_end, md->bl.id, idx);
-}
-/**
- * Adds a new skill unit entry for this player to recast after map load
- **/
-void skill_usave_add(struct map_session_data * sd, uint16 skill_id, uint16 skill_lv) {
- struct skill_usave * sus = NULL;
-
- if( idb_exists(skillusave_db,sd->status.char_id) ) {
- idb_remove(skillusave_db,sd->status.char_id);
- }
-
- CREATE( sus, struct skill_usave, 1 );
- idb_put( skillusave_db, sd->status.char_id, sus );
-
- sus->skill_id = skill_id;
- sus->skill_lv = skill_lv;
-
- return;
-}
-void skill_usave_trigger(struct map_session_data *sd) {
- struct skill_usave * sus = NULL;
-
- if( ! (sus = idb_get(skillusave_db,sd->status.char_id)) ) {
- return;
- }
-
- skill_unitsetting(&sd->bl,sus->skill_id,sus->skill_lv,sd->bl.x,sd->bl.y,0);
-
- idb_remove(skillusave_db,sd->status.char_id);
-
- return;
-}
-/*
- *
- */
-int skill_split_str (char *str, char **val, int num)
-{
- int i;
-
- for( i = 0; i < num && str; i++ )
- {
- val[i] = str;
- str = strchr(str,',');
- if( str )
- *str++=0;
- }
-
- return i;
-}
-/*
- *
- */
-int skill_split_atoi (char *str, int *val)
-{
- int i, j, diff, step = 1;
-
- for (i=0; i<MAX_SKILL_LEVEL; i++) {
- if (!str) break;
- val[i] = atoi(str);
- str = strchr(str,':');
- if (str)
- *str++=0;
- }
- if(i==0) //No data found.
- return 0;
- if(i==1)
- { //Single value, have the whole range have the same value.
- for (; i < MAX_SKILL_LEVEL; i++)
- val[i] = val[i-1];
- return i;
- }
- //Check for linear change with increasing steps until we reach half of the data acquired.
- for (step = 1; step <= i/2; step++)
- {
- diff = val[i-1] - val[i-step-1];
- for(j = i-1; j >= step; j--)
- if ((val[j]-val[j-step]) != diff)
- break;
-
- if (j>=step) //No match, try next step.
- continue;
-
- for(; i < MAX_SKILL_LEVEL; i++)
- { //Apply linear increase
- val[i] = val[i-step]+diff;
- if (val[i] < 1 && val[i-1] >=0) //Check if we have switched from + to -, cap the decrease to 0 in said cases.
- { val[i] = 1; diff = 0; step = 1; }
- }
- return i;
- }
- //Okay.. we can't figure this one out, just fill out the stuff with the previous value.
- for (;i<MAX_SKILL_LEVEL; i++)
- val[i] = val[i-1];
- return i;
-}
-
-/*
- *
- */
-void skill_init_unit_layout (void)
-{
- int i,j,size,pos = 0;
-
- memset(skill_unit_layout,0,sizeof(skill_unit_layout));
-
- // standard square layouts go first
- for (i=0; i<=MAX_SQUARE_LAYOUT; i++) {
- size = i*2+1;
- skill_unit_layout[i].count = size*size;
- for (j=0; j<size*size; j++) {
- skill_unit_layout[i].dx[j] = (j%size-i);
- skill_unit_layout[i].dy[j] = (j/size-i);
- }
- }
-
- // afterwards add special ones
- pos = i;
- for (i=0;i<MAX_SKILL_DB;i++) {
- if (!skill_db[i].unit_id[0] || skill_db[i].unit_layout_type[0] != -1)
- continue;
- if( i >= HM_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) {
- int skill = i;
-
- if( i >= EL_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) {
- skill -= EL_SKILLRANGEMIN;
- skill += EL_SKILLBASE;
- }
- if( skill == EL_FIRE_MANTLE ) {
- static const int dx[] = {-1, 0, 1, 1, 1, 0,-1,-1};
- static const int dy[] = { 1, 1, 1, 0,-1,-1,-1, 0};
- skill_unit_layout[pos].count = 8;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- } else {
- switch (i) {
- case MG_FIREWALL:
- case WZ_ICEWALL:
- case WL_EARTHSTRAIN://Warlock
- // these will be handled later
- break;
- case PR_SANCTUARY:
- case NPC_EVILLAND: {
- 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};
- skill_unit_layout[pos].count = 21;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- 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};
- skill_unit_layout[pos].count = 33;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- break;
- case MH_POISON_MIST:
- case AS_VENOMDUST: {
- static const int dx[] = {-1, 0, 0, 0, 1};
- static const int dy[] = { 0,-1, 0, 1, 0};
- skill_unit_layout[pos].count = 5;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- break;
- case CR_GRANDCROSS:
- case NPC_GRANDDARKNESS: {
- 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};
- skill_unit_layout[pos].count = 29;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- break;
- case PF_FOGWALL: {
- static const int dx[] = {
- -2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
- static const int dy[] = {
- -1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};
- skill_unit_layout[pos].count = 15;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- break;
- case PA_GOSPEL: {
- 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};
- skill_unit_layout[pos].count = 33;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- break;
- case NJ_KAENSIN: {
- static const int dx[] = {-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
- static const int dy[] = { 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2};
- skill_unit_layout[pos].count = 24;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- break;
- case NJ_TATAMIGAESHI: {
- //Level 1 (count 4, cross of 3x3)
- static const int dx1[] = {-1, 1, 0, 0};
- static const int dy1[] = { 0, 0,-1, 1};
- //Level 2-3 (count 8, cross of 5x5)
- static const int dx2[] = {-2,-1, 1, 2, 0, 0, 0, 0};
- static const int dy2[] = { 0, 0, 0, 0,-2,-1, 1, 2};
- //Level 4-5 (count 12, cross of 7x7
- static const int dx3[] = {-3,-2,-1, 1, 2, 3, 0, 0, 0, 0, 0, 0};
- static const int dy3[] = { 0, 0, 0, 0, 0, 0,-3,-2,-1, 1, 2, 3};
- //lv1
- j = 0;
- skill_unit_layout[pos].count = 4;
- memcpy(skill_unit_layout[pos].dx,dx1,sizeof(dx1));
- memcpy(skill_unit_layout[pos].dy,dy1,sizeof(dy1));
- skill_db[i].unit_layout_type[j] = pos;
- //lv2/3
- j++;
- pos++;
- skill_unit_layout[pos].count = 8;
- memcpy(skill_unit_layout[pos].dx,dx2,sizeof(dx2));
- memcpy(skill_unit_layout[pos].dy,dy2,sizeof(dy2));
- skill_db[i].unit_layout_type[j] = pos;
- skill_db[i].unit_layout_type[++j] = pos;
- //lv4/5
- j++;
- pos++;
- skill_unit_layout[pos].count = 12;
- memcpy(skill_unit_layout[pos].dx,dx3,sizeof(dx3));
- memcpy(skill_unit_layout[pos].dy,dy3,sizeof(dy3));
- skill_db[i].unit_layout_type[j] = pos;
- skill_db[i].unit_layout_type[++j] = pos;
- //Fill in the rest using lv 5.
- for (;j<MAX_SKILL_LEVEL;j++)
- skill_db[i].unit_layout_type[j] = pos;
- //Skip, this way the check below will fail and continue to the next skill.
- pos++;
- }
- break;
- case GN_WALLOFTHORN: {
- static const int dx[] = {-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2, 2, 2, 1, 0};
- static const int dy[] = { 2, 2, 1, 0,-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2};
- skill_unit_layout[pos].count = 16;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- break;
- default:
- ShowError("unknown unit layout at skill %d\n",i);
- break;
- }
- }
- if (!skill_unit_layout[pos].count)
- continue;
- for (j=0;j<MAX_SKILL_LEVEL;j++)
- skill_db[i].unit_layout_type[j] = pos;
- pos++;
- }
-
- // firewall and icewall have 8 layouts (direction-dependent)
- firewall_unit_pos = pos;
- for (i=0;i<8;i++) {
- if (i&1) {
- skill_unit_layout[pos].count = 5;
- if (i&0x2) {
- int dx[] = {-1,-1, 0, 0, 1};
- int dy[] = { 1, 0, 0,-1,-1};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- } else {
- int dx[] = { 1, 1 ,0, 0,-1};
- int dy[] = { 1, 0, 0,-1,-1};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- } else {
- skill_unit_layout[pos].count = 3;
- if (i%4==0) {
- int dx[] = {-1, 0, 1};
- int dy[] = { 0, 0, 0};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- } else {
- int dx[] = { 0, 0, 0};
- int dy[] = {-1, 0, 1};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- }
- pos++;
- }
- icewall_unit_pos = pos;
- for (i=0;i<8;i++) {
- skill_unit_layout[pos].count = 5;
- if (i&1) {
- if (i&0x2) {
- int dx[] = {-2,-1, 0, 1, 2};
- int dy[] = { 2, 1, 0,-1,-2};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- } else {
- int dx[] = { 2, 1 ,0,-1,-2};
- int dy[] = { 2, 1, 0,-1,-2};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- } else {
- if (i%4==0) {
- int dx[] = {-2,-1, 0, 1, 2};
- int dy[] = { 0, 0, 0, 0, 0};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- } else {
- int dx[] = { 0, 0, 0, 0, 0};
- int dy[] = {-2,-1, 0, 1, 2};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- }
- pos++;
- }
- earthstrain_unit_pos = pos;
- for( i = 0; i < 8; i++ )
- { // For each Direction
- skill_unit_layout[pos].count = 15;
- switch( i )
- {
- case 0: case 1: case 3: case 4: case 5: case 7:
- {
- int dx[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7};
- int dy[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- break;
- case 2:
- case 6:
- {
- int dx[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
- int dy[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- break;
- }
- pos++;
- }
-
-}
-
-int skill_block_check(struct block_list *bl, sc_type type , uint16 skill_id) {
- int inf = 0;
- struct status_change *sc = status_get_sc(bl);
-
- if( !sc || !bl || !skill_id )
- return 0; // Can do it
-
- switch(type){
- case SC_STASIS:
- inf = skill_get_inf2(skill_id);
- if( inf == INF2_SONG_DANCE || /*skill_get_inf2(skill_id) == INF2_CHORUS_SKILL ||*/ inf == INF2_SPIRIT_SKILL )
- return 1; // Can't do it.
- switch( skill_id )
- {
- case NV_FIRSTAID: case TF_HIDING: case AS_CLOAKING: case WZ_SIGHTRASHER:
- case RG_STRIPWEAPON: case RG_STRIPSHIELD: case RG_STRIPARMOR: case WZ_METEOR:
- case RG_STRIPHELM: case SC_STRIPACCESSARY: case ST_FULLSTRIP: case WZ_SIGHTBLASTER:
- case ST_CHASEWALK: case SC_ENERVATION: case SC_GROOMY: case WZ_ICEWALL:
- case SC_IGNORANCE: case SC_LAZINESS: case SC_UNLUCKY: case WZ_STORMGUST:
- case SC_WEAKNESS: case AL_RUWACH: case AL_PNEUMA: case WZ_JUPITEL:
- case AL_HEAL: case AL_BLESSING: case AL_INCAGI: case WZ_VERMILION:
- case AL_TELEPORT: case AL_WARP: case AL_HOLYWATER: case WZ_EARTHSPIKE:
- case AL_HOLYLIGHT: case PR_IMPOSITIO: case PR_ASPERSIO: case WZ_HEAVENDRIVE:
- case PR_SANCTUARY: case PR_STRECOVERY: case PR_MAGNIFICAT: case WZ_QUAGMIRE:
- case ALL_RESURRECTION: case PR_LEXDIVINA: case PR_LEXAETERNA: case HW_GRAVITATION:
- case PR_MAGNUS: case PR_TURNUNDEAD: case MG_SRECOVERY: case HW_MAGICPOWER:
- case MG_SIGHT: case MG_NAPALMBEAT: case MG_SAFETYWALL: case HW_GANBANTEIN:
- case MG_SOULSTRIKE: case MG_COLDBOLT: case MG_FROSTDIVER: case WL_DRAINLIFE:
- case MG_STONECURSE: case MG_FIREBALL: case MG_FIREWALL: case WL_SOULEXPANSION:
- case MG_FIREBOLT: case MG_LIGHTNINGBOLT: case MG_THUNDERSTORM: case MG_ENERGYCOAT:
- case WL_WHITEIMPRISON: case WL_SUMMONFB: case WL_SUMMONBL: case WL_SUMMONWB:
- case WL_SUMMONSTONE: case WL_SIENNAEXECRATE: case WL_RELEASE: case WL_EARTHSTRAIN:
- case WL_RECOGNIZEDSPELL: case WL_READING_SB: case SA_MAGICROD: case SA_SPELLBREAKER:
- case SA_DISPELL: case SA_FLAMELAUNCHER: case SA_FROSTWEAPON: case SA_LIGHTNINGLOADER:
- case SA_SEISMICWEAPON: case SA_VOLCANO: case SA_DELUGE: case SA_VIOLENTGALE:
- case SA_LANDPROTECTOR: case PF_HPCONVERSION: case PF_SOULCHANGE: case PF_SPIDERWEB:
- case PF_FOGWALL: case TK_RUN: case TK_HIGHJUMP: case TK_SEVENWIND:
- case SL_KAAHI: case SL_KAUPE: case SL_KAITE:
-
- // Skills that need to be confirmed.
- case SO_FIREWALK: case SO_ELECTRICWALK: case SO_SPELLFIST: case SO_EARTHGRAVE:
- case SO_DIAMONDDUST: case SO_POISON_BUSTER: case SO_PSYCHIC_WAVE: case SO_CLOUD_KILL:
- case SO_STRIKING: case SO_WARMER: case SO_VACUUM_EXTREME: case SO_VARETYR_SPEAR:
- case SO_ARRULLO:
- return 1; // Can't do it.
- }
- break;
- case SC_KAGEHUMI:
- switch(skill_id){
- case TF_HIDING: case AS_CLOAKING: case GC_CLOAKINGEXCEED: case SC_SHADOWFORM:
- case MI_HARMONIZE: case CG_MARIONETTE: case AL_TELEPORT: case TF_BACKSLIDING:
- case RA_CAMOUFLAGE: case ST_CHASEWALK: case GD_EMERGENCYCALL:
- return 1; // needs more info
- }
- break;
- }
-
- return 0;
-}
-
-int skill_get_elemental_type( uint16 skill_id , uint16 skill_lv ) {
- int type = 0;
-
- switch( skill_id ) {
- case SO_SUMMON_AGNI: type = 2114; break;
- case SO_SUMMON_AQUA: type = 2117; break;
- case SO_SUMMON_VENTUS: type = 2120; break;
- case SO_SUMMON_TERA: type = 2123; break;
- }
-
- type += skill_lv - 1;
-
- return type;
-}
-
-/**
- * reload stored skill cooldowns when a player logs in.
- * @param sd the affected player structure
- */
-void skill_cooldown_load(struct map_session_data * sd)
-{
- int i;
- struct skill_cd* cd = NULL;
-
- // always check to make sure the session properly exists
- nullpo_retv(sd);
-
- if( !(cd = idb_get(skillcd_db, sd->status.char_id)) )
- {// no skill cooldown is associated with this character
- return;
- }
-
- // process each individual cooldown associated with the character
- for( i = 0; i < cd->cursor; i++ )
- {
- // block the skill from usage but ensure it is not recorded (load = true)
- skill_blockpc_start_( sd, cd->nameid[i], cd->duration[i], true );
- }
-}
-
-/*==========================================
- * sub-function of DB reading.
- * skill_db.txt
- *------------------------------------------*/
-
-static bool skill_parse_row_skilldb(char* split[], int columns, int current)
-{// id,range,hit,inf,element,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count,name,description
- uint16 skill_id = atoi(split[0]);
- uint16 idx;
- if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX)
- || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX)
- || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX)
- || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) ) {
- ShowWarning("skill_parse_row_skilldb: Skill id %d is forbidden (interferes with guild/homun/mercenary skill mapping)!\n", skill_id);
- return false;
- }
-
- idx = skill_get_index(skill_id);
- if( !idx ) // invalid skill id
- return false;
-
- skill_split_atoi(split[1],skill_db[idx].range);
- skill_db[idx].hit = atoi(split[2]);
- skill_db[idx].inf = atoi(split[3]);
- skill_split_atoi(split[4],skill_db[idx].element);
- skill_db[idx].nk = (int)strtol(split[5], NULL, 0);
- skill_split_atoi(split[6],skill_db[idx].splash);
- skill_db[idx].max = atoi(split[7]);
- skill_split_atoi(split[8],skill_db[idx].num);
-
- if( strcmpi(split[9],"yes") == 0 )
- skill_db[idx].castcancel = 1;
- else
- skill_db[idx].castcancel = 0;
- skill_db[idx].cast_def_rate = atoi(split[10]);
- skill_db[idx].inf2 = (int)strtol(split[11], NULL, 0);
- skill_split_atoi(split[12],skill_db[idx].maxcount);
- if( strcmpi(split[13],"weapon") == 0 )
- skill_db[idx].skill_type = BF_WEAPON;
- else if( strcmpi(split[13],"magic") == 0 )
- skill_db[idx].skill_type = BF_MAGIC;
- else if( strcmpi(split[13],"misc") == 0 )
- skill_db[idx].skill_type = BF_MISC;
- else
- skill_db[idx].skill_type = 0;
- skill_split_atoi(split[14],skill_db[idx].blewcount);
- safestrncpy(skill_db[idx].name, trim(split[15]), sizeof(skill_db[idx].name));
- safestrncpy(skill_db[idx].desc, trim(split[16]), sizeof(skill_db[idx].desc));
- strdb_iput(skilldb_name2id, skill_db[idx].name, skill_id);
-
- return true;
-}
-
-static bool skill_parse_row_requiredb(char* split[], int columns, int current)
-{// skill_id,HPCost,MaxHPTrigger,SPCost,HPRateCost,SPRateCost,ZenyCost,RequiredWeapons,RequiredAmmoTypes,RequiredAmmoAmount,RequiredState,SpiritSphereCost,RequiredItemID1,RequiredItemAmount1,RequiredItemID2,RequiredItemAmount2,RequiredItemID3,RequiredItemAmount3,RequiredItemID4,RequiredItemAmount4,RequiredItemID5,RequiredItemAmount5,RequiredItemID6,RequiredItemAmount6,RequiredItemID7,RequiredItemAmount7,RequiredItemID8,RequiredItemAmount8,RequiredItemID9,RequiredItemAmount9,RequiredItemID10,RequiredItemAmount10
- char* p;
- int j;
-
- uint16 skill_id = atoi(split[0]);
- uint16 idx = skill_get_index(skill_id);
- if( !idx ) // invalid skill id
- return false;
-
- skill_split_atoi(split[1],skill_db[idx].hp);
- skill_split_atoi(split[2],skill_db[idx].mhp);
- skill_split_atoi(split[3],skill_db[idx].sp);
- skill_split_atoi(split[4],skill_db[idx].hp_rate);
- skill_split_atoi(split[5],skill_db[idx].sp_rate);
- skill_split_atoi(split[6],skill_db[idx].zeny);
-
- //Wich weapon type are required, see doc/item_db for types
- p = split[7];
- for( j = 0; j < 32; j++ )
- {
- int l = atoi(p);
- if( l == 99 ) // Any weapon
- {
- skill_db[idx].weapon = 0;
- break;
- }
- else
- skill_db[idx].weapon |= 1<<l;
- p = strchr(p,':');
- if(!p)
- break;
- p++;
- }
-
- //FIXME: document this
- p = split[8];
- for( j = 0; j < 32; j++ )
- {
- int l = atoi(p);
- if( l == 99 ) // Any ammo type
- {
- skill_db[idx].ammo = 0xFFFFFFFF;
- break;
- }
- else if( l ) // 0 stands for no requirement
- skill_db[idx].ammo |= 1<<l;
- p = strchr(p,':');
- if( !p )
- break;
- p++;
- }
- skill_split_atoi(split[9],skill_db[idx].ammo_qty);
-
- if( strcmpi(split[10],"hiding")==0 ) skill_db[idx].state = ST_HIDING;
- else if( strcmpi(split[10],"cloaking")==0 ) skill_db[idx].state = ST_CLOAKING;
- else if( strcmpi(split[10],"hidden")==0 ) skill_db[idx].state = ST_HIDDEN;
- else if( strcmpi(split[10],"riding")==0 ) skill_db[idx].state = ST_RIDING;
- else if( strcmpi(split[10],"falcon")==0 ) skill_db[idx].state = ST_FALCON;
- else if( strcmpi(split[10],"cart")==0 ) skill_db[idx].state = ST_CART;
- else if( strcmpi(split[10],"shield")==0 ) skill_db[idx].state = ST_SHIELD;
- else if( strcmpi(split[10],"sight")==0 ) skill_db[idx].state = ST_SIGHT;
- else if( strcmpi(split[10],"explosionspirits")==0 ) skill_db[idx].state = ST_EXPLOSIONSPIRITS;
- else if( strcmpi(split[10],"cartboost")==0 ) skill_db[idx].state = ST_CARTBOOST;
- else if( strcmpi(split[10],"recover_weight_rate")==0 ) skill_db[idx].state = ST_RECOV_WEIGHT_RATE;
- else if( strcmpi(split[10],"move_enable")==0 ) skill_db[idx].state = ST_MOVE_ENABLE;
- else if( strcmpi(split[10],"water")==0 ) skill_db[idx].state = ST_WATER;
- /**
- * New States
- **/
- else if( strcmpi(split[10],"dragon")==0 ) skill_db[idx].state = ST_RIDINGDRAGON;
- else if( strcmpi(split[10],"warg")==0 ) skill_db[idx].state = ST_WUG;
- else if( strcmpi(split[10],"ridingwarg")==0 ) skill_db[idx].state = ST_RIDINGWUG;
- else if( strcmpi(split[10],"mado")==0 ) skill_db[idx].state = ST_MADO;
- else if( strcmpi(split[10],"elementalspirit")==0 ) skill_db[idx].state = ST_ELEMENTALSPIRIT;
- else if (strcmpi(split[10], "poisonweapon") == 0) skill_db[idx].state = ST_POISONINGWEAPON;
- else if (strcmpi(split[10], "rollingcutter") == 0) skill_db[idx].state = ST_ROLLINGCUTTER;
- else if (strcmpi(split[10], "mh_fighting") == 0) skill_db[idx].state = ST_MH_FIGHTING;
- else if (strcmpi(split[10], "mh_grappling") == 0) skill_db[idx].state = ST_MH_GRAPPLING;
-
- /**
- * Unknown or no state
- **/
- else skill_db[idx].state = ST_NONE;
-
- skill_split_atoi(split[11],skill_db[idx].spiritball);
- for( j = 0; j < MAX_SKILL_ITEM_REQUIRE; j++ ) {
- skill_db[idx].itemid[j] = atoi(split[12+ 2*j]);
- skill_db[idx].amount[j] = atoi(split[13+ 2*j]);
- }
-
- return true;
-}
-
-static bool skill_parse_row_castdb(char* split[], int columns, int current)
-{// skill_id,CastingTime,AfterCastActDelay,AfterCastWalkDelay,Duration1,Duration2
- uint16 skill_id = atoi(split[0]);
- uint16 idx = skill_get_index(skill_id);
- if( !idx ) // invalid skill id
- return false;
-
- skill_split_atoi(split[1],skill_db[idx].cast);
- skill_split_atoi(split[2],skill_db[idx].delay);
- skill_split_atoi(split[3],skill_db[idx].walkdelay);
- skill_split_atoi(split[4],skill_db[idx].upkeep_time);
- skill_split_atoi(split[5],skill_db[idx].upkeep_time2);
- skill_split_atoi(split[6],skill_db[idx].cooldown);
-#ifdef RENEWAL_CAST
- skill_split_atoi(split[7],skill_db[idx].fixed_cast);
-#endif
- return true;
-}
-
-static bool skill_parse_row_castnodexdb(char* split[], int columns, int current)
-{// Skill id,Cast,Delay (optional)
- uint16 skill_id = atoi(split[0]);
- uint16 idx = skill_get_index(skill_id);
- if( !idx ) // invalid skill id
- return false;
-
- skill_split_atoi(split[1],skill_db[idx].castnodex);
- if( split[2] ) // optional column
- skill_split_atoi(split[2],skill_db[idx].delaynodex);
-
- return true;
-}
-
-static bool skill_parse_row_nocastdb(char* split[], int columns, int current)
-{// skill_id,Flag
- uint16 skill_id = atoi(split[0]);
- uint16 idx = skill_get_index(skill_id);
- if( !idx ) // invalid skill id
- return false;
-
- skill_db[idx].nocast |= atoi(split[1]);
-
- return true;
-}
-
-static bool skill_parse_row_unitdb(char* split[], int columns, int current)
-{// ID,unit ID,unit ID 2,layout,range,interval,target,flag
- uint16 skill_id = atoi(split[0]);
- uint16 idx = skill_get_index(skill_id);
- if( !idx ) // invalid skill id
- return false;
-
- skill_db[idx].unit_id[0] = strtol(split[1],NULL,16);
- skill_db[idx].unit_id[1] = strtol(split[2],NULL,16);
- skill_split_atoi(split[3],skill_db[idx].unit_layout_type);
- skill_split_atoi(split[4],skill_db[idx].unit_range);
- skill_db[idx].unit_interval = atoi(split[5]);
-
- if( strcmpi(split[6],"noenemy")==0 ) skill_db[idx].unit_target = BCT_NOENEMY;
- else if( strcmpi(split[6],"friend")==0 ) skill_db[idx].unit_target = BCT_NOENEMY;
- else if( strcmpi(split[6],"party")==0 ) skill_db[idx].unit_target = BCT_PARTY;
- else if( strcmpi(split[6],"ally")==0 ) skill_db[idx].unit_target = BCT_PARTY|BCT_GUILD;
- else if( strcmpi(split[6],"guild")==0 ) skill_db[idx].unit_target = BCT_GUILD;
- else if( strcmpi(split[6],"all")==0 ) skill_db[idx].unit_target = BCT_ALL;
- else if( strcmpi(split[6],"enemy")==0 ) skill_db[idx].unit_target = BCT_ENEMY;
- else if( strcmpi(split[6],"self")==0 ) skill_db[idx].unit_target = BCT_SELF;
- else if( strcmpi(split[6],"noone")==0 ) skill_db[idx].unit_target = BCT_NOONE;
- else skill_db[idx].unit_target = strtol(split[6],NULL,16);
-
- skill_db[idx].unit_flag = strtol(split[7],NULL,16);
-
- if (skill_db[idx].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy)
- skill_db[idx].unit_target = BCT_NOENEMY;
-
- //By default, target just characters.
- skill_db[idx].unit_target |= BL_CHAR;
- if (skill_db[idx].unit_flag&UF_NOPC)
- skill_db[idx].unit_target &= ~BL_PC;
- if (skill_db[idx].unit_flag&UF_NOMOB)
- skill_db[idx].unit_target &= ~BL_MOB;
- if (skill_db[idx].unit_flag&UF_SKILL)
- skill_db[idx].unit_target |= BL_SKILL;
-
- return true;
-}
-
-static bool skill_parse_row_producedb(char* split[], int columns, int current)
-{// ProduceItemID,ItemLV,RequireSkill,Requireskill_lv,MaterialID1,MaterialAmount1,......
- int x,y;
-
- int i = atoi(split[0]);
- if( !i )
- return false;
-
- skill_produce_db[current].nameid = i;
- skill_produce_db[current].itemlv = atoi(split[1]);
- skill_produce_db[current].req_skill = atoi(split[2]);
- skill_produce_db[current].req_skill_lv = atoi(split[3]);
-
- for( x = 4, y = 0; x+1 < columns && split[x] && split[x+1] && y < MAX_PRODUCE_RESOURCE; x += 2, y++ )
- {
- skill_produce_db[current].mat_id[y] = atoi(split[x]);
- skill_produce_db[current].mat_amount[y] = atoi(split[x+1]);
- }
-
- return true;
-}
-
-static bool skill_parse_row_createarrowdb(char* split[], int columns, int current)
-{// SourceID,MakeID1,MakeAmount1,...,MakeID5,MakeAmount5
- int x,y;
-
- int i = atoi(split[0]);
- if( !i )
- return false;
-
- skill_arrow_db[current].nameid = i;
-
- for( x = 1, y = 0; x+1 < columns && split[x] && split[x+1] && y < MAX_ARROW_RESOURCE; x += 2, y++ )
- {
- skill_arrow_db[current].cre_id[y] = atoi(split[x]);
- skill_arrow_db[current].cre_amount[y] = atoi(split[x+1]);
- }
-
- return true;
-}
-static bool skill_parse_row_spellbookdb(char* split[], int columns, int current)
-{// skill_id,PreservePoints
-
- uint16 skill_id = atoi(split[0]);
- int points = atoi(split[1]);
- int nameid = atoi(split[2]);
-
- if( !skill_get_index(skill_id) || !skill_get_max(skill_id) )
- ShowError("spellbook_db: Invalid skill ID %d\n", skill_id);
- if ( !skill_get_inf(skill_id) )
- ShowError("spellbook_db: Passive skills cannot be memorized (%d/%s)\n", skill_id, skill_get_name(skill_id));
- if( points < 1 )
- ShowError("spellbook_db: PreservePoints have to be 1 or above! (%d/%s)\n", skill_id, skill_get_name(skill_id));
- else
- {
- skill_spellbook_db[current].skill_id = skill_id;
- skill_spellbook_db[current].point = points;
- skill_spellbook_db[current].nameid = nameid;
-
- return true;
- }
-
- return false;
-}
-static bool skill_parse_row_improvisedb(char* split[], int columns, int current)
-{// SkillID,Rate
- uint16 skill_id = atoi(split[0]);
- short j = atoi(split[1]);
-
- if( !skill_get_index(skill_id) || !skill_get_max(skill_id) ) {
- ShowError("skill_improvise_db: Invalid skill ID %d\n", skill_id);
- return false;
- }
- if ( !skill_get_inf(skill_id) ) {
- ShowError("skill_improvise_db: Passive skills cannot be casted (%d/%s)\n", skill_id, skill_get_name(skill_id));
- return false;
- }
- if( j < 1 ) {
- ShowError("skill_improvise_db: Chances have to be 1 or above! (%d/%s)\n", skill_id, skill_get_name(skill_id));
- return false;
- }
- if( current >= MAX_SKILL_IMPROVISE_DB ) {
- ShowError("skill_improvise_db: Maximum amount of entries reached (%d), increase MAX_SKILL_IMPROVISE_DB\n",MAX_SKILL_IMPROVISE_DB);
- }
- skill_improvise_db[current].skill_id = skill_id;
- skill_improvise_db[current].per = j; // Still need confirm it.
-
- return true;
-}
-static bool skill_parse_row_magicmushroomdb(char* split[], int column, int current)
-{// SkillID
- uint16 skill_id = atoi(split[0]);
-
- if( !skill_get_index(skill_id) || !skill_get_max(skill_id) )
- {
- ShowError("magicmushroom_db: Invalid skill ID %d\n", skill_id);
- return false;
- }
- if ( !skill_get_inf(skill_id) )
- {
- ShowError("magicmushroom_db: Passive skills cannot be casted (%d/%s)\n", skill_id, skill_get_name(skill_id));
- return false;
- }
-
- skill_magicmushroom_db[current].skill_id = skill_id;
-
- return true;
-}
-
-static bool skill_parse_row_reproducedb(char* split[], int column, int current) {
- uint16 skill_id = atoi(split[0]);
- uint16 idx = skill_get_index(skill_id);
- if( !idx )
- return false;
-
- skill_reproduce_db[idx] = true;
-
- return true;
-}
-
-
-static bool skill_parse_row_abradb(char* split[], int columns, int current)
-{// skill_id,DummyName,RequiredHocusPocusLevel,Rate
- uint16 skill_id = atoi(split[0]);
- if( !skill_get_index(skill_id) || !skill_get_max(skill_id) )
- {
- ShowError("abra_db: Invalid skill ID %d\n", skill_id);
- return false;
- }
- if ( !skill_get_inf(skill_id) )
- {
- ShowError("abra_db: Passive skills cannot be casted (%d/%s)\n", skill_id, skill_get_name(skill_id));
- return false;
- }
-
- skill_abra_db[current].skill_id = skill_id;
- skill_abra_db[current].req_lv = atoi(split[2]);
- skill_abra_db[current].per = atoi(split[3]);
-
- return true;
-}
-
-static bool skill_parse_row_changematerialdb(char* split[], int columns, int current)
-{// ProductID,BaseRate,MakeAmount1,MakeAmountRate1...,MakeAmount5,MakeAmountRate5
- uint16 skill_id = atoi(split[0]);
- short j = atoi(split[1]);
- int x,y;
-
- for(x=0; x<MAX_SKILL_PRODUCE_DB; x++){
- if( skill_produce_db[x].nameid == skill_id )
- if( skill_produce_db[x].req_skill == GN_CHANGEMATERIAL )
- break;
- }
-
- if( x >= MAX_SKILL_PRODUCE_DB ){
- ShowError("changematerial_db: Not supported item ID(%d) for Change Material. \n", skill_id);
- return false;
- }
-
- if( current >= MAX_SKILL_PRODUCE_DB ) {
- ShowError("skill_changematerial_db: Maximum amount of entries reached (%d), increase MAX_SKILL_PRODUCE_DB\n",MAX_SKILL_PRODUCE_DB);
- }
-
- skill_changematerial_db[current].itemid = skill_id;
- skill_changematerial_db[current].rate = j;
-
- for( x = 2, y = 0; x+1 < columns && split[x] && split[x+1] && y < 5; x += 2, y++ )
- {
- skill_changematerial_db[current].qty[y] = atoi(split[x]);
- skill_changematerial_db[current].qty_rate[y] = atoi(split[x+1]);
- }
-
- return true;
-}
-
-/*===============================
- * DB reading.
- * skill_db.txt
- * skill_require_db.txt
- * skill_cast_db.txt
- * skill_castnodex_db.txt
- * skill_nocast_db.txt
- * skill_unit_db.txt
- * produce_db.txt
- * create_arrow_db.txt
- * abra_db.txt
- *------------------------------*/
-static void skill_readdb(void)
-{
- // init skill db structures
- db_clear(skilldb_name2id);
- memset(skill_db,0,sizeof(skill_db));
- memset(skill_produce_db,0,sizeof(skill_produce_db));
- memset(skill_arrow_db,0,sizeof(skill_arrow_db));
- memset(skill_abra_db,0,sizeof(skill_abra_db));
- memset(skill_spellbook_db,0,sizeof(skill_spellbook_db));
- memset(skill_magicmushroom_db,0,sizeof(skill_magicmushroom_db));
- memset(skill_reproduce_db,0,sizeof(skill_reproduce_db));
- memset(skill_changematerial_db,0,sizeof(skill_changematerial_db));
-
- // load skill databases
- safestrncpy(skill_db[0].name, "UNKNOWN_SKILL", sizeof(skill_db[0].name));
- safestrncpy(skill_db[0].desc, "Unknown Skill", sizeof(skill_db[0].desc));
-
- sv_readdb(db_path, DBPATH"skill_db.txt" , ',', 17, 17, MAX_SKILL_DB, skill_parse_row_skilldb);
- sv_readdb(db_path, DBPATH"skill_require_db.txt" , ',', 32, 32, MAX_SKILL_DB, skill_parse_row_requiredb);
-#ifdef RENEWAL_CAST
- sv_readdb(db_path, "re/skill_cast_db.txt" , ',', 8, 8, MAX_SKILL_DB, skill_parse_row_castdb);
-#else
- sv_readdb(db_path, "pre-re/skill_cast_db.txt" , ',', 7, 7, MAX_SKILL_DB, skill_parse_row_castdb);
-#endif
- sv_readdb(db_path, DBPATH"skill_castnodex_db.txt", ',', 2, 3, MAX_SKILL_DB, skill_parse_row_castnodexdb);
- sv_readdb(db_path, DBPATH"skill_unit_db.txt" , ',', 8, 8, MAX_SKILL_DB, skill_parse_row_unitdb);
-
- sv_readdb(db_path, DBPATH"skill_nocast_db.txt" , ',', 2, 2, MAX_SKILL_DB, skill_parse_row_nocastdb);
-
- skill_init_unit_layout();
- sv_readdb(db_path, "produce_db.txt" , ',', 4, 4+2*MAX_PRODUCE_RESOURCE, MAX_SKILL_PRODUCE_DB, skill_parse_row_producedb);
- sv_readdb(db_path, "create_arrow_db.txt" , ',', 1+2, 1+2*MAX_ARROW_RESOURCE, MAX_SKILL_ARROW_DB, skill_parse_row_createarrowdb);
- sv_readdb(db_path, "abra_db.txt" , ',', 4, 4, MAX_SKILL_ABRA_DB, skill_parse_row_abradb);
- //Warlock
- sv_readdb(db_path, "spellbook_db.txt" , ',', 3, 3, MAX_SKILL_SPELLBOOK_DB, skill_parse_row_spellbookdb);
- //Guillotine Cross
- sv_readdb(db_path, "magicmushroom_db.txt" , ',', 1, 1, MAX_SKILL_MAGICMUSHROOM_DB, skill_parse_row_magicmushroomdb);
- sv_readdb(db_path, "skill_reproduce_db.txt", ',', 1, 1, MAX_SKILL_DB, skill_parse_row_reproducedb);
- sv_readdb(db_path, "skill_improvise_db.txt" , ',', 2, 2, MAX_SKILL_IMPROVISE_DB, skill_parse_row_improvisedb);
- sv_readdb(db_path, "skill_changematerial_db.txt" , ',', 4, 4+2*5, MAX_SKILL_PRODUCE_DB, skill_parse_row_changematerialdb);
-
-}
-
-void skill_reload (void) {
- struct s_mapiterator *iter;
- struct map_session_data *sd;
- skill_readdb();
- /* lets update all players skill tree : so that if any skill modes were changed they're properly updated */
- iter = mapit_getallusers();
- for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) )
- clif_skillinfoblock(sd);
- mapit_free(iter);
-
-}
-
-/*==========================================
- *
- *------------------------------------------*/
-int do_init_skill (void)
-{
- skilldb_name2id = strdb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, 0);
- skill_readdb();
-
- group_db = idb_alloc(DB_OPT_BASE);
- skillunit_db = idb_alloc(DB_OPT_BASE);
- skillcd_db = idb_alloc(DB_OPT_RELEASE_DATA);
- skillusave_db = idb_alloc(DB_OPT_RELEASE_DATA);
- skill_unit_ers = ers_new(sizeof(struct skill_unit_group),"skill.c::skill_unit_ers",ERS_OPT_NONE);
- skill_timer_ers = ers_new(sizeof(struct skill_timerskill),"skill.c::skill_timer_ers",ERS_OPT_NONE);
-
- add_timer_func_list(skill_unit_timer,"skill_unit_timer");
- add_timer_func_list(skill_castend_id,"skill_castend_id");
- add_timer_func_list(skill_castend_pos,"skill_castend_pos");
- add_timer_func_list(skill_timerskill,"skill_timerskill");
- add_timer_func_list(skill_blockpc_end, "skill_blockpc_end");
-
- add_timer_interval(gettick()+SKILLUNITTIMER_INTERVAL,skill_unit_timer,0,0,SKILLUNITTIMER_INTERVAL);
-
- return 0;
-}
-
-int do_final_skill(void)
-{
- db_destroy(skilldb_name2id);
- db_destroy(group_db);
- db_destroy(skillunit_db);
- db_destroy(skillcd_db);
- db_destroy(skillusave_db);
- ers_destroy(skill_unit_ers);
- ers_destroy(skill_timer_ers);
- return 0;
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "../common/cbasetypes.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/random.h"
+#include "../common/showmsg.h"
+#include "../common/strlib.h"
+#include "../common/utils.h"
+#include "../common/ers.h"
+
+#include "map.h"
+#include "path.h"
+#include "clif.h"
+#include "pc.h"
+#include "status.h"
+#include "skill.h"
+#include "pet.h"
+#include "homunculus.h"
+#include "mercenary.h"
+#include "elemental.h"
+#include "mob.h"
+#include "npc.h"
+#include "battle.h"
+#include "battleground.h"
+#include "party.h"
+#include "itemdb.h"
+#include "script.h"
+#include "intif.h"
+#include "log.h"
+#include "chrif.h"
+#include "guild.h"
+#include "date.h"
+#include "unit.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+
+
+#define SKILLUNITTIMER_INTERVAL 100
+
+// ranges reserved for mapping skill ids to skilldb offsets
+#define HM_SKILLRANGEMIN 700
+#define HM_SKILLRANGEMAX HM_SKILLRANGEMIN + MAX_HOMUNSKILL
+#define MC_SKILLRANGEMIN HM_SKILLRANGEMAX + 1
+#define MC_SKILLRANGEMAX MC_SKILLRANGEMIN + MAX_MERCSKILL
+#define EL_SKILLRANGEMIN MC_SKILLRANGEMAX + 1
+#define EL_SKILLRANGEMAX EL_SKILLRANGEMIN + MAX_ELEMENTALSKILL
+#define GD_SKILLRANGEMIN EL_SKILLRANGEMAX + 1
+#define GD_SKILLRANGEMAX GD_SKILLRANGEMIN + MAX_GUILDSKILL
+
+#if GD_SKILLRANGEMAX > 999
+ #error GD_SKILLRANGEMAX is greater than 999
+#endif
+static struct eri *skill_unit_ers = NULL; //For handling skill_unit's [Skotlex]
+static struct eri *skill_timer_ers = NULL; //For handling skill_timerskills [Skotlex]
+
+DBMap* skillunit_db = NULL; // int id -> struct skill_unit*
+
+DBMap* skilldb_name2id = NULL;
+
+/**
+ * Skill Cool Down Delay Saving
+ * Struct skill_cd is not a member of struct map_session_data
+ * to keep cooldowns in memory between player log-ins.
+ * All cooldowns are reset when server is restarted.
+ **/
+DBMap* skillcd_db = NULL; // char_id -> struct skill_cd
+struct skill_cd {
+ int duration[MAX_SKILL_TREE];//milliseconds
+ short skidx[MAX_SKILL_TREE];//the skill index entries belong to
+ short nameid[MAX_SKILL_TREE];//skill id
+ unsigned char cursor;
+};
+
+/**
+ * Skill Unit Persistency during endack routes (mostly for songs see bugreport:4574)
+ **/
+DBMap* skillusave_db = NULL; // char_id -> struct skill_usave
+struct skill_usave {
+ uint16 skill_id, skill_lv;
+};
+
+struct s_skill_db skill_db[MAX_SKILL_DB];
+struct s_skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB];
+struct s_skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB];
+struct s_skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB];
+struct s_skill_improvise_db {
+ uint16 skill_id;
+ short per;//1-10000
+};
+struct s_skill_improvise_db skill_improvise_db[MAX_SKILL_IMPROVISE_DB];
+bool skill_reproduce_db[MAX_SKILL_DB];
+struct s_skill_changematerial_db {
+ int itemid;
+ short rate;
+ int qty[5];
+ short qty_rate[5];
+};
+struct s_skill_changematerial_db skill_changematerial_db[MAX_SKILL_PRODUCE_DB];
+
+//Warlock
+struct s_skill_spellbook_db {
+ int nameid;
+ uint16 skill_id;
+ int point;
+};
+
+struct s_skill_spellbook_db skill_spellbook_db[MAX_SKILL_SPELLBOOK_DB];
+//Guillotine Cross
+struct s_skill_magicmushroom_db skill_magicmushroom_db[MAX_SKILL_MAGICMUSHROOM_DB];
+
+struct s_skill_unit_layout skill_unit_layout[MAX_SKILL_UNIT_LAYOUT];
+int firewall_unit_pos;
+int icewall_unit_pos;
+int earthstrain_unit_pos;
+//early declaration
+int skill_block_check(struct block_list *bl, enum sc_type type, uint16 skill_id);
+static int skill_check_unit_range (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv);
+static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv);
+static int skill_destroy_trap( struct block_list *bl, va_list ap );
+//Since only mob-casted splash skills can hit ice-walls
+static inline int splash_target(struct block_list* bl)
+{
+#ifndef RENEWAL
+ return ( bl->type == BL_MOB ) ? BL_SKILL|BL_CHAR : BL_CHAR;
+#else // Some skills can now hit ground skills(traps, ice wall & etc.)
+ return BL_SKILL|BL_CHAR;
+#endif
+}
+
+/// Returns the id of the skill, or 0 if not found.
+int skill_name2id(const char* name)
+{
+ if( name == NULL )
+ return 0;
+
+ return strdb_iget(skilldb_name2id, name);
+}
+
+/// Maps skill ids to skill db offsets.
+/// Returns the skill's array index, or 0 (Unknown Skill).
+int skill_get_index( uint16 skill_id )
+{
+ // avoid ranges reserved for mapping guild/homun/mercenary skills
+ if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX)
+ || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX)
+ || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX)
+ || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) )
+ return 0;
+
+ // map skill id to skill db index
+ if( skill_id >= GD_SKILLBASE )
+ skill_id = GD_SKILLRANGEMIN + skill_id - GD_SKILLBASE;
+ else if( skill_id >= EL_SKILLBASE )
+ skill_id = EL_SKILLRANGEMIN + skill_id - EL_SKILLBASE;
+ else if( skill_id >= MC_SKILLBASE )
+ skill_id = MC_SKILLRANGEMIN + skill_id - MC_SKILLBASE;
+ else if( skill_id >= HM_SKILLBASE )
+ skill_id = HM_SKILLRANGEMIN + skill_id - HM_SKILLBASE;
+
+ // validate result
+ if( !skill_id || skill_id >= MAX_SKILL_DB )
+ return 0;
+
+ return skill_id;
+}
+
+const char* skill_get_name( uint16 skill_id )
+{
+ return skill_db[skill_get_index(skill_id)].name;
+}
+
+const char* skill_get_desc( uint16 skill_id )
+{
+ return skill_db[skill_get_index(skill_id)].desc;
+}
+
+// out of bounds error checking [celest]
+static void skill_chk(int16* skill_id, uint16 skill_lv)
+{
+ *skill_id = skill_get_index(*skill_id); // checks/adjusts id
+ if( skill_lv > MAX_SKILL_LEVEL ) *skill_id = 0;
+}
+
+#define skill_get(var,id,lv) { skill_chk(&id,lv); if(!id) return 0; return var; }
+
+// Skill DB
+int skill_get_hit( uint16 skill_id ) { skill_get (skill_db[skill_id].hit, skill_id, 1); }
+int skill_get_inf( uint16 skill_id ) { skill_get (skill_db[skill_id].inf, skill_id, 1); }
+int skill_get_ele( uint16 skill_id , uint16 skill_lv ) { skill_get (skill_db[skill_id].element[skill_lv-1], skill_id, skill_lv); }
+int skill_get_nk( uint16 skill_id ) { skill_get (skill_db[skill_id].nk, skill_id, 1); }
+int skill_get_max( uint16 skill_id ) { skill_get (skill_db[skill_id].max, skill_id, 1); }
+int skill_get_range( uint16 skill_id , uint16 skill_lv ) { skill_get (skill_db[skill_id].range[skill_lv-1], skill_id, skill_lv); }
+int skill_get_splash( uint16 skill_id , uint16 skill_lv ) { skill_get ( (skill_db[skill_id].splash[skill_lv-1]>=0?skill_db[skill_id].splash[skill_lv-1]:AREA_SIZE), skill_id, skill_lv); }
+int skill_get_hp( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].hp[skill_lv-1], skill_id, skill_lv); }
+int skill_get_sp( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].sp[skill_lv-1], skill_id, skill_lv); }
+int skill_get_hp_rate(uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].hp_rate[skill_lv-1], skill_id, skill_lv); }
+int skill_get_sp_rate(uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].sp_rate[skill_lv-1], skill_id, skill_lv); }
+int skill_get_state(uint16 skill_id) { skill_get (skill_db[skill_id].state, skill_id, 1); }
+int skill_get_spiritball(uint16 skill_id, uint16 skill_lv) { skill_get (skill_db[skill_id].spiritball[skill_lv-1], skill_id, skill_lv); }
+int skill_get_itemid(uint16 skill_id, int idx) { skill_get (skill_db[skill_id].itemid[idx], skill_id, 1); }
+int skill_get_itemqty(uint16 skill_id, int idx) { skill_get (skill_db[skill_id].amount[idx], skill_id, 1); }
+int skill_get_zeny( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].zeny[skill_lv-1], skill_id, skill_lv); }
+int skill_get_num( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].num[skill_lv-1], skill_id, skill_lv); }
+int skill_get_cast( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].cast[skill_lv-1], skill_id, skill_lv); }
+int skill_get_delay( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].delay[skill_lv-1], skill_id, skill_lv); }
+int skill_get_walkdelay( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].walkdelay[skill_lv-1], skill_id, skill_lv); }
+int skill_get_time( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].upkeep_time[skill_lv-1], skill_id, skill_lv); }
+int skill_get_time2( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].upkeep_time2[skill_lv-1], skill_id, skill_lv); }
+int skill_get_castdef( uint16 skill_id ) { skill_get (skill_db[skill_id].cast_def_rate, skill_id, 1); }
+int skill_get_weapontype( uint16 skill_id ) { skill_get (skill_db[skill_id].weapon, skill_id, 1); }
+int skill_get_ammotype( uint16 skill_id ) { skill_get (skill_db[skill_id].ammo, skill_id, 1); }
+int skill_get_ammo_qty( uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].ammo_qty[skill_lv-1], skill_id, skill_lv); }
+int skill_get_inf2( uint16 skill_id ) { skill_get (skill_db[skill_id].inf2, skill_id, 1); }
+int skill_get_castcancel( uint16 skill_id ) { skill_get (skill_db[skill_id].castcancel, skill_id, 1); }
+int skill_get_maxcount( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].maxcount[skill_lv-1], skill_id, skill_lv); }
+int skill_get_blewcount( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].blewcount[skill_lv-1], skill_id, skill_lv); }
+int skill_get_mhp( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].mhp[skill_lv-1], skill_id, skill_lv); }
+int skill_get_castnodex( uint16 skill_id ,uint16 skill_lv ) { skill_get (skill_db[skill_id].castnodex[skill_lv-1], skill_id, skill_lv); }
+int skill_get_delaynodex( uint16 skill_id ,uint16 skill_lv ){ skill_get (skill_db[skill_id].delaynodex[skill_lv-1], skill_id, skill_lv); }
+int skill_get_nocast ( uint16 skill_id ) { skill_get (skill_db[skill_id].nocast, skill_id, 1); }
+int skill_get_type( uint16 skill_id ) { skill_get (skill_db[skill_id].skill_type, skill_id, 1); }
+int skill_get_unit_id ( uint16 skill_id, int flag ){ skill_get (skill_db[skill_id].unit_id[flag], skill_id, 1); }
+int skill_get_unit_interval( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_interval, skill_id, 1); }
+int skill_get_unit_range( uint16 skill_id, uint16 skill_lv ){ skill_get (skill_db[skill_id].unit_range[skill_lv-1], skill_id, skill_lv); }
+int skill_get_unit_target( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_target&BCT_ALL, skill_id, 1); }
+int skill_get_unit_bl_target( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_target&BL_ALL, skill_id, 1); }
+int skill_get_unit_flag( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_flag, skill_id, 1); }
+int skill_get_unit_layout_type( uint16 skill_id ,uint16 skill_lv ){ skill_get (skill_db[skill_id].unit_layout_type[skill_lv-1], skill_id, skill_lv); }
+int skill_get_cooldown( uint16 skill_id, uint16 skill_lv ) { skill_get (skill_db[skill_id].cooldown[skill_lv-1], skill_id, skill_lv); }
+#ifdef RENEWAL_CAST
+int skill_get_fixed_cast( uint16 skill_id ,uint16 skill_lv ){ skill_get (skill_db[skill_id].fixed_cast[skill_lv-1], skill_id, skill_lv); }
+#endif
+int skill_tree_get_max(uint16 skill_id, int b_class)
+{
+ int i;
+ b_class = pc_class2idx(b_class);
+
+ ARR_FIND( 0, MAX_SKILL_TREE, i, skill_tree[b_class][i].id == 0 || skill_tree[b_class][i].id == skill_id );
+ if( i < MAX_SKILL_TREE && skill_tree[b_class][i].id == skill_id )
+ return skill_tree[b_class][i].max;
+ else
+ return skill_get_max(skill_id);
+}
+
+int skill_frostjoke_scream(struct block_list *bl,va_list ap);
+int skill_attack_area(struct block_list *bl,va_list ap);
+struct skill_unit_group *skill_locate_element_field(struct block_list *bl); // [Skotlex]
+int skill_graffitiremover(struct block_list *bl, va_list ap); // [Valaris]
+int skill_greed(struct block_list *bl, va_list ap);
+static void skill_toggle_magicpower(struct block_list *bl, uint16 skill_id);
+static int skill_cell_overlap(struct block_list *bl, va_list ap);
+static int skill_trap_splash(struct block_list *bl, va_list ap);
+struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list *bl,struct skill_unit_group *sg,int tick);
+static int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,unsigned int tick);
+static int skill_unit_onleft(uint16 skill_id, struct block_list *bl,unsigned int tick);
+static int skill_unit_effect(struct block_list *bl,va_list ap);
+
+int enchant_eff[5] = { 10, 14, 17, 19, 20 };
+int deluge_eff[5] = { 5, 9, 12, 14, 15 };
+
+int skill_get_casttype (uint16 skill_id)
+{
+ int inf = skill_get_inf(skill_id);
+ if (inf&(INF_GROUND_SKILL))
+ return CAST_GROUND;
+ if (inf&INF_SUPPORT_SKILL)
+ return CAST_NODAMAGE;
+ if (inf&INF_SELF_SKILL) {
+ if(skill_get_inf2(skill_id)&INF2_NO_TARGET_SELF)
+ return CAST_DAMAGE; //Combo skill.
+ return CAST_NODAMAGE;
+ }
+ if (skill_get_nk(skill_id)&NK_NO_DAMAGE)
+ return CAST_NODAMAGE;
+ return CAST_DAMAGE;
+}
+
+//Returns actual skill range taking into account attack range and AC_OWL [Skotlex]
+int skill_get_range2 (struct block_list *bl, uint16 skill_id, uint16 skill_lv)
+{
+ int range;
+ if( bl->type == BL_MOB && battle_config.mob_ai&0x400 )
+ return 9; //Mobs have a range of 9 regardless of skill used.
+
+ range = skill_get_range(skill_id, skill_lv);
+
+ if( range < 0 )
+ {
+ if( battle_config.use_weapon_skill_range&bl->type )
+ return status_get_range(bl);
+ range *=-1;
+ }
+
+ //TODO: Find a way better than hardcoding the list of skills affected by AC_VULTURE
+ switch( skill_id )
+ {
+ case AC_SHOWER: case MA_SHOWER:
+ case AC_DOUBLE: case MA_DOUBLE:
+ case HT_BLITZBEAT:
+ case AC_CHARGEARROW:
+ case MA_CHARGEARROW:
+ case SN_FALCONASSAULT:
+ case HT_POWER:
+ /**
+ * Ranger
+ **/
+ case RA_ARROWSTORM:
+ case RA_AIMEDBOLT:
+ case RA_WUGBITE:
+ if( bl->type == BL_PC )
+ range += pc_checkskill((TBL_PC*)bl, AC_VULTURE);
+ else
+ range += 10; //Assume level 10?
+ break;
+ // added to allow GS skills to be effected by the range of Snake Eyes [Reddozen]
+ case GS_RAPIDSHOWER:
+ case GS_PIERCINGSHOT:
+ case GS_FULLBUSTER:
+ case GS_SPREADATTACK:
+ case GS_GROUNDDRIFT:
+ if (bl->type == BL_PC)
+ range += pc_checkskill((TBL_PC*)bl, GS_SNAKEEYE);
+ else
+ range += 10; //Assume level 10?
+ break;
+ case NJ_KIRIKAGE:
+ if (bl->type == BL_PC)
+ range = skill_get_range(NJ_SHADOWJUMP,pc_checkskill((TBL_PC*)bl,NJ_SHADOWJUMP));
+ break;
+ /**
+ * Warlock
+ **/
+ case WL_WHITEIMPRISON:
+ case WL_SOULEXPANSION:
+ case WL_FROSTMISTY:
+ case WL_MARSHOFABYSS:
+ case WL_SIENNAEXECRATE:
+ case WL_DRAINLIFE:
+ case WL_CRIMSONROCK:
+ case WL_HELLINFERNO:
+ case WL_COMET:
+ case WL_CHAINLIGHTNING:
+ case WL_TETRAVORTEX:
+ case WL_RELEASE:
+ if( bl->type == BL_PC )
+ range += pc_checkskill((TBL_PC*)bl, WL_RADIUS);
+ break;
+ /**
+ * Ranger Bonus
+ **/
+ case HT_LANDMINE:
+ case HT_FREEZINGTRAP:
+ case HT_BLASTMINE:
+ case HT_CLAYMORETRAP:
+ case RA_CLUSTERBOMB:
+ case RA_FIRINGTRAP:
+ case RA_ICEBOUNDTRAP:
+ if( bl->type == BL_PC )
+ range += (1 + pc_checkskill((TBL_PC*)bl, RA_RESEARCHTRAP))/2;
+ }
+
+ if( !range && bl->type != BL_PC )
+ return 9; // Enable non players to use self skills on others. [Skotlex]
+ return range;
+}
+
+int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, bool heal) {
+ int skill, hp;
+ struct map_session_data *sd = BL_CAST(BL_PC, src);
+ struct map_session_data *tsd = BL_CAST(BL_PC, target);
+ struct status_change* sc;
+
+ switch( skill_id ) {
+ case BA_APPLEIDUN:
+ #ifdef RENEWAL
+ hp = 100+5*skill_lv+5*(status_get_vit(src)/10); // HP recovery
+ #else
+ hp = 30+5*skill_lv+5*(status_get_vit(src)/10); // HP recovery
+ #endif
+ if( sd )
+ hp += 5*pc_checkskill(sd,BA_MUSICALLESSON);
+ break;
+ case PR_SANCTUARY:
+ hp = (skill_lv>6)?777:skill_lv*100;
+ break;
+ case NPC_EVILLAND:
+ hp = (skill_lv>6)?666:skill_lv*100;
+ break;
+ default:
+ if (skill_lv >= battle_config.max_heal_lv)
+ return battle_config.max_heal;
+ #ifdef RENEWAL
+ /**
+ * Renewal Heal Formula
+ * Formula: ( [(Base Level + INT) / 5] × 30 ) × (Heal Level / 10) × (Modifiers) + MATK
+ **/
+ hp = (status_get_lv(src) + status_get_int(src)) / 5 * 30 * skill_lv / 10;
+ #else
+ hp = ( status_get_lv(src) + status_get_int(src) ) / 8 * (4 + ( skill_id == AB_HIGHNESSHEAL ? ( sd ? pc_checkskill(sd,AL_HEAL) : 10 ) : skill_lv ) * 8);
+ #endif
+ if( sd && ((skill = pc_checkskill(sd, HP_MEDITATIO)) > 0) )
+ hp += hp * skill * 2 / 100;
+ else if( src->type == BL_HOM && (skill = merc_hom_checkskill(((TBL_HOM*)src), HLIF_BRAIN)) > 0 )
+ hp += hp * skill * 2 / 100;
+ break;
+ }
+
+ if( ( (target && target->type == BL_MER) || !heal ) && skill_id != NPC_EVILLAND )
+ hp >>= 1;
+
+ if( sd && (skill = pc_skillheal_bonus(sd, skill_id)) )
+ hp += hp*skill/100;
+
+ if( tsd && (skill = pc_skillheal2_bonus(tsd, skill_id)) )
+ hp += hp*skill/100;
+
+ sc = status_get_sc(target);
+ if( sc && sc->count ) {
+ if( sc->data[SC_CRITICALWOUND] && heal ) // Critical Wound has no effect on offensive heal. [Inkfish]
+ hp -= hp * sc->data[SC_CRITICALWOUND]->val2/100;
+ if( sc->data[SC_DEATHHURT] && heal )
+ hp -= hp * 20/100;
+ if( sc->data[SC_INCHEALRATE] && skill_id != NPC_EVILLAND && skill_id != BA_APPLEIDUN )
+ hp += hp * sc->data[SC_INCHEALRATE]->val1/100; // Only affects Heal, Sanctuary and PotionPitcher.(like bHealPower) [Inkfish]
+ if( sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2)
+ hp += hp / 10;
+ }
+
+#ifdef RENEWAL
+ // MATK part of the RE heal formula [malufett]
+ // Note: in this part matk bonuses from items or skills are not applied
+ switch( skill_id ) {
+ case BA_APPLEIDUN: case PR_SANCTUARY:
+ case NPC_EVILLAND: break;
+ default:
+ {
+ struct status_data *status = status_get_status_data(src);
+ int min, max, wMatk, variance;
+
+ min = max = status_base_matk(status, status_get_lv(src));
+ if( status->rhw.matk > 0 ){
+ wMatk = status->rhw.matk;
+ variance = wMatk * status->rhw.wlv / 10;
+ min += wMatk - variance;
+ max += wMatk + variance;
+ }
+
+ if( sc && sc->data[SC_RECOGNIZEDSPELL] )
+ min = max;
+
+ if( sd && sd->right_weapon.overrefine > 0 ){
+ min++;
+ max += sd->right_weapon.overrefine - 1;
+ }
+
+ if(max > min)
+ hp += min+rnd()%(max-min);
+ else
+ hp += min;
+ }
+ }
+#endif
+ return hp;
+}
+
+// Making plagiarize check its own function [Aru]
+int can_copy (struct map_session_data *sd, uint16 skill_id, struct block_list* bl)
+{
+ // Never copy NPC/Wedding Skills
+ if (skill_get_inf2(skill_id)&(INF2_NPC_SKILL|INF2_WEDDING_SKILL))
+ return 0;
+
+ // High-class skills
+ if((skill_id >= LK_AURABLADE && skill_id <= ASC_CDP) || (skill_id >= ST_PRESERVE && skill_id <= CR_CULTIVATION))
+ {
+ if(battle_config.copyskill_restrict == 2)
+ return 0;
+ else if(battle_config.copyskill_restrict)
+ return (sd->status.class_ == JOB_STALKER);
+ }
+
+ //Added so plagarize can't copy agi/bless if you're undead since it damages you
+ if ((skill_id == AL_INCAGI || skill_id == AL_BLESSING ||
+ skill_id == CASH_BLESSING || skill_id == CASH_INCAGI ||
+ skill_id == MER_INCAGI || skill_id == MER_BLESSING))
+ return 0;
+
+ // Couldn't preserve 3rd Class skills except only when using Reproduce skill. [Jobbie]
+ if( !(sd->sc.data[SC__REPRODUCE]) && (skill_id >= RK_ENCHANTBLADE && skill_id <= SR_RIDEINLIGHTNING) )
+ return 0;
+ // Reproduce will only copy skills according on the list. [Jobbie]
+ else if( sd->sc.data[SC__REPRODUCE] && !skill_reproduce_db[skill_id] )
+ return 0;
+
+ return 1;
+}
+
+// [MouseJstr] - skill ok to cast? and when?
+int skillnotok (uint16 skill_id, struct map_session_data *sd)
+{
+ int16 idx,m;
+ nullpo_retr (1, sd);
+ m = sd->bl.m;
+ idx = skill_get_index(skill_id);
+
+ if (idx == 0)
+ return 1; // invalid skill id
+
+ if (pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL))
+ return 0; // can do any damn thing they want
+
+ if( skill_id == AL_TELEPORT && sd->skillitem == skill_id && sd->skillitemlv > 2 )
+ return 0; // Teleport lv 3 bypasses this check.[Inkfish]
+
+ // Epoque:
+ // This code will compare the player's attack motion value which is influenced by ASPD before
+ // allowing a skill to be cast. This is to prevent no-delay ACT files from spamming skills such as
+ // AC_DOUBLE which do not have a skill delay and are not regarded in terms of attack motion.
+ if( !sd->state.autocast && sd->skillitem != skill_id && sd->canskill_tick &&
+ DIFF_TICK(gettick(), sd->canskill_tick) < (sd->battle_status.amotion * (battle_config.skill_amotion_leniency) / 100) )
+ {// attempted to cast a skill before the attack motion has finished
+ return 1;
+ }
+
+ if (sd->blockskill[idx] > 0){
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_SKILLINTERVAL, 0);
+ return 1;
+ }
+ /**
+ * It has been confirmed on a official server (thanks to Yommy) that item-cast skills bypass all the restrictions above
+ * Also, without this check, an exploit where an item casting + healing (or any other kind buff) isn't deleted after used on a restricted map
+ **/
+ if( sd->skillitem == skill_id )
+ return 0;
+ // Check skill restrictions [Celest]
+ if( (!map_flag_vs(m) && skill_get_nocast (skill_id) & 1) ||
+ (map[m].flag.pvp && skill_get_nocast (skill_id) & 2) ||
+ (map_flag_gvg(m) && skill_get_nocast (skill_id) & 4) ||
+ (map[m].flag.battleground && skill_get_nocast (skill_id) & 8) ||
+ (map[m].flag.restricted && map[m].zone && skill_get_nocast (skill_id) & (8*map[m].zone)) ){
+ clif_msg(sd, 0x536); // This skill cannot be used within this area
+ return 1;
+ }
+
+ if( sd->sc.option&OPTION_MOUNTING )
+ return 1;//You can't use skills while in the new mounts (The client doesn't let you, this is to make cheat-safe)
+
+ switch (skill_id) {
+ case AL_WARP:
+ case RETURN_TO_ELDICASTES:
+ case ALL_GUARDIAN_RECALL:
+ if(map[m].flag.nowarp) {
+ clif_skill_teleportmessage(sd,0);
+ return 1;
+ }
+ return 0;
+ case AL_TELEPORT:
+ case SC_FATALMENACE:
+ case SC_DIMENSIONDOOR:
+ if(map[m].flag.noteleport) {
+ clif_skill_teleportmessage(sd,0);
+ return 1;
+ }
+ return 0; // gonna be checked in 'skill_castend_nodamage_id'
+ case WE_CALLPARTNER:
+ case WE_CALLPARENT:
+ case WE_CALLBABY:
+ if (map[m].flag.nomemo) {
+ clif_skill_teleportmessage(sd,1);
+ return 1;
+ }
+ break;
+ case MC_VENDING:
+ case MC_IDENTIFY:
+ case ALL_BUYING_STORE:
+ return 0; // always allowed
+ case WZ_ICEWALL:
+ // noicewall flag [Valaris]
+ if (map[m].flag.noicewall) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+ break;
+ case GC_DARKILLUSION:
+ if( map_flag_gvg(m) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+ break;
+ case GD_EMERGENCYCALL:
+ if (
+ !(battle_config.emergency_call&((agit_flag || agit2_flag)?2:1)) ||
+ !(battle_config.emergency_call&(map[m].flag.gvg || map[m].flag.gvg_castle?8:4)) ||
+ (battle_config.emergency_call&16 && map[m].flag.nowarpto && !map[m].flag.gvg_castle)
+ ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+ break;
+ case BS_GREED:
+ case WS_CARTBOOST:
+ case BS_HAMMERFALL:
+ case BS_ADRENALINE:
+ case MC_CARTREVOLUTION:
+ case MC_MAMMONITE:
+ case WS_MELTDOWN:
+ case MG_SIGHT:
+ case TF_HIDING:
+ /**
+ * These skills cannot be used while in mado gear (credits to Xantara)
+ **/
+ if( pc_ismadogear(sd) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+ break;
+
+ case WM_SIRCLEOFNATURE:
+ case WM_SOUND_OF_DESTRUCTION:
+ case SC_MANHOLE:
+ case WM_LULLABY_DEEPSLEEP:
+ case WM_SATURDAY_NIGHT_FEVER:
+ if( !map_flag_vs(m) ) {
+ clif_skill_teleportmessage(sd,2); // This skill uses this msg instead of skill fails.
+ return 1;
+ }
+ break;
+
+ }
+ return (map[m].flag.noskill);
+}
+
+int skillnotok_hom(uint16 skill_id, struct homun_data *hd)
+{
+ uint16 idx = skill_get_index(skill_id);
+ nullpo_retr(1,hd);
+
+ if (idx == 0)
+ return 1; // invalid skill id
+
+ if (hd->blockskill[idx] > 0)
+ return 1;
+ switch(skill_id){
+ case MH_LIGHT_OF_REGENE:
+ if(hd->homunculus.intimacy <= 750) //if not cordial
+ return 1;
+ break;
+ case MH_OVERED_BOOST:
+ if(hd->homunculus.hunger <= 1) //if we starving
+ return 1;
+ case MH_GOLDENE_FERSE: //can be used with angriff
+ if(hd->sc.data[SC_ANGRIFFS_MODUS])
+ return 1;
+ case MH_ANGRIFFS_MODUS:
+ if(hd->sc.data[SC_GOLDENE_FERSE])
+ return 1;
+ break;
+ }
+
+ //Use master's criteria.
+ return skillnotok(skill_id, hd->master);
+}
+
+int skillnotok_mercenary(uint16 skill_id, struct mercenary_data *md)
+{
+ uint16 idx = skill_get_index(skill_id);
+ nullpo_retr(1,md);
+
+ if( idx == 0 )
+ return 1; // Invalid Skill ID
+ if( md->blockskill[idx] > 0 )
+ return 1;
+
+ return skillnotok(skill_id, md->master);
+}
+
+struct s_skill_unit_layout* skill_get_unit_layout (uint16 skill_id, uint16 skill_lv, struct block_list* src, int x, int y)
+{
+ int pos = skill_get_unit_layout_type(skill_id,skill_lv);
+ uint8 dir;
+
+ if (pos < -1 || pos >= MAX_SKILL_UNIT_LAYOUT) {
+ ShowError("skill_get_unit_layout: unsupported layout type %d for skill %d (level %d)\n", pos, skill_id, skill_lv);
+ pos = cap_value(pos, 0, MAX_SQUARE_LAYOUT); // cap to nearest square layout
+ }
+
+ if (pos != -1) // simple single-definition layout
+ return &skill_unit_layout[pos];
+
+ dir = (src->x == x && src->y == y) ? 6 : map_calc_dir(src,x,y); // 6 - default aegis direction
+
+ if (skill_id == MG_FIREWALL)
+ return &skill_unit_layout [firewall_unit_pos + dir];
+ else if (skill_id == WZ_ICEWALL)
+ return &skill_unit_layout [icewall_unit_pos + dir];
+ else if( skill_id == WL_EARTHSTRAIN ) //Warlock
+ return &skill_unit_layout [earthstrain_unit_pos + dir];
+
+ ShowError("skill_get_unit_layout: unknown unit layout for skill %d (level %d)\n", skill_id, skill_lv);
+ return &skill_unit_layout[0]; // default 1x1 layout
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_additional_effect (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, int dmg_lv, unsigned int tick)
+{
+ struct map_session_data *sd, *dstsd;
+ struct mob_data *md, *dstmd;
+ struct status_data *sstatus, *tstatus;
+ struct status_change *sc, *tsc;
+
+ enum sc_type status;
+ int skill;
+ int rate;
+
+ nullpo_ret(src);
+ nullpo_ret(bl);
+
+ if(skill_id > 0 && !skill_lv) return 0; // don't forget auto attacks! - celest
+
+ if( dmg_lv < ATK_BLOCK ) // Don't apply effect if miss.
+ return 0;
+
+ sd = BL_CAST(BL_PC, src);
+ md = BL_CAST(BL_MOB, src);
+ dstsd = BL_CAST(BL_PC, bl);
+ dstmd = BL_CAST(BL_MOB, bl);
+
+ sc = status_get_sc(src);
+ tsc = status_get_sc(bl);
+ sstatus = status_get_status_data(src);
+ tstatus = status_get_status_data(bl);
+ if (!tsc) //skill additional effect is about adding effects to the target...
+ //So if the target can't be inflicted with statuses, this is pointless.
+ return 0;
+
+ if( sd )
+ { // These statuses would be applied anyway even if the damage was blocked by some skills. [Inkfish]
+ if( skill_id != WS_CARTTERMINATION && skill_id != AM_DEMONSTRATION && skill_id != CR_REFLECTSHIELD && skill_id != MS_REFLECTSHIELD && skill_id != ASC_BREAKER )
+ { // Trigger status effects
+ enum sc_type type;
+ int i;
+ for( i = 0; i < ARRAYLENGTH(sd->addeff) && sd->addeff[i].flag; i++ )
+ {
+ rate = sd->addeff[i].rate;
+ if( attack_type&BF_LONG ) // Any ranged physical attack takes status arrows into account (Grimtooth...) [DracoRPG]
+ rate += sd->addeff[i].arrow_rate;
+ if( !rate ) continue;
+
+ if( (sd->addeff[i].flag&(ATF_WEAPON|ATF_MAGIC|ATF_MISC)) != (ATF_WEAPON|ATF_MAGIC|ATF_MISC) )
+ { // Trigger has attack type consideration.
+ if( (sd->addeff[i].flag&ATF_WEAPON && attack_type&BF_WEAPON) ||
+ (sd->addeff[i].flag&ATF_MAGIC && attack_type&BF_MAGIC) ||
+ (sd->addeff[i].flag&ATF_MISC && attack_type&BF_MISC) ) ;
+ else
+ continue;
+ }
+
+ if( (sd->addeff[i].flag&(ATF_LONG|ATF_SHORT)) != (ATF_LONG|ATF_SHORT) )
+ { // Trigger has range consideration.
+ if((sd->addeff[i].flag&ATF_LONG && !(attack_type&BF_LONG)) ||
+ (sd->addeff[i].flag&ATF_SHORT && !(attack_type&BF_SHORT)))
+ continue; //Range Failed.
+ }
+
+ type = sd->addeff[i].id;
+ skill = skill_get_time2(status_sc2skill(type),7);
+
+ if (sd->addeff[i].flag&ATF_TARGET)
+ status_change_start(bl,type,rate,7,0,0,0,skill,0);
+
+ if (sd->addeff[i].flag&ATF_SELF)
+ status_change_start(src,type,rate,7,0,0,0,skill,0);
+ }
+ }
+
+ if( skill_id )
+ { // Trigger status effects on skills
+ enum sc_type type;
+ int i;
+ for( i = 0; i < ARRAYLENGTH(sd->addeff3) && sd->addeff3[i].skill; i++ )
+ {
+ if( skill_id != sd->addeff3[i].skill || !sd->addeff3[i].rate )
+ continue;
+ type = sd->addeff3[i].id;
+ skill = skill_get_time2(status_sc2skill(type),7);
+
+ if( sd->addeff3[i].target&ATF_TARGET )
+ status_change_start(bl,type,sd->addeff3[i].rate,7,0,0,0,skill,0);
+ if( sd->addeff3[i].target&ATF_SELF )
+ status_change_start(src,type,sd->addeff3[i].rate,7,0,0,0,skill,0);
+ }
+ }
+ }
+
+ if( dmg_lv < ATK_DEF ) // no damage, return;
+ return 0;
+
+ switch(skill_id)
+ {
+ case 0: // Normal attacks (no skill used)
+ {
+ if( attack_type&BF_SKILL )
+ break; // If a normal attack is a skill, it's splash damage. [Inkfish]
+ if(sd) {
+ // Automatic trigger of Blitz Beat
+ if (pc_isfalcon(sd) && sd->status.weapon == W_BOW && (skill=pc_checkskill(sd,HT_BLITZBEAT))>0 &&
+ rnd()%1000 <= sstatus->luk*10/3+1 ) {
+ rate=(sd->status.job_level+9)/10;
+ skill_castend_damage_id(src,bl,HT_BLITZBEAT,(skill<rate)?skill:rate,tick,SD_LEVEL);
+ }
+ // Automatic trigger of Warg Strike [Jobbie]
+ if( pc_iswug(sd) && (sd->status.weapon == W_BOW || sd->status.weapon == W_FIST) && (skill=pc_checkskill(sd,RA_WUGSTRIKE)) > 0 && rnd()%1000 <= sstatus->luk*10/3+1 )
+ skill_castend_damage_id(src,bl,RA_WUGSTRIKE,skill,tick,0);
+ // Gank
+ if(dstmd && sd->status.weapon != W_BOW &&
+ (skill=pc_checkskill(sd,RG_SNATCHER)) > 0 &&
+ (skill*15 + 55) + pc_checkskill(sd,TF_STEAL)*10 > rnd()%1000) {
+ if(pc_steal_item(sd,bl,pc_checkskill(sd,TF_STEAL)))
+ clif_skill_nodamage(src,bl,TF_STEAL,skill,1);
+ else
+ clif_skill_fail(sd,RG_SNATCHER,USESKILL_FAIL_LEVEL,0);
+ }
+ // Chance to trigger Taekwon kicks [Dralnu]
+ if(sc && !sc->data[SC_COMBO]) {
+ if(sc->data[SC_READYSTORM] &&
+ sc_start(src,SC_COMBO, 15, TK_STORMKICK,
+ (2000 - 4*sstatus->agi - 2*sstatus->dex)))
+ ; //Stance triggered
+ else if(sc->data[SC_READYDOWN] &&
+ sc_start(src,SC_COMBO, 15, TK_DOWNKICK,
+ (2000 - 4*sstatus->agi - 2*sstatus->dex)))
+ ; //Stance triggered
+ else if(sc->data[SC_READYTURN] &&
+ sc_start(src,SC_COMBO, 15, TK_TURNKICK,
+ (2000 - 4*sstatus->agi - 2*sstatus->dex)))
+ ; //Stance triggered
+ else if (sc->data[SC_READYCOUNTER]) { //additional chance from SG_FRIEND [Komurka]
+ rate = 20;
+ if (sc->data[SC_SKILLRATE_UP] && sc->data[SC_SKILLRATE_UP]->val1 == TK_COUNTER) {
+ rate += rate*sc->data[SC_SKILLRATE_UP]->val2/100;
+ status_change_end(src, SC_SKILLRATE_UP, INVALID_TIMER);
+ }
+ sc_start2(src, SC_COMBO, rate, TK_COUNTER, bl->id,
+ (2000 - 4*sstatus->agi - 2*sstatus->dex));
+ }
+ }
+ if(sc && sc->data[SC_PYROCLASTIC] && (rnd() % 1000 <= sstatus->luk * 10 / 3 + 1) )
+ skill_castend_pos2(src, bl->x, bl->y, BS_HAMMERFALL,sc->data[SC_PYROCLASTIC]->val1, tick, 0);
+ }
+
+ if (sc) {
+ struct status_change_entry *sce;
+ // Enchant Poison gives a chance to poison attacked enemies
+ if((sce=sc->data[SC_ENCPOISON])) //Don't use sc_start since chance comes in 1/10000 rate.
+ status_change_start(bl,SC_POISON,sce->val2, sce->val1,src->id,0,0,
+ skill_get_time2(AS_ENCHANTPOISON,sce->val1),0);
+ // Enchant Deadly Poison gives a chance to deadly poison attacked enemies
+ if((sce=sc->data[SC_EDP]))
+ sc_start4(bl,SC_DPOISON,sce->val2, sce->val1,src->id,0,0,
+ skill_get_time2(ASC_EDP,sce->val1));
+ }
+ }
+ break;
+
+ case SM_BASH:
+ if( sd && skill_lv > 5 && pc_checkskill(sd,SM_FATALBLOW)>0 ){
+ //TODO: How much % per base level it actually is?
+ sc_start(bl,SC_STUN,(5*(skill_lv-5)+(int)sd->status.base_level/10),
+ skill_lv,skill_get_time2(SM_FATALBLOW,skill_lv));
+ }
+ break;
+
+ case MER_CRASH:
+ sc_start(bl,SC_STUN,(6*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case AS_VENOMKNIFE:
+ if (sd) //Poison chance must be that of Envenom. [Skotlex]
+ skill_lv = pc_checkskill(sd, TF_POISON);
+ case TF_POISON:
+ case AS_SPLASHER:
+ if(!sc_start2(bl,SC_POISON,(4*skill_lv+10),skill_lv,src->id,skill_get_time2(skill_id,skill_lv))
+ && sd && skill_id==TF_POISON
+ )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+
+ case AS_SONICBLOW:
+ sc_start(bl,SC_STUN,(2*skill_lv+10),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case WZ_FIREPILLAR:
+ unit_set_walkdelay(bl, tick, skill_get_time2(skill_id, skill_lv), 1);
+ break;
+
+ case MG_FROSTDIVER:
+#ifndef RENEWAL
+ case WZ_FROSTNOVA:
+#endif
+ sc_start(bl,SC_FREEZE,skill_lv*3+35,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+#ifdef RENEWAL
+ case WZ_FROSTNOVA:
+ sc_start(bl,SC_FREEZE,skill_lv*5+33,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+#endif
+
+ case WZ_STORMGUST:
+ /**
+ * Storm Gust counter was dropped in renewal
+ **/
+ #ifdef RENEWAL
+ sc_start(bl,SC_FREEZE,65-(5*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
+ #else
+ //Tharis pointed out that this is normal freeze chance with a base of 300%
+ if(tsc->sg_counter >= 3 &&
+ sc_start(bl,SC_FREEZE,300,skill_lv,skill_get_time2(skill_id,skill_lv)))
+ tsc->sg_counter = 0;
+ /**
+ * being it only resets on success it'd keep stacking and eventually overflowing on mvps, so we reset at a high value
+ **/
+ else if( tsc->sg_counter > 250 )
+ tsc->sg_counter = 0;
+ #endif
+ break;
+
+ case WZ_METEOR:
+ sc_start(bl,SC_STUN,3*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case WZ_VERMILION:
+ sc_start(bl,SC_BLIND,4*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case HT_FREEZINGTRAP:
+ case MA_FREEZINGTRAP:
+ sc_start(bl,SC_FREEZE,(3*skill_lv+35),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case HT_FLASHER:
+ sc_start(bl,SC_BLIND,(10*skill_lv+30),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case HT_LANDMINE:
+ case MA_LANDMINE:
+ sc_start(bl,SC_STUN,(5*skill_lv+30),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case HT_SHOCKWAVE:
+ status_percent_damage(src, bl, 0, 15*skill_lv+5, false);
+ break;
+
+ case HT_SANDMAN:
+ case MA_SANDMAN:
+ sc_start(bl,SC_SLEEP,(10*skill_lv+40),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case TF_SPRINKLESAND:
+ sc_start(bl,SC_BLIND,20,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case TF_THROWSTONE:
+ sc_start(bl,SC_STUN,3,skill_lv,skill_get_time(skill_id,skill_lv));
+ sc_start(bl,SC_BLIND,3,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case NPC_DARKCROSS:
+ case CR_HOLYCROSS:
+ sc_start(bl,SC_BLIND,3*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ //Chance to cause blind status vs demon and undead element, but not against players
+ if(!dstsd && (battle_check_undead(tstatus->race,tstatus->def_ele) || tstatus->race == RC_DEMON))
+ sc_start(bl,SC_BLIND,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ attack_type |= BF_WEAPON;
+ break;
+
+ case AM_ACIDTERROR:
+ sc_start(bl,SC_BLEEDING,(skill_lv*3),skill_lv,skill_get_time2(skill_id,skill_lv));
+ if (skill_break_equip(bl, EQP_ARMOR, 100*skill_get_time(skill_id,skill_lv), BCT_ENEMY))
+ clif_emotion(bl,E_OMG);
+ break;
+
+ case AM_DEMONSTRATION:
+ skill_break_equip(bl, EQP_WEAPON, 100*skill_lv, BCT_ENEMY);
+ break;
+
+ case CR_SHIELDCHARGE:
+ sc_start(bl,SC_STUN,(15+skill_lv*5),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case PA_PRESSURE:
+ status_percent_damage(src, bl, 0, 15+5*skill_lv, false);
+ break;
+
+ case RG_RAID:
+ sc_start(bl,SC_STUN,(10+3*skill_lv),skill_lv,skill_get_time(skill_id,skill_lv));
+ sc_start(bl,SC_BLIND,(10+3*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
+
+#ifdef RENEWAL
+ sc_start(bl,SC_RAID,100,7,5000);
+ break;
+
+ case RG_BACKSTAP:
+ sc_start(bl,SC_STUN,(5+2*skill_lv),skill_lv,skill_get_time(skill_id,skill_lv));
+#endif
+ break;
+
+ case BA_FROSTJOKER:
+ sc_start(bl,SC_FREEZE,(15+5*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case DC_SCREAM:
+ sc_start(bl,SC_STUN,(25+5*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case BD_LULLABY:
+ sc_start(bl,SC_SLEEP,15,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case DC_UGLYDANCE:
+ rate = 5+5*skill_lv;
+ if(sd && (skill=pc_checkskill(sd,DC_DANCINGLESSON)))
+ rate += 5+skill;
+ status_zap(bl, 0, rate);
+ break;
+ case SL_STUN:
+ if (tstatus->size==SZ_MEDIUM) //Only stuns mid-sized mobs.
+ sc_start(bl,SC_STUN,(30+10*skill_lv),skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+
+ case NPC_PETRIFYATTACK:
+ sc_start4(bl,status_skill2sc(skill_id),50+10*skill_lv,
+ skill_lv,0,0,skill_get_time(skill_id,skill_lv),
+ skill_get_time2(skill_id,skill_lv));
+ break;
+ case NPC_CURSEATTACK:
+ case NPC_SLEEPATTACK:
+ case NPC_BLINDATTACK:
+ case NPC_POISON:
+ case NPC_SILENCEATTACK:
+ case NPC_STUNATTACK:
+ case NPC_HELLPOWER:
+ sc_start(bl,status_skill2sc(skill_id),50+10*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case NPC_ACIDBREATH:
+ case NPC_ICEBREATH:
+ sc_start(bl,status_skill2sc(skill_id),70,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case NPC_BLEEDING:
+ sc_start(bl,SC_BLEEDING,(20*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case NPC_MENTALBREAKER:
+ { //Based on observations by Tharis, Mental Breaker should do SP damage
+ //equal to Matk*skLevel.
+ rate = sstatus->matk_min;
+ if (rate < sstatus->matk_max)
+ rate += rnd()%(sstatus->matk_max - sstatus->matk_min);
+ rate*=skill_lv;
+ status_zap(bl, 0, rate);
+ break;
+ }
+ // Equipment breaking monster skills [Celest]
+ case NPC_WEAPONBRAKER:
+ skill_break_equip(bl, EQP_WEAPON, 150*skill_lv, BCT_ENEMY);
+ break;
+ case NPC_ARMORBRAKE:
+ skill_break_equip(bl, EQP_ARMOR, 150*skill_lv, BCT_ENEMY);
+ break;
+ case NPC_HELMBRAKE:
+ skill_break_equip(bl, EQP_HELM, 150*skill_lv, BCT_ENEMY);
+ break;
+ case NPC_SHIELDBRAKE:
+ skill_break_equip(bl, EQP_SHIELD, 150*skill_lv, BCT_ENEMY);
+ break;
+
+ case CH_TIGERFIST:
+ sc_start(bl,SC_STOP,(10+skill_lv*10),0,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case LK_SPIRALPIERCE:
+ case ML_SPIRALPIERCE:
+ sc_start(bl,SC_STOP,(15+skill_lv*5),0,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case ST_REJECTSWORD:
+ sc_start(bl,SC_AUTOCOUNTER,(skill_lv*15),skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+
+ case PF_FOGWALL:
+ if (src != bl && !tsc->data[SC_DELUGE])
+ sc_start(bl,SC_BLIND,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case LK_HEADCRUSH: //Headcrush has chance of causing Bleeding status, except on demon and undead element
+ if (!(battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race == RC_DEMON))
+ sc_start(bl, SC_BLEEDING,50, skill_lv, skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case LK_JOINTBEAT:
+ status = status_skill2sc(skill_id);
+ if (tsc->jb_flag) {
+ sc_start2(bl,status,(5*skill_lv+5),skill_lv,tsc->jb_flag&BREAK_FLAGS,skill_get_time2(skill_id,skill_lv));
+ tsc->jb_flag = 0;
+ }
+ break;
+ case ASC_METEORASSAULT:
+ //Any enemies hit by this skill will receive Stun, Darkness, or external bleeding status ailment with a 5%+5*skill_lv% chance.
+ switch(rnd()%3) {
+ case 0:
+ sc_start(bl,SC_BLIND,(5+skill_lv*5),skill_lv,skill_get_time2(skill_id,1));
+ break;
+ case 1:
+ sc_start(bl,SC_STUN,(5+skill_lv*5),skill_lv,skill_get_time2(skill_id,2));
+ break;
+ default:
+ sc_start(bl,SC_BLEEDING,(5+skill_lv*5),skill_lv,skill_get_time2(skill_id,3));
+ }
+ break;
+
+ case HW_NAPALMVULCAN:
+ sc_start(bl,SC_CURSE,5*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case WS_CARTTERMINATION: // Cart termination
+ sc_start(bl,SC_STUN,5*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case CR_ACIDDEMONSTRATION:
+ skill_break_equip(bl, EQP_WEAPON|EQP_ARMOR, 100*skill_lv, BCT_ENEMY);
+ break;
+
+ case TK_DOWNKICK:
+ sc_start(bl,SC_STUN,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case TK_JUMPKICK:
+ if( dstsd && dstsd->class_ != MAPID_SOUL_LINKER && !tsc->data[SC_PRESERVE] )
+ {// debuff the following statuses
+ status_change_end(bl, SC_SPIRIT, INVALID_TIMER);
+ status_change_end(bl, SC_ADRENALINE2, INVALID_TIMER);
+ status_change_end(bl, SC_KAITE, INVALID_TIMER);
+ status_change_end(bl, SC_KAAHI, INVALID_TIMER);
+ status_change_end(bl, SC_ONEHAND, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION2, INVALID_TIMER);
+ }
+ break;
+ case TK_TURNKICK:
+ case MO_BALKYOUNG: //Note: attack_type is passed as BF_WEAPON for the actual target, BF_MISC for the splash-affected mobs.
+ if(attack_type&BF_MISC) //70% base stun chance...
+ sc_start(bl,SC_STUN,70,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case GS_BULLSEYE: //0.1% coma rate.
+ if(tstatus->race == RC_BRUTE || tstatus->race == RC_DEMIHUMAN)
+ status_change_start(bl,SC_COMA,10,skill_lv,0,src->id,0,0,0);
+ break;
+ case GS_PIERCINGSHOT:
+ sc_start(bl,SC_BLEEDING,(skill_lv*3),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case NJ_HYOUSYOURAKU:
+ sc_start(bl,SC_FREEZE,(10+10*skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case GS_FLING:
+ sc_start(bl,SC_FLING,100, sd?sd->spiritball_old:5,skill_get_time(skill_id,skill_lv));
+ break;
+ case GS_DISARM:
+ rate = 3*skill_lv;
+ if (sstatus->dex > tstatus->dex)
+ rate += (sstatus->dex - tstatus->dex)/5; //TODO: Made up formula
+ skill_strip_equip(bl, EQP_WEAPON, rate, skill_lv, skill_get_time(skill_id,skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case NPC_EVILLAND:
+ sc_start(bl,SC_BLIND,5*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case NPC_HELLJUDGEMENT:
+ sc_start(bl,SC_CURSE,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case NPC_CRITICALWOUND:
+ sc_start(bl,SC_CRITICALWOUND,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case RK_HUNDREDSPEAR:
+ if( !sd || pc_checkskill(sd,KN_SPEARBOOMERANG) == 0 )
+ break; // Spear Boomerang auto cast chance only works if you have mastered Spear Boomerang.
+ rate = 10 + 3 * skill_lv;
+ if( rnd()%100 < rate )
+ skill_castend_damage_id(src,bl,KN_SPEARBOOMERANG,1,tick,0);
+ break;
+ case RK_WINDCUTTER:
+ sc_start(bl,SC_FEAR,3+2*skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case RK_DRAGONBREATH:
+ sc_start4(bl,SC_BURNING,5+5*skill_lv,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv));
+ break;
+ case AB_ADORAMUS:
+ if( tsc && !tsc->data[SC_DECREASEAGI] ) //Prevent duplicate agi-down effect.
+ sc_start(bl, SC_ADORAMUS, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case WL_CRIMSONROCK:
+ sc_start(bl, SC_STUN, 40, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case WL_COMET:
+ sc_start4(bl,SC_BURNING,100,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv));
+ break;
+ case WL_EARTHSTRAIN:
+ {
+ int rate = 0, i;
+ const int pos[5] = { EQP_WEAPON, EQP_HELM, EQP_SHIELD, EQP_ARMOR, EQP_ACC };
+ rate = 6 * skill_lv + sstatus->dex / 10 + (sd? sd->status.job_level / 4 : 0) - tstatus->dex /5;// The tstatus->dex / 5 part is unofficial, but players gotta have some kind of way to have resistance. [Rytech]
+ //rate -= rate * tstatus->dex / 200; // Disabled until official resistance is found.
+
+ for( i = 0; i < skill_lv; i++ )
+ skill_strip_equip(bl,pos[i],rate,skill_lv,skill_get_time2(skill_id,skill_lv));
+ }
+ break;
+ case WL_JACKFROST:
+ sc_start(bl,SC_FREEZE,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case RA_WUGBITE:
+ sc_start(bl, SC_BITE, (sd ? pc_checkskill(sd,RA_TOOTHOFWUG)*2 : 0), skill_lv, (skill_get_time(skill_id,skill_lv) + (sd ? pc_checkskill(sd,RA_TOOTHOFWUG)*500 : 0)) );
+ break;
+ case RA_SENSITIVEKEEN:
+ if( rnd()%100 < 8 * skill_lv )
+ skill_castend_damage_id(src, bl, RA_WUGBITE, sd ? pc_checkskill(sd, RA_WUGBITE):skill_lv, tick, SD_ANIMATION);
+ break;
+ case RA_FIRINGTRAP:
+ case RA_ICEBOUNDTRAP:
+ sc_start(bl, (skill_id == RA_FIRINGTRAP) ? SC_BURNING:SC_FREEZING, 40 + 10 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv));
+ break;
+ case NC_PILEBUNKER:
+ if( rnd()%100 < 5 + 15*skill_lv )
+ { //Deactivatable Statuses: Kyrie Eleison, Auto Guard, Steel Body, Assumptio, and Millennium Shield
+ status_change_end(bl, SC_KYRIE, INVALID_TIMER);
+ status_change_end(bl, SC_AUTOGUARD, INVALID_TIMER);
+ status_change_end(bl, SC_STEELBODY, INVALID_TIMER);
+ status_change_end(bl, SC_ASSUMPTIO, INVALID_TIMER);
+ status_change_end(bl, SC_MILLENNIUMSHIELD, INVALID_TIMER);
+ }
+ break;
+ case NC_FLAMELAUNCHER:
+ sc_start4(bl, SC_BURNING, 50 + 10 * skill_lv, skill_lv, 1000, src->id, 0, skill_get_time2(skill_id, skill_lv));
+ break;
+ case NC_COLDSLOWER:
+ sc_start(bl, SC_FREEZE, 10 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
+ sc_start(bl, SC_FREEZING, 20 + 10 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case NC_POWERSWING:
+ sc_start(bl, SC_STUN, 5*skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
+ if( rnd()%100 < 5*skill_lv )
+ skill_castend_damage_id(src, bl, NC_AXEBOOMERANG, pc_checkskill(sd, NC_AXEBOOMERANG), tick, 1);
+ break;
+ case GC_WEAPONCRUSH:
+ skill_castend_nodamage_id(src,bl,skill_id,skill_lv,tick,BCT_ENEMY);
+ break;
+ case LG_SHIELDPRESS:
+ sc_start(bl, SC_STUN, 30 + 8 * skill_lv, skill_lv, skill_get_time(skill_id,skill_lv));
+ break;
+ case LG_PINPOINTATTACK:
+ rate = 30 + (((5 * (sd?pc_checkskill(sd,LG_PINPOINTATTACK):skill_lv)) + (sstatus->agi + status_get_lv(src))) / 10);
+ switch( skill_lv ) {
+ case 1:
+ sc_start(bl,SC_BLEEDING,rate,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case 2:
+ if( dstsd && dstsd->spiritball && rnd()%100 < rate )
+ pc_delspiritball(dstsd, dstsd->spiritball, 0);
+ break;
+ default:
+ skill_break_equip(bl,(skill_lv == 3) ? EQP_SHIELD : (skill_lv == 4) ? EQP_ARMOR : EQP_WEAPON,rate * 100,BCT_ENEMY);
+ break;
+ }
+ break;
+ case LG_MOONSLASHER:
+ rate = 32 + 8 * skill_lv;
+ if( rnd()%100 < rate && dstsd ) // Uses skill_addtimerskill to avoid damage and setsit packet overlaping. Officially clif_setsit is received about 500 ms after damage packet.
+ skill_addtimerskill(src,tick+500,bl->id,0,0,skill_id,skill_lv,BF_WEAPON,0);
+ else if( dstmd && !is_boss(bl) )
+ sc_start(bl,SC_STOP,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case LG_RAYOFGENESIS: // 50% chance to cause Blind on Undead and Demon monsters.
+ if ( battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race == RC_DEMON )
+ sc_start(bl, SC_BLIND,50, skill_lv, skill_get_time(skill_id,skill_lv));
+ break;
+ case LG_EARTHDRIVE:
+ skill_break_equip(src, EQP_SHIELD, 500, BCT_SELF);
+ sc_start(bl, SC_EARTHDRIVE, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case SR_DRAGONCOMBO:
+ sc_start(bl, SC_STUN, 1 + skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case SR_FALLENEMPIRE:
+ sc_start(bl, SC_STOP, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case SR_WINDMILL:
+ if( dstsd )
+ skill_addtimerskill(src,tick+status_get_amotion(src),bl->id,0,0,skill_id,skill_lv,BF_WEAPON,0);
+ else if( dstmd && !is_boss(bl) )
+ sc_start(bl, SC_STUN, 100, skill_lv, 1000 + 1000 * (rnd() %3));
+ break;
+ case SR_GENTLETOUCH_QUIET: // [(Skill Level x 5) + (Caster?s DEX + Caster?s Base Level) / 10]
+ sc_start(bl, SC_SILENCE, 5 * skill_lv + (sstatus->dex + status_get_lv(src)) / 10, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case SR_EARTHSHAKER:
+ sc_start(bl,SC_STUN, 25 + 5 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case SR_HOWLINGOFLION:
+ sc_start(bl, SC_FEAR, 5 + 5 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case WM_SOUND_OF_DESTRUCTION:
+ if( rnd()%100 < 5 + 5 * skill_lv ) { // Temporarly Check Until We Get the Official Formula
+ status_change_end(bl, SC_DANCING, INVALID_TIMER);
+ status_change_end(bl, SC_RICHMANKIM, INVALID_TIMER);
+ status_change_end(bl, SC_ETERNALCHAOS, INVALID_TIMER);
+ status_change_end(bl, SC_DRUMBATTLE, INVALID_TIMER);
+ status_change_end(bl, SC_NIBELUNGEN, INVALID_TIMER);
+ status_change_end(bl, SC_INTOABYSS, INVALID_TIMER);
+ status_change_end(bl, SC_SIEGFRIED, INVALID_TIMER);
+ status_change_end(bl, SC_WHISTLE, INVALID_TIMER);
+ status_change_end(bl, SC_ASSNCROS, INVALID_TIMER);
+ status_change_end(bl, SC_POEMBRAGI, INVALID_TIMER);
+ status_change_end(bl, SC_APPLEIDUN, INVALID_TIMER);
+ status_change_end(bl, SC_HUMMING, INVALID_TIMER);
+ status_change_end(bl, SC_FORTUNE, INVALID_TIMER);
+ status_change_end(bl, SC_SERVICE4U, INVALID_TIMER);
+ status_change_end(bl, SC_LONGING, INVALID_TIMER);
+ status_change_end(bl, SC_SWINGDANCE, INVALID_TIMER);
+ status_change_end(bl, SC_SYMPHONYOFLOVER, INVALID_TIMER);
+ status_change_end(bl, SC_MOONLITSERENADE, INVALID_TIMER);
+ status_change_end(bl, SC_RUSHWINDMILL, INVALID_TIMER);
+ status_change_end(bl, SC_ECHOSONG, INVALID_TIMER);
+ status_change_end(bl, SC_HARMONIZE, INVALID_TIMER);
+ status_change_end(bl, SC_WINKCHARM, INVALID_TIMER);
+ status_change_end(bl, SC_SONGOFMANA, INVALID_TIMER);
+ status_change_end(bl, SC_DANCEWITHWUG, INVALID_TIMER);
+ status_change_end(bl, SC_LERADSDEW, INVALID_TIMER);
+ status_change_end(bl, SC_MELODYOFSINK, INVALID_TIMER);
+ status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER);
+ status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER);
+ }
+ break;
+ case SO_EARTHGRAVE:
+ sc_start(bl, SC_BLEEDING, 5 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv)); // Need official rate. [LimitLine]
+ break;
+ case SO_DIAMONDDUST:
+ rate = 5 + 5 * skill_lv;
+ if( sc && sc->data[SC_COOLER_OPTION] )
+ rate += rate * sc->data[SC_COOLER_OPTION]->val2 / 100;
+ sc_start(bl, SC_CRYSTALIZE, rate, skill_lv, skill_get_time2(skill_id, skill_lv));
+ break;
+ case SO_VARETYR_SPEAR:
+ sc_start(bl, SC_STUN, 5 + 5 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv));
+ break;
+ case GN_SLINGITEM_RANGEMELEEATK:
+ if( sd ) {
+ switch( sd->itemid ) { // Starting SCs here instead of do it in skill_additional_effect to simplify the code.
+ case 13261:
+ sc_start(bl, SC_STUN, 100, skill_lv, skill_get_time2(GN_SLINGITEM, skill_lv));
+ sc_start(bl, SC_BLEEDING, 100, skill_lv, skill_get_time2(GN_SLINGITEM, skill_lv));
+ break;
+ case 13262:
+ sc_start(bl, SC_MELON_BOMB, 100, skill_lv, skill_get_time(GN_SLINGITEM, skill_lv)); // Reduces ASPD and moviment speed
+ break;
+ case 13264:
+ sc_start(bl, SC_BANANA_BOMB, 100, skill_lv, skill_get_time(GN_SLINGITEM, skill_lv)); // Reduces LUK ??Needed confirm it, may be it's bugged in kRORE?
+ sc_start(bl, SC_BANANA_BOMB_SITDOWN, 75, skill_lv, skill_get_time(GN_SLINGITEM_RANGEMELEEATK,skill_lv)); // Sitdown for 3 seconds.
+ break;
+ }
+ sd->itemid = -1;
+ }
+ break;
+ case GN_HELLS_PLANT_ATK:
+ sc_start(bl, SC_STUN, 5 + 5 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv));
+ sc_start(bl, SC_BLEEDING, 20 + 10 * skill_lv, skill_lv, skill_get_time2(skill_id, skill_lv));
+ break;
+ case EL_WIND_SLASH: // Non confirmed rate.
+ sc_start(bl, SC_BLEEDING, 25, skill_lv, skill_get_time(skill_id,skill_lv));
+ break;
+ case EL_STONE_HAMMER:
+ rate = 10 * skill_lv;
+ sc_start(bl, SC_STUN, rate, skill_lv, skill_get_time(skill_id,skill_lv));
+ break;
+ case EL_ROCK_CRUSHER:
+ case EL_ROCK_CRUSHER_ATK:
+ sc_start(bl,status_skill2sc(skill_id),50,skill_lv,skill_get_time(EL_ROCK_CRUSHER,skill_lv));
+ break;
+ case EL_TYPOON_MIS:
+ sc_start(bl,SC_SILENCE,10*skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case KO_JYUMONJIKIRI: // needs more info
+ sc_start(bl,SC_JYUMONJIKIRI,25,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case KO_MAKIBISHI:
+ sc_start(bl, SC_STUN, 100, skill_lv, skill_get_time2(skill_id,skill_lv));
+ break;
+ case MH_LAVA_SLIDE:
+ if (tsc && !tsc->data[SC_BURNING]) sc_start4(bl, SC_BURNING, 10 * skill_lv, skill_lv, 1000, src->id, 0, skill_get_time(skill_id, skill_lv));
+ break;
+ case MH_STAHL_HORN:
+ sc_start(bl, SC_STUN, (20 + 4 * (skill_lv-1)), skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ case MH_NEEDLE_OF_PARALYZE:
+ sc_start(bl, SC_PARALYSIS, 40 + (5*skill_lv), skill_lv, skill_get_time(skill_id, skill_lv));
+ break;
+ }
+
+ if (md && battle_config.summons_trigger_autospells && md->master_id && md->special_state.ai)
+ { //Pass heritage to Master for status causing effects. [Skotlex]
+ sd = map_id2sd(md->master_id);
+ src = sd?&sd->bl:src;
+ }
+
+ if( attack_type&BF_WEAPON )
+ { // Coma, Breaking Equipment
+ if( sd && sd->special_state.bonus_coma )
+ {
+ rate = sd->weapon_coma_ele[tstatus->def_ele];
+ rate += sd->weapon_coma_race[tstatus->race];
+ rate += sd->weapon_coma_race[tstatus->mode&MD_BOSS?RC_BOSS:RC_NONBOSS];
+ if (rate)
+ status_change_start(bl, SC_COMA, rate, 0, 0, src->id, 0, 0, 0);
+ }
+ if( sd && battle_config.equip_self_break_rate )
+ { // Self weapon breaking
+ rate = battle_config.equip_natural_break_rate;
+ if( sc )
+ {
+ if(sc->data[SC_OVERTHRUST])
+ rate += 10;
+ if(sc->data[SC_MAXOVERTHRUST])
+ rate += 10;
+ }
+ if( rate )
+ skill_break_equip(src, EQP_WEAPON, rate, BCT_SELF);
+ }
+ if( battle_config.equip_skill_break_rate && skill_id != WS_CARTTERMINATION && skill_id != ITM_TOMAHAWK )
+ { // Cart Termination/Tomahawk won't trigger breaking data. Why? No idea, go ask Gravity.
+ // Target weapon breaking
+ rate = 0;
+ if( sd )
+ rate += sd->bonus.break_weapon_rate;
+ if( sc && sc->data[SC_MELTDOWN] )
+ rate += sc->data[SC_MELTDOWN]->val2;
+ if( rate )
+ skill_break_equip(bl, EQP_WEAPON, rate, BCT_ENEMY);
+
+ // Target armor breaking
+ rate = 0;
+ if( sd )
+ rate += sd->bonus.break_armor_rate;
+ if( sc && sc->data[SC_MELTDOWN] )
+ rate += sc->data[SC_MELTDOWN]->val3;
+ if( rate )
+ skill_break_equip(bl, EQP_ARMOR, rate, BCT_ENEMY);
+ }
+ }
+
+ if( sd && sd->ed && sc && !status_isdead(bl) && !skill_id ){
+ struct unit_data *ud = unit_bl2ud(src);
+
+ if( sc->data[SC_WILD_STORM_OPTION] )
+ skill = sc->data[SC_WILD_STORM_OPTION]->val2;
+ else if( sc->data[SC_UPHEAVAL_OPTION] )
+ skill = sc->data[SC_UPHEAVAL_OPTION]->val2;
+ else if( sc->data[SC_TROPIC_OPTION] )
+ skill = sc->data[SC_TROPIC_OPTION]->val3;
+ else if( sc->data[SC_CHILLY_AIR_OPTION] )
+ skill = sc->data[SC_CHILLY_AIR_OPTION]->val3;
+ else
+ skill = 0;
+
+ if ( rnd()%100 < 25 && skill ){
+ skill_castend_damage_id(src, bl, skill, 5, tick, 0);
+
+ if (ud) {
+ rate = skill_delayfix(src, skill, skill_lv);
+ if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){
+ ud->canact_tick = tick+rate;
+ if ( battle_config.display_status_timers )
+ clif_status_change(src, SI_ACTIONDELAY, 1, rate, 0, 0, 0);
+ }
+ }
+ }
+ }
+
+ // Autospell when attacking
+ if( sd && !status_isdead(bl) && sd->autospell[0].id )
+ {
+ struct block_list *tbl;
+ struct unit_data *ud;
+ int i, skill_lv, type, notok;
+
+ for (i = 0; i < ARRAYLENGTH(sd->autospell) && sd->autospell[i].id; i++) {
+
+ if(!(sd->autospell[i].flag&attack_type&BF_WEAPONMASK &&
+ sd->autospell[i].flag&attack_type&BF_RANGEMASK &&
+ sd->autospell[i].flag&attack_type&BF_SKILLMASK))
+ continue; // one or more trigger conditions were not fulfilled
+
+ skill = (sd->autospell[i].id > 0) ? sd->autospell[i].id : -sd->autospell[i].id;
+
+ sd->state.autocast = 1;
+ notok = skillnotok(skill, sd);
+ sd->state.autocast = 0;
+
+ if ( notok )
+ continue;
+
+ skill_lv = sd->autospell[i].lv?sd->autospell[i].lv:1;
+ if (skill_lv < 0) skill_lv = 1+rnd()%(-skill_lv);
+
+ rate = (!sd->state.arrow_atk) ? sd->autospell[i].rate : sd->autospell[i].rate / 2;
+
+ if (rnd()%1000 >= rate)
+ continue;
+
+ tbl = (sd->autospell[i].id < 0) ? src : bl;
+
+ if( (type = skill_get_casttype(skill)) == CAST_GROUND ) {
+ int maxcount = 0;
+ if( !(BL_PC&battle_config.skill_reiteration) &&
+ skill_get_unit_flag(skill)&UF_NOREITERATION &&
+ skill_check_unit_range(src,tbl->x,tbl->y,skill,skill_lv)
+ ) {
+ continue;
+ }
+ if( BL_PC&battle_config.skill_nofootset &&
+ skill_get_unit_flag(skill)&UF_NOFOOTSET &&
+ skill_check_unit_range2(src,tbl->x,tbl->y,skill,skill_lv)
+ ) {
+ continue;
+ }
+ if( BL_PC&battle_config.land_skill_limit &&
+ (maxcount = skill_get_maxcount(skill, skill_lv)) > 0
+ ) {
+ int v;
+ for(v=0;v<MAX_SKILLUNITGROUP && sd->ud.skillunit[v] && maxcount;v++) {
+ if(sd->ud.skillunit[v]->skill_id == skill)
+ maxcount--;
+ }
+ if( maxcount == 0 ) {
+ continue;
+ }
+ }
+ }
+ if( battle_config.autospell_check_range &&
+ !battle_check_range(src, tbl, skill_get_range2(src, skill,skill_lv) + (skill == RG_CLOSECONFINE?0:1)) )
+ continue;
+
+ if (skill == AS_SONICBLOW)
+ pc_stop_attack(sd); //Special case, Sonic Blow autospell should stop the player attacking.
+ if (skill == PF_SPIDERWEB) //Special case, due to its nature of coding.
+ type = CAST_GROUND;
+
+ sd->state.autocast = 1;
+ skill_consume_requirement(sd,skill,skill_lv,1);
+ skill_toggle_magicpower(src, skill);
+ switch (type) {
+ case CAST_GROUND:
+ skill_castend_pos2(src, tbl->x, tbl->y, skill, skill_lv, tick, 0);
+ break;
+ case CAST_NODAMAGE:
+ skill_castend_nodamage_id(src, tbl, skill, skill_lv, tick, 0);
+ break;
+ case CAST_DAMAGE:
+ skill_castend_damage_id(src, tbl, skill, skill_lv, tick, 0);
+ break;
+ }
+ sd->state.autocast = 0;
+ //Set canact delay. [Skotlex]
+ ud = unit_bl2ud(src);
+ if (ud) {
+ rate = skill_delayfix(src, skill, skill_lv);
+ if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){
+ ud->canact_tick = tick+rate;
+ if ( battle_config.display_status_timers && sd )
+ clif_status_change(src, SI_ACTIONDELAY, 1, rate, 0, 0, 0);
+ }
+ }
+ }
+ }
+
+ //Autobonus when attacking
+ if( sd && sd->autobonus[0].rate )
+ {
+ int i;
+ for( i = 0; i < ARRAYLENGTH(sd->autobonus); i++ )
+ {
+ if( rnd()%1000 >= sd->autobonus[i].rate )
+ continue;
+ if( sd->autobonus[i].active != INVALID_TIMER )
+ continue;
+ if(!(sd->autobonus[i].atk_type&attack_type&BF_WEAPONMASK &&
+ sd->autobonus[i].atk_type&attack_type&BF_RANGEMASK &&
+ sd->autobonus[i].atk_type&attack_type&BF_SKILLMASK))
+ continue; // one or more trigger conditions were not fulfilled
+ pc_exeautobonus(sd,&sd->autobonus[i]);
+ }
+ }
+
+ //Polymorph
+ if(sd && sd->bonus.classchange && attack_type&BF_WEAPON &&
+ dstmd && !(tstatus->mode&MD_BOSS) &&
+ (rnd()%10000 < sd->bonus.classchange))
+ {
+ struct mob_db *mob;
+ int class_;
+ skill = 0;
+ do {
+ do {
+ class_ = rnd() % MAX_MOB_DB;
+ } while (!mobdb_checkid(class_));
+
+ rate = rnd() % 1000000;
+ mob = mob_db(class_);
+ } while (
+ (mob->status.mode&(MD_BOSS|MD_PLANT) || mob->summonper[0] <= rate) &&
+ (skill++) < 2000);
+ if (skill < 2000)
+ mob_class_change(dstmd,class_);
+ }
+
+ return 0;
+}
+
+int skill_onskillusage(struct map_session_data *sd, struct block_list *bl, uint16 skill_id, unsigned int tick) {
+ int skill, skill_lv, i, type, notok;
+ struct block_list *tbl;
+
+ if( sd == NULL || !skill_id )
+ return 0;
+
+ for( i = 0; i < ARRAYLENGTH(sd->autospell3) && sd->autospell3[i].flag; i++ ) {
+ if( sd->autospell3[i].flag != skill_id )
+ continue;
+
+ if( sd->autospell3[i].lock )
+ continue; // autospell already being executed
+
+ skill = (sd->autospell3[i].id > 0) ? sd->autospell3[i].id : -sd->autospell3[i].id;
+
+ sd->state.autocast = 1;
+ notok = skillnotok(skill, sd);
+ sd->state.autocast = 0;
+
+ if ( notok )
+ continue;
+
+ skill_lv = sd->autospell3[i].lv ? sd->autospell3[i].lv : 1;
+ if( skill_lv < 0 ) skill_lv = 1 + rnd()%(-skill_lv);
+
+ if( sd->autospell3[i].id >= 0 && bl == NULL )
+ continue; // No target
+ if( rnd()%1000 >= sd->autospell3[i].rate )
+ continue;
+
+ tbl = (sd->autospell3[i].id < 0) ? &sd->bl : bl;
+
+ if( (type = skill_get_casttype(skill)) == CAST_GROUND ) {
+ int maxcount = 0;
+ if( !(BL_PC&battle_config.skill_reiteration) &&
+ skill_get_unit_flag(skill)&UF_NOREITERATION &&
+ skill_check_unit_range(&sd->bl,tbl->x,tbl->y,skill,skill_lv)
+ ) {
+ continue;
+ }
+ if( BL_PC&battle_config.skill_nofootset &&
+ skill_get_unit_flag(skill)&UF_NOFOOTSET &&
+ skill_check_unit_range2(&sd->bl,tbl->x,tbl->y,skill,skill_lv)
+ ) {
+ continue;
+ }
+ if( BL_PC&battle_config.land_skill_limit &&
+ (maxcount = skill_get_maxcount(skill, skill_lv)) > 0
+ ) {
+ int v;
+ for(v=0;v<MAX_SKILLUNITGROUP && sd->ud.skillunit[v] && maxcount;v++) {
+ if(sd->ud.skillunit[v]->skill_id == skill)
+ maxcount--;
+ }
+ if( maxcount == 0 ) {
+ continue;
+ }
+ }
+ }
+ if( battle_config.autospell_check_range &&
+ !battle_check_range(&sd->bl, tbl, skill_get_range2(&sd->bl, skill,skill_lv) + (skill == RG_CLOSECONFINE?0:1)) )
+ continue;
+
+ sd->state.autocast = 1;
+ sd->autospell3[i].lock = true;
+ skill_consume_requirement(sd,skill,skill_lv,1);
+ switch( type )
+ {
+ case CAST_GROUND: skill_castend_pos2(&sd->bl, tbl->x, tbl->y, skill, skill_lv, tick, 0); break;
+ case CAST_NODAMAGE: skill_castend_nodamage_id(&sd->bl, tbl, skill, skill_lv, tick, 0); break;
+ case CAST_DAMAGE: skill_castend_damage_id(&sd->bl, tbl, skill, skill_lv, tick, 0); break;
+ }
+ sd->autospell3[i].lock = false;
+ sd->state.autocast = 0;
+ }
+
+ if( sd && sd->autobonus3[0].rate )
+ {
+ for( i = 0; i < ARRAYLENGTH(sd->autobonus3); i++ )
+ {
+ if( rnd()%1000 >= sd->autobonus3[i].rate )
+ continue;
+ if( sd->autobonus3[i].active != INVALID_TIMER )
+ continue;
+ if( sd->autobonus3[i].atk_type != skill_id )
+ continue;
+ pc_exeautobonus(sd,&sd->autobonus3[i]);
+ }
+ }
+
+ return 1;
+}
+
+/* Splitted off from skill_additional_effect, which is never called when the
+ * attack skill kills the enemy. Place in this function counter status effects
+ * when using skills (eg: Asura's sp regen penalty, or counter-status effects
+ * from cards) that will take effect on the source, not the target. [Skotlex]
+ * Note: Currently this function only applies to Extremity Fist and BF_WEAPON
+ * type of skills, so not every instance of skill_additional_effect needs a call
+ * to this one.
+ */
+int skill_counter_additional_effect (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int attack_type, unsigned int tick)
+{
+ int rate;
+ struct map_session_data *sd=NULL;
+ struct map_session_data *dstsd=NULL;
+
+ nullpo_ret(src);
+ nullpo_ret(bl);
+
+ if(skill_id > 0 && !skill_lv) return 0; // don't forget auto attacks! - celest
+
+ sd = BL_CAST(BL_PC, src);
+ dstsd = BL_CAST(BL_PC, bl);
+
+ if(dstsd && attack_type&BF_WEAPON)
+ { //Counter effects.
+ enum sc_type type;
+ int i, time;
+ for(i=0; i < ARRAYLENGTH(dstsd->addeff2) && dstsd->addeff2[i].flag; i++)
+ {
+ rate = dstsd->addeff2[i].rate;
+ if (attack_type&BF_LONG)
+ rate+=dstsd->addeff2[i].arrow_rate;
+ if (!rate) continue;
+
+ if ((dstsd->addeff2[i].flag&(ATF_LONG|ATF_SHORT)) != (ATF_LONG|ATF_SHORT))
+ { //Trigger has range consideration.
+ if((dstsd->addeff2[i].flag&ATF_LONG && !(attack_type&BF_LONG)) ||
+ (dstsd->addeff2[i].flag&ATF_SHORT && !(attack_type&BF_SHORT)))
+ continue; //Range Failed.
+ }
+ type = dstsd->addeff2[i].id;
+ time = skill_get_time2(status_sc2skill(type),7);
+
+ if (dstsd->addeff2[i].flag&ATF_TARGET)
+ status_change_start(src,type,rate,7,0,0,0,time,0);
+
+ if (dstsd->addeff2[i].flag&ATF_SELF && !status_isdead(bl))
+ status_change_start(bl,type,rate,7,0,0,0,time,0);
+ }
+ }
+
+ switch(skill_id){
+ case MO_EXTREMITYFIST:
+ sc_start(src,SC_EXTREMITYFIST,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case GS_FULLBUSTER:
+ sc_start(src,SC_BLIND,2*skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case HFLI_SBR44: //[orn]
+ case HVAN_EXPLOSION:
+ if(src->type == BL_HOM){
+ TBL_HOM *hd = (TBL_HOM*)src;
+ hd->homunculus.intimacy = 200;
+ if (hd->master)
+ clif_send_homdata(hd->master,SP_INTIMATE,hd->homunculus.intimacy/100);
+ }
+ break;
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ attack_type |= BF_WEAPON;
+ break;
+ }
+
+ if(sd && (sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR &&
+ rnd()%10000 < battle_config.sg_miracle_skill_ratio) //SG_MIRACLE [Komurka]
+ sc_start(src,SC_MIRACLE,100,1,battle_config.sg_miracle_skill_duration);
+
+ if(sd && skill_id && attack_type&BF_MAGIC && status_isdead(bl) &&
+ !(skill_get_inf(skill_id)&(INF_GROUND_SKILL|INF_SELF_SKILL)) &&
+ (rate=pc_checkskill(sd,HW_SOULDRAIN))>0
+ ){ //Soul Drain should only work on targetted spells [Skotlex]
+ if (pc_issit(sd)) pc_setstand(sd); //Character stuck in attacking animation while 'sitting' fix. [Skotlex]
+ clif_skill_nodamage(src,bl,HW_SOULDRAIN,rate,1);
+ status_heal(src, 0, status_get_lv(bl)*(95+15*rate)/100, 2);
+ }
+
+ if( sd && status_isdead(bl) ) {
+ int sp = 0, hp = 0;
+ if( attack_type&BF_WEAPON ) {
+ sp += sd->bonus.sp_gain_value;
+ sp += sd->sp_gain_race[status_get_race(bl)];
+ sp += sd->sp_gain_race[is_boss(bl)?RC_BOSS:RC_NONBOSS];
+ hp += sd->bonus.hp_gain_value;
+ }
+ if( attack_type&BF_MAGIC ) {
+ sp += sd->bonus.magic_sp_gain_value;
+ hp += sd->bonus.magic_hp_gain_value;
+ if( skill_id == WZ_WATERBALL ) {//(bugreport:5303)
+ struct status_change *sc = NULL;
+ if( ( sc = status_get_sc(src) ) ) {
+ if(sc->data[SC_SPIRIT] &&
+ sc->data[SC_SPIRIT]->val2 == SL_WIZARD &&
+ sc->data[SC_SPIRIT]->val3 == WZ_WATERBALL)
+ sc->data[SC_SPIRIT]->val3 = 0; //Clear bounced spell check.
+ }
+ }
+ }
+ if( hp || sp ) { // updated to force healing to allow healing through berserk
+ status_heal(src, hp, sp, battle_config.show_hp_sp_gain ? 3 : 1);
+ }
+ }
+
+ // Trigger counter-spells to retaliate against damage causing skills.
+ if(dstsd && !status_isdead(bl) && dstsd->autospell2[0].id &&
+ !(skill_id && skill_get_nk(skill_id)&NK_NO_DAMAGE))
+ {
+ struct block_list *tbl;
+ struct unit_data *ud;
+ int i, skill_id, skill_lv, rate, type, notok;
+
+ for (i = 0; i < ARRAYLENGTH(dstsd->autospell2) && dstsd->autospell2[i].id; i++) {
+
+ if(!(dstsd->autospell2[i].flag&attack_type&BF_WEAPONMASK &&
+ dstsd->autospell2[i].flag&attack_type&BF_RANGEMASK &&
+ dstsd->autospell2[i].flag&attack_type&BF_SKILLMASK))
+ continue; // one or more trigger conditions were not fulfilled
+
+ skill_id = (dstsd->autospell2[i].id > 0) ? dstsd->autospell2[i].id : -dstsd->autospell2[i].id;
+ skill_lv = dstsd->autospell2[i].lv?dstsd->autospell2[i].lv:1;
+ if (skill_lv < 0) skill_lv = 1+rnd()%(-skill_lv);
+
+ rate = dstsd->autospell2[i].rate;
+ if (attack_type&BF_LONG)
+ rate>>=1;
+
+ dstsd->state.autocast = 1;
+ notok = skillnotok(skill_id, dstsd);
+ dstsd->state.autocast = 0;
+
+ if ( notok )
+ continue;
+
+ if (rnd()%1000 >= rate)
+ continue;
+
+ tbl = (dstsd->autospell2[i].id < 0) ? bl : src;
+
+ if( (type = skill_get_casttype(skill_id)) == CAST_GROUND ) {
+ int maxcount = 0;
+ if( !(BL_PC&battle_config.skill_reiteration) &&
+ skill_get_unit_flag(skill_id)&UF_NOREITERATION &&
+ skill_check_unit_range(bl,tbl->x,tbl->y,skill_id,skill_lv)
+ ) {
+ continue;
+ }
+ if( BL_PC&battle_config.skill_nofootset &&
+ skill_get_unit_flag(skill_id)&UF_NOFOOTSET &&
+ skill_check_unit_range2(bl,tbl->x,tbl->y,skill_id,skill_lv)
+ ) {
+ continue;
+ }
+ if( BL_PC&battle_config.land_skill_limit &&
+ (maxcount = skill_get_maxcount(skill_id, skill_lv)) > 0
+ ) {
+ int v;
+ for(v=0;v<MAX_SKILLUNITGROUP && dstsd->ud.skillunit[v] && maxcount;v++) {
+ if(dstsd->ud.skillunit[v]->skill_id == skill_id)
+ maxcount--;
+ }
+ if( maxcount == 0 ) {
+ continue;
+ }
+ }
+ }
+
+ if( !battle_check_range(src, tbl, skill_get_range2(src, skill_id,skill_lv) + (skill_id == RG_CLOSECONFINE?0:1)) && battle_config.autospell_check_range )
+ continue;
+
+ dstsd->state.autocast = 1;
+ skill_consume_requirement(dstsd,skill_id,skill_lv,1);
+ switch (type) {
+ case CAST_GROUND:
+ skill_castend_pos2(bl, tbl->x, tbl->y, skill_id, skill_lv, tick, 0);
+ break;
+ case CAST_NODAMAGE:
+ skill_castend_nodamage_id(bl, tbl, skill_id, skill_lv, tick, 0);
+ break;
+ case CAST_DAMAGE:
+ skill_castend_damage_id(bl, tbl, skill_id, skill_lv, tick, 0);
+ break;
+ }
+ dstsd->state.autocast = 0;
+ //Set canact delay. [Skotlex]
+ ud = unit_bl2ud(bl);
+ if (ud) {
+ rate = skill_delayfix(bl, skill_id, skill_lv);
+ if (DIFF_TICK(ud->canact_tick, tick + rate) < 0){
+ ud->canact_tick = tick+rate;
+ if ( battle_config.display_status_timers && dstsd )
+ clif_status_change(bl, SI_ACTIONDELAY, 1, rate, 0, 0, 0);
+ }
+ }
+ }
+ }
+
+ //Autobonus when attacked
+ if( dstsd && !status_isdead(bl) && dstsd->autobonus2[0].rate && !(skill_id && skill_get_nk(skill_id)&NK_NO_DAMAGE) )
+ {
+ int i;
+ for( i = 0; i < ARRAYLENGTH(dstsd->autobonus2); i++ )
+ {
+ if( rnd()%1000 >= dstsd->autobonus2[i].rate )
+ continue;
+ if( dstsd->autobonus2[i].active != INVALID_TIMER )
+ continue;
+ if(!(dstsd->autobonus2[i].atk_type&attack_type&BF_WEAPONMASK &&
+ dstsd->autobonus2[i].atk_type&attack_type&BF_RANGEMASK &&
+ dstsd->autobonus2[i].atk_type&attack_type&BF_SKILLMASK))
+ continue; // one or more trigger conditions were not fulfilled
+ pc_exeautobonus(dstsd,&dstsd->autobonus2[i]);
+ }
+ }
+
+ return 0;
+}
+/*=========================================================================
+ Breaks equipment. On-non players causes the corresponding strip effect.
+ - rate goes from 0 to 10000 (100.00%)
+ - flag is a BCT_ flag to indicate which type of adjustment should be used
+ (BCT_ENEMY/BCT_PARTY/BCT_SELF) are the valid values.
+--------------------------------------------------------------------------*/
+int skill_break_equip (struct block_list *bl, unsigned short where, int rate, int flag)
+{
+ const int where_list[4] = {EQP_WEAPON, EQP_ARMOR, EQP_SHIELD, EQP_HELM};
+ const enum sc_type scatk[4] = {SC_STRIPWEAPON, SC_STRIPARMOR, SC_STRIPSHIELD, SC_STRIPHELM};
+ const enum sc_type scdef[4] = {SC_CP_WEAPON, SC_CP_ARMOR, SC_CP_SHIELD, SC_CP_HELM};
+ struct status_change *sc = status_get_sc(bl);
+ int i,j;
+ TBL_PC *sd;
+ sd = BL_CAST(BL_PC, bl);
+ if (sc && !sc->count)
+ sc = NULL;
+
+ if (sd) {
+ if (sd->bonus.unbreakable_equip)
+ where &= ~sd->bonus.unbreakable_equip;
+ if (sd->bonus.unbreakable)
+ rate -= rate*sd->bonus.unbreakable/100;
+ if (where&EQP_WEAPON) {
+ switch (sd->status.weapon) {
+ case W_FIST: //Bare fists should not break :P
+ case W_1HAXE:
+ case W_2HAXE:
+ case W_MACE: // Axes and Maces can't be broken [DracoRPG]
+ case W_2HMACE:
+ case W_STAFF:
+ case W_2HSTAFF:
+ case W_BOOK: //Rods and Books can't be broken [Skotlex]
+ case W_HUUMA:
+ where &= ~EQP_WEAPON;
+ }
+ }
+ }
+ if (flag&BCT_ENEMY) {
+ if (battle_config.equip_skill_break_rate != 100)
+ rate = rate*battle_config.equip_skill_break_rate/100;
+ } else if (flag&(BCT_PARTY|BCT_SELF)) {
+ if (battle_config.equip_self_break_rate != 100)
+ rate = rate*battle_config.equip_self_break_rate/100;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (where&where_list[i]) {
+ if (sc && sc->count && sc->data[scdef[i]])
+ where&=~where_list[i];
+ else if (rnd()%10000 >= rate)
+ where&=~where_list[i];
+ else if (!sd && !(status_get_mode(bl)&MD_BOSS)) //Cause Strip effect.
+ sc_start(bl,scatk[i],100,0,skill_get_time(status_sc2skill(scatk[i]),1));
+ }
+ }
+ if (!where) //Nothing to break.
+ return 0;
+ if (sd) {
+ for (i = 0; i < EQI_MAX; i++) {
+ j = sd->equip_index[i];
+ if (j < 0 || sd->status.inventory[j].attribute == 1 || !sd->inventory_data[j])
+ continue;
+
+ switch(i) {
+ case EQI_HEAD_TOP: //Upper Head
+ flag = (where&EQP_HELM);
+ break;
+ case EQI_ARMOR: //Body
+ flag = (where&EQP_ARMOR);
+ break;
+ case EQI_HAND_R: //Left/Right hands
+ case EQI_HAND_L:
+ flag = (
+ (where&EQP_WEAPON && sd->inventory_data[j]->type == IT_WEAPON) ||
+ (where&EQP_SHIELD && sd->inventory_data[j]->type == IT_ARMOR));
+ break;
+ case EQI_SHOES:
+ flag = (where&EQP_SHOES);
+ break;
+ case EQI_GARMENT:
+ flag = (where&EQP_GARMENT);
+ break;
+ default:
+ continue;
+ }
+ if (flag) {
+ sd->status.inventory[j].attribute = 1;
+ pc_unequipitem(sd, j, 3);
+ }
+ }
+ clif_equiplist(sd);
+ }
+
+ return where; //Return list of pieces broken.
+}
+
+int skill_strip_equip(struct block_list *bl, unsigned short where, int rate, int lv, int time)
+{
+ struct status_change *sc;
+ const int pos[5] = {EQP_WEAPON, EQP_SHIELD, EQP_ARMOR, EQP_HELM, EQP_ACC};
+ const enum sc_type sc_atk[5] = {SC_STRIPWEAPON, SC_STRIPSHIELD, SC_STRIPARMOR, SC_STRIPHELM, SC__STRIPACCESSORY};
+ const enum sc_type sc_def[5] = {SC_CP_WEAPON, SC_CP_SHIELD, SC_CP_ARMOR, SC_CP_HELM, 0};
+ int i;
+
+ if (rnd()%100 >= rate)
+ return 0;
+
+ sc = status_get_sc(bl);
+ if (!sc || sc->option&OPTION_MADOGEAR ) //Mado Gear cannot be divested [Ind]
+ return 0;
+
+ for (i = 0; i < ARRAYLENGTH(pos); i++) {
+ if (where&pos[i] && sc->data[sc_def[i]])
+ where&=~pos[i];
+ }
+ if (!where) return 0;
+
+ for (i = 0; i < ARRAYLENGTH(pos); i++) {
+ if (where&pos[i] && !sc_start(bl, sc_atk[i], 100, lv, time))
+ where&=~pos[i];
+ }
+ return where?1:0;
+}
+//Early declaration
+static int skill_area_temp[8];
+/*=========================================================================
+ Used to knock back players, monsters, traps, etc
+ - 'count' is the number of squares to knock back
+ - 'direction' indicates the way OPPOSITE to the knockback direction (or -1 for default behavior)
+ - if 'flag&0x1', position update packets must not be sent.
+ - if 'flag&0x2', skill blown ignores players' special_state.no_knockback
+ -------------------------------------------------------------------------*/
+int skill_blown(struct block_list* src, struct block_list* target, int count, int8 dir, int flag)
+{
+ int dx = 0, dy = 0;
+ struct skill_unit* su = NULL;
+
+ nullpo_ret(src);
+
+ if (src != target && (map_flag_gvg(target->m) || map[target->m].flag.battleground))
+ return 0; //No knocking back in WoE
+ if (count == 0)
+ return 0; //Actual knockback distance is 0.
+
+ switch (target->type) {
+ case BL_MOB: {
+ struct mob_data* md = BL_CAST(BL_MOB, target);
+ if( md->class_ == MOBID_EMPERIUM )
+ return 0;
+ if(src != target && is_boss(target)) //Bosses can't be knocked-back
+ return 0;
+ }
+ break;
+ case BL_PC: {
+ struct map_session_data *sd = BL_CAST(BL_PC, target);
+ if( sd->sc.data[SC_BASILICA] && sd->sc.data[SC_BASILICA]->val4 == sd->bl.id && !is_boss(src))
+ return 0; // Basilica caster can't be knocked-back by normal monsters.
+ if( !(flag&0x2) && src != target && sd->special_state.no_knockback )
+ return 0;
+ }
+ break;
+ case BL_SKILL:
+ su = (struct skill_unit *)target;
+ if( su && su->group && su->group->unit_id == UNT_ANKLESNARE )
+ return 0; // ankle snare cannot be knocked back
+ break;
+ }
+
+ if (dir == -1) // <optimized>: do the computation here instead of outside
+ dir = map_calc_dir(target, src->x, src->y); // direction from src to target, reversed
+
+ if (dir >= 0 && dir < 8)
+ { // take the reversed 'direction' and reverse it
+ dx = -dirx[dir];
+ dy = -diry[dir];
+ }
+
+ return unit_blown(target, dx, dy, count, flag); // send over the proper flag
+}
+
+
+//Checks if 'bl' should reflect back a spell cast by 'src'.
+//type is the type of magic attack: 0: indirect (aoe), 1: direct (targetted)
+static int skill_magic_reflect(struct block_list* src, struct block_list* bl, int type)
+{
+ struct status_change *sc = status_get_sc(bl);
+ struct map_session_data* sd = BL_CAST(BL_PC, bl);
+
+ if( sc && sc->data[SC_KYOMU] ) // Nullify reflecting ability
+ return 0;
+
+ // item-based reflection
+ if( sd && sd->bonus.magic_damage_return && type && rnd()%100 < sd->bonus.magic_damage_return )
+ return 1;
+
+ if( is_boss(src) )
+ return 0;
+
+ // status-based reflection
+ if( !sc || sc->count == 0 )
+ return 0;
+
+ if( sc->data[SC_MAGICMIRROR] && rnd()%100 < sc->data[SC_MAGICMIRROR]->val2 )
+ return 1;
+
+ if( sc->data[SC_KAITE] && (src->type == BL_PC || status_get_lv(src) <= 80) )
+ {// Kaite only works against non-players if they are low-level.
+ clif_specialeffect(bl, 438, AREA);
+ if( --sc->data[SC_KAITE]->val2 <= 0 )
+ status_change_end(bl, SC_KAITE, INVALID_TIMER);
+ return 2;
+ }
+
+ return 0;
+}
+
+/*
+ * =========================================================================
+ * Does a skill attack with the given properties.
+ * src is the master behind the attack (player/mob/pet)
+ * dsrc is the actual originator of the damage, can be the same as src, or a BL_SKILL
+ * bl is the target to be attacked.
+ * flag can hold a bunch of information:
+ * flag&0xFFF is passed to the underlying battle_calc_attack for processing
+ * (usually holds number of targets, or just 1 for simple splash attacks)
+ * flag&0x1000 is used to tag that this is a splash-attack (so the damage
+ * packet shouldn't display a skill animation)
+ * flag&0x2000 is used to signal that the skill_lv should be passed as -1 to the
+ * client (causes player characters to not scream skill name)
+ *-------------------------------------------------------------------------*/
+int skill_attack (int attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
+{
+ struct Damage dmg;
+ struct status_data *sstatus, *tstatus;
+ struct status_change *sc;
+ struct map_session_data *sd, *tsd;
+ int type,damage,rdamage=0;
+ int8 rmdamage=0;//magic reflected
+
+ if(skill_id > 0 && !skill_lv) return 0;
+
+ nullpo_ret(src); //Source is the master behind the attack (player/mob/pet)
+ nullpo_ret(dsrc); //dsrc is the actual originator of the damage, can be the same as src, or a skill casted by src.
+ nullpo_ret(bl); //Target to be attacked.
+
+ if (src != dsrc) {
+ //When caster is not the src of attack, this is a ground skill, and as such, do the relevant target checking. [Skotlex]
+ if (!status_check_skilluse(battle_config.skill_caster_check?src:NULL, bl, skill_id, 2))
+ return 0;
+ } else if ((flag&SD_ANIMATION) && skill_get_nk(skill_id)&NK_SPLASH) {
+ //Note that splash attacks often only check versus the targetted mob, those around the splash area normally don't get checked for being hidden/cloaked/etc. [Skotlex]
+ if (!status_check_skilluse(src, bl, skill_id, 2))
+ return 0;
+ }
+
+ sd = BL_CAST(BL_PC, src);
+ tsd = BL_CAST(BL_PC, bl);
+
+ sstatus = status_get_status_data(src);
+ tstatus = status_get_status_data(bl);
+ sc= status_get_sc(bl);
+ if (sc && !sc->count) sc = NULL; //Don't need it.
+
+ // Is this check really needed? FrostNova won't hurt you if you step right where the caster is?
+ if(skill_id == WZ_FROSTNOVA && dsrc->x == bl->x && dsrc->y == bl->y)
+ return 0;
+ //Trick Dead protects you from damage, but not from buffs and the like, hence it's placed here.
+ if (sc && sc->data[SC_TRICKDEAD] && !(sstatus->mode&MD_BOSS))
+ return 0;
+
+ dmg = battle_calc_attack(attack_type,src,bl,skill_id,skill_lv,flag&0xFFF);
+
+ //Skotlex: Adjusted to the new system
+ if(src->type==BL_PET)
+ { // [Valaris]
+ struct pet_data *pd = (TBL_PET*)src;
+ if (pd->a_skill && pd->a_skill->div_ && pd->a_skill->id == skill_id)
+ {
+ int element = skill_get_ele(skill_id, skill_lv);
+ /*if (skill_id == -1) Does it ever worked?
+ element = sstatus->rhw.ele;*/
+ if (element != ELE_NEUTRAL || !(battle_config.attack_attr_none&BL_PET))
+ dmg.damage=battle_attr_fix(src, bl, skill_lv, element, tstatus->def_ele, tstatus->ele_lv);
+ else
+ dmg.damage= skill_lv;
+ dmg.damage2=0;
+ dmg.div_= pd->a_skill->div_;
+ }
+ }
+
+ if( dmg.flag&BF_MAGIC && ( skill_id != NPC_EARTHQUAKE || (battle_config.eq_single_target_reflectable && (flag&0xFFF) == 1) ) )
+ { // Earthquake on multiple targets is not counted as a target skill. [Inkfish]
+ if( (dmg.damage || dmg.damage2) && (type = skill_magic_reflect(src, bl, src==dsrc)) )
+ { //Magic reflection, switch caster/target
+ struct block_list *tbl = bl;
+ rmdamage = 1;
+ bl = src;
+ src = tbl;
+ sd = BL_CAST(BL_PC, src);
+ tsd = BL_CAST(BL_PC, bl);
+ sc = status_get_sc(bl);
+ if (sc && !sc->count)
+ sc = NULL; //Don't need it.
+ /* bugreport:2564 flag&2 disables double casting trigger */
+ flag |= 2;
+
+ //Spirit of Wizard blocks Kaite's reflection
+ if( type == 2 && sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_WIZARD )
+ { //Consume one Fragment per hit of the casted skill? [Skotlex]
+ type = tsd?pc_search_inventory (tsd, 7321):0;
+ if (type >= 0) {
+ if ( tsd ) pc_delitem(tsd, type, 1, 0, 1, LOG_TYPE_CONSUME);
+ dmg.damage = dmg.damage2 = 0;
+ dmg.dmg_lv = ATK_MISS;
+ sc->data[SC_SPIRIT]->val3 = skill_id;
+ sc->data[SC_SPIRIT]->val4 = dsrc->id;
+ }
+ }
+
+ /**
+ * Official Magic Reflection Behavior : damage reflected depends on gears caster wears, not target
+ **/
+ #if MAGIC_REFLECTION_TYPE
+ if( dmg.dmg_lv != ATK_MISS )//Wiz SL cancelled and consumed fragment
+ dmg = battle_calc_attack(BF_MAGIC,bl,bl,skill_id,skill_lv,flag&0xFFF);
+ #endif
+ }
+ if(sc && sc->data[SC_MAGICROD] && src == dsrc) {
+ int sp = skill_get_sp(skill_id,skill_lv);
+ dmg.damage = dmg.damage2 = 0;
+ dmg.dmg_lv = ATK_MISS; //This will prevent skill additional effect from taking effect. [Skotlex]
+ sp = sp * sc->data[SC_MAGICROD]->val2 / 100;
+ if(skill_id == WZ_WATERBALL && skill_lv > 1)
+ sp = sp/((skill_lv|1)*(skill_lv|1)); //Estimate SP cost of a single water-ball
+ status_heal(bl, 0, sp, 2);
+ }
+ }
+
+ damage = dmg.damage + dmg.damage2;
+
+ if( (skill_id == AL_INCAGI || skill_id == AL_BLESSING ||
+ skill_id == CASH_BLESSING || skill_id == CASH_INCAGI ||
+ skill_id == MER_INCAGI || skill_id == MER_BLESSING) && tsd->sc.data[SC_CHANGEUNDEAD] )
+ damage = 1;
+
+ if( damage > 0 && (( dmg.flag&BF_WEAPON && src != bl && ( src == dsrc || ( dsrc->type == BL_SKILL && ( skill_id == SG_SUN_WARM || skill_id == SG_MOON_WARM || skill_id == SG_STAR_WARM ) ) ))
+ || (sc && sc->data[SC_REFLECTDAMAGE])) )
+ rdamage = battle_calc_return_damage(bl,src, &damage, dmg.flag, skill_id);
+
+ if( damage && sc && sc->data[SC_GENSOU] && dmg.flag&BF_MAGIC ){
+ struct block_list *nbl = NULL;
+ nbl = battle_getenemyarea(bl,bl->x,bl->y,2,BL_CHAR,bl->id);
+ if( nbl ){ // Only one target is chosen.
+ damage = damage / 2; // Deflect half of the damage to a target nearby
+ clif_skill_damage(bl, nbl, tick, status_get_amotion(src), 0, status_fix_damage(bl,nbl,damage,0), dmg.div_, OB_OBOROGENSOU_TRANSITION_ATK, -1, 6);
+ }
+ }
+
+ //Skill hit type
+ type=(skill_id==0)?5:skill_get_hit(skill_id);
+
+ if(damage < dmg.div_
+ //Only skills that knockback even when they miss. [Skotlex]
+ && skill_id != CH_PALMSTRIKE)
+ dmg.blewcount = 0;
+
+ if(skill_id == CR_GRANDCROSS||skill_id == NPC_GRANDDARKNESS) {
+ if(battle_config.gx_disptype) dsrc = src;
+ if(src == bl) type = 4;
+ else flag|=SD_ANIMATION;
+ }
+ if(skill_id == NJ_TATAMIGAESHI) {
+ dsrc = src; //For correct knockback.
+ flag|=SD_ANIMATION;
+ }
+
+ if(sd) {
+ int flag = 0; //Used to signal if this skill can be combo'ed later on.
+ struct status_change_entry *sce;
+ if ((sce = sd->sc.data[SC_COMBO])) {//End combo state after skill is invoked. [Skotlex]
+ switch (skill_id) {
+ case TK_TURNKICK:
+ case TK_STORMKICK:
+ case TK_DOWNKICK:
+ case TK_COUNTER:
+ if (pc_famerank(sd->status.char_id,MAPID_TAEKWON)) {//Extend combo time.
+ sce->val1 = skill_id; //Update combo-skill
+ sce->val3 = skill_id;
+ if( sce->timer != INVALID_TIMER )
+ delete_timer(sce->timer, status_change_timer);
+ sce->timer = add_timer(tick+sce->val4, status_change_timer, src->id, SC_COMBO);
+ break;
+ }
+ unit_cancel_combo(src); // Cancel combo wait
+ break;
+ default:
+ if( src == dsrc ) // Ground skills are exceptions. [Inkfish]
+ status_change_end(src, SC_COMBO, INVALID_TIMER);
+ }
+ }
+ switch(skill_id) {
+ case MO_TRIPLEATTACK:
+ if (pc_checkskill(sd, MO_CHAINCOMBO) > 0 || pc_checkskill(sd, SR_DRAGONCOMBO) > 0)
+ flag=1;
+ break;
+ case MO_CHAINCOMBO:
+ if(pc_checkskill(sd, MO_COMBOFINISH) > 0 && sd->spiritball > 0)
+ flag=1;
+ break;
+ case MO_COMBOFINISH:
+ if (sd->status.party_id>0) //bonus from SG_FRIEND [Komurka]
+ party_skill_check(sd, sd->status.party_id, MO_COMBOFINISH, skill_lv);
+ if (pc_checkskill(sd, CH_TIGERFIST) > 0 && sd->spiritball > 0)
+ flag=1;
+ case CH_TIGERFIST:
+ if (!flag && pc_checkskill(sd, CH_CHAINCRUSH) > 0 && sd->spiritball > 1)
+ flag=1;
+ case CH_CHAINCRUSH:
+ if (!flag && pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball > 0 && sd->sc.data[SC_EXPLOSIONSPIRITS])
+ flag=1;
+ break;
+ case AC_DOUBLE:
+ if( (tstatus->race == RC_BRUTE || tstatus->race == RC_INSECT) && pc_checkskill(sd, HT_POWER))
+ {
+ //TODO: This code was taken from Triple Blows, is this even how it should be? [Skotlex]
+ sc_start2(src,SC_COMBO,100,HT_POWER,bl->id,2000);
+ clif_combo_delay(src,2000);
+ }
+ break;
+ case TK_COUNTER:
+ { //bonus from SG_FRIEND [Komurka]
+ int level;
+ if(sd->status.party_id>0 && (level = pc_checkskill(sd,SG_FRIEND)))
+ party_skill_check(sd, sd->status.party_id, TK_COUNTER,level);
+ }
+ break;
+ case SL_STIN:
+ case SL_STUN:
+ if (skill_lv >= 7 && !sd->sc.data[SC_SMA])
+ sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA, skill_lv));
+ break;
+ case GS_FULLBUSTER:
+ //Can't attack nor use items until skill's delay expires. [Skotlex]
+ sd->ud.attackabletime = sd->canuseitem_tick = sd->ud.canact_tick;
+ break;
+ case SR_DRAGONCOMBO:
+ if( pc_checkskill(sd, SR_FALLENEMPIRE) > 0 )
+ flag = 1;
+ break;
+ case SR_FALLENEMPIRE:
+ if( pc_checkskill(sd, SR_TIGERCANNON) > 0 || pc_checkskill(sd, SR_GATEOFHELL) > 0 )
+ flag = 1;
+ break;
+ } //Switch End
+ if (flag) { //Possible to chain
+ flag = DIFF_TICK(sd->ud.canact_tick, tick);
+ if (flag < 1) flag = 1;
+ sc_start2(src,SC_COMBO,100,skill_id,bl->id,flag);
+ clif_combo_delay(src, flag);
+ }
+ }
+
+ //Display damage.
+ switch( skill_id )
+ {
+ case PA_GOSPEL: //Should look like Holy Cross [Skotlex]
+ dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, CR_HOLYCROSS, -1, 5);
+ break;
+ //Skills that need be passed as a normal attack for the client to display correctly.
+ case HVAN_EXPLOSION:
+ case NPC_SELFDESTRUCTION:
+ if(src->type==BL_PC)
+ dmg.blewcount = 10;
+ dmg.amotion = 0; //Disable delay or attack will do no damage since source is dead by the time it takes effect. [Skotlex]
+ // fall through
+ case KN_AUTOCOUNTER:
+ case NPC_CRITICALSLASH:
+ case TF_DOUBLE:
+ case GS_CHAINACTION:
+ dmg.dmotion = clif_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,dmg.type,dmg.damage2);
+ break;
+
+ case AS_SPLASHER:
+ if( flag&SD_ANIMATION ) // the surrounding targets
+ dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -1, 5); // needs -1 as skill level
+ else // the central target doesn't display an animation
+ dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -2, 5); // needs -2(!) as skill level
+ break;
+ case WL_HELLINFERNO:
+ case SR_EARTHSHAKER:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,skill_id,-2,6);
+ break;
+ case WL_SOULEXPANSION:
+ case WL_COMET:
+ case KO_MUCHANAGE:
+ case NJ_HUUMA:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,skill_lv,8);
+ break;
+ case WL_CHAINLIGHTNING_ATK:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,WL_CHAINLIGHTNING,-2,6);
+ break;
+ case LG_OVERBRAND_BRANDISH:
+ case LG_OVERBRAND_PLUSATK:
+ case EL_FIRE_BOMB:
+ case EL_FIRE_BOMB_ATK:
+ case EL_FIRE_WAVE:
+ case EL_FIRE_WAVE_ATK:
+ case EL_FIRE_MANTLE:
+ case EL_CIRCLE_OF_FIRE:
+ case EL_FIRE_ARROW:
+ case EL_ICE_NEEDLE:
+ case EL_WATER_SCREW:
+ case EL_WATER_SCREW_ATK:
+ case EL_WIND_SLASH:
+ case EL_TIDAL_WEAPON:
+ case EL_ROCK_CRUSHER:
+ case EL_ROCK_CRUSHER_ATK:
+ case EL_HURRICANE:
+ case EL_HURRICANE_ATK:
+ case KO_BAKURETSU:
+ case GN_CRAZYWEED_ATK:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,5);
+ break;
+ case GN_SLINGITEM_RANGEMELEEATK:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,GN_SLINGITEM,-2,6);
+ break;
+ case EL_STONE_RAIN:
+ dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,(flag&1)?8:5);
+ break;
+ case WM_SEVERE_RAINSTORM_MELEE:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,WM_SEVERE_RAINSTORM,skill_lv,5);
+ break;
+ case WM_REVERBERATION_MELEE:
+ case WM_REVERBERATION_MAGIC:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,WM_REVERBERATION,-2,6);
+ break;
+ case HT_CLAYMORETRAP:
+ case HT_BLASTMINE:
+ case HT_FLASHER:
+ case HT_FREEZINGTRAP:
+ case RA_CLUSTERBOMB:
+ case RA_FIRINGTRAP:
+ case RA_ICEBOUNDTRAP:
+ dmg.dmotion = clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id,flag&SD_LEVEL?-1:skill_lv, 5);
+ if( dsrc != src ) // avoid damage display redundancy
+ break;
+ case HT_LANDMINE:
+ dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, -1, type);
+ break;
+ case WZ_SIGHTBLASTER:
+ dmg.dmotion = clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, 5);
+ break;
+ case AB_DUPLELIGHT_MELEE:
+ case AB_DUPLELIGHT_MAGIC:
+ dmg.amotion = 300;/* makes the damage value not overlap with previous damage (when displayed by the client) */
+ default:
+ if( flag&SD_ANIMATION && dmg.div_ < 2 ) //Disabling skill animation doesn't works on multi-hit.
+ type = 5;
+ if( bl->type == BL_SKILL ){
+ TBL_SKILL *su = (TBL_SKILL*)bl;
+ if( su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP )// show damage on trap targets
+ clif_skill_damage(src,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, 5);
+ }
+ dmg.dmotion = clif_skill_damage(dsrc,bl,tick, dmg.amotion, dmg.dmotion, damage, dmg.div_, skill_id, flag&SD_LEVEL?-1:skill_lv, type);
+ break;
+ }
+
+ map_freeblock_lock();
+
+ if(damage > 0 && dmg.flag&BF_SKILL && tsd
+ && pc_checkskill(tsd,RG_PLAGIARISM)
+ && (!sc || !sc->data[SC_PRESERVE])
+ && damage < tsd->battle_status.hp)
+ { //Updated to not be able to copy skills if the blow will kill you. [Skotlex]
+ int copy_skill = skill_id;
+ /**
+ * Copy Referal: dummy skills should point to their source upon copying
+ **/
+ switch( skill_id ) {
+ case AB_DUPLELIGHT_MELEE:
+ case AB_DUPLELIGHT_MAGIC:
+ copy_skill = AB_DUPLELIGHT;
+ break;
+ case WL_CHAINLIGHTNING_ATK:
+ copy_skill = WL_CHAINLIGHTNING;
+ break;
+ case WM_REVERBERATION_MELEE:
+ case WM_REVERBERATION_MAGIC:
+ copy_skill = WM_REVERBERATION;
+ break;
+ case WM_SEVERE_RAINSTORM_MELEE:
+ copy_skill = WM_SEVERE_RAINSTORM;
+ break;
+ case GN_CRAZYWEED_ATK:
+ copy_skill = GN_CRAZYWEED;
+ break;
+ case GN_HELLS_PLANT_ATK:
+ copy_skill = GN_HELLS_PLANT;
+ break;
+ case LG_OVERBRAND_BRANDISH:
+ case LG_OVERBRAND_PLUSATK:
+ copy_skill = LG_OVERBRAND;
+ break;
+ }
+
+ if ((tsd->status.skill[copy_skill].id == 0 || tsd->status.skill[copy_skill].flag == SKILL_FLAG_PLAGIARIZED) &&
+ can_copy(tsd,copy_skill,bl)) // Split all the check into their own function [Aru]
+ {
+ int lv;
+ if( sc && sc->data[SC__REPRODUCE] && (lv = sc->data[SC__REPRODUCE]->val1) ) {
+ //Level dependent and limitation.
+ lv = min(lv,skill_get_max(copy_skill));
+ if( tsd->reproduceskill_id && tsd->status.skill[tsd->reproduceskill_id].flag == SKILL_FLAG_PLAGIARIZED ) {
+ tsd->status.skill[tsd->reproduceskill_id].id = 0;
+ tsd->status.skill[tsd->reproduceskill_id].lv = 0;
+ tsd->status.skill[tsd->reproduceskill_id].flag = 0;
+ clif_deleteskill(tsd,tsd->reproduceskill_id);
+ }
+
+ tsd->reproduceskill_id = copy_skill;
+ pc_setglobalreg(tsd, "REPRODUCE_SKILL", copy_skill);
+ pc_setglobalreg(tsd, "REPRODUCE_SKILL_LV", lv);
+
+ tsd->status.skill[copy_skill].id = copy_skill;
+ tsd->status.skill[copy_skill].lv = lv;
+ tsd->status.skill[copy_skill].flag = SKILL_FLAG_PLAGIARIZED;
+ clif_addskill(tsd,copy_skill);
+ } else {
+ lv = skill_lv;
+ if (tsd->cloneskill_id && tsd->status.skill[tsd->cloneskill_id].flag == SKILL_FLAG_PLAGIARIZED){
+ tsd->status.skill[tsd->cloneskill_id].id = 0;
+ tsd->status.skill[tsd->cloneskill_id].lv = 0;
+ tsd->status.skill[tsd->cloneskill_id].flag = 0;
+ clif_deleteskill(tsd,tsd->cloneskill_id);
+ }
+
+ if ((type = pc_checkskill(tsd,RG_PLAGIARISM)) < lv)
+ lv = type;
+
+ tsd->cloneskill_id = copy_skill;
+ pc_setglobalreg(tsd, "CLONE_SKILL", copy_skill);
+ pc_setglobalreg(tsd, "CLONE_SKILL_LV", lv);
+
+ tsd->status.skill[skill_id].id = copy_skill;
+ tsd->status.skill[skill_id].lv = lv;
+ tsd->status.skill[skill_id].flag = SKILL_FLAG_PLAGIARIZED;
+ clif_addskill(tsd,skill_id);
+ }
+ }
+ }
+
+ if (dmg.dmg_lv >= ATK_MISS && (type = skill_get_walkdelay(skill_id, skill_lv)) > 0)
+ { //Skills with can't walk delay also stop normal attacking for that
+ //duration when the attack connects. [Skotlex]
+ struct unit_data *ud = unit_bl2ud(src);
+ if (ud && DIFF_TICK(ud->attackabletime, tick + type) < 0)
+ ud->attackabletime = tick + type;
+ }
+
+ if( !dmg.amotion )
+ { //Instant damage
+ if( !sc || (!sc->data[SC_DEVOTION] && skill_id != CR_REFLECTSHIELD) )
+ status_fix_damage(src,bl,damage,dmg.dmotion); //Deal damage before knockback to allow stuff like firewall+storm gust combo.
+ if( !status_isdead(bl) )
+ skill_additional_effect(src,bl,skill_id,skill_lv,dmg.flag,dmg.dmg_lv,tick);
+ if( damage > 0 ) //Counter status effects [Skotlex]
+ skill_counter_additional_effect(src,bl,skill_id,skill_lv,dmg.flag,tick);
+ }
+ // Hell Inferno burning status only starts if Fire part hits.
+ if( skill_id == WL_HELLINFERNO && dmg.damage > 0 )
+ sc_start4(bl,SC_BURNING,55+5*skill_lv,skill_lv,1000,src->id,0,skill_get_time(skill_id,skill_lv));
+ // Apply knock back chance in SC_TRIANGLESHOT skill.
+ else if( skill_id == SC_TRIANGLESHOT && rnd()%100 > (1 + skill_lv) )
+ dmg.blewcount = 0;
+
+ //Only knockback if it's still alive, otherwise a "ghost" is left behind. [Skotlex]
+ //Reflected spells do not bounce back (bl == dsrc since it only happens for direct skills)
+ if (dmg.blewcount > 0 && bl!=dsrc && !status_isdead(bl)) {
+ int8 dir = -1; // default
+ switch(skill_id) {//direction
+ case MG_FIREWALL:
+ case PR_SANCTUARY:
+ case SC_TRIANGLESHOT:
+ case LG_OVERBRAND:
+ case SR_KNUCKLEARROW:
+ case GN_WALLOFTHORN:
+ case EL_FIRE_MANTLE:
+ dir = unit_getdir(bl);// backwards
+ break;
+ // This ensures the storm randomly pushes instead of exactly a cell backwards per official mechanics.
+ case WZ_STORMGUST:
+ dir = rand()%8;
+ break;
+ case WL_CRIMSONROCK:
+ dir = map_calc_dir(bl,skill_area_temp[4],skill_area_temp[5]);
+ break;
+
+ }
+ //blown-specific handling
+ switch( skill_id ) {
+ case LG_OVERBRAND:
+ if( skill_blown(dsrc,bl,dmg.blewcount,dir,0) ) {
+ short dir_x, dir_y;
+ dir_x = dirx[(dir+4)%8];
+ dir_y = diry[(dir+4)%8];
+ if( map_getcell(bl->m, bl->x+dir_x, bl->y+dir_y, CELL_CHKNOPASS) != 0 )
+ skill_addtimerskill(src, tick + status_get_amotion(src), bl->id, 0, 0, LG_OVERBRAND_PLUSATK, skill_lv, BF_WEAPON, flag );
+ } else
+ skill_addtimerskill(src, tick + status_get_amotion(src), bl->id, 0, 0, LG_OVERBRAND_PLUSATK, skill_lv, BF_WEAPON, flag );
+ break;
+ case SR_KNUCKLEARROW:
+ if( skill_blown(dsrc,bl,dmg.blewcount,dir,0) && !(flag&4) ) {
+ short dir_x, dir_y;
+ dir_x = dirx[(dir+4)%8];
+ dir_y = diry[(dir+4)%8];
+ if( map_getcell(bl->m, bl->x+dir_x, bl->y+dir_y, CELL_CHKNOPASS) != 0 )
+ skill_addtimerskill(src, tick + 300 * ((flag&2) ? 1 : 2), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|4);
+ }
+ break;
+ case GN_WALLOFTHORN:
+ unit_stop_walking(bl,1);
+ skill_blown(dsrc,bl,dmg.blewcount,dir, 0x2 );
+ clif_fixpos(bl);
+ break;
+ default:
+ skill_blown(dsrc,bl,dmg.blewcount,dir, 0x0 );
+ if ( !dmg.blewcount && bl->type == BL_SKILL && damage > 0 ){
+ TBL_SKILL *su = (TBL_SKILL*)bl;
+ if( su->group && su->group->skill_id == HT_BLASTMINE)
+ skill_blown(src, bl, 3, -1, 0);
+ }
+ break;
+ }
+ }
+
+ //Delayed damage must be dealt after the knockback (it needs to know actual position of target)
+ if (dmg.amotion)
+ battle_delay_damage(tick, dmg.amotion,src,bl,dmg.flag,skill_id,skill_lv,damage,dmg.dmg_lv,dmg.dmotion);
+
+ if( sc && sc->data[SC_DEVOTION] && skill_id != PA_PRESSURE )
+ {
+ struct status_change_entry *sce = sc->data[SC_DEVOTION];
+ struct block_list *d_bl = map_id2bl(sce->val1);
+
+ if( d_bl && (
+ (d_bl->type == BL_MER && ((TBL_MER*)d_bl)->master && ((TBL_MER*)d_bl)->master->bl.id == bl->id) ||
+ (d_bl->type == BL_PC && ((TBL_PC*)d_bl)->devotion[sce->val2] == bl->id)
+ ) && check_distance_bl(bl, d_bl, sce->val3) )
+ {
+ if(!rmdamage){
+ clif_damage(d_bl,d_bl, gettick(), 0, 0, damage, 0, 0, 0);
+ status_fix_damage(NULL,d_bl, damage, 0);
+ }
+ else{//Reflected magics are done directly on the target not on paladin
+ //This check is only for magical skill.
+ //For BF_WEAPON skills types track var rdamage and function battle_calc_return_damage
+ clif_damage(bl,bl, gettick(), 0, 0, damage, 0, 0, 0);
+ status_fix_damage(bl,bl, damage, 0);
+ }
+ }
+ else {
+ status_change_end(bl, SC_DEVOTION, INVALID_TIMER);
+ if( !dmg.amotion )
+ status_fix_damage(src,bl,damage,dmg.dmotion);
+ }
+ }
+
+ if(damage > 0 && !(tstatus->mode&MD_BOSS)) {
+ if( skill_id == RG_INTIMIDATE ) {
+ int rate = 50 + skill_lv * 5;
+ rate = rate + (status_get_lv(src) - status_get_lv(bl));
+ if(rnd()%100 < rate)
+ skill_addtimerskill(src,tick + 800,bl->id,0,0,skill_id,skill_lv,0,flag);
+ } else if( skill_id == SC_FATALMENACE )
+ skill_addtimerskill(src,tick + 800,bl->id,skill_area_temp[4],skill_area_temp[5],skill_id,skill_lv,0,flag);
+ }
+
+ if(skill_id == CR_GRANDCROSS || skill_id == NPC_GRANDDARKNESS)
+ dmg.flag |= BF_WEAPON;
+
+ if( sd && src != bl && damage > 0 && ( dmg.flag&BF_WEAPON ||
+ (dmg.flag&BF_MISC && (skill_id == RA_CLUSTERBOMB || skill_id == RA_FIRINGTRAP || skill_id == RA_ICEBOUNDTRAP || skill_id == RK_DRAGONBREATH)) ) )
+ {
+ if (battle_config.left_cardfix_to_right)
+ battle_drain(sd, bl, dmg.damage, dmg.damage, tstatus->race, tstatus->mode&MD_BOSS);
+ else
+ battle_drain(sd, bl, dmg.damage, dmg.damage2, tstatus->race, tstatus->mode&MD_BOSS);
+ }
+
+ if( rdamage > 0 ) {
+ if( sc && sc->data[SC_REFLECTDAMAGE] ) {
+ if( src != bl )// Don't reflect your own damage (Grand Cross)
+ map_foreachinshootrange(battle_damage_area,bl,skill_get_splash(LG_REFLECTDAMAGE,1),BL_CHAR,tick,bl,dmg.amotion,sstatus->dmotion,rdamage,tstatus->race);
+ } else {
+ if( dmg.amotion )
+ battle_delay_damage(tick, dmg.amotion,bl,src,0,CR_REFLECTSHIELD,0,rdamage,ATK_DEF,0);
+ else
+ status_fix_damage(bl,src,rdamage,0);
+ clif_damage(src,src,tick, dmg.amotion,0,rdamage,1,4,0); // in aegis damage reflected is shown in single hit.
+ //Use Reflect Shield to signal this kind of skill trigger. [Skotlex]
+ if( tsd && src != bl )
+ battle_drain(tsd, src, rdamage, rdamage, sstatus->race, is_boss(src));
+ skill_additional_effect(bl, src, CR_REFLECTSHIELD, 1, BF_WEAPON|BF_SHORT|BF_NORMAL,ATK_DEF,tick);
+ }
+ }
+ if( damage > 0 ) {
+ /**
+ * Post-damage effects
+ **/
+ switch( skill_id ) {
+ case RK_CRUSHSTRIKE:
+ skill_break_equip(src,EQP_WEAPON,2000,BCT_SELF); // 20% chance to destroy the weapon.
+ break;
+ case GC_VENOMPRESSURE: {
+ struct status_change *ssc = status_get_sc(src);
+ if( ssc && ssc->data[SC_POISONINGWEAPON] && rnd()%100 < 70 + 5*skill_lv ) {
+ sc_start(bl,ssc->data[SC_POISONINGWEAPON]->val2,100,ssc->data[SC_POISONINGWEAPON]->val1,skill_get_time2(GC_POISONINGWEAPON, 1));
+ status_change_end(src,SC_POISONINGWEAPON,INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ }
+ break;
+ case WM_METALICSOUND:
+ status_zap(bl, 0, damage*100/(100*(110-pc_checkskill(sd,WM_LESSON)*10)));
+ break;
+ case SR_TIGERCANNON:
+ status_zap(bl, 0, damage/10); // 10% of damage dealt
+ break;
+ }
+ if( sd )
+ skill_onskillusage(sd, bl, skill_id, tick);
+ }
+
+ if (!(flag&2) &&
+ (
+ skill_id == MG_COLDBOLT || skill_id == MG_FIREBOLT || skill_id == MG_LIGHTNINGBOLT
+ ) &&
+ (sc = status_get_sc(src)) &&
+ sc->data[SC_DOUBLECAST] &&
+ rnd() % 100 < sc->data[SC_DOUBLECAST]->val2)
+ {
+// skill_addtimerskill(src, tick + dmg.div_*dmg.amotion, bl->id, 0, 0, skill_id, skill_lv, BF_MAGIC, flag|2);
+ skill_addtimerskill(src, tick + dmg.amotion, bl->id, 0, 0, skill_id, skill_lv, BF_MAGIC, flag|2);
+ }
+
+ map_freeblock_unlock();
+
+ return damage;
+}
+
+/*==========================================
+ * sub fonction for recursive skill call.
+ * Checking bl battle flag and display dammage
+ * then call func with source,target,skill_id,skill_lv,tick,flag
+ *------------------------------------------*/
+typedef int (*SkillFunc)(struct block_list *, struct block_list *, int, int, unsigned int, int);
+int skill_area_sub (struct block_list *bl, va_list ap)
+{
+ struct block_list *src;
+ uint16 skill_id,skill_lv;
+ int flag;
+ unsigned int tick;
+ SkillFunc func;
+
+ nullpo_ret(bl);
+
+ src=va_arg(ap,struct block_list *);
+ 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)
+ {
+ // several splash skills need this initial dummy packet to display correctly
+ if (flag&SD_PREAMBLE && skill_area_temp[2] == 0)
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+
+ if (flag&(SD_SPLASH|SD_PREAMBLE))
+ skill_area_temp[2]++;
+
+ return func(src,bl,skill_id,skill_lv,tick,flag);
+ }
+ return 0;
+}
+
+static int skill_check_unit_range_sub (struct block_list *bl, va_list ap)
+{
+ struct skill_unit *unit;
+ uint16 skill_id,g_skill_id;
+
+ unit = (struct skill_unit *)bl;
+
+ if(bl->prev == NULL || bl->type != BL_SKILL)
+ return 0;
+
+ if(!unit->alive)
+ return 0;
+
+ skill_id = va_arg(ap,int);
+ g_skill_id = unit->group->skill_id;
+
+ switch (skill_id) {
+ case MH_STEINWAND:
+ case MG_SAFETYWALL:
+ case AL_PNEUMA:
+ case SC_MAELSTROM:
+ if(g_skill_id != MH_STEINWAND && g_skill_id != MG_SAFETYWALL && g_skill_id != AL_PNEUMA && g_skill_id != SC_MAELSTROM)
+ return 0;
+ break;
+ case AL_WARP:
+ case HT_SKIDTRAP:
+ case MA_SKIDTRAP:
+ case HT_LANDMINE:
+ case MA_LANDMINE:
+ case HT_ANKLESNARE:
+ case HT_SHOCKWAVE:
+ case HT_SANDMAN:
+ case MA_SANDMAN:
+ case HT_FLASHER:
+ case HT_FREEZINGTRAP:
+ case MA_FREEZINGTRAP:
+ case HT_BLASTMINE:
+ case HT_CLAYMORETRAP:
+ case HT_TALKIEBOX:
+ case HP_BASILICA:
+ case RA_ELECTRICSHOCKER:
+ case RA_CLUSTERBOMB:
+ case RA_MAGENTATRAP:
+ case RA_COBALTTRAP:
+ case RA_MAIZETRAP:
+ case RA_VERDURETRAP:
+ case RA_FIRINGTRAP:
+ case RA_ICEBOUNDTRAP:
+ case SC_DIMENSIONDOOR:
+ case SC_BLOODYLUST:
+ //Non stackable on themselves and traps (including venom dust which does not has the trap inf2 set)
+ if (skill_id != g_skill_id && !(skill_get_inf2(g_skill_id)&INF2_TRAP) && g_skill_id != AS_VENOMDUST && g_skill_id != MH_POISON_MIST)
+ return 0;
+ break;
+ default: //Avoid stacking with same kind of trap. [Skotlex]
+ if (g_skill_id != skill_id)
+ return 0;
+ break;
+ }
+
+ return 1;
+}
+
+static int skill_check_unit_range (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv)
+{
+ //Non players do not check for the skill's splash-trigger area.
+ int range = bl->type==BL_PC?skill_get_unit_range(skill_id, skill_lv):0;
+ int layout_type = skill_get_unit_layout_type(skill_id,skill_lv);
+ if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) {
+ ShowError("skill_check_unit_range: unsupported layout type %d for skill %d\n",layout_type,skill_id);
+ return 0;
+ }
+
+ range += layout_type;
+ return map_foreachinarea(skill_check_unit_range_sub,bl->m,x-range,y-range,x+range,y+range,BL_SKILL,skill_id);
+}
+
+static int skill_check_unit_range2_sub (struct block_list *bl, va_list ap)
+{
+ uint16 skill_id;
+
+ if(bl->prev == NULL)
+ return 0;
+
+ skill_id = va_arg(ap,int);
+
+ if( status_isdead(bl) && skill_id != AL_WARP )
+ return 0;
+
+ if( skill_id == HP_BASILICA && bl->type == BL_PC )
+ return 0;
+
+ if( skill_id == AM_DEMONSTRATION && bl->type == BL_MOB && ((TBL_MOB*)bl)->class_ == MOBID_EMPERIUM )
+ return 0; //Allow casting Bomb/Demonstration Right under emperium [Skotlex]
+ return 1;
+}
+
+static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 skill_id, uint16 skill_lv)
+{
+ int range, type;
+
+ switch (skill_id) { // to be expanded later
+ case WZ_ICEWALL:
+ range = 2;
+ break;
+ default:
+ {
+ int layout_type = skill_get_unit_layout_type(skill_id,skill_lv);
+ if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) {
+ ShowError("skill_check_unit_range2: unsupported layout type %d for skill %d\n",layout_type,skill_id);
+ return 0;
+ }
+ range = skill_get_unit_range(skill_id,skill_lv) + layout_type;
+ }
+ break;
+ }
+
+ // if the caster is a monster/NPC, only check for players
+ // otherwise just check characters
+ if (bl->type == BL_PC)
+ type = BL_CHAR;
+ else
+ type = BL_PC;
+
+ return map_foreachinarea(skill_check_unit_range2_sub, bl->m,
+ x - range, y - range, x + range, y + range,
+ type, skill_id);
+}
+
+int skill_guildaura_sub (struct map_session_data* sd, int id, int strvit, int agidex)
+{
+ if(id == sd->bl.id && battle_config.guild_aura&16)
+ return 0; // Do not affect guild leader
+
+ if (sd->sc.data[SC_GUILDAURA]) {
+ struct status_change_entry *sce = sd->sc.data[SC_GUILDAURA];
+ if( sce->val3 != strvit || sce->val4 != agidex ) {
+ sce->val3 = strvit;
+ sce->val4 = agidex;
+ status_calc_bl(&sd->bl, status_sc2scb_flag(SC_GUILDAURA));
+ }
+ return 0;
+ }
+ sc_start4(&sd->bl, SC_GUILDAURA,100, 1, id, strvit, agidex, 1000);
+ return 1;
+}
+
+/*==========================================
+ * Checks that you have the requirements for casting a skill for homunculus/mercenary.
+ * Flag:
+ * &1: finished casting the skill (invoke hp/sp/item consumption)
+ * &2: picked menu entry (Warp Portal, Teleport and other menu based skills)
+ *------------------------------------------*/
+static int skill_check_condition_mercenary(struct block_list *bl, int skill, int lv, int type)
+{
+ struct status_data *status;
+ struct map_session_data *sd = NULL;
+ int i, hp, sp, hp_rate, sp_rate, state, mhp;
+ uint16 idx;
+ int itemid[MAX_SKILL_ITEM_REQUIRE],amount[ARRAYLENGTH(itemid)],index[ARRAYLENGTH(itemid)];
+
+ if( lv < 1 || lv > MAX_SKILL_LEVEL )
+ return 0;
+ nullpo_ret(bl);
+
+ switch( bl->type )
+ {
+ case BL_HOM: sd = ((TBL_HOM*)bl)->master; break;
+ case BL_MER: sd = ((TBL_MER*)bl)->master; break;
+ }
+
+ status = status_get_status_data(bl);
+ if( (idx = skill_get_index(skill)) == 0 )
+ return 0;
+
+ // Requeriments
+ for( i = 0; i < ARRAYLENGTH(itemid); i++ )
+ {
+ itemid[i] = skill_db[idx].itemid[i];
+ amount[i] = skill_db[idx].amount[i];
+ }
+ hp = skill_db[idx].hp[lv-1];
+ sp = skill_db[idx].sp[lv-1];
+ hp_rate = skill_db[idx].hp_rate[lv-1];
+ sp_rate = skill_db[idx].sp_rate[lv-1];
+ state = skill_db[idx].state;
+ if( (mhp = skill_db[idx].mhp[lv-1]) > 0 )
+ hp += (status->max_hp * mhp) / 100;
+ if( hp_rate > 0 )
+ hp += (status->hp * hp_rate) / 100;
+ else
+ hp += (status->max_hp * (-hp_rate)) / 100;
+ if( sp_rate > 0 )
+ sp += (status->sp * sp_rate) / 100;
+ else
+ sp += (status->max_sp * (-sp_rate)) / 100;
+
+ if( bl->type == BL_HOM )
+ { // Intimacy Requeriments
+ struct homun_data *hd = BL_CAST(BL_HOM, bl);
+ switch( skill )
+ {
+ case HFLI_SBR44:
+ if( hd->homunculus.intimacy <= 200 )
+ return 0;
+ break;
+ case HVAN_EXPLOSION:
+ if( hd->homunculus.intimacy < (unsigned int)battle_config.hvan_explosion_intimate )
+ return 0;
+ break;
+ }
+ }
+
+ if( !(type&2) )
+ {
+ if( hp > 0 && status->hp <= (unsigned int)hp )
+ {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_HP_INSUFFICIENT, 0);
+ return 0;
+ }
+ if( sp > 0 && status->sp <= (unsigned int)sp )
+ {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_SP_INSUFFICIENT, 0);
+ return 0;
+ }
+ }
+
+ if( !type )
+ switch( state )
+ {
+ case ST_MOVE_ENABLE:
+ if( !unit_can_move(bl) )
+ {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_LEVEL, 0);
+ return 0;
+ }
+ break;
+ }
+ if( !(type&1) )
+ return 1;
+
+ // Check item existences
+ for( i = 0; i < ARRAYLENGTH(itemid); i++ )
+ {
+ index[i] = -1;
+ if( itemid[i] < 1 ) continue; // No item
+ index[i] = pc_search_inventory(sd, itemid[i]);
+ if( index[i] < 0 || sd->status.inventory[index[i]].amount < amount[i] )
+ {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_LEVEL, 0);
+ return 0;
+ }
+ }
+
+ // Consume items
+ for( i = 0; i < ARRAYLENGTH(itemid); i++ )
+ {
+ if( index[i] >= 0 ) pc_delitem(sd, index[i], amount[i], 0, 1, LOG_TYPE_CONSUME);
+ }
+
+ if( type&2 )
+ return 1;
+
+ if( sp || hp )
+ status_zap(bl, hp, sp);
+
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_area_sub_count (struct block_list *src, struct block_list *target, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
+{
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct block_list *src = map_id2bl(id),*target;
+ struct unit_data *ud = unit_bl2ud(src);
+ struct skill_timerskill *skl = NULL;
+ int range;
+
+ nullpo_ret(src);
+ nullpo_ret(ud);
+ skl = ud->skilltimerskill[data];
+ nullpo_ret(skl);
+ ud->skilltimerskill[data] = NULL;
+
+ do {
+ if(src->prev == NULL)
+ break; // Source not on Map
+ if(skl->target_id) {
+ target = map_id2bl(skl->target_id);
+ if( ( skl->skill_id == RG_INTIMIDATE || skl->skill_id == SC_FATALMENACE ) && (!target || target->prev == NULL || !check_distance_bl(src,target,AREA_SIZE)) )
+ target = src; //Required since it has to warp.
+ if(target == NULL)
+ break; // Target offline?
+ if(target->prev == NULL)
+ break; // Target not on Map
+ if(src->m != target->m)
+ break; // Different Maps
+ if(status_isdead(src))
+ break; // Caster is Dead
+ if(status_isdead(target) && skl->skill_id != RG_INTIMIDATE && skl->skill_id != WZ_WATERBALL)
+ break;
+
+ switch(skl->skill_id) {
+ case RG_INTIMIDATE:
+ if (unit_warp(src,-1,-1,-1,CLR_TELEPORT) == 0) {
+ short x,y;
+ map_search_freecell(src, 0, &x, &y, 1, 1, 0);
+ if (target != src && !status_isdead(target))
+ unit_warp(target, -1, x, y, CLR_TELEPORT);
+ }
+ break;
+ case BA_FROSTJOKER:
+ case DC_SCREAM:
+ range= skill_get_splash(skl->skill_id, skl->skill_lv);
+ map_foreachinarea(skill_frostjoke_scream,skl->map,skl->x-range,skl->y-range,
+ skl->x+range,skl->y+range,BL_CHAR,src,skl->skill_id,skl->skill_lv,tick);
+ break;
+ case NPC_EARTHQUAKE:
+ if( skl->type > 1 )
+ skill_addtimerskill(src,tick+250,src->id,0,0,skl->skill_id,skl->skill_lv,skl->type-1,skl->flag);
+ skill_area_temp[0] = map_foreachinrange(skill_area_sub, src, skill_get_splash(skl->skill_id, skl->skill_lv), BL_CHAR, src, skl->skill_id, skl->skill_lv, tick, BCT_ENEMY, skill_area_sub_count);
+ skill_area_temp[1] = src->id;
+ skill_area_temp[2] = 0;
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skl->skill_id, skl->skill_lv), splash_target(src), src, skl->skill_id, skl->skill_lv, tick, skl->flag, skill_castend_damage_id);
+ break;
+ case WZ_WATERBALL:
+ skill_toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify
+ if (!status_isdead(target))
+ skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag);
+ if (skl->type>1 && !status_isdead(target) && !status_isdead(src)) {
+ skill_addtimerskill(src,tick+125,target->id,0,0,skl->skill_id,skl->skill_lv,skl->type-1,skl->flag);
+ } else {
+ struct status_change *sc = status_get_sc(src);
+ if(sc) {
+ if(sc->data[SC_SPIRIT] &&
+ sc->data[SC_SPIRIT]->val2 == SL_WIZARD &&
+ sc->data[SC_SPIRIT]->val3 == skl->skill_id)
+ sc->data[SC_SPIRIT]->val3 = 0; //Clear bounced spell check.
+ }
+ }
+ break;
+ /**
+ * Warlock
+ **/
+ case WL_CHAINLIGHTNING_ATK:
+ {
+ struct block_list *nbl = NULL; // Next Target of Chain
+ skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); // Hit a Lightning on the current Target
+ skill_toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify
+ if( skl->type > 1 )
+ { // Remaining Chains Hit
+ nbl = battle_getenemyarea(src,target->x,target->y,2,BL_CHAR|BL_SKILL,target->id); // Search for a new Target around current one...
+ if( nbl == NULL && skl->x > 1 )
+ {
+ nbl = target;
+ skl->x--;
+ }
+ else skl->x = 3;
+ }
+
+ if( nbl )
+ skill_addtimerskill(src,tick+status_get_adelay(src),nbl->id,skl->x,0,WL_CHAINLIGHTNING_ATK,skl->skill_lv,skl->type-1,skl->flag);
+ }
+ break;
+ case WL_TETRAVORTEX_FIRE:
+ case WL_TETRAVORTEX_WATER:
+ case WL_TETRAVORTEX_WIND:
+ case WL_TETRAVORTEX_GROUND:
+ skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag|SD_ANIMATION);
+ skill_toggle_magicpower(src, skl->skill_id); // only the first hit will be amplify
+ if( skl->type >= 3 )
+ { // Final Hit
+ if( !status_isdead(target) )
+ { // Final Status Effect
+ int effects[4] = { SC_BURNING, SC_FREEZING, SC_BLEEDING, SC_STUN },
+ applyeffects[4] = { 0, 0, 0, 0 },
+ i, j = 0, k = 0;
+ for( i = 1; i <= 8; i = i + i )
+ {
+ if( skl->x&i )
+ {
+ applyeffects[j] = effects[k];
+ j++;
+ }
+ k++;
+ }
+ if( j )
+ {
+ i = applyeffects[rnd()%j];
+ status_change_start(target, i, 10000, skl->skill_lv,
+ (i == SC_BURNING ? 1000 : 0),
+ (i == SC_BURNING ? src->id : 0),
+ 0, skill_get_time(WL_TETRAVORTEX,skl->skill_lv), 0);
+ }
+ }
+ }
+ break;
+ case WM_REVERBERATION_MELEE:
+ case WM_REVERBERATION_MAGIC:
+ skill_castend_damage_id(src, target, skl->skill_id, skl->skill_lv, tick, skl->flag|SD_LEVEL); // damage should split among targets
+ break;
+ case SC_FATALMENACE:
+ if( src == target ) // Casters Part
+ unit_warp(src, -1, skl->x, skl->y, 3);
+ else { // Target's Part
+ short x = skl->x, y = skl->y;
+ map_search_freecell(NULL, target->m, &x, &y, 2, 2, 1);
+ unit_warp(target,-1,x,y,3);
+ }
+ break;
+ case LG_MOONSLASHER:
+ case SR_WINDMILL:
+ if( target->type == BL_PC ) {
+ struct map_session_data *tsd = NULL;
+ if( (tsd = ((TBL_PC*)target)) && !pc_issit(tsd) ) {
+ pc_setsit(tsd);
+ skill_sit(tsd,1);
+ clif_sitting(&tsd->bl);
+ }
+ }
+ break;
+ case LG_OVERBRAND_BRANDISH:
+ case LG_OVERBRAND_PLUSATK:
+ case SR_KNUCKLEARROW:
+ skill_attack(BF_WEAPON, src, src, target, skl->skill_id, skl->skill_lv, tick, skl->flag|SD_LEVEL);
+ break;
+ case GN_SPORE_EXPLOSION:
+ map_foreachinrange(skill_area_sub, target, skill_get_splash(skl->skill_id, skl->skill_lv), BL_CHAR,
+ src, skl->skill_id, skl->skill_lv, 0, skl->flag|1|BCT_ENEMY, skill_castend_damage_id);
+ break;
+ case CH_PALMSTRIKE:
+ {
+ struct status_change* tsc = status_get_sc(target);
+ struct status_change* sc = status_get_sc(src);
+ if( tsc && tsc->option&OPTION_HIDE ||
+ sc && sc->option&OPTION_HIDE ){
+ skill_blown(src,target,skill_get_blewcount(skl->skill_id, skl->skill_lv), -1, 0x0 );
+ 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)
+ break;
+ switch( skl->skill_id )
+ {
+ case WZ_METEOR:
+ if( skl->type >= 0 )
+ {
+ int x = skl->type>>16, y = skl->type&0xFFFF;
+ if( path_search_long(NULL, src->m, src->x, src->y, x, y, CELL_CHKWALL) )
+ skill_unitsetting(src,skl->skill_id,skl->skill_lv,x,y,skl->flag);
+ if( path_search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) )
+ clif_skill_poseffect(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,tick);
+ }
+ else if( path_search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) )
+ skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,skl->flag);
+ break;
+ case GN_CRAZYWEED_ATK:
+ {
+ int dummy = 1, i = skill_get_unit_range(skl->skill_id,skl->skill_lv);
+ map_foreachinarea(skill_cell_overlap, src->m, skl->x-i, skl->y-i, skl->x+i, skl->y+i, BL_SKILL, skl->skill_id, &dummy, src);
+ }
+ case WL_EARTHSTRAIN:
+ skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,(skl->type<<16)|skl->flag);
+ break;
+
+ }
+ }
+ } while (0);
+ //Free skl now that it is no longer needed.
+ ers_free(skill_timer_ers, skl);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_addtimerskill (struct block_list *src, unsigned int tick, int target, int x,int y, uint16 skill_id, uint16 skill_lv, int type, int flag)
+{
+ int i;
+ struct unit_data *ud;
+ nullpo_retr(1, src);
+ if (src->prev == NULL)
+ return 0;
+ ud = unit_bl2ud(src);
+ nullpo_retr(1, ud);
+
+ ARR_FIND( 0, MAX_SKILLTIMERSKILL, i, ud->skilltimerskill[i] == 0 );
+ if( i == MAX_SKILLTIMERSKILL ) return 1;
+
+ ud->skilltimerskill[i] = ers_alloc(skill_timer_ers, struct skill_timerskill);
+ ud->skilltimerskill[i]->timer = add_timer(tick, skill_timerskill, src->id, i);
+ ud->skilltimerskill[i]->src_id = src->id;
+ ud->skilltimerskill[i]->target_id = target;
+ ud->skilltimerskill[i]->skill_id = skill_id;
+ ud->skilltimerskill[i]->skill_lv = skill_lv;
+ ud->skilltimerskill[i]->map = src->m;
+ ud->skilltimerskill[i]->x = x;
+ ud->skilltimerskill[i]->y = y;
+ ud->skilltimerskill[i]->type = type;
+ ud->skilltimerskill[i]->flag = flag;
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_cleartimerskill (struct block_list *src)
+{
+ int i;
+ struct unit_data *ud;
+ nullpo_ret(src);
+ ud = unit_bl2ud(src);
+ nullpo_ret(ud);
+
+ for(i=0;i<MAX_SKILLTIMERSKILL;i++) {
+ if(ud->skilltimerskill[i]) {
+ delete_timer(ud->skilltimerskill[i]->timer, skill_timerskill);
+ ers_free(skill_timer_ers, ud->skilltimerskill[i]);
+ ud->skilltimerskill[i]=NULL;
+ }
+ }
+ return 1;
+}
+static int skill_ative_reverberation( struct block_list *bl, va_list ap) {
+ struct skill_unit *su = (TBL_SKILL*)bl;
+ struct skill_unit_group *sg;
+ if( bl->type != BL_SKILL )
+ return 0;
+ if( su->alive && (sg = su->group) && sg->skill_id == WM_REVERBERATION ) {
+ map_foreachinrange(skill_trap_splash, bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, bl, gettick());
+ su->limit=DIFF_TICK(gettick(),sg->tick);
+ sg->unit_id = UNT_USED_TRAPS;
+ }
+ return 0;
+}
+
+static int skill_reveal_trap (struct block_list *bl, va_list ap)
+{
+ TBL_SKILL *su = (TBL_SKILL*)bl;
+ if (su->alive && su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP)
+ { //Reveal trap.
+ //Change look is not good enough, the client ignores it as an actual trap still. [Skotlex]
+ //clif_changetraplook(bl, su->group->unit_id);
+ clif_skill_setunit(su);
+ return 1;
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *
+ *------------------------------------------*/
+int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
+{
+ struct map_session_data *sd = NULL;
+ struct status_data *tstatus;
+ struct status_change *sc;
+
+ if (skill_id > 0 && !skill_lv) return 0;
+
+ nullpo_retr(1, src);
+ nullpo_retr(1, bl);
+
+ if (src->m != bl->m)
+ return 1;
+
+ if (bl->prev == NULL)
+ return 1;
+
+ sd = BL_CAST(BL_PC, src);
+
+ if (status_isdead(bl))
+ return 1;
+
+ if (skill_id && skill_get_type(skill_id) == BF_MAGIC && status_isimmune(bl) == 100)
+ { //GTB makes all targetted magic display miss with a single bolt.
+ sc_type sct = status_skill2sc(skill_id);
+ if(sct != SC_NONE)
+ status_change_end(bl, sct, INVALID_TIMER);
+ clif_skill_damage(src, bl, tick, status_get_amotion(src), status_get_dmotion(bl), 0, 1, skill_id, skill_lv, skill_get_hit(skill_id));
+ return 1;
+ }
+
+ sc = status_get_sc(src);
+ if (sc && !sc->count)
+ sc = NULL; //Unneeded
+
+ tstatus = status_get_status_data(bl);
+
+ map_freeblock_lock();
+
+ switch(skill_id)
+ {
+ case MER_CRASH:
+ case SM_BASH:
+ case MS_BASH:
+ case MC_MAMMONITE:
+ case TF_DOUBLE:
+ case AC_DOUBLE:
+ case MA_DOUBLE:
+ case AS_SONICBLOW:
+ case KN_PIERCE:
+ case ML_PIERCE:
+ case KN_SPEARBOOMERANG:
+ case TF_POISON:
+ case TF_SPRINKLESAND:
+ case AC_CHARGEARROW:
+ case MA_CHARGEARROW:
+ case RG_INTIMIDATE:
+ case AM_ACIDTERROR:
+ case BA_MUSICALSTRIKE:
+ case DC_THROWARROW:
+ case BA_DISSONANCE:
+ case CR_HOLYCROSS:
+ case NPC_DARKCROSS:
+ case CR_SHIELDCHARGE:
+ case CR_SHIELDBOOMERANG:
+ case NPC_PIERCINGATT:
+ case NPC_MENTALBREAKER:
+ case NPC_RANGEATTACK:
+ case NPC_CRITICALSLASH:
+ case NPC_COMBOATTACK:
+ case NPC_GUIDEDATTACK:
+ case NPC_POISON:
+ case NPC_RANDOMATTACK:
+ case NPC_WATERATTACK:
+ case NPC_GROUNDATTACK:
+ case NPC_FIREATTACK:
+ case NPC_WINDATTACK:
+ case NPC_POISONATTACK:
+ case NPC_HOLYATTACK:
+ case NPC_DARKNESSATTACK:
+ case NPC_TELEKINESISATTACK:
+ case NPC_UNDEADATTACK:
+ case NPC_ARMORBRAKE:
+ case NPC_WEAPONBRAKER:
+ case NPC_HELMBRAKE:
+ case NPC_SHIELDBRAKE:
+ case NPC_BLINDATTACK:
+ case NPC_SILENCEATTACK:
+ case NPC_STUNATTACK:
+ case NPC_PETRIFYATTACK:
+ case NPC_CURSEATTACK:
+ case NPC_SLEEPATTACK:
+ case LK_AURABLADE:
+ case LK_SPIRALPIERCE:
+ case ML_SPIRALPIERCE:
+ case LK_HEADCRUSH:
+ case CG_ARROWVULCAN:
+ case HW_MAGICCRASHER:
+ case ITM_TOMAHAWK:
+ case MO_TRIPLEATTACK:
+ case CH_CHAINCRUSH:
+ case CH_TIGERFIST:
+ case PA_SHIELDCHAIN: // Shield Chain
+ case PA_SACRIFICE:
+ case WS_CARTTERMINATION: // Cart Termination
+ case AS_VENOMKNIFE:
+ case HT_PHANTASMIC:
+ case HT_POWER:
+ case TK_DOWNKICK:
+ case TK_COUNTER:
+ case GS_CHAINACTION:
+ case GS_TRIPLEACTION:
+ case GS_MAGICALBULLET:
+ case GS_TRACKING:
+ case GS_PIERCINGSHOT:
+ case GS_RAPIDSHOWER:
+ case GS_DUST:
+ case GS_DISARM: // Added disarm. [Reddozen]
+ case GS_FULLBUSTER:
+ case NJ_SYURIKEN:
+ case NJ_KUNAI:
+ case ASC_BREAKER:
+ case HFLI_MOON: //[orn]
+ case HFLI_SBR44: //[orn]
+ case NPC_BLEEDING:
+ case NPC_CRITICALWOUND:
+ case NPC_HELLPOWER:
+ case RK_SONICWAVE:
+ case RK_HUNDREDSPEAR:
+ case AB_DUPLELIGHT_MELEE:
+ case RA_AIMEDBOLT:
+ case NC_AXEBOOMERANG:
+ case NC_POWERSWING:
+ case GC_CROSSIMPACT:
+ case GC_VENOMPRESSURE:
+ case SC_TRIANGLESHOT:
+ case SC_FEINTBOMB:
+ case LG_BANISHINGPOINT:
+ case LG_SHIELDPRESS:
+ case LG_RAGEBURST:
+ case LG_RAYOFGENESIS:
+ case LG_HESPERUSLIT:
+ case SR_FALLENEMPIRE:
+ case SR_CRESCENTELBOW_AUTOSPELL:
+ case SR_GATEOFHELL:
+ case SR_GENTLETOUCH_QUIET:
+ case WM_SEVERE_RAINSTORM_MELEE:
+ case WM_GREAT_ECHO:
+ case GN_SLINGITEM_RANGEMELEEATK:
+ case KO_JYUMONJIKIRI:
+ case KO_SETSUDAN:
+ case KO_KAIHOU:
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ /**
+ * Mechanic (MADO GEAR)
+ **/
+ case NC_BOOSTKNUCKLE:
+ case NC_PILEBUNKER:
+ case NC_VULCANARM:
+ case NC_COLDSLOWER:
+ case NC_ARMSCANNON:
+ if (sd) pc_overheat(sd,1);
+ case RK_WINDCUTTER:
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag|SD_ANIMATION);
+ break;
+
+ case LK_JOINTBEAT: // decide the ailment first (affects attack damage and effect)
+ switch( rnd()%6 ){
+ case 0: flag |= BREAK_ANKLE; break;
+ case 1: flag |= BREAK_WRIST; break;
+ case 2: flag |= BREAK_KNEE; break;
+ case 3: flag |= BREAK_SHOULDER; break;
+ case 4: flag |= BREAK_WAIST; break;
+ case 5: flag |= BREAK_NECK; break;
+ }
+ //TODO: is there really no cleaner way to do this?
+ sc = status_get_sc(bl);
+ if (sc) sc->jb_flag = flag;
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case MO_COMBOFINISH:
+ if (!(flag&1) && sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_MONK)
+ { //Becomes a splash attack when Soul Linked.
+ map_foreachinrange(skill_area_sub, bl,
+ skill_get_splash(skill_id, skill_lv),splash_target(src),
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ } else
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case TK_STORMKICK: // Taekwon kicks [Dralnu]
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_area_temp[1] = 0;
+ map_foreachinrange(skill_attack_area, src,
+ skill_get_splash(skill_id, skill_lv), splash_target(src),
+ BF_WEAPON, src, src, skill_id, skill_lv, tick, flag, BCT_ENEMY);
+ break;
+
+ case KN_CHARGEATK:
+ {
+ bool path = path_search_long(NULL, src->m, src->x, src->y, bl->x, bl->y,CELL_CHKWALL);
+ unsigned int dist = distance_bl(src, bl);
+ uint8 dir = map_calc_dir(bl, src->x, src->y);
+
+ // teleport to target (if not on WoE grounds)
+ if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground && unit_movepos(src, bl->x, bl->y, 0, 1) )
+ clif_slide(src, bl->x, bl->y);
+
+ // cause damage and knockback if the path to target was a straight one
+ if( path )
+ {
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, dist);
+ skill_blown(src, bl, dist, dir, 0);
+ //HACK: since knockback officially defaults to the left, the client also turns to the left... therefore,
+ // make the caster look in the direction of the target
+ unit_setdir(src, (dir+4)%8);
+ }
+
+ }
+ break;
+
+ case NC_FLAMELAUNCHER:
+ if (sd) pc_overheat(sd,1);
+ case SN_SHARPSHOOTING:
+ case MA_SHARPSHOOTING:
+ case NJ_KAMAITACHI:
+ case LG_CANNONSPEAR:
+ //It won't shoot through walls since on castend there has to be a direct
+ //line of sight between caster and target.
+ skill_area_temp[1] = bl->id;
+ map_foreachinpath (skill_attack_area,src->m,src->x,src->y,bl->x,bl->y,
+ skill_get_splash(skill_id, skill_lv),skill_get_maxcount(skill_id,skill_lv), splash_target(src),
+ skill_get_type(skill_id),src,src,skill_id,skill_lv,tick,flag,BCT_ENEMY);
+ break;
+
+ case NPC_ACIDBREATH:
+ case NPC_DARKNESSBREATH:
+ case NPC_FIREBREATH:
+ case NPC_ICEBREATH:
+ case NPC_THUNDERBREATH:
+ skill_area_temp[1] = bl->id;
+ map_foreachinpath(skill_attack_area,src->m,src->x,src->y,bl->x,bl->y,
+ skill_get_splash(skill_id, skill_lv),skill_get_maxcount(skill_id,skill_lv), splash_target(src),
+ skill_get_type(skill_id),src,src,skill_id,skill_lv,tick,flag,BCT_ENEMY);
+ break;
+
+ case MO_INVESTIGATE:
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
+ break;
+
+ case RG_BACKSTAP:
+ {
+ uint8 dir = map_calc_dir(src, bl->x, bl->y), t_dir = unit_getdir(bl);
+ if ((!check_distance_bl(src, bl, 0) && !map_check_dir(dir, t_dir)) || bl->type == BL_SKILL) {
+ status_change_end(src, SC_HIDING, INVALID_TIMER);
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
+ dir = dir < 4 ? dir+4 : dir-4; // change direction [Celest]
+ unit_setdir(bl,dir);
+ }
+ else if (sd)
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+
+ case MO_FINGEROFFENSIVE:
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ if (battle_config.finger_offensive_type && sd) {
+ int i;
+ for (i = 1; i < sd->spiritball_old; i++)
+ skill_addtimerskill(src, tick + i * 200, bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag);
+ }
+ status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
+ break;
+
+ case MO_CHAINCOMBO:
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
+ break;
+
+ case NJ_ISSEN:
+ status_change_end(src, SC_NEN, INVALID_TIMER);
+ status_change_end(src, SC_HIDING, INVALID_TIMER);
+ // fall through
+ case MO_EXTREMITYFIST:
+ {
+ short x, y, i = 2; // Move 2 cells for Issen(from target)
+ struct block_list *mbl = bl;
+ short dir = 0;
+
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+
+ if( skill_id == MO_EXTREMITYFIST )
+ {
+ mbl = src;
+ i = 3; // for Asura(from caster)
+ status_set_sp(src, 0, 0);
+ status_change_end(src, SC_EXPLOSIONSPIRITS, INVALID_TIMER);
+ status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
+#ifdef RENEWAL
+ sc_start(src,SC_EXTREMITYFIST2,100,skill_lv,skill_get_time(skill_id,skill_lv));
+#endif
+ }else
+ status_set_hp(src,
+#ifdef RENEWAL
+ max(status_get_max_hp(src)/100, 1)
+#else
+ 1
+#endif
+ , 0);
+
+ dir = map_calc_dir(src,bl->x,bl->y);
+ if( dir > 0 && dir < 4) x = -i;
+ else if( dir > 4 ) x = i;
+ else x = 0;
+ if( dir > 2 && dir < 6 ) y = -i;
+ else if( dir == 7 || dir < 2 ) y = i;
+ else y = 0;
+ if( (mbl == src || !map_flag_gvg(src->m) && !map[src->m].flag.battleground) && // only NJ_ISSEN don't have slide effect in GVG
+ unit_movepos(src, mbl->x+x, mbl->y+y, 1, 1) ) {
+ clif_slide(src, src->x, src->y);
+ //uncomment this if you want to remove MO_EXTREMITYFIST glitchy walking effect. [malufett]
+ //clif_fixpos(src);
+ }
+ }
+ break;
+
+ //Splash attack skills.
+ case AS_GRIMTOOTH:
+ case MC_CARTREVOLUTION:
+ case NPC_SPLASHATTACK:
+ flag |= SD_PREAMBLE; // a fake packet will be sent for the first target to be hit
+ case AS_SPLASHER:
+ case SM_MAGNUM:
+ case MS_MAGNUM:
+ case HT_BLITZBEAT:
+ case AC_SHOWER:
+ case MA_SHOWER:
+ case MG_NAPALMBEAT:
+ case MG_FIREBALL:
+ case RG_RAID:
+ case HW_NAPALMVULCAN:
+ case NJ_HUUMA:
+ case NJ_BAKUENRYU:
+ case ASC_METEORASSAULT:
+ case GS_DESPERADO:
+ case GS_SPREADATTACK:
+ case NPC_EARTHQUAKE:
+ case NPC_PULSESTRIKE:
+ case NPC_HELLJUDGEMENT:
+ case NPC_VAMPIRE_GIFT:
+ case RK_IGNITIONBREAK:
+ case AB_JUDEX:
+ case WL_SOULEXPANSION:
+ case WL_CRIMSONROCK:
+ case WL_COMET:
+ case WL_JACKFROST:
+ case RA_ARROWSTORM:
+ case RA_WUGDASH:
+ case NC_SELFDESTRUCTION:
+ case NC_AXETORNADO:
+ case GC_ROLLINGCUTTER:
+ case GC_COUNTERSLASH:
+ case LG_MOONSLASHER:
+ case LG_EARTHDRIVE:
+ case SR_TIGERCANNON:
+ case SR_RAMPAGEBLASTER:
+ case SR_SKYNETBLOW:
+ case SR_WINDMILL:
+ case SR_RIDEINLIGHTNING:
+ case WM_SOUND_OF_DESTRUCTION:
+ case WM_REVERBERATION_MELEE:
+ case WM_REVERBERATION_MAGIC:
+ case SO_VARETYR_SPEAR:
+ case GN_CART_TORNADO:
+ case GN_CARTCANNON:
+ case KO_HAPPOKUNAI:
+ case KO_HUUMARANKA:
+ case KO_MUCHANAGE:
+ case KO_BAKURETSU:
+ if( flag&1 ) {//Recursive invocation
+ // skill_area_temp[0] holds number of targets in area
+ // skill_area_temp[1] holds the id of the original target
+ // skill_area_temp[2] counts how many targets have already been processed
+ int sflag = skill_area_temp[0] & 0xFFF, heal;
+ if( flag&SD_LEVEL )
+ sflag |= SD_LEVEL; // -1 will be used in packets instead of the skill level
+ if( skill_area_temp[1] != bl->id && !(skill_get_inf2(skill_id)&INF2_NPC_SKILL) )
+ sflag |= SD_ANIMATION; // original target gets no animation (as well as all NPC skills)
+
+ heal = skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, sflag);
+ if( skill_id == NPC_VAMPIRE_GIFT && heal > 0 ) {
+ clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1);
+ status_heal(src,heal,0,0);
+ }
+ } else {
+ switch ( skill_id ) {
+ case NJ_BAKUENRYU:
+ case LG_EARTHDRIVE:
+ case GN_CARTCANNON:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case LG_MOONSLASHER:
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ break;
+ case NPC_EARTHQUAKE://FIXME: Isn't EarthQuake a ground skill after all?
+ skill_addtimerskill(src,tick+250,src->id,0,0,skill_id,skill_lv,2,flag|BCT_ENEMY|SD_SPLASH|1);
+ default:
+ break;
+ }
+
+ skill_area_temp[0] = 0;
+ skill_area_temp[1] = bl->id;
+ skill_area_temp[2] = 0;
+ if( skill_id == WL_CRIMSONROCK ) {
+ skill_area_temp[4] = bl->x;
+ skill_area_temp[5] = bl->y;
+ }
+ if( skill_id == WM_REVERBERATION_MELEE || skill_id == WM_REVERBERATION_MAGIC )
+ skill_area_temp[1] = 0;
+ // if skill damage should be split among targets, count them
+ //SD_LEVEL -> Forced splash damage for Auto Blitz-Beat -> count targets
+ //special case: Venom Splasher uses a different range for searching than for splashing
+ if( flag&SD_LEVEL || skill_get_nk(skill_id)&NK_SPLASHSPLIT )
+ skill_area_temp[0] = map_foreachinrange(skill_area_sub, bl, (skill_id == AS_SPLASHER)?1:skill_get_splash(skill_id, skill_lv), BL_CHAR, src, skill_id, skill_lv, tick, BCT_ENEMY, skill_area_sub_count);
+
+ // recursive invocation of skill_castend_damage_id() with flag|1
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), ( skill_id == WM_REVERBERATION_MELEE || skill_id == WM_REVERBERATION_MAGIC )?BL_CHAR:splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id);
+ }
+ break;
+
+ case KN_BRANDISHSPEAR:
+ case ML_BRANDISH:
+ //Coded apart for it needs the flag passed to the damage calculation.
+ if (skill_area_temp[1] != bl->id)
+ skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag|SD_ANIMATION);
+ else
+ skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
+ break;
+
+ case KN_BOWLINGBASH:
+ case MS_BOWLINGBASH:
+ if(flag&1){
+ if(bl->id==skill_area_temp[1])
+ break;
+ //two hits for 500%
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,SD_ANIMATION);
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,SD_ANIMATION);
+ } else {
+ int i,c;
+ c = skill_get_blewcount(skill_id,skill_lv);
+ // keep moving target in the direction that src is looking, square by square
+ for(i=0;i<c;i++){
+ if (!skill_blown(src,bl,1,(unit_getdir(src)+4)%8,0x1))
+ break; //Can't knockback
+ skill_area_temp[0] = map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR, src, skill_id, skill_lv, tick, flag|BCT_ENEMY, skill_area_sub_count);
+ if( skill_area_temp[0] > 1 ) break; // collision
+ }
+ clif_blown(bl); //Update target pos.
+ if (i!=c) { //Splash
+ skill_area_temp[1] = bl->id;
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_damage_id);
+ }
+ //Weirdo dual-hit property, two attacks for 500%
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,0);
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,0);
+ }
+ break;
+
+ case KN_SPEARSTAB:
+ if(flag&1) {
+ if (bl->id==skill_area_temp[1])
+ break;
+ if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,SD_ANIMATION))
+ skill_blown(src,bl,skill_area_temp[2],-1,0);
+ } else {
+ int x=bl->x,y=bl->y,i,dir;
+ dir = map_calc_dir(bl,src->x,src->y);
+ skill_area_temp[1] = bl->id;
+ skill_area_temp[2] = skill_get_blewcount(skill_id,skill_lv);
+ // all the enemies between the caster and the target are hit, as well as the target
+ if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,0))
+ skill_blown(src,bl,skill_area_temp[2],-1,0);
+ for (i=0;i<4;i++) {
+ map_foreachincell(skill_area_sub,bl->m,x,y,BL_CHAR,
+ src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ x += dirx[dir];
+ y += diry[dir];
+ }
+ }
+ break;
+
+ case TK_TURNKICK:
+ case MO_BALKYOUNG: //Active part of the attack. Skill-attack [Skotlex]
+ {
+ skill_area_temp[1] = bl->id; //NOTE: This is used in skill_castend_nodamage_id to avoid affecting the target.
+ if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag))
+ map_foreachinrange(skill_area_sub,bl,
+ skill_get_splash(skill_id, skill_lv),BL_CHAR,
+ src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+ case CH_PALMSTRIKE: // Palm Strike takes effect 1sec after casting. [Skotlex]
+ // clif_skill_nodamage(src,bl,skill_id,skill_lv,0); //Can't make this one display the correct attack animation delay :/
+ clif_damage(src,bl,tick,status_get_amotion(src),0,-1,1,4,0); //Display an absorbed damage attack.
+ skill_addtimerskill(src, tick + (1000+status_get_amotion(src)), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag);
+ break;
+
+ case PR_TURNUNDEAD:
+ case ALL_RESURRECTION:
+ if (!battle_check_undead(tstatus->race, tstatus->def_ele))
+ break;
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case MG_SOULSTRIKE:
+ case NPC_DARKSTRIKE:
+ case MG_COLDBOLT:
+ case MG_FIREBOLT:
+ case MG_LIGHTNINGBOLT:
+ case WZ_EARTHSPIKE:
+ case AL_HEAL:
+ case AL_HOLYLIGHT:
+ case WZ_JUPITEL:
+ case NPC_DARKTHUNDER:
+ case PR_ASPERSIO:
+ case MG_FROSTDIVER:
+ case WZ_SIGHTBLASTER:
+ case WZ_SIGHTRASHER:
+ case NJ_KOUENKA:
+ case NJ_HYOUSENSOU:
+ case NJ_HUUJIN:
+ case AB_ADORAMUS:
+ case AB_RENOVATIO:
+ case AB_HIGHNESSHEAL:
+ case AB_DUPLELIGHT_MAGIC:
+ case WM_METALICSOUND:
+ case MH_ERASER_CUTTER:
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case NPC_MAGICALATTACK:
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ sc_start(src,status_skill2sc(skill_id),100,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+
+ case HVAN_CAPRICE: //[blackhole89]
+ {
+ int ran=rnd()%4;
+ int sid = 0;
+ switch(ran)
+ {
+ case 0: sid=MG_COLDBOLT; break;
+ case 1: sid=MG_FIREBOLT; break;
+ case 2: sid=MG_LIGHTNINGBOLT; break;
+ case 3: sid=WZ_EARTHSPIKE; break;
+ }
+ skill_attack(BF_MAGIC,src,src,bl,sid,skill_lv,tick,flag|SD_LEVEL);
+ }
+ break;
+ case WZ_WATERBALL:
+ {
+ int range = skill_lv / 2;
+ int maxlv = skill_get_max(skill_id); // learnable level
+ int count = 0;
+ int x, y;
+ struct skill_unit* unit;
+
+ if( skill_lv > maxlv )
+ {
+ if( src->type == BL_MOB && skill_lv == 10 )
+ range = 4;
+ else
+ range = maxlv / 2;
+ }
+
+ for( y = src->y - range; y <= src->y + range; ++y )
+ for( x = src->x - range; x <= src->x + range; ++x )
+ {
+ if( !map_find_skill_unit_oncell(src,x,y,SA_LANDPROTECTOR,NULL,1) )
+ {
+ if( src->type != BL_PC || map_getcell(src->m,x,y,CELL_CHKWATER) ) // non-players bypass the water requirement
+ count++; // natural water cell
+ else if( (unit = map_find_skill_unit_oncell(src,x,y,SA_DELUGE,NULL,1)) != NULL || (unit = map_find_skill_unit_oncell(src,x,y,NJ_SUITON,NULL,1)) != NULL )
+ {
+ count++; // skill-induced water cell
+ skill_delunit(unit); // consume cell
+ }
+ }
+ }
+
+ if( count > 1 ) // queue the remaining count - 1 timerskill Waterballs
+ skill_addtimerskill(src,tick+150,bl->id,0,0,skill_id,skill_lv,count-1,flag);
+ }
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case PR_BENEDICTIO:
+ //Should attack undead and demons. [Skotlex]
+ if (battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race == RC_DEMON)
+ skill_attack(BF_MAGIC, src, src, bl, skill_id, skill_lv, tick, flag);
+ break;
+
+ case SL_SMA:
+ status_change_end(src, SC_SMA, INVALID_TIMER);
+ case SL_STIN:
+ case SL_STUN:
+ if (sd && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) {
+ status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,500,10);
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case NPC_DARKBREATH:
+ clif_emotion(src,E_AG);
+ case SN_FALCONASSAULT:
+ case PA_PRESSURE:
+ case CR_ACIDDEMONSTRATION:
+ case TF_THROWSTONE:
+ case NPC_SMOKING:
+ case GS_FLING:
+ case NJ_ZENYNAGE:
+ case GN_THORNS_TRAP:
+ case GN_HELLS_PLANT_ATK:
+ skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+ /**
+ * Rune Knight
+ **/
+ case RK_DRAGONBREATH: {
+ struct status_change *tsc = NULL;
+ if( (tsc = status_get_sc(bl)) && (tsc->data[SC_HIDING] )) {
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ } else
+ skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag);
+ }
+ break;
+
+ case NPC_SELFDESTRUCTION: {
+ struct status_change *tsc = NULL;
+ if( (tsc = status_get_sc(bl)) && tsc->data[SC_HIDING] )
+ break;
+ }
+ case HVAN_EXPLOSION:
+ if (src != bl)
+ skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ // Celest
+ case PF_SOULBURN:
+ if (rnd()%100 < (skill_lv < 5 ? 30 + skill_lv * 10 : 70)) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if (skill_lv == 5)
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ status_percent_damage(src, bl, 0, 100, false);
+ } else {
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ if (skill_lv == 5)
+ skill_attack(BF_MAGIC,src,src,src,skill_id,skill_lv,tick,flag);
+ status_percent_damage(src, src, 0, 100, false);
+ }
+ break;
+
+ case NPC_BLOODDRAIN:
+ case NPC_ENERGYDRAIN:
+ {
+ int heal = skill_attack( (skill_id == NPC_BLOODDRAIN) ? BF_WEAPON : BF_MAGIC,
+ src, src, bl, skill_id, skill_lv, tick, flag);
+ if (heal > 0){
+ clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1);
+ status_heal(src, heal, 0, 0);
+ }
+ }
+ break;
+
+ case GS_BULLSEYE:
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case NJ_KASUMIKIRI:
+ if (skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag) > 0)
+ sc_start(src,SC_HIDING,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case NJ_KIRIKAGE:
+ if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground )
+ { //You don't move on GVG grounds.
+ short x, y;
+ map_search_freecell(bl, 0, &x, &y, 1, 1, 0);
+ if (unit_movepos(src, x, y, 0, 0))
+ clif_slide(src,src->x,src->y);
+ }
+ status_change_end(src, SC_HIDING, INVALID_TIMER);
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+ case RK_PHANTOMTHRUST:
+ unit_setdir(src,map_calc_dir(src, bl->x, bl->y));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+
+ skill_blown(src,bl,distance_bl(src,bl)-1,unit_getdir(src),0);
+ if( battle_check_target(src,bl,BCT_ENEMY) )
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case RK_STORMBLAST:
+ case RK_CRUSHSTRIKE:
+ if( sd ) {
+ if( pc_checkskill(sd,RK_RUNEMASTERY) >= ( skill_id == RK_CRUSHSTRIKE ? 7 : 3 ) )
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ } else //non-sd support
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+ case GC_DARKILLUSION:
+ {
+ short x, y;
+ short dir = map_calc_dir(src,bl->x,bl->y);
+
+ if( dir > 0 && dir < 4) x = 2;
+ else if( dir > 4 ) x = -2;
+ else x = 0;
+ if( dir > 2 && dir < 6 ) y = 2;
+ else if( dir == 7 || dir < 2 ) y = -2;
+ else y = 0;
+
+ if( unit_movepos(src, bl->x+x, bl->y+y, 1, 1) )
+ {
+ clif_slide(src,bl->x+x,bl->y+y);
+ clif_fixpos(src); // the official server send these two packts.
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ if( rnd()%100 < 4 * skill_lv )
+ skill_castend_damage_id(src,bl,GC_CROSSIMPACT,skill_lv,tick,flag);
+ }
+
+ }
+ break;
+
+ case GC_WEAPONCRUSH:
+ if( sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == GC_WEAPONBLOCKING )
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ else if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_GC_WEAPONBLOCKING,0);
+ break;
+
+ case GC_CROSSRIPPERSLASHER:
+ if( sd && !(sc && sc->data[SC_ROLLINGCUTTER]) )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_CONDITION,0);
+ else
+ {
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ status_change_end(src,SC_ROLLINGCUTTER,INVALID_TIMER);
+ }
+ break;
+
+ case GC_PHANTOMMENACE:
+ if( flag&1 )
+ { // Only Hits Invisible Targets
+ struct status_change *tsc = status_get_sc(bl);
+ if(tsc && (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC__INVISIBILITY]) )
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ }
+ break;
+ case WL_CHAINLIGHTNING:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_addtimerskill(src,tick + 150,bl->id,3,0,WL_CHAINLIGHTNING_ATK,skill_lv,4+skill_lv,flag);
+ break;
+ case WL_DRAINLIFE:
+ {
+ int heal = skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
+ int rate = 70 + 5 * skill_lv;
+
+ heal = heal * (5 + 5 * skill_lv) / 100;
+
+ if( bl->type == BL_SKILL )
+ heal = 0; // Don't absorb heal from Ice Walls or other skill units.
+
+ if( heal && rnd()%100 < rate )
+ {
+ status_heal(src, heal, 0, 0);
+ clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1);
+ }
+ }
+ break;
+
+ case WL_TETRAVORTEX:
+ if( sd )
+ {
+ int spheres[5] = { 0, 0, 0, 0, 0 },
+ positions[5] = {-1,-1,-1,-1,-1 },
+ i, j = 0, k, subskill = 0;
+
+ for( i = SC_SPHERE_1; i <= SC_SPHERE_5; i++ )
+ if( sc && sc->data[i] )
+ {
+ spheres[j] = i;
+ positions[j] = sc->data[i]->val2;
+ j++; //
+ }
+
+ if( j < 4 )
+ { // Need 4 spheres minimum
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+
+ // Sphere Sort, this time from new to old
+ for( i = 0; i <= j - 2; i++ )
+ for( k = i + 1; k <= j - 1; k++ )
+ if( positions[i] < positions[k] )
+ {
+ swap(positions[i],positions[k]);
+ swap(spheres[i],spheres[k]);
+ }
+
+ k = 0;
+ for( i = 0; i < 4; i++ )
+ {
+ switch( sc->data[spheres[i]]->val1 )
+ {
+ case WLS_FIRE: subskill = WL_TETRAVORTEX_FIRE; k |= 1; break;
+ case WLS_WIND: subskill = WL_TETRAVORTEX_WIND; k |= 4; break;
+ case WLS_WATER: subskill = WL_TETRAVORTEX_WATER; k |= 2; break;
+ case WLS_STONE: subskill = WL_TETRAVORTEX_GROUND; k |= 8; break;
+ }
+ skill_addtimerskill(src, tick + i * 200, bl->id, k, 0, subskill, skill_lv, i, flag);
+ clif_skill_nodamage(src, bl, subskill, skill_lv, 1);
+ status_change_end(src, spheres[i], INVALID_TIMER);
+ }
+ }
+ break;
+
+ case WL_RELEASE:
+ if( sd )
+ {
+ int i;
+ // Priority is to release SpellBook
+ if( sc && sc->data[SC_READING_SB] )
+ { // SpellBook
+ uint16 skill_id, skill_lv, point, s = 0;
+ int spell[SC_MAXSPELLBOOK-SC_SPELLBOOK1 + 1];
+
+ for(i = SC_MAXSPELLBOOK; i >= SC_SPELLBOOK1; i--) // List all available spell to be released
+ if( sc->data[i] ) spell[s++] = i;
+
+ if ( s == 0 )
+ break;
+
+ i = spell[s==1?0:rand()%s];// Random select of spell to be released.
+ if( s && sc->data[i] ){// Now extract the data from the preserved spell
+ skill_id = sc->data[i]->val1;
+ skill_lv = sc->data[i]->val2;
+ point = sc->data[i]->val3;
+ status_change_end(src, (sc_type)i, INVALID_TIMER);
+ }else //something went wrong :(
+ break;
+
+ if( sc->data[SC_READING_SB]->val2 > point )
+ sc->data[SC_READING_SB]->val2 -= point;
+ else // Last spell to be released
+ status_change_end(src, SC_READING_SB, INVALID_TIMER);
+
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ if( !skill_check_condition_castbegin(sd, skill_id, skill_lv) )
+ break;
+
+ switch( skill_get_casttype(skill_id) )
+ {
+ case CAST_GROUND:
+ skill_castend_pos2(src, bl->x, bl->y, skill_id, skill_lv, tick, 0);
+ break;
+ case CAST_NODAMAGE:
+ skill_castend_nodamage_id(src, bl, skill_id, skill_lv, tick, 0);
+ break;
+ case CAST_DAMAGE:
+ skill_castend_damage_id(src, bl, skill_id, skill_lv, tick, 0);
+ break;
+ }
+
+ sd->ud.canact_tick = tick + skill_delayfix(src, skill_id, skill_lv);
+ clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, skill_id, skill_lv), 0, 0, 0);
+ }
+ else
+ { // Summon Balls
+ int j = 0, k, skele;
+ int spheres[5] = { 0, 0, 0, 0, 0 },
+ positions[5] = {-1,-1,-1,-1,-1 };
+
+ for( i = SC_SPHERE_1; i <= SC_SPHERE_5; i++ )
+ if( sc && sc->data[i] )
+ {
+ spheres[j] = i;
+ positions[j] = sc->data[i]->val2;
+ sc->data[i]->val2--; // Prepares for next position
+ j++;
+ }
+
+ if( j == 0 )
+ { // No Spheres
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_SUMMON_NONE,0);
+ break;
+ }
+
+ // Sphere Sort
+ for( i = 0; i <= j - 2; i++ )
+ for( k = i + 1; k <= j - 1; k++ )
+ if( positions[i] > positions[k] )
+ {
+ swap(positions[i],positions[k]);
+ swap(spheres[i],spheres[k]);
+ }
+
+ if( skill_lv == 1 ) j = 1; // Limit only to one ball
+ for( i = 0; i < j; i++ )
+ {
+ skele = WL_RELEASE - 5 + sc->data[spheres[i]]->val1 - WLS_FIRE; // Convert Ball Element into Skill ATK for balls
+ // WL_SUMMON_ATK_FIRE, WL_SUMMON_ATK_WIND, WL_SUMMON_ATK_WATER, WL_SUMMON_ATK_GROUND
+ skill_addtimerskill(src,tick+status_get_adelay(src)*i,bl->id,0,0,skele,sc->data[spheres[i]]->val3,BF_MAGIC,flag|SD_LEVEL);
+ status_change_end(src, spheres[i], INVALID_TIMER); // Eliminate ball
+ }
+ clif_skill_nodamage(src,bl,skill_id,0,1);
+ }
+ }
+ break;
+ case WL_FROSTMISTY:
+ // Causes Freezing status through walls.
+ sc_start(bl,status_skill2sc(skill_id),20+12*skill_lv+(sd ? sd->status.job_level : 50)/5,skill_lv,skill_get_time(skill_id,skill_lv));
+ // Doesn't deal damage through non-shootable walls.
+ if( path_search(NULL,src->m,src->x,src->y,bl->x,bl->y,1,CELL_CHKWALL) )
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag|SD_ANIMATION);
+ break;
+ case WL_HELLINFERNO:
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag|ELE_DARK);
+ break;
+ case RA_WUGSTRIKE:
+ if( sd && pc_isridingwug(sd) ){
+ short x[8]={0,-1,-1,-1,0,1,1,1};
+ short y[8]={1,1,0,-1,-1,-1,0,1};
+ uint8 dir = map_calc_dir(bl, src->x, src->y);
+
+ if( unit_movepos(src, bl->x+x[dir], bl->y+y[dir], 1, 1) )
+ {
+ clif_slide(src, bl->x+x[dir], bl->y+y[dir]);
+ clif_fixpos(src);
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
+ }
+ break;
+ }
+ case RA_WUGBITE:
+ if( path_search(NULL,src->m,src->x,src->y,bl->x,bl->y,1,CELL_CHKNOREACH) ) {
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ }else if( sd && skill_id == RA_WUGBITE ) // Only RA_WUGBITE has the skill fail message.
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+
+ break;
+
+ case RA_SENSITIVEKEEN:
+ if( bl->type != BL_SKILL ) { // Only Hits Invisible Targets
+ struct status_change * tsc = status_get_sc(bl);
+ if( tsc && tsc->option&(OPTION_HIDE|OPTION_CLOAK) ){
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
+ }
+ }
+ else
+ {
+ struct skill_unit *su = BL_CAST(BL_SKILL,bl);
+ struct skill_unit_group* sg;
+
+ if( su && (sg=su->group) && skill_get_inf2(sg->skill_id)&INF2_TRAP )
+ {
+ if( !(sg->unit_id == UNT_USED_TRAPS || (sg->unit_id == UNT_ANKLESNARE && sg->val2 != 0 )) )
+ {
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid = sg->item_id?sg->item_id:ITEMID_TRAP;
+ item_tmp.identify = 1;
+ if( item_tmp.nameid )
+ map_addflooritem(&item_tmp,1,bl->m,bl->x,bl->y,0,0,0,0);
+ }
+ skill_delunit(su);
+ }
+ }
+ break;
+ case NC_INFRAREDSCAN:
+ if( flag&1 )
+ { //TODO: Need a confirmation if the other type of hidden status is included to be scanned. [Jobbie]
+ if( rnd()%100 < 50 )
+ sc_start(bl, SC_INFRAREDSCAN, 10000, skill_lv, skill_get_time(skill_id, skill_lv));
+ status_change_end(bl, SC_HIDING, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER); // Need confirm it.
+ }
+ else
+ {
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id);
+ clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( sd ) pc_overheat(sd,1);
+ }
+ break;
+
+ case NC_MAGNETICFIELD:
+ sc_start2(bl,SC_MAGNETICFIELD,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv));
+ break;
+ case SC_FATALMENACE:
+ if( flag&1 )
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ else
+ {
+ short x, y;
+ map_search_freecell(src, 0, &x, &y, -1, -1, 0);
+ // Destination area
+ skill_area_temp[4] = x;
+ skill_area_temp[5] = y;
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_damage_id);
+ skill_addtimerskill(src,tick + 800,src->id,x,y,skill_id,skill_lv,0,flag); // To teleport Self
+ clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,6);
+ }
+ break;
+ case LG_PINPOINTATTACK:
+ if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground && unit_movepos(src, bl->x, bl->y, 1, 1) )
+ clif_slide(src,bl->x,bl->y);
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case LG_SHIELDSPELL:
+ // flag&1: Phisycal Attack, flag&2: Magic Attack.
+ skill_attack((flag&1)?BF_WEAPON:BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case LG_OVERBRAND:
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag|SD_LEVEL);
+ break;
+
+ case LG_OVERBRAND_BRANDISH:
+ skill_addtimerskill(src, tick + status_get_amotion(src)*8/10, bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|SD_LEVEL);
+ break;
+ case SR_DRAGONCOMBO:
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case SR_KNUCKLEARROW:
+ if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground && unit_movepos(src, bl->x, bl->y, 1, 1) ) {
+ clif_slide(src,bl->x,bl->y);
+ clif_fixpos(src); // Aegis send this packet too.
+ }
+
+ if( flag&1 )
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag|SD_LEVEL);
+ else
+ skill_addtimerskill(src, tick + 300, bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag|SD_LEVEL|2);
+ break;
+
+ case SR_HOWLINGOFLION:
+ status_change_end(bl, SC_SWINGDANCE, INVALID_TIMER);
+ status_change_end(bl, SC_SYMPHONYOFLOVER, INVALID_TIMER);
+ status_change_end(bl, SC_MOONLITSERENADE, INVALID_TIMER);
+ status_change_end(bl, SC_RUSHWINDMILL, INVALID_TIMER);
+ status_change_end(bl, SC_ECHOSONG, INVALID_TIMER);
+ status_change_end(bl, SC_HARMONIZE, INVALID_TIMER);
+ status_change_end(bl, SC_SIRCLEOFNATURE, INVALID_TIMER);
+ status_change_end(bl, SC_SATURDAYNIGHTFEVER, INVALID_TIMER);
+ status_change_end(bl, SC_DANCEWITHWUG, INVALID_TIMER);
+ status_change_end(bl, SC_LERADSDEW, INVALID_TIMER);
+ status_change_end(bl, SC_MELODYOFSINK, INVALID_TIMER);
+ status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER);
+ status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER);
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag|SD_ANIMATION);
+ break;
+
+ case SR_EARTHSHAKER:
+ if( flag&1 ) { //by default cloaking skills are remove by aoe skills so no more checking/removing except hiding and cloaking exceed.
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
+ status_change_end(bl, SC_HIDING, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
+ } else{
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ }
+ break;
+
+ case SO_POISON_BUSTER: {
+ struct status_change *tsc = status_get_sc(bl);
+ if( tsc && tsc->data[SC_POISON] ) {
+ skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
+ status_change_end(bl, SC_POISON, INVALID_TIMER);
+ }
+ else if( sd )
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ }
+ break;
+
+ case GN_SPORE_EXPLOSION:
+ if( flag&1 )
+ skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
+ else {
+ clif_skill_nodamage(src, bl, skill_id, 0, 1);
+ skill_addtimerskill(src, gettick() + skill_get_time(skill_id, skill_lv) - 1000, bl->id, 0, 0, skill_id, skill_lv, 0, 0);
+ }
+ break;
+
+ case EL_FIRE_BOMB:
+ case EL_FIRE_WAVE:
+ case EL_WATER_SCREW:
+ case EL_HURRICANE:
+ case EL_TYPOON_MIS:
+ if( flag&1 )
+ skill_attack(skill_get_type(skill_id+1),src,src,bl,skill_id+1,skill_lv,tick,flag);
+ else {
+ int i = skill_get_splash(skill_id,skill_lv);
+ clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
+ clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( rnd()%100 < 30 )
+ map_foreachinrange(skill_area_sub,bl,i,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ else
+ skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
+ }
+ break;
+
+ case EL_ROCK_CRUSHER:
+ clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( rnd()%100 < 50 )
+ skill_attack(BF_MAGIC,src,src,bl,skill_id,skill_lv,tick,flag);
+ else
+ skill_attack(BF_WEAPON,src,src,bl,EL_ROCK_CRUSHER_ATK,skill_lv,tick,flag);
+ break;
+
+ case EL_STONE_RAIN:
+ if( flag&1 )
+ skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
+ else {
+ int i = skill_get_splash(skill_id,skill_lv);
+ clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( rnd()%100 < 30 )
+ map_foreachinrange(skill_area_sub,bl,i,BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ else
+ skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
+ }
+ break;
+
+ case EL_FIRE_ARROW:
+ case EL_ICE_NEEDLE:
+ case EL_WIND_SLASH:
+ case EL_STONE_HAMMER:
+ clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
+ clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+
+ case EL_TIDAL_WEAPON:
+ if( src->type == BL_ELEM ) {
+ struct elemental_data *ele = BL_CAST(BL_ELEM,src);
+ struct status_change *sc = status_get_sc(&ele->bl);
+ struct status_change *tsc = status_get_sc(bl);
+ sc_type type = status_skill2sc(skill_id), type2;
+ type2 = type-1;
+
+ clif_skill_nodamage(src,battle_get_master(src),skill_id,skill_lv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
+ elemental_clean_single_effect(ele, skill_id);
+ }
+ if( rnd()%100 < 50 )
+ skill_attack(skill_get_type(skill_id),src,src,bl,skill_id,skill_lv,tick,flag);
+ else {
+ sc_start(src,type2,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ sc_start(battle_get_master(src),type,100,ele->bl.id,skill_get_time(skill_id,skill_lv));
+ }
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ }
+ break;
+
+
+ //recursive homon skill
+ case MH_MAGMA_FLOW:
+ case MH_XENO_SLASHER:
+ case MH_HEILIGE_STANGE:
+ if(flag & 1)
+ skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
+ else {
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag | BCT_ENEMY | SD_SPLASH | 1, skill_castend_damage_id);
+ }
+ break;
+
+ case MH_STAHL_HORN:
+ case MH_NEEDLE_OF_PARALYZE:
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
+ break;
+ case MH_TINDER_BREAKER:
+ if (unit_movepos(src, bl->x, bl->y, 1, 1)) {
+#if PACKETVER >= 20111005
+ clif_snap(src, bl->x, bl->y);
+#else
+ clif_skill_poseffect(src,skill_id,skill_lv,bl->x,bl->y,tick);
+#endif
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start4(bl,SC_CLOSECONFINE2,100,skill_lv,src->id,0,0,skill_get_time(skill_id,skill_lv)));
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
+ break;
+
+ case 0:/* no skill - basic/normal attack */
+ if(sd) {
+ if (flag & 3){
+ if (bl->id != skill_area_temp[1])
+ skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, SD_LEVEL|flag);
+ } else {
+ skill_area_temp[1] = bl->id;
+ map_foreachinrange(skill_area_sub, bl,
+ sd->bonus.splash_range, BL_CHAR,
+ src, skill_id, skill_lv, tick, flag | BCT_ENEMY | 1,
+ skill_castend_damage_id);
+ flag|=1; //Set flag to 1 so ammo is not double-consumed. [Skotlex]
+ }
+ }
+ break;
+
+ default:
+ ShowWarning("skill_castend_damage_id: Unknown skill used:%d\n",skill_id);
+ clif_skill_damage(src, bl, tick, status_get_amotion(src), tstatus->dmotion,
+ 0, abs(skill_get_num(skill_id, skill_lv)),
+ skill_id, skill_lv, skill_get_hit(skill_id));
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] ) //Should only remove after the skill has been casted.
+ status_change_end(src,SC_CURSEDCIRCLE_ATKER,INVALID_TIMER);
+
+ map_freeblock_unlock();
+
+ if( sd && !(flag&1) )
+ {// ensure that the skill last-cast tick is recorded
+ sd->canskill_tick = gettick();
+
+ if( sd->state.arrow_atk )
+ {// consume arrow on last invocation to this skill.
+ battle_consume_ammo(sd, skill_id, skill_lv);
+ }
+
+ // perform skill requirement consumption
+ skill_consume_requirement(sd,skill_id,skill_lv,2);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
+{
+ struct map_session_data *sd, *dstsd;
+ struct mob_data *md, *dstmd;
+ struct homun_data *hd;
+ struct mercenary_data *mer;
+ struct status_data *sstatus, *tstatus;
+ struct status_change *tsc;
+ struct status_change_entry *tsce;
+
+ int i = 0;
+ enum sc_type type;
+
+ if(skill_id > 0 && !skill_lv) return 0; // celest
+
+ nullpo_retr(1, src);
+ nullpo_retr(1, bl);
+
+ if (src->m != bl->m)
+ return 1;
+
+ sd = BL_CAST(BL_PC, src);
+ hd = BL_CAST(BL_HOM, src);
+ md = BL_CAST(BL_MOB, src);
+ mer = BL_CAST(BL_MER, src);
+
+ dstsd = BL_CAST(BL_PC, bl);
+ dstmd = BL_CAST(BL_MOB, bl);
+
+ if(bl->prev == NULL)
+ return 1;
+ if(status_isdead(src))
+ return 1;
+
+ if( src != bl && status_isdead(bl) ) {
+ /**
+ * Skills that may be cast on dead targets
+ **/
+ switch( skill_id ) {
+ case NPC_WIDESOULDRAIN:
+ case PR_REDEMPTIO:
+ case ALL_RESURRECTION:
+ case WM_DEADHILLHERE:
+ break;
+ default:
+ return 1;
+ }
+ }
+
+ tstatus = status_get_status_data(bl);
+ sstatus = status_get_status_data(src);
+
+ //Check for undead skills that convert a no-damage skill into a damage one. [Skotlex]
+ switch (skill_id) {
+ case HLIF_HEAL: //[orn]
+ if (bl->type != BL_HOM) {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0) ;
+ break ;
+ }
+ case AL_HEAL:
+ case ALL_RESURRECTION:
+ case PR_ASPERSIO:
+ /**
+ * Arch Bishop
+ **/
+ case AB_RENOVATIO:
+ case AB_HIGHNESSHEAL:
+ //Apparently only player casted skills can be offensive like this.
+ if (sd && battle_check_undead(tstatus->race,tstatus->def_ele)) {
+ if (battle_check_target(src, bl, BCT_ENEMY) < 1) {
+ //Offensive heal does not works on non-enemies. [Skotlex]
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ return skill_castend_damage_id (src, bl, skill_id, skill_lv, tick, flag);
+ }
+ break;
+ case NPC_SMOKING: //Since it is a self skill, this one ends here rather than in damage_id. [Skotlex]
+ return skill_castend_damage_id (src, bl, skill_id, skill_lv, tick, flag);
+ case MH_STEINWAND: {
+ struct block_list *s_src = battle_get_master(src);
+ short ret = 0;
+ if(!skill_check_unit_range(src, src->x, src->y, skill_id, skill_lv)) //prevent reiteration
+ ret = skill_castend_pos2(src,src->x,src->y,skill_id,skill_lv,tick,flag); //cast on homon
+ if(s_src && !skill_check_unit_range(s_src, s_src->x, s_src->y, skill_id, skill_lv))
+ ret |= skill_castend_pos2(s_src,s_src->x,s_src->y,skill_id,skill_lv,tick,flag); //cast on master
+ if (hd)
+ skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+ return ret;
+ }
+ break;
+ default:
+ //Skill is actually ground placed.
+ if (src == bl && skill_get_unit_id(skill_id,0))
+ return skill_castend_pos2(src,bl->x,bl->y,skill_id,skill_lv,tick,0);
+ }
+
+ type = status_skill2sc(skill_id);
+ tsc = status_get_sc(bl);
+ tsce = (tsc && type != -1)?tsc->data[type]:NULL;
+
+ if (src!=bl && type > -1 &&
+ (i = skill_get_ele(skill_id, skill_lv)) > ELE_NEUTRAL &&
+ skill_get_inf(skill_id) != INF_SUPPORT_SKILL &&
+ battle_attr_fix(NULL, NULL, 100, i, tstatus->def_ele, tstatus->ele_lv) <= 0)
+ return 1; //Skills that cause an status should be blocked if the target element blocks its element.
+
+ map_freeblock_lock();
+ switch(skill_id)
+ {
+ case HLIF_HEAL: //[orn]
+ case AL_HEAL:
+ /**
+ * Arch Bishop
+ **/
+ case AB_HIGHNESSHEAL:
+ {
+ int heal = skill_calc_heal(src, bl, (skill_id == AB_HIGHNESSHEAL)?AL_HEAL:skill_id, (skill_id == AB_HIGHNESSHEAL)?10:skill_lv, true);
+ int heal_get_jobexp;
+ //Highness Heal: starts at 1.5 boost + 0.5 for each level
+ if( skill_id == AB_HIGHNESSHEAL ) {
+ heal = heal * ( 15 + 5 * skill_lv ) / 10;
+ }
+ if( status_isimmune(bl) ||
+ (dstmd && (dstmd->class_ == MOBID_EMPERIUM || mob_is_battleground(dstmd))) ||
+ (dstsd && pc_ismadogear(dstsd)) )//Mado is immune to heal
+ heal=0;
+
+ if( sd && dstsd && sd->status.partner_id == dstsd->status.char_id && (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.sex == 0 )
+ heal = heal*2;
+
+ if( tsc && tsc->count )
+ {
+ if( tsc->data[SC_KAITE] && !(sstatus->mode&MD_BOSS) )
+ { //Bounce back heal
+ if (--tsc->data[SC_KAITE]->val2 <= 0)
+ status_change_end(bl, SC_KAITE, INVALID_TIMER);
+ if (src == bl)
+ heal=0; //When you try to heal yourself under Kaite, the heal is voided.
+ else {
+ bl = src;
+ dstsd = sd;
+ }
+ }
+ else if (tsc->data[SC_BERSERK] || tsc->data[SC_SATURDAYNIGHTFEVER] || tsc->data[SC__BLOODYLUST])
+ heal = 0; //Needed so that it actually displays 0 when healing.
+ }
+ clif_skill_nodamage (src, bl, skill_id, heal, 1);
+ if( tsc && tsc->data[SC_AKAITSUKI] && heal && skill_id != HLIF_HEAL )
+ heal = ~heal + 1;
+ heal_get_jobexp = status_heal(bl,heal,0,0);
+
+ if(sd && dstsd && heal > 0 && sd != dstsd && 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 (sd, bl, 0, heal_get_jobexp, false);
+ }
+ }
+ break;
+
+ case PR_REDEMPTIO:
+ if (sd && !(flag&1)) {
+ if (sd->status.party_id == 0) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ skill_area_temp[0] = 0;
+ party_foreachsamemap(skill_area_sub,
+ sd,skill_get_splash(skill_id, skill_lv),
+ src,skill_id,skill_lv,tick, flag|BCT_PARTY|1,
+ skill_castend_nodamage_id);
+ if (skill_area_temp[0] == 0) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ skill_area_temp[0] = 5 - skill_area_temp[0]; // The actual penalty...
+ if (skill_area_temp[0] > 0 && !map[src->m].flag.noexppenalty) { //Apply penalty
+ sd->status.base_exp -= min(sd->status.base_exp, pc_nextbaseexp(sd) * skill_area_temp[0] * 2/1000); //0.2% penalty per each.
+ sd->status.job_exp -= min(sd->status.job_exp, pc_nextjobexp(sd) * skill_area_temp[0] * 2/1000);
+ clif_updatestatus(sd,SP_BASEEXP);
+ clif_updatestatus(sd,SP_JOBEXP);
+ }
+ status_set_hp(src, 1, 0);
+ status_set_sp(src, 0, 0);
+ break;
+ } else if (status_isdead(bl) && flag&1) { //Revive
+ skill_area_temp[0]++; //Count it in, then fall-through to the Resurrection code.
+ skill_lv = 3; //Resurrection level 3 is used
+ } else //Invalid target, skip resurrection.
+ break;
+
+ case ALL_RESURRECTION:
+ if(sd && (map_flag_gvg(bl->m) || map[bl->m].flag.battleground))
+ { //No reviving in WoE grounds!
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if (!status_isdead(bl))
+ break;
+ {
+ int per = 0, sper = 0;
+ if (tsc && tsc->data[SC_HELLPOWER])
+ break;
+
+ if (map[bl->m].flag.pvp && dstsd && dstsd->pvp_point < 0)
+ break;
+
+ switch(skill_lv){
+ case 1: per=10; break;
+ case 2: per=30; break;
+ case 3: per=50; break;
+ case 4: per=80; break;
+ }
+ if(dstsd && dstsd->special_state.restart_full_recover)
+ per = sper = 100;
+ if (status_revive(bl, per, sper))
+ {
+ clif_skill_nodamage(src,bl,ALL_RESURRECTION,skill_lv,1); //Both Redemptio and Res show this skill-animation.
+ if(sd && dstsd && battle_config.resurrection_exp > 0)
+ {
+ int exp = 0,jexp = 0;
+ int lv = dstsd->status.base_level - sd->status.base_level, jlv = dstsd->status.job_level - sd->status.job_level;
+ if(lv > 0 && pc_nextbaseexp(dstsd)) {
+ exp = (int)((double)dstsd->status.base_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.);
+ if (exp < 1) exp = 1;
+ }
+ if(jlv > 0 && pc_nextjobexp(dstsd)) {
+ jexp = (int)((double)dstsd->status.job_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.);
+ if (jexp < 1) jexp = 1;
+ }
+ if(exp > 0 || jexp > 0)
+ pc_gainexp (sd, bl, exp, jexp, false);
+ }
+ }
+ }
+ break;
+
+ case AL_DECAGI:
+ case MER_DECAGI:
+ clif_skill_nodamage (src, bl, skill_id, skill_lv,
+ sc_start(bl, type, (40 + skill_lv * 2 + (status_get_lv(src) + sstatus->int_)/5), skill_lv, skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case AL_CRUCIS:
+ if (flag&1)
+ sc_start(bl,type, 23+skill_lv*4 +status_get_lv(src) -status_get_lv(bl), skill_lv,skill_get_time(skill_id,skill_lv));
+ else {
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv), BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ }
+ break;
+
+ case PR_LEXDIVINA:
+ case MER_LEXDIVINA:
+ if( tsce )
+ status_change_end(bl,type, INVALID_TIMER);
+ else
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ clif_skill_nodamage (src, bl, skill_id, skill_lv, 1);
+ break;
+
+ case SA_ABRACADABRA:
+ {
+ int abra_skill_id = 0, abra_skill_lv;
+ do {
+ i = rnd() % MAX_SKILL_ABRA_DB;
+ abra_skill_id = skill_abra_db[i].skill_id;
+ } while (abra_skill_id == 0 ||
+ skill_abra_db[i].req_lv > skill_lv || //Required lv for it to appear
+ rnd()%10000 >= skill_abra_db[i].per
+ );
+ abra_skill_lv = min(skill_lv, skill_get_max(abra_skill_id));
+ clif_skill_nodamage (src, bl, skill_id, skill_lv, 1);
+
+ if( sd )
+ {// player-casted
+ sd->state.abra_flag = 1;
+ sd->skillitem = abra_skill_id;
+ sd->skillitemlv = abra_skill_lv;
+ clif_item_skill(sd, abra_skill_id, abra_skill_lv);
+ }
+ else
+ {// mob-casted
+ struct unit_data *ud = unit_bl2ud(src);
+ int inf = skill_get_inf(abra_skill_id);
+ int target_id = 0;
+ if (!ud) break;
+ if (inf&INF_SELF_SKILL || inf&INF_SUPPORT_SKILL) {
+ if (src->type == BL_PET)
+ bl = (struct block_list*)((TBL_PET*)src)->msd;
+ if (!bl) bl = src;
+ unit_skilluse_id(src, bl->id, abra_skill_id, abra_skill_lv);
+ } else { //Assume offensive skills
+ if (ud->target)
+ target_id = ud->target;
+ else switch (src->type) {
+ case BL_MOB: target_id = ((TBL_MOB*)src)->target_id; break;
+ case BL_PET: target_id = ((TBL_PET*)src)->target_id; break;
+ }
+ if (!target_id)
+ break;
+ if (skill_get_casttype(abra_skill_id) == CAST_GROUND) {
+ bl = map_id2bl(target_id);
+ if (!bl) bl = src;
+ unit_skilluse_pos(src, bl->x, bl->y, abra_skill_id, abra_skill_lv);
+ } else
+ unit_skilluse_id(src, target_id, abra_skill_id, abra_skill_lv);
+ }
+ }
+ }
+ break;
+
+ case SA_COMA:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time2(skill_id,skill_lv)));
+ break;
+ case SA_FULLRECOVERY:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if (status_isimmune(bl))
+ break;
+ status_percent_heal(bl, 100, 100);
+ break;
+ case NPC_ALLHEAL:
+ {
+ int heal;
+ if( status_isimmune(bl) )
+ break;
+ heal = status_percent_heal(bl, 100, 0);
+ clif_skill_nodamage(NULL, bl, AL_HEAL, heal, 1);
+ if( dstmd )
+ { // Reset Damage Logs
+ memset(dstmd->dmglog, 0, sizeof(dstmd->dmglog));
+ dstmd->tdmg = 0;
+ }
+ }
+ break;
+ case SA_SUMMONMONSTER:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if (sd) mob_once_spawn(sd, src->m, src->x, src->y," --ja--", -1, 1, "", SZ_SMALL, AI_NONE);
+ break;
+ case SA_LEVELUP:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if (sd && pc_nextbaseexp(sd)) pc_gainexp(sd, NULL, pc_nextbaseexp(sd) * 10 / 100, 0, false);
+ break;
+ case SA_INSTANTDEATH:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ status_set_hp(bl,1,0);
+ break;
+ case SA_QUESTION:
+ case SA_GRAVITY:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case SA_CLASSCHANGE:
+ case SA_MONOCELL:
+ if (dstmd)
+ {
+ int class_;
+ if ( sd && dstmd->status.mode&MD_BOSS )
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ class_ = skill_id==SA_MONOCELL?1002:mob_get_random_id(4, 1, 0);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ mob_class_change(dstmd,class_);
+ if( tsc && dstmd->status.mode&MD_BOSS )
+ {
+ const enum sc_type scs[] = { SC_QUAGMIRE, SC_PROVOKE, SC_ROKISWEIL, SC_GRAVITATION, SC_SUITON, SC_STRIPWEAPON, SC_STRIPSHIELD, SC_STRIPARMOR, SC_STRIPHELM, SC_BLADESTOP };
+ for (i = SC_COMMON_MIN; i <= SC_COMMON_MAX; i++)
+ if (tsc->data[i]) status_change_end(bl, (sc_type)i, INVALID_TIMER);
+ for (i = 0; i < ARRAYLENGTH(scs); i++)
+ if (tsc->data[scs[i]]) status_change_end(bl, scs[i], INVALID_TIMER);
+ }
+ }
+ break;
+ case SA_DEATH:
+ if ( sd && dstmd && dstmd->status.mode&MD_BOSS )
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ status_kill(bl);
+ break;
+ case SA_REVERSEORCISH:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv)));
+ break;
+ case SA_FORTUNE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if(sd) pc_getzeny(sd,status_get_lv(bl)*100,LOG_TYPE_STEAL,NULL);
+ break;
+ case SA_TAMINGMONSTER:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if (sd && dstmd) {
+ ARR_FIND( 0, MAX_PET_DB, i, dstmd->class_ == pet_db[i].class_ );
+ if( i < MAX_PET_DB )
+ pet_catch_process1(sd, dstmd->class_);
+ }
+ break;
+
+ case CR_PROVIDENCE:
+ if(sd && dstsd){ //Check they are not another crusader [Skotlex]
+ if ((dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case CG_MARIONETTE:
+ {
+ struct status_change* sc = status_get_sc(src);
+
+ if( sd && dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER && dstsd->status.sex == sd->status.sex )
+ {// Cannot cast on another bard/dancer-type class of the same gender as caster
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ if( sc && tsc )
+ {
+ if( !sc->data[SC_MARIONETTE] && !tsc->data[SC_MARIONETTE2] )
+ {
+ sc_start(src,SC_MARIONETTE,100,bl->id,skill_get_time(skill_id,skill_lv));
+ sc_start(bl,SC_MARIONETTE2,100,src->id,skill_get_time(skill_id,skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ else
+ if( sc->data[SC_MARIONETTE ] && sc->data[SC_MARIONETTE ]->val1 == bl->id &&
+ tsc->data[SC_MARIONETTE2] && tsc->data[SC_MARIONETTE2]->val1 == src->id )
+ {
+ status_change_end(src, SC_MARIONETTE, INVALID_TIMER);
+ status_change_end(bl, SC_MARIONETTE2, INVALID_TIMER);
+ }
+ else
+ {
+ if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+
+ map_freeblock_unlock();
+ return 1;
+ }
+ }
+ }
+ break;
+
+ case RG_CLOSECONFINE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start4(bl,type,100,skill_lv,src->id,0,0,skill_get_time(skill_id,skill_lv)));
+ 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 (dstsd) {
+ if(dstsd->status.weapon == W_FIST ||
+ (dstsd->sc.count && !dstsd->sc.data[type] &&
+ ( //Allow re-enchanting to lenghten time. [Skotlex]
+ dstsd->sc.data[SC_FIREWEAPON] ||
+ dstsd->sc.data[SC_WATERWEAPON] ||
+ dstsd->sc.data[SC_WINDWEAPON] ||
+ dstsd->sc.data[SC_EARTHWEAPON] ||
+ dstsd->sc.data[SC_SHADOWWEAPON] ||
+ dstsd->sc.data[SC_GHOSTWEAPON] ||
+ dstsd->sc.data[SC_ENCPOISON]
+ ))
+ ) {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
+ break;
+ }
+ }
+ // 100% success rate at lv4 & 5, but lasts longer at lv5
+ if(!clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start(bl,type,(60+skill_lv*10),skill_lv, skill_get_time(skill_id,skill_lv)))) {
+ if (sd)
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ if (skill_break_equip(bl, EQP_WEAPON, 10000, BCT_PARTY) && sd && sd != dstsd)
+ clif_displaymessage(sd->fd, msg_txt(669));
+ }
+ break;
+
+ case PR_ASPERSIO:
+ if (sd && dstmd) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case ITEM_ENCHANTARMS:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(bl,type,100,skill_lv,
+ skill_get_ele(skill_id,skill_lv), skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case TK_SEVENWIND:
+ switch(skill_get_ele(skill_id,skill_lv)) {
+ case ELE_EARTH : type = SC_EARTHWEAPON; break;
+ case ELE_WIND : type = SC_WINDWEAPON; break;
+ case ELE_WATER : type = SC_WATERWEAPON; break;
+ case ELE_FIRE : type = SC_FIREWEAPON; break;
+ case ELE_GHOST : type = SC_GHOSTWEAPON; break;
+ case ELE_DARK : type = SC_SHADOWWEAPON; break;
+ case ELE_HOLY : type = SC_ASPERSIO; break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+
+ sc_start(bl,SC_SEVENWIND,100,skill_lv,skill_get_time(skill_id,skill_lv));
+
+ break;
+
+ case PR_KYRIE:
+ case MER_KYRIE:
+ clif_skill_nodamage(bl,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+ //Passive Magnum, should had been casted on yourself.
+ case SM_MAGNUM:
+ case MS_MAGNUM:
+ skill_area_temp[1] = 0;
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv), BL_SKILL|BL_CHAR,
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1, skill_castend_damage_id);
+ clif_skill_nodamage (src,src,skill_id,skill_lv,1);
+ // Initiate 10% of your damage becomes fire element.
+ sc_start4(src,SC_WATK_ELEMENT,100,3,20,0,0,skill_get_time2(skill_id, skill_lv));
+ if( sd )
+ skill_blockpc_start(sd, skill_id, skill_get_time(skill_id, skill_lv));
+ else if( bl->type == BL_MER )
+ skill_blockmerc_start((TBL_MER*)bl, skill_id, skill_get_time(skill_id, skill_lv));
+ break;
+
+ case TK_JUMPKICK:
+ /* Check if the target is an enemy; if not, skill should fail so the character doesn't unit_movepos (exploitable) */
+ if( battle_check_target(src, bl, BCT_ENEMY) > 0 )
+ {
+ if( unit_movepos(src, bl->x, bl->y, 1, 1) )
+ {
+ skill_attack(BF_WEAPON,src,src,bl,skill_id,skill_lv,tick,flag);
+ clif_slide(src,bl->x,bl->y);
+ }
+ }
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL,0);
+ break;
+
+ case AL_INCAGI:
+ case AL_BLESSING:
+ case MER_INCAGI:
+ case MER_BLESSING:
+ if (dstsd != NULL && tsc->data[SC_CHANGEUNDEAD]) {
+ skill_attack(BF_MISC,src,src,bl,skill_id,skill_lv,tick,flag);
+ break;
+ }
+ case PR_SLOWPOISON:
+ case PR_IMPOSITIO:
+ case PR_LEXAETERNA:
+ case PR_SUFFRAGIUM:
+ case PR_BENEDICTIO:
+ case LK_BERSERK:
+ case MS_BERSERK:
+ case KN_AUTOCOUNTER:
+ case KN_TWOHANDQUICKEN:
+ case KN_ONEHAND:
+ case MER_QUICKEN:
+ case CR_SPEARQUICKEN:
+ case CR_REFLECTSHIELD:
+ case MS_REFLECTSHIELD:
+ case AS_POISONREACT:
+ case MC_LOUD:
+ case MG_ENERGYCOAT:
+ case MO_EXPLOSIONSPIRITS:
+ case MO_STEELBODY:
+ case MO_BLADESTOP:
+ case LK_AURABLADE:
+ case LK_PARRYING:
+ case MS_PARRYING:
+ case LK_CONCENTRATION:
+ case WS_CARTBOOST:
+ case SN_SIGHT:
+ case WS_MELTDOWN:
+ case WS_OVERTHRUSTMAX:
+ case ST_REJECTSWORD:
+ case HW_MAGICPOWER:
+ case PF_MEMORIZE:
+ case PA_SACRIFICE:
+ case ASC_EDP:
+ case PF_DOUBLECASTING:
+ case SG_SUN_COMFORT:
+ case SG_MOON_COMFORT:
+ case SG_STAR_COMFORT:
+ case NPC_HALLUCINATION:
+ case GS_MADNESSCANCEL:
+ case GS_ADJUSTMENT:
+ case GS_INCREASING:
+ case NJ_KASUMIKIRI:
+ case NJ_UTSUSEMI:
+ case NJ_NEN:
+ case NPC_DEFENDER:
+ case NPC_MAGICMIRROR:
+ case ST_PRESERVE:
+ case NPC_INVINCIBLE:
+ case NPC_INVINCIBLEOFF:
+ case RK_DEATHBOUND:
+ case AB_RENOVATIO:
+ case AB_EXPIATIO:
+ case AB_DUPLELIGHT:
+ case AB_SECRAMENT:
+ case NC_ACCELERATION:
+ case NC_HOVERING:
+ case NC_SHAPESHIFT:
+ case WL_RECOGNIZEDSPELL:
+ case GC_VENOMIMPRESS:
+ case SC_DEADLYINFECT:
+ case LG_EXEEDBREAK:
+ case LG_PRESTIGE:
+ case SR_CRESCENTELBOW:
+ case SR_LIGHTNINGWALK:
+ case SR_GENTLETOUCH_ENERGYGAIN:
+ case GN_CARTBOOST:
+ case KO_MEIKYOUSISUI:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case SO_STRIKING:
+ if (sd) {
+ int bonus = 25 + 10 * skill_lv;
+ bonus += (pc_checkskill(sd, SA_FLAMELAUNCHER)+pc_checkskill(sd, SA_FROSTWEAPON)+pc_checkskill(sd, SA_LIGHTNINGLOADER)+pc_checkskill(sd, SA_SEISMICWEAPON))*5;
+ clif_skill_nodamage( src, bl, skill_id, skill_lv,
+ battle_check_target(src,bl,BCT_PARTY) ?
+ sc_start2(bl, type, 100, skill_lv, bonus, skill_get_time(skill_id,skill_lv)) :
+ 0
+ );
+ }
+ break;
+
+ case NPC_STOP:
+ if( clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(bl,type,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv)) ) )
+ sc_start2(src,type,100,skill_lv,bl->id,skill_get_time(skill_id,skill_lv));
+ break;
+ case HP_ASSUMPTIO:
+ if( sd && dstmd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ else
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+ case MG_SIGHT:
+ case MER_SIGHT:
+ case AL_RUWACH:
+ case WZ_SIGHTBLASTER:
+ case NPC_WIDESIGHT:
+ case NPC_STONESKIN:
+ case NPC_ANTIMAGIC:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(bl,type,100,skill_lv,skill_id,skill_get_time(skill_id,skill_lv)));
+ break;
+ case HLIF_AVOID:
+ case HAMI_DEFENCE:
+ i = skill_get_time(skill_id,skill_lv);
+ clif_skill_nodamage(bl,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,i)); // Master
+ clif_skill_nodamage(src,src,skill_id,skill_lv,sc_start(src,type,100,skill_lv,i)); // Homunc
+ break;
+ case NJ_BUNSINJYUTSU:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ status_change_end(bl, SC_NEN, INVALID_TIMER);
+ break;
+/* Was modified to only affect targetted char. [Skotlex]
+ case HP_ASSUMPTIO:
+ if (flag&1)
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ else
+ {
+ map_foreachinrange(skill_area_sub, bl,
+ skill_get_splash(skill_id, skill_lv), BL_PC,
+ src, skill_id, skill_lv, tick, flag|BCT_ALL|1,
+ skill_castend_nodamage_id);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+*/
+ case SM_ENDURE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ if (sd)
+ skill_blockpc_start (sd, skill_id, skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case AS_ENCHANTPOISON: // Prevent spamming [Valaris]
+ if (sd && dstsd && dstsd->sc.count) {
+ if (dstsd->sc.data[SC_FIREWEAPON] ||
+ dstsd->sc.data[SC_WATERWEAPON] ||
+ dstsd->sc.data[SC_WINDWEAPON] ||
+ dstsd->sc.data[SC_EARTHWEAPON] ||
+ dstsd->sc.data[SC_SHADOWWEAPON] ||
+ dstsd->sc.data[SC_GHOSTWEAPON]
+ // dstsd->sc.data[SC_ENCPOISON] //People say you should be able to recast to lengthen the timer. [Skotlex]
+ ) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case LK_TENSIONRELAX:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start4(bl,type,100,skill_lv,0,0,skill_get_time2(skill_id,skill_lv),
+ skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case MC_CHANGECART:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+
+ case TK_MISSION:
+ if (sd) {
+ int id;
+ if (sd->mission_mobid && (sd->mission_count || rnd()%100)) { //Cannot change target when already have one
+ clif_mission_info(sd, sd->mission_mobid, sd->mission_count);
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ id = mob_get_random_id(0,0xF, sd->status.base_level);
+ if (!id) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ sd->mission_mobid = id;
+ sd->mission_count = 0;
+ pc_setglobalreg(sd,"TK_MISSION_ID", id);
+ clif_mission_info(sd, id, 0);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case AC_CONCENTRATION:
+ {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ map_foreachinrange( status_change_timer_sub, src,
+ skill_get_splash(skill_id, skill_lv), BL_CHAR,
+ src,NULL,type,tick);
+ }
+ break;
+
+ case SM_PROVOKE:
+ case SM_SELFPROVOKE:
+ case MER_PROVOKE:
+ if( (tstatus->mode&MD_BOSS) || battle_check_undead(tstatus->race,tstatus->def_ele) )
+ {
+ map_freeblock_unlock();
+ return 1;
+ }
+ //TODO: How much does base level affects? Dummy value of 1% per level difference used. [Skotlex]
+ clif_skill_nodamage(src,bl,skill_id == SM_SELFPROVOKE ? SM_PROVOKE : skill_id,skill_lv,
+ (i = sc_start(bl,type, skill_id == SM_SELFPROVOKE ? 100:( 50 + 3*skill_lv + status_get_lv(src) - status_get_lv(bl)), skill_lv, skill_get_time(skill_id,skill_lv))));
+ if( !i )
+ {
+ if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ unit_skillcastcancel(bl, 2);
+
+ if( tsc && tsc->count )
+ {
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ if( tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE )
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ status_change_end(bl, SC_SLEEP, INVALID_TIMER);
+ status_change_end(bl, SC_TRICKDEAD, INVALID_TIMER);
+ }
+
+ if( dstmd )
+ {
+ dstmd->state.provoke_flag = src->id;
+ mob_target(dstmd, src, skill_get_range2(src,skill_id,skill_lv));
+ }
+ break;
+
+ case ML_DEVOTION:
+ case CR_DEVOTION:
+ {
+ int count, lv;
+ if( !dstsd || (!sd && !mer) )
+ { // Only players can be devoted
+ if( sd )
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ break;
+ }
+
+ if( (lv = status_get_lv(src) - dstsd->status.base_level) < 0 )
+ lv = -lv;
+ if( lv > battle_config.devotion_level_difference || // Level difference requeriments
+ (dstsd->sc.data[type] && dstsd->sc.data[type]->val1 != src->id) || // Cannot Devote a player devoted from another source
+ (skill_id == ML_DEVOTION && (!mer || mer != dstsd->md)) || // Mercenary only can devote owner
+ (dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER || // Crusader Cannot be devoted
+ (dstsd->sc.data[SC_HELLPOWER])) // Players affected by SC_HELLPOWERR cannot be devoted.
+ {
+ if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ i = 0;
+ count = (sd)? min(skill_lv,5) : 1; // Mercenary only can Devote owner
+ if( sd )
+ { // Player Devoting Player
+ ARR_FIND(0, count, i, sd->devotion[i] == bl->id );
+ if( i == count )
+ {
+ ARR_FIND(0, count, i, sd->devotion[i] == 0 );
+ if( i == count )
+ { // No free slots, skill Fail
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ }
+
+ sd->devotion[i] = bl->id;
+ }
+ else
+ mer->devotion_flag = 1; // Mercenary Devoting Owner
+
+ clif_skill_nodamage(src, bl, skill_id, skill_lv,
+ sc_start4(bl, type, 100, src->id, i, skill_get_range2(src,skill_id,skill_lv),0, skill_get_time2(skill_id, skill_lv)));
+ clif_devotion(src, NULL);
+ }
+ break;
+
+ case MO_CALLSPIRITS:
+ if(sd) {
+ int limit = skill_lv;
+ if( sd->sc.data[SC_RAISINGDRAGON] )
+ limit += sd->sc.data[SC_RAISINGDRAGON]->val1;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ pc_addspiritball(sd,skill_get_time(skill_id,skill_lv),limit);
+ }
+ break;
+
+ case CH_SOULCOLLECT:
+ if(sd) {
+ int limit = 5;
+ if( sd->sc.data[SC_RAISINGDRAGON] )
+ limit += sd->sc.data[SC_RAISINGDRAGON]->val1;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ for (i = 0; i < limit; i++)
+ pc_addspiritball(sd,skill_get_time(skill_id,skill_lv),limit);
+ }
+ break;
+
+ case MO_KITRANSLATION:
+ if(dstsd && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER) {
+ pc_addspiritball(dstsd,skill_get_time(skill_id,skill_lv),5);
+ }
+ break;
+
+ case TK_TURNKICK:
+ case MO_BALKYOUNG: //Passive part of the attack. Splash knock-back+stun. [Skotlex]
+ if (skill_area_temp[1] != bl->id) {
+ skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),-1,0);
+ skill_additional_effect(src,bl,skill_id,skill_lv,BF_MISC,ATK_DEF,tick); //Use Misc rather than weapon to signal passive pushback
+ }
+ break;
+
+ case MO_ABSORBSPIRITS:
+ i = 0;
+ if (dstsd && dstsd->spiritball && (sd == dstsd || map_flag_vs(src->m)) && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER)
+ { // split the if for readability, and included gunslingers in the check so that their coins cannot be removed [Reddozen]
+ i = dstsd->spiritball * 7;
+ pc_delspiritball(dstsd,dstsd->spiritball,0);
+ } else if (dstmd && !(tstatus->mode&MD_BOSS) && rnd() % 100 < 20)
+ { // check if target is a monster and not a Boss, for the 20% chance to absorb 2 SP per monster's level [Reddozen]
+ i = 2 * dstmd->level;
+ mob_target(dstmd,src,0);
+ }
+ if (i) status_heal(src, 0, i, 3);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,i?1:0);
+ break;
+
+ case AC_MAKINGARROW:
+ if(sd) {
+ clif_arrow_create_list(sd);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case AM_PHARMACY:
+ if(sd) {
+ clif_skill_produce_mix_list(sd,skill_id,22);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case SA_CREATECON:
+ if(sd) {
+ clif_elementalconverter_list(sd);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case BS_HAMMERFALL:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,SC_STUN,(20 + 10 * skill_lv),skill_lv,skill_get_time2(skill_id,skill_lv)));
+ break;
+ case RG_RAID:
+ skill_area_temp[1] = 0;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub, bl,
+ skill_get_splash(skill_id, skill_lv), splash_target(src),
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ status_change_end(src, SC_HIDING, INVALID_TIMER);
+ break;
+
+ case ASC_METEORASSAULT:
+ case GS_SPREADATTACK:
+ case RK_STORMBLAST:
+ case NC_AXETORNADO:
+ case GC_COUNTERSLASH:
+ case SR_SKYNETBLOW:
+ case SR_RAMPAGEBLASTER:
+ case SR_HOWLINGOFLION:
+ case KO_HAPPOKUNAI:
+ skill_area_temp[1] = 0;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ i = map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src),
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_damage_id);
+ if( !i && ( skill_id == NC_AXETORNADO || skill_id == SR_SKYNETBLOW || skill_id == KO_HAPPOKUNAI ) )
+ clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ break;
+
+ case NC_EMERGENCYCOOL:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ status_change_end(src,SC_OVERHEAT_LIMITPOINT,INVALID_TIMER);
+ status_change_end(src,SC_OVERHEAT,INVALID_TIMER);
+ break;
+ case SR_WINDMILL:
+ case GN_CART_TORNADO:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ case SR_EARTHSHAKER:
+ case NC_INFRAREDSCAN:
+ case NPC_EARTHQUAKE:
+ case NPC_VAMPIRE_GIFT:
+ case NPC_HELLJUDGEMENT:
+ case NPC_PULSESTRIKE:
+ case LG_MOONSLASHER:
+ skill_castend_damage_id(src, src, skill_id, skill_lv, tick, flag);
+ break;
+
+ case KN_BRANDISHSPEAR:
+ case ML_BRANDISH:
+ skill_brandishspear(src, bl, skill_id, skill_lv, tick, flag);
+ break;
+
+ case WZ_SIGHTRASHER:
+ //Passive side of the attack.
+ status_change_end(src, SC_SIGHT, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub,src,
+ skill_get_splash(skill_id, skill_lv),BL_CHAR|BL_SKILL,
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ break;
+
+ case NJ_HYOUSYOURAKU:
+ case NJ_RAIGEKISAI:
+ case WZ_FROSTNOVA:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_area_temp[1] = 0;
+ map_foreachinrange(skill_attack_area, src,
+ skill_get_splash(skill_id, skill_lv), splash_target(src),
+ BF_MAGIC, src, src, skill_id, skill_lv, tick, flag, BCT_ENEMY);
+ break;
+
+ case HVAN_EXPLOSION: //[orn]
+ case NPC_SELFDESTRUCTION:
+ //Self Destruction hits everyone in range (allies+enemies)
+ //Except for Summoned Marine spheres on non-versus maps, where it's just enemy.
+ i = ((!md || md->special_state.ai == 2) && !map_flag_vs(src->m))?
+ BCT_ENEMY:BCT_ALL;
+ clif_skill_nodamage(src, src, skill_id, -1, 1);
+ map_delblock(src); //Required to prevent chain-self-destructions hitting back.
+ map_foreachinrange(skill_area_sub, bl,
+ skill_get_splash(skill_id, skill_lv), splash_target(src),
+ src, skill_id, skill_lv, tick, flag|i,
+ skill_castend_damage_id);
+ map_addblock(src);
+ status_damage(src, src, sstatus->max_hp,0,0,1);
+ break;
+
+ case AL_ANGELUS:
+ case PR_MAGNIFICAT:
+ case PR_GLORIA:
+ case SN_WINDWALK:
+ case CASH_BLESSING:
+ case CASH_INCAGI:
+ case CASH_ASSUMPTIO:
+ if( sd == NULL || sd->status.party_id == 0 || (flag & 1) )
+ clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ else if( sd )
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ break;
+ case MER_MAGNIFICAT:
+ if( mer != NULL )
+ {
+ clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ if( mer->master && mer->master->status.party_id != 0 && !(flag&1) )
+ party_foreachsamemap(skill_area_sub, mer->master, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ else if( mer->master && !(flag&1) )
+ clif_skill_nodamage(src, &mer->master->bl, skill_id, skill_lv, sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ }
+ break;
+
+ case BS_ADRENALINE:
+ case BS_ADRENALINE2:
+ case BS_WEAPONPERFECT:
+ case BS_OVERTHRUST:
+ if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) {
+ clif_skill_nodamage(bl,bl,skill_id,skill_lv,
+ sc_start2(bl,type,100,skill_lv,(src == bl)? 1:0,skill_get_time(skill_id,skill_lv)));
+ } else if (sd) {
+ party_foreachsamemap(skill_area_sub,
+ sd,skill_get_splash(skill_id, skill_lv),
+ src,skill_id,skill_lv,tick, flag|BCT_PARTY|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+
+ case BS_MAXIMIZE:
+ case NV_TRICKDEAD:
+ case CR_DEFENDER:
+ case ML_DEFENDER:
+ case CR_AUTOGUARD:
+ case ML_AUTOGUARD:
+ case TK_READYSTORM:
+ case TK_READYDOWN:
+ case TK_READYTURN:
+ case TK_READYCOUNTER:
+ case TK_DODGE:
+ case CR_SHRINK:
+ case SG_FUSION:
+ case GS_GATLINGFEVER:
+ if( tsce )
+ {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,status_change_end(bl, type, INVALID_TIMER));
+ map_freeblock_unlock();
+ return 0;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+ case SL_KAITE:
+ case SL_KAAHI:
+ case SL_KAIZEL:
+ case SL_KAUPE:
+ if (sd) {
+ if (!dstsd || !(
+ (sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_SOULLINKER) ||
+ (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER ||
+ dstsd->status.char_id == sd->status.char_id ||
+ dstsd->status.char_id == sd->status.partner_id ||
+ dstsd->status.char_id == sd->status.child
+ )) {
+ status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,500,8);
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv)));
+ break;
+ case SM_AUTOBERSERK:
+ case MER_AUTOBERSERK:
+ if( tsce )
+ i = status_change_end(bl, type, INVALID_TIMER);
+ else
+ i = sc_start(bl,type,100,skill_lv,60000);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,i);
+ break;
+ case TF_HIDING:
+ case ST_CHASEWALK:
+ case KO_YAMIKUMO:
+ if (tsce)
+ {
+ clif_skill_nodamage(src,bl,skill_id,-1,status_change_end(bl, type, INVALID_TIMER)); //Hide skill-scream animation.
+ map_freeblock_unlock();
+ return 0;
+ } else if( tsc && tsc->option&OPTION_MADOGEAR ) {
+ //Mado Gear cannot hide
+ if( sd ) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ clif_skill_nodamage(src,bl,skill_id,-1,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+ case TK_RUN:
+ if (tsce)
+ {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,status_change_end(bl, type, INVALID_TIMER));
+ map_freeblock_unlock();
+ return 0;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start4(bl,type,100,skill_lv,unit_getdir(bl),0,0,0));
+ if (sd) // If the client receives a skill-use packet inmediately before a walkok packet, it will discard the walk packet! [Skotlex]
+ clif_walkok(sd); // So aegis has to resend the walk ok.
+ break;
+ case AS_CLOAKING:
+ case GC_CLOAKINGEXCEED:
+ case LG_FORCEOFVANGUARD:
+ case SC_REPRODUCE:
+ case SC_INVISIBILITY:
+ if (tsce) {
+ i = status_change_end(bl, type, INVALID_TIMER);
+ if( i )
+ clif_skill_nodamage(src,bl,skill_id,( skill_id == LG_FORCEOFVANGUARD ) ? skill_lv : -1,i);
+ else if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ case RA_CAMOUFLAGE:
+ i = sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ if( i )
+ clif_skill_nodamage(src,bl,skill_id,( skill_id == LG_FORCEOFVANGUARD ) ? skill_lv : -1,i);
+ else if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+
+ case BD_ADAPTATION:
+ if(tsc && tsc->data[SC_DANCING]){
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ status_change_end(bl, SC_DANCING, INVALID_TIMER);
+ }
+ break;
+
+ case BA_FROSTJOKER:
+ case DC_SCREAM:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_addtimerskill(src,tick+2000,bl->id,src->x,src->y,skill_id,skill_lv,0,flag);
+
+ if (md) {
+ // custom hack to make the mob display the skill, because these skills don't show the skill use text themselves
+ //NOTE: mobs don't have the sprite animation that is used when performing this skill (will cause glitches)
+ char temp[70];
+ snprintf(temp, sizeof(temp), "%s : %s !!",md->name,skill_db[skill_id].desc);
+ clif_message(&md->bl,temp);
+ }
+ break;
+
+ case BA_PANGVOICE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start(bl,SC_CONFUSION,50,7,skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case DC_WINKCHARM:
+ if( dstsd )
+ clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start(bl,SC_CONFUSION,30,7,skill_get_time2(skill_id,skill_lv)));
+ else
+ if( dstmd )
+ {
+ if( status_get_lv(src) > status_get_lv(bl)
+ && (tstatus->race == RC_DEMON || tstatus->race == RC_DEMIHUMAN || tstatus->race == RC_ANGEL)
+ && !(tstatus->mode&MD_BOSS) )
+ clif_skill_nodamage(src,bl,skill_id,skill_lv, sc_start2(bl,type,70,skill_lv,src->id,skill_get_time(skill_id,skill_lv)));
+ else
+ {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
+ if(sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ }
+ break;
+
+ case TF_STEAL:
+ if(sd) {
+ if(pc_steal_item(sd,bl,skill_lv))
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL,0);
+ }
+ break;
+
+ case RG_STEALCOIN:
+ if(sd) {
+ if(pc_steal_coin(sd,bl))
+ {
+ dstmd->state.provoke_flag = src->id;
+ mob_target(dstmd, src, skill_get_range2(src,skill_id,skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+
+ }
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+
+ case MG_STONECURSE:
+ {
+ int brate = 0;
+ if (tstatus->mode&MD_BOSS) {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if(status_isimmune(bl) || !tsc)
+ break;
+
+ if (sd && sd->sc.data[SC_PETROLOGY_OPTION])
+ brate = sd->sc.data[SC_PETROLOGY_OPTION]->val3;
+
+ if (tsc->data[SC_STONE]) {
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if (sc_start4(bl,SC_STONE,(skill_lv*4+20)+brate,
+ skill_lv, 0, 0, skill_get_time(skill_id, skill_lv),
+ skill_get_time2(skill_id,skill_lv)))
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ else if(sd) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ // Level 6-10 doesn't consume a red gem if it fails [celest]
+ if (skill_lv > 5)
+ { // not to consume items
+ map_freeblock_unlock();
+ return 0;
+ }
+ }
+ }
+ break;
+
+ case NV_FIRSTAID:
+ clif_skill_nodamage(src,bl,skill_id,5,1);
+ status_heal(bl,5,0,0);
+ break;
+
+ case AL_CURE:
+ if(status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
+ break;
+ }
+ status_change_end(bl, SC_SILENCE, INVALID_TIMER);
+ status_change_end(bl, SC_BLIND, INVALID_TIMER);
+ status_change_end(bl, SC_CONFUSION, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+
+ case TF_DETOXIFY:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ status_change_end(bl, SC_POISON, INVALID_TIMER);
+ status_change_end(bl, SC_DPOISON, INVALID_TIMER);
+ break;
+
+ case PR_STRECOVERY:
+ if(status_isimmune(bl)) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
+ break;
+ }
+ if (tsc && tsc->opt1) {
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ status_change_end(bl, SC_SLEEP, INVALID_TIMER);
+ status_change_end(bl, SC_STUN, INVALID_TIMER);
+ status_change_end(bl, SC_WHITEIMPRISON, INVALID_TIMER);
+ }
+ //Is this equation really right? It looks so... special.
+ if(battle_check_undead(tstatus->race,tstatus->def_ele))
+ {
+ status_change_start(bl, SC_BLIND,
+ 100*(100-(tstatus->int_/2+tstatus->vit/3+tstatus->luk/10)),
+ 1,0,0,0,
+ skill_get_time2(skill_id, skill_lv) * (100-(tstatus->int_+tstatus->vit)/2)/100,0);
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if(dstmd)
+ mob_unlocktarget(dstmd,tick);
+ break;
+
+ // Mercenary Supportive Skills
+ case MER_BENEDICTION:
+ status_change_end(bl, SC_CURSE, INVALID_TIMER);
+ status_change_end(bl, SC_BLIND, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case MER_COMPRESS:
+ status_change_end(bl, SC_BLEEDING, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case MER_MENTALCURE:
+ status_change_end(bl, SC_CONFUSION, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case MER_RECUPERATE:
+ status_change_end(bl, SC_POISON, INVALID_TIMER);
+ status_change_end(bl, SC_SILENCE, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case MER_REGAIN:
+ status_change_end(bl, SC_SLEEP, INVALID_TIMER);
+ status_change_end(bl, SC_STUN, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case MER_TENDER:
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+
+ case MER_SCAPEGOAT:
+ if( mer && mer->master )
+ {
+ status_heal(&mer->master->bl, mer->battle_status.hp, 0, 2);
+ status_damage(src, src, mer->battle_status.max_hp, 0, 0, 1);
+ }
+ break;
+
+ case MER_ESTIMATION:
+ if( !mer )
+ break;
+ sd = mer->master;
+ case WZ_ESTIMATION:
+ if( sd == NULL )
+ break;
+ if( dstsd )
+ { // Fail on Players
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if( dstmd && dstmd->class_ == MOBID_EMPERIUM )
+ break; // Cannot be Used on Emperium
+
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ clif_skill_estimation(sd, bl);
+ if( skill_id == MER_ESTIMATION )
+ sd = NULL;
+ break;
+
+ case BS_REPAIRWEAPON:
+ if(sd && dstsd)
+ clif_item_repair_list(sd,dstsd,skill_lv);
+ break;
+
+ case MC_IDENTIFY:
+ if(sd)
+ clif_item_identify_list(sd);
+ break;
+
+ // Weapon Refining [Celest]
+ case WS_WEAPONREFINE:
+ if(sd)
+ clif_item_refine_list(sd);
+ break;
+
+ case MC_VENDING:
+ if(sd)
+ { //Prevent vending of GMs with unnecessary Level to trade/drop. [Skotlex]
+ if ( !pc_can_give_items(sd) )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ else {
+ sd->state.prevend = 1;
+ clif_openvendingreq(sd,2+skill_lv);
+ }
+ }
+ break;
+
+ case AL_TELEPORT:
+ if(sd)
+ {
+ if (map[bl->m].flag.noteleport && skill_lv <= 2) {
+ clif_skill_teleportmessage(sd,0);
+ break;
+ }
+ if(!battle_config.duel_allow_teleport && sd->duel_group && skill_lv <= 2) { // duel restriction [LuzZza]
+ char output[128]; sprintf(output, msg_txt(365), skill_get_name(AL_TELEPORT));
+ clif_displaymessage(sd->fd, output); //"Duel: Can't use %s in duel."
+ break;
+ }
+
+ if( sd->state.autocast || ( (sd->skillitem == AL_TELEPORT || battle_config.skip_teleport_lv1_menu) && skill_lv == 1 ) || skill_lv == 3 )
+ {
+ if( skill_lv == 1 )
+ pc_randomwarp(sd,CLR_TELEPORT);
+ else
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
+ break;
+ }
+
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if( skill_lv == 1 )
+ clif_skill_warppoint(sd,skill_id,skill_lv, (unsigned short)-1,0,0,0);
+ else
+ clif_skill_warppoint(sd,skill_id,skill_lv, (unsigned short)-1,sd->status.save_point.map,0,0);
+ } else
+ unit_warp(bl,-1,-1,-1,CLR_TELEPORT);
+ break;
+
+ case NPC_EXPULSION:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ unit_warp(bl,-1,-1,-1,CLR_TELEPORT);
+ break;
+
+ case AL_HOLYWATER:
+ if(sd) {
+ if (skill_produce_mix(sd, skill_id, 523, 0, 0, 0, 1))
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+
+ case TF_PICKSTONE:
+ if(sd) {
+ int eflag;
+ struct item item_tmp;
+ struct block_list tbl;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ memset(&item_tmp,0,sizeof(item_tmp));
+ memset(&tbl,0,sizeof(tbl)); // [MouseJstr]
+ item_tmp.nameid = ITEMID_STONE;
+ item_tmp.identify = 1;
+ tbl.id = 0;
+ clif_takeitem(&sd->bl,&tbl);
+ eflag = pc_additem(sd,&item_tmp,1,LOG_TYPE_PRODUCE);
+ if(eflag) {
+ clif_additem(sd,0,0,eflag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ break;
+ case ASC_CDP:
+ if(sd) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_produce_mix(sd, skill_id, 678, 0, 0, 0, 1); //Produce a Deadly Poison Bottle.
+ }
+ break;
+
+ case RG_STRIPWEAPON:
+ case RG_STRIPSHIELD:
+ case RG_STRIPARMOR:
+ case RG_STRIPHELM:
+ case ST_FULLSTRIP:
+ case GC_WEAPONCRUSH:
+ case SC_STRIPACCESSARY: {
+ unsigned short location = 0;
+ int d = 0;
+
+ //Rate in percent
+ if ( skill_id == ST_FULLSTRIP ) {
+ i = 5 + 2*skill_lv + (sstatus->dex - tstatus->dex)/5;
+ } else if( skill_id == SC_STRIPACCESSARY ) {
+ i = 12 + 2 * skill_lv + (sstatus->dex - tstatus->dex)/5;
+ } else {
+ i = 5 + 5*skill_lv + (sstatus->dex - tstatus->dex)/5;
+ }
+
+ if (i < 5) i = 5; //Minimum rate 5%
+
+ //Duration in ms
+ if( skill_id == GC_WEAPONCRUSH){
+ d = skill_get_time(skill_id,skill_lv);
+ if(bl->type == BL_PC)
+ d += skill_lv * 15 + (sstatus->dex - tstatus->dex);
+ else
+ d += skill_lv * 30 + (sstatus->dex - tstatus->dex) / 2;
+ }else
+ d = skill_get_time(skill_id,skill_lv) + (sstatus->dex - tstatus->dex)*500;
+
+ if (d < 0) d = 0; //Minimum duration 0ms
+
+ switch (skill_id) {
+ case RG_STRIPWEAPON:
+ case GC_WEAPONCRUSH:
+ location = EQP_WEAPON;
+ break;
+ case RG_STRIPSHIELD:
+ location = EQP_SHIELD;
+ break;
+ case RG_STRIPARMOR:
+ location = EQP_ARMOR;
+ break;
+ case RG_STRIPHELM:
+ location = EQP_HELM;
+ break;
+ case ST_FULLSTRIP:
+ location = EQP_WEAPON|EQP_SHIELD|EQP_ARMOR|EQP_HELM;
+ break;
+ case SC_STRIPACCESSARY:
+ location = EQP_ACC;
+ break;
+ }
+
+ //Special message when trying to use strip on FCP [Jobbie]
+ if( sd && skill_id == ST_FULLSTRIP && tsc && tsc->data[SC_CP_WEAPON] && tsc->data[SC_CP_HELM] && tsc->data[SC_CP_ARMOR] && tsc->data[SC_CP_SHIELD])
+ {
+ clif_gospel_info(sd, 0x28);
+ break;
+ }
+
+ //Attempts to strip at rate i and duration d
+ if( (i = skill_strip_equip(bl, location, i, skill_lv, d)) || (skill_id != ST_FULLSTRIP && skill_id != GC_WEAPONCRUSH ) )
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,i);
+
+ //Nothing stripped.
+ if( sd && !i )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+
+ case AM_BERSERKPITCHER:
+ case AM_POTIONPITCHER: {
+ int i,x,hp = 0,sp = 0,bonus=100;
+ if( dstmd && dstmd->class_ == MOBID_EMPERIUM ) {
+ map_freeblock_unlock();
+ return 1;
+ }
+ if( sd ) {
+ x = skill_lv%11 - 1;
+ i = pc_search_inventory(sd,skill_db[skill_id].itemid[x]);
+ if( i < 0 || skill_db[skill_id].itemid[x] <= 0 ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ if(sd->inventory_data[i] == NULL || sd->status.inventory[i].amount < skill_db[skill_id].amount[x]) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ if( skill_id == AM_BERSERKPITCHER ) {
+ if( dstsd && dstsd->status.base_level < (unsigned int)sd->inventory_data[i]->elv ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ }
+ potion_flag = 1;
+ potion_hp = potion_sp = potion_per_hp = potion_per_sp = 0;
+ potion_target = bl->id;
+ run_script(sd->inventory_data[i]->script,0,sd->bl.id,0);
+ potion_flag = potion_target = 0;
+ if( sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_ALCHEMIST )
+ bonus += sd->status.base_level;
+ if( potion_per_hp > 0 || potion_per_sp > 0 ) {
+ hp = tstatus->max_hp * potion_per_hp / 100;
+ hp = hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ if( dstsd ) {
+ sp = dstsd->status.max_sp * potion_per_sp / 100;
+ sp = sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ }
+ } else {
+ if( potion_hp > 0 ) {
+ hp = potion_hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ hp = hp * (100 + (tstatus->vit<<1)) / 100;
+ if( dstsd )
+ hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100;
+ }
+ if( potion_sp > 0 ) {
+ sp = potion_sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000;
+ sp = sp * (100 + (tstatus->int_<<1)) / 100;
+ if( dstsd )
+ sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10) / 100;
+ }
+ }
+
+ if (sd->itemgrouphealrate[IG_POTION]>0) {
+ hp += hp * sd->itemgrouphealrate[IG_POTION] / 100;
+ sp += sp * sd->itemgrouphealrate[IG_POTION] / 100;
+ }
+
+ if( (i = pc_skillheal_bonus(sd, skill_id)) ) {
+ hp += hp * i / 100;
+ sp += sp * i / 100;
+ }
+ } else {
+ hp = (1 + rnd()%400) * (100 + skill_lv*10) / 100;
+ hp = hp * (100 + (tstatus->vit<<1)) / 100;
+ if( dstsd )
+ hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100;
+ }
+ if( dstsd && (i = pc_skillheal2_bonus(dstsd, skill_id)) ) {
+ hp += hp * i / 100;
+ sp += sp * i / 100;
+ }
+ if( tsc && tsc->count ) {
+ if( tsc->data[SC_CRITICALWOUND] ) {
+ hp -= hp * tsc->data[SC_CRITICALWOUND]->val2 / 100;
+ sp -= sp * tsc->data[SC_CRITICALWOUND]->val2 / 100;
+ }
+ if( tsc->data[SC_DEATHHURT] ) {
+ hp -= hp * 20 / 100;
+ sp -= sp * 20 / 100;
+ }
+ if( tsc->data[SC_WATER_INSIGNIA] && tsc->data[SC_WATER_INSIGNIA]->val1 == 2 ) {
+ hp += hp / 10;
+ sp += sp / 10;
+ }
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if( hp > 0 || (skill_id == AM_POTIONPITCHER && sp <= 0) )
+ clif_skill_nodamage(NULL,bl,AL_HEAL,hp,1);
+ if( sp > 0 )
+ clif_skill_nodamage(NULL,bl,MG_SRECOVERY,sp,1);
+#ifdef RENEWAL
+ if( tsc && tsc->data[SC_EXTREMITYFIST2] )
+ sp = 0;
+#endif
+ status_heal(bl,hp,sp,0);
+ }
+ break;
+ case AM_CP_WEAPON:
+ case AM_CP_SHIELD:
+ case AM_CP_ARMOR:
+ case AM_CP_HELM:
+ {
+ unsigned int equip[] = {EQP_WEAPON, EQP_SHIELD, EQP_ARMOR, EQP_HEAD_TOP};
+
+ if( sd && ( bl->type != BL_PC || ( dstsd && pc_checkequip(dstsd,equip[skill_id - AM_CP_WEAPON]) < 0 ) ) ){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock(); // Don't consume item requirements
+ return 0;
+ }
+
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ }
+ break;
+ case AM_TWILIGHT1:
+ if (sd) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ //Prepare 200 White Potions.
+ if (!skill_produce_mix(sd, skill_id, 504, 0, 0, 0, 200))
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+ case AM_TWILIGHT2:
+ if (sd) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ //Prepare 200 Slim White Potions.
+ if (!skill_produce_mix(sd, skill_id, 547, 0, 0, 0, 200))
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+ case AM_TWILIGHT3:
+ if (sd) {
+ int ebottle = pc_search_inventory(sd,713);
+ if( ebottle >= 0 )
+ ebottle = sd->status.inventory[ebottle].amount;
+ //check if you can produce all three, if not, then fail:
+ if (!skill_can_produce_mix(sd,970,-1, 100) //100 Alcohol
+ || !skill_can_produce_mix(sd,7136,-1, 50) //50 Acid Bottle
+ || !skill_can_produce_mix(sd,7135,-1, 50) //50 Flame Bottle
+ || ebottle < 200 //200 empty bottle are required at total.
+ ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_produce_mix(sd, skill_id, 970, 0, 0, 0, 100);
+ skill_produce_mix(sd, skill_id, 7136, 0, 0, 0, 50);
+ skill_produce_mix(sd, skill_id, 7135, 0, 0, 0, 50);
+ }
+ break;
+ case SA_DISPELL:
+ if (flag&1 || (i = skill_get_splash(skill_id, skill_lv)) < 1)
+ {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if((dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER)
+ || (tsc && tsc->data[SC_SPIRIT] && tsc->data[SC_SPIRIT]->val2 == SL_ROGUE) //Rogue's spirit defends againt dispel.
+ || rnd()%100 >= 50+10*skill_lv
+ || ( tsc && tsc->option&OPTION_MADOGEAR ) )//Mado Gear is immune to dispell according to bug report 49 [Ind]
+ {
+ if (sd)
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if(status_isimmune(bl) || !tsc || !tsc->count)
+ break;
+ for(i=0;i<SC_MAX;i++)
+ {
+ if (!tsc->data[i])
+ continue;
+ switch (i) {
+ case SC_WEIGHT50: case SC_WEIGHT90: case SC_HALLUCINATION:
+ 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: case SC_COMBO:
+ case SC_STRFOOD: case SC_AGIFOOD: case SC_VITFOOD:
+ case SC_INTFOOD: case SC_DEXFOOD: case SC_LUKFOOD:
+ case SC_HITFOOD: case SC_FLEEFOOD: case SC_BATKFOOD:
+ case SC_WATKFOOD: case SC_MATKFOOD: case SC_DANCING:
+ case SC_EDP: case SC_AUTOBERSERK:
+ case SC_CARTBOOST: case SC_MELTDOWN: case SC_SAFETYWALL:
+ case SC_SMA: case SC_SPEEDUP0: case SC_NOCHAT:
+ case SC_ANKLE: case SC_SPIDERWEB: case SC_JAILED:
+ case SC_ITEMBOOST: case SC_EXPBOOST: case SC_LIFEINSURANCE:
+ case SC_BOSSMAPINFO: case SC_PNEUMA: case SC_AUTOSPELL:
+ case SC_INCHITRATE: case SC_INCATKRATE: case SC_NEN:
+ case SC_READYSTORM: case SC_READYDOWN: case SC_READYTURN:
+ case SC_READYCOUNTER: case SC_DODGE: case SC_WARM:
+ case SC_SPEEDUP1: case SC_AUTOTRADE: case SC_CRITICALWOUND:
+ case SC_JEXPBOOST: case SC_INVINCIBLE: case SC_INVINCIBLEOFF:
+ case SC_HELLPOWER: case SC_MANU_ATK: case SC_MANU_DEF:
+ case SC_SPL_ATK: case SC_SPL_DEF: case SC_MANU_MATK:
+ case SC_SPL_MATK: case SC_RICHMANKIM: case SC_ETERNALCHAOS:
+ case SC_DRUMBATTLE: case SC_NIBELUNGEN: case SC_ROKISWEIL:
+ case SC_INTOABYSS: case SC_SIEGFRIED: case SC_FOOD_STR_CASH:
+ case SC_FOOD_AGI_CASH: case SC_FOOD_VIT_CASH: case SC_FOOD_DEX_CASH:
+ case SC_FOOD_INT_CASH: case SC_FOOD_LUK_CASH: case SC_SEVENWIND:
+ case SC_MIRACLE: case SC_S_LIFEPOTION: case SC_L_LIFEPOTION:
+ case SC_INCHEALRATE: case SC_ELECTRICSHOCKER: case SC__STRIPACCESSORY:
+ //case SC_SAVAGE_STEAK: case SC_COCKTAIL_WARG_BLOOD: case SC_MINOR_BBQ:
+ //case SC_SIROMA_ICE_TEA: case SC_DROCERA_HERB_STEAMED: case SC_PUTTI_TAILS_NOODLES:
+ case SC_NEUTRALBARRIER_MASTER: case SC_NEUTRALBARRIER: case SC_STEALTHFIELD_MASTER:
+ case SC_STEALTHFIELD: case SC_GIANTGROWTH: case SC_MILLENNIUMSHIELD:
+ case SC_REFRESH: case SC_STONEHARDSKIN: case SC_VITALITYACTIVATION:
+ case SC_FIGHTINGSPIRIT: case SC_ABUNDANCE: case SC__SHADOWFORM:
+ case SC_LEADERSHIP: case SC_GLORYWOUNDS: case SC_SOULCOLD:
+ case SC_HAWKEYES: case SC_GUILDAURA: case SC_PUSH_CART:
+ case SC_RAISINGDRAGON: case SC_GT_ENERGYGAIN: case SC_GT_CHANGE:
+ case SC_GT_REVITALIZE: case SC_REFLECTDAMAGE: case SC_INSPIRATION:
+ case SC_EXEEDBREAK: case SC_FORCEOFVANGUARD: case SC_BANDING:
+ case SC_DUPLELIGHT: case SC_EXPIATIO: case SC_LAUDAAGNUS:
+ case SC_LAUDARAMUS: case SC_GATLINGFEVER: case SC_INCREASING:
+ case SC_ADJUSTMENT: case SC_MADNESSCANCEL:
+#ifdef RENEWAL
+ case SC_EXTREMITYFIST2:
+#endif
+ continue;
+ /**
+ * bugreport:4888 these songs may only be dispelled if you're not in their song area anymore
+ **/
+ case SC_WHISTLE:
+ case SC_ASSNCROS:
+ case SC_POEMBRAGI:
+ case SC_APPLEIDUN:
+ case SC_HUMMING:
+ case SC_DONTFORGETME:
+ case SC_FORTUNE:
+ case SC_SERVICE4U:
+ if( tsc->data[i]->val4 ) //val4 = out-of-song-area
+ continue;
+ break;
+ case SC_ASSUMPTIO:
+ if( bl->type == BL_MOB )
+ continue;
+ break;
+ }
+ if(i==SC_BERSERK || i==SC_SATURDAYNIGHTFEVER) tsc->data[i]->val2=0; //Mark a dispelled berserk to avoid setting hp to 100 by setting hp penalty to 0.
+ status_change_end(bl, (sc_type)i, INVALID_TIMER);
+ }
+ break;
+ }
+ //Affect all targets on splash area.
+ map_foreachinrange(skill_area_sub, bl, i, BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|1,
+ skill_castend_damage_id);
+ break;
+
+ case TF_BACKSLIDING: //This is the correct implementation as per packet logging information. [Skotlex]
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),unit_getdir(bl),0);
+ break;
+
+ case TK_HIGHJUMP:
+ {
+ int x,y, dir = unit_getdir(src);
+
+ //Fails on noteleport maps, except for GvG and BG maps [Skotlex]
+ if( map[src->m].flag.noteleport &&
+ !(map[src->m].flag.battleground || map_flag_gvg2(src->m) )
+ ) {
+ x = src->x;
+ y = src->y;
+ } else {
+ x = src->x + dirx[dir]*skill_lv*2;
+ y = src->y + diry[dir]*skill_lv*2;
+ }
+
+ clif_skill_nodamage(src,bl,TK_HIGHJUMP,skill_lv,1);
+ if(!map_count_oncell(src->m,x,y,BL_PC|BL_NPC|BL_MOB) && map_getcell(src->m,x,y,CELL_CHKREACH)) {
+ clif_slide(src,x,y);
+ unit_movepos(src, x, y, 1, 0);
+ }
+ }
+ break;
+
+ case SA_CASTCANCEL:
+ case SO_SPELLFIST:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ unit_skillcastcancel(src,1);
+ if(sd) {
+ int sp = skill_get_sp(sd->skill_id_old,sd->skill_lv_old);
+ if( skill_id == SO_SPELLFIST ){
+ sc_start4(src,type,100,skill_lv+1,skill_lv,sd->skill_id_old,sd->skill_lv_old,skill_get_time(skill_id,skill_lv));
+ sd->skill_id_old = sd->skill_lv_old = 0;
+ break;
+ }
+ sp = sp * (90 - (skill_lv-1)*20) / 100;
+ if(sp < 0) sp = 0;
+ status_zap(src, 0, sp);
+ }
+ break;
+ case SA_SPELLBREAKER:
+ {
+ int sp;
+ if(tsc && tsc->data[SC_MAGICROD]) {
+ sp = skill_get_sp(skill_id,skill_lv);
+ sp = sp * tsc->data[SC_MAGICROD]->val2 / 100;
+ if(sp < 1) sp = 1;
+ status_heal(bl,0,sp,2);
+ status_percent_damage(bl, src, 0, -20, false); //20% max SP damage.
+ } else {
+ struct unit_data *ud = unit_bl2ud(bl);
+ int bl_skill_id=0,bl_skill_lv=0,hp = 0;
+ if (!ud || ud->skilltimer == INVALID_TIMER)
+ break; //Nothing to cancel.
+ bl_skill_id = ud->skill_id;
+ bl_skill_lv = ud->skill_lv;
+ if (tstatus->mode & MD_BOSS)
+ { //Only 10% success chance against bosses. [Skotlex]
+ if (rnd()%100 < 90)
+ {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ } else if (!dstsd || map_flag_vs(bl->m)) //HP damage only on pvp-maps when against players.
+ hp = tstatus->max_hp/50; //Recover 2% HP [Skotlex]
+
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ unit_skillcastcancel(bl,0);
+ sp = skill_get_sp(bl_skill_id,bl_skill_lv);
+ status_zap(bl, hp, sp);
+
+ if (hp && skill_lv >= 5)
+ hp>>=1; //Recover half damaged HP at level 5 [Skotlex]
+ else
+ hp = 0;
+
+ if (sp) //Recover some of the SP used
+ sp = sp*(25*(skill_lv-1))/100;
+
+ if(hp || sp)
+ status_heal(src, hp, sp, 2);
+ }
+ }
+ break;
+ case SA_MAGICROD:
+ clif_skill_nodamage(src,src,SA_MAGICROD,skill_lv,1);
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ case SA_AUTOSPELL:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if(sd)
+ clif_autospell(sd,skill_lv);
+ else {
+ int maxlv=1,spellid=0;
+ static const int spellarray[3] = { MG_COLDBOLT,MG_FIREBOLT,MG_LIGHTNINGBOLT };
+ if(skill_lv >= 10) {
+ spellid = MG_FROSTDIVER;
+// if (tsc && tsc->data[SC_SPIRIT] && tsc->data[SC_SPIRIT]->val2 == SA_SAGE)
+// maxlv = 10;
+// else
+ maxlv = skill_lv - 9;
+ }
+ else if(skill_lv >=8) {
+ spellid = MG_FIREBALL;
+ maxlv = skill_lv - 7;
+ }
+ else if(skill_lv >=5) {
+ spellid = MG_SOULSTRIKE;
+ maxlv = skill_lv - 4;
+ }
+ else if(skill_lv >=2) {
+ int i = rnd()%3;
+ spellid = spellarray[i];
+ maxlv = skill_lv - 1;
+ }
+ else if(skill_lv > 0) {
+ spellid = MG_NAPALMBEAT;
+ maxlv = 3;
+ }
+ if(spellid > 0)
+ sc_start4(src,SC_AUTOSPELL,100,skill_lv,spellid,maxlv,0,
+ skill_get_time(SA_AUTOSPELL,skill_lv));
+ }
+ break;
+
+ case BS_GREED:
+ if(sd){
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_greed,bl,
+ skill_get_splash(skill_id, skill_lv),BL_ITEM,bl);
+ }
+ break;
+
+ case SA_ELEMENTWATER:
+ case SA_ELEMENTFIRE:
+ case SA_ELEMENTGROUND:
+ case SA_ELEMENTWIND:
+ if(sd && !dstmd) //Only works on monsters.
+ break;
+ if(tstatus->mode&MD_BOSS)
+ 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:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(bl, type, 100, skill_lv, skill_get_ele(skill_id,skill_lv),
+ skill_get_time(skill_id, skill_lv)));
+ break;
+ case NPC_CHANGEUNDEAD:
+ //This skill should fail if target is wearing bathory/evil druid card [Brainstorm]
+ //TO-DO This is ugly, fix it
+ if(tstatus->def_ele==ELE_UNDEAD || tstatus->def_ele==ELE_DARK) break;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(bl, type, 100, skill_lv, skill_get_ele(skill_id,skill_lv),
+ skill_get_time(skill_id, skill_lv)));
+ break;
+
+ case NPC_PROVOCATION:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if (md) mob_unlocktarget(md, tick);
+ break;
+
+ case NPC_KEEPING:
+ case NPC_BARRIER:
+ {
+ int skill_time = skill_get_time(skill_id,skill_lv);
+ struct unit_data *ud = unit_bl2ud(bl);
+ if (clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_time))
+ && ud) { //Disable attacking/acting/moving for skill's duration.
+ ud->attackabletime =
+ ud->canact_tick =
+ ud->canmove_tick = tick + skill_time;
+ }
+ }
+ break;
+
+ case NPC_REBIRTH:
+ if( md && md->state.rebirth )
+ break; // only works once
+ sc_start(bl,type,100,skill_lv,-1);
+ break;
+
+ case NPC_DARKBLESSING:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(bl,type,(50+skill_lv*5),skill_lv,skill_lv,skill_get_time2(skill_id,skill_lv)));
+ break;
+
+ case NPC_LICK:
+ status_zap(bl, 0, 100);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,(skill_lv*5),skill_lv,skill_get_time2(skill_id,skill_lv)));
+ break;
+
+ case NPC_SUICIDE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ status_kill(src); //When suiciding, neither exp nor drops is given.
+ break;
+
+ case NPC_SUMMONSLAVE:
+ case NPC_SUMMONMONSTER:
+ if(md && md->skill_idx >= 0)
+ mob_summonslave(md,md->db->skill[md->skill_idx].val,skill_lv,skill_id);
+ break;
+
+ case NPC_CALLSLAVE:
+ mob_warpslave(src,MOB_SLAVEDISTANCE);
+ break;
+
+ case NPC_RANDOMMOVE:
+ if (md) {
+ md->next_walktime = tick - 1;
+ mob_randomwalk(md,tick);
+ }
+ break;
+
+ case NPC_SPEEDUP:
+ {
+ // or does it increase casting rate? just a guess xD
+ int i = SC_ASPDPOTION0 + skill_lv - 1;
+ if (i > SC_ASPDPOTION3)
+ i = SC_ASPDPOTION3;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,(sc_type)i,100,skill_lv,skill_lv * 60000));
+ }
+ break;
+
+ case NPC_REVENGE:
+ // not really needed... but adding here anyway ^^
+ if (md && md->master_id > 0) {
+ struct block_list *mbl, *tbl;
+ if ((mbl = map_id2bl(md->master_id)) == NULL ||
+ (tbl = battle_gettargeted(mbl)) == NULL)
+ break;
+ md->state.provoke_flag = tbl->id;
+ mob_target(md, tbl, sstatus->rhw.range);
+ }
+ break;
+
+ case NPC_RUN:
+ {
+ const int mask[8][2] = {{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1}};
+ uint8 dir = (bl == src)?unit_getdir(src):map_calc_dir(src,bl->x,bl->y); //If cast on self, run forward, else run away.
+ unit_stop_attack(src);
+ //Run skillv tiles overriding the can-move check.
+ if (unit_walktoxy(src, src->x + skill_lv * mask[dir][0], src->y + skill_lv * mask[dir][1], 2) && md)
+ md->state.skillstate = MSS_WALK; //Otherwise it isn't updated in the ai.
+ }
+ break;
+
+ case NPC_TRANSFORMATION:
+ case NPC_METAMORPHOSIS:
+ if(md && md->skill_idx >= 0) {
+ int class_ = mob_random_class (md->db->skill[md->skill_idx].val,0);
+ if (skill_lv > 1) //Multiply the rest of mobs. [Skotlex]
+ mob_summonslave(md,md->db->skill[md->skill_idx].val,skill_lv-1,skill_id);
+ if (class_) mob_class_change(md, class_);
+ }
+ break;
+
+ case NPC_EMOTION_ON:
+ case NPC_EMOTION:
+ //va[0] is the emotion to use.
+ //NPC_EMOTION & NPC_EMOTION_ON can change a mob's mode 'permanently' [Skotlex]
+ //val[1] 'sets' the mode
+ //val[2] adds to the current mode
+ //val[3] removes from the current mode
+ //val[4] if set, asks to delete the previous mode change.
+ if(md && md->skill_idx >= 0 && tsc)
+ {
+ clif_emotion(bl, md->db->skill[md->skill_idx].val[0]);
+ if(md->db->skill[md->skill_idx].val[4] && tsce)
+ status_change_end(bl, type, INVALID_TIMER);
+
+ if(md->db->skill[md->skill_idx].val[1] || md->db->skill[md->skill_idx].val[2])
+ sc_start4(src, type, 100, skill_lv,
+ md->db->skill[md->skill_idx].val[1],
+ md->db->skill[md->skill_idx].val[2],
+ md->db->skill[md->skill_idx].val[3],
+ skill_get_time(skill_id, skill_lv));
+ }
+ break;
+
+ case NPC_POWERUP:
+ sc_start(bl,SC_INCATKRATE,100,200,skill_get_time(skill_id, skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,100,skill_get_time(skill_id, skill_lv)));
+ break;
+
+ case NPC_AGIUP:
+ sc_start(bl,SC_SPEEDUP1,100,skill_lv,skill_get_time(skill_id, skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,100,skill_get_time(skill_id, skill_lv)));
+ break;
+
+ case NPC_INVISIBLE:
+ //Have val4 passed as 6 is for "infinite cloak" (do not end on attack/skill use).
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start4(bl,type,100,skill_lv,0,0,6,skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case NPC_SIEGEMODE:
+ // not sure what it does
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+
+ case WE_MALE:
+ {
+ int hp_rate=(!skill_lv)? 0:skill_db[skill_id].hp_rate[skill_lv-1];
+ int gain_hp= tstatus->max_hp*abs(hp_rate)/100; // The earned is the same % of the target HP than it costed the caster. [Skotlex]
+ clif_skill_nodamage(src,bl,skill_id,status_heal(bl, gain_hp, 0, 0),1);
+ }
+ break;
+ case WE_FEMALE:
+ {
+ int sp_rate=(!skill_lv)? 0:skill_db[skill_id].sp_rate[skill_lv-1];
+ int gain_sp=tstatus->max_sp*abs(sp_rate)/100;// The earned is the same % of the target SP than it costed the caster. [Skotlex]
+ clif_skill_nodamage(src,bl,skill_id,status_heal(bl, 0, gain_sp, 0),1);
+ }
+ break;
+
+ // parent-baby skills
+ case WE_BABY:
+ if(sd){
+ struct map_session_data *f_sd = pc_get_father(sd);
+ struct map_session_data *m_sd = pc_get_mother(sd);
+ // if neither was found
+ if(!f_sd && !m_sd){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ status_change_start(bl,SC_STUN,10000,skill_lv,0,0,0,skill_get_time2(skill_id,skill_lv),8);
+ if (f_sd) sc_start(&f_sd->bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ if (m_sd) sc_start(&m_sd->bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ }
+ break;
+
+ case PF_HPCONVERSION:
+ {
+ int hp, sp;
+ hp = sstatus->max_hp/10;
+ sp = hp * 10 * skill_lv / 100;
+ if (!status_charge(src,hp,0)) {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ status_heal(bl,0,sp,2);
+ }
+ break;
+
+ case MA_REMOVETRAP:
+ case HT_REMOVETRAP:
+ {
+ struct skill_unit* su;
+ struct skill_unit_group* sg;
+ su = BL_CAST(BL_SKILL, bl);
+
+ // Mercenaries can remove any trap
+ // Players can only remove their own traps or traps on Vs maps.
+ if( su && (sg = su->group) && (src->type == BL_MER || sg->src_id == src->id || map_flag_vs(bl->m)) && (skill_get_inf2(sg->skill_id)&INF2_TRAP) )
+ {
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ if( sd && !(sg->unit_id == UNT_USED_TRAPS || (sg->unit_id == UNT_ANKLESNARE && sg->val2 != 0 )) )
+ { // prevent picking up expired traps
+ if( battle_config.skill_removetrap_type )
+ { // get back all items used to deploy the trap
+ for( i = 0; i < 10; i++ )
+ {
+ if( skill_db[su->group->skill_id].itemid[i] > 0 )
+ {
+ int flag;
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid = skill_db[su->group->skill_id].itemid[i];
+ item_tmp.identify = 1;
+ if( item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,skill_db[su->group->skill_id].amount[i],LOG_TYPE_OTHER)) )
+ {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,skill_db[su->group->skill_id].amount[i],sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ }
+ }
+ else
+ { // get back 1 trap
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid = su->group->item_id?su->group->item_id:ITEMID_TRAP;
+ item_tmp.identify = 1;
+ if( item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,1,LOG_TYPE_OTHER)) )
+ {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+ }
+ skill_delunit(su);
+ }else if(sd)
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+
+ }
+ break;
+ case HT_SPRINGTRAP:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ {
+ struct skill_unit *su=NULL;
+ if((bl->type==BL_SKILL) && (su=(struct skill_unit *)bl) && (su->group) ){
+ switch(su->group->unit_id){
+ case UNT_ANKLESNARE: // ankle snare
+ if (su->group->val2 != 0)
+ // if it is already trapping something don't spring it,
+ // remove trap should be used instead
+ break;
+ // otherwise fallthrough to below
+ case UNT_BLASTMINE:
+ case UNT_SKIDTRAP:
+ case UNT_LANDMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_FREEZINGTRAP:
+ case UNT_CLAYMORETRAP:
+ case UNT_TALKIEBOX:
+ su->group->unit_id = UNT_USED_TRAPS;
+ clif_changetraplook(bl, UNT_USED_TRAPS);
+ 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,skill_id,skill_lv,1);
+ if(sd)
+ unit_skilluse_id(src,src->id,sd->skill_id_dance,sd->skill_lv_dance);
+ break;
+
+ case AS_SPLASHER:
+ if(tstatus->mode&MD_BOSS
+ /**
+ * Renewal dropped the 3/4 hp requirement
+ **/
+ #ifndef RENEWAL
+ || tstatus-> hp > tstatus->max_hp*3/4
+ #endif
+ ) {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 1;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start4(bl,type,100,skill_lv,skill_id,src->id,skill_get_time(skill_id,skill_lv),1000));
+#ifndef RENEWAL
+ if (sd) skill_blockpc_start (sd, skill_id, skill_get_time(skill_id, skill_lv)+3000);
+#endif
+ break;
+
+ case PF_MINDBREAKER:
+ {
+ if(tstatus->mode&MD_BOSS || battle_check_undead(tstatus->race,tstatus->def_ele))
+ {
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ if (tsce)
+ { //HelloKitty2 (?) explained that this silently fails when target is
+ //already inflicted. [Skotlex]
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ //Has a 55% + skill_lv*5% success chance.
+ if (!clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,55+5*skill_lv,skill_lv,skill_get_time(skill_id,skill_lv))))
+ {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+
+ unit_skillcastcancel(bl,0);
+
+ if(tsc && tsc->count){
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ if(tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE)
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ status_change_end(bl, SC_SLEEP, INVALID_TIMER);
+ }
+
+ if(dstmd)
+ mob_target(dstmd,src,skill_get_range2(src,skill_id,skill_lv));
+ }
+ break;
+
+ case PF_SOULCHANGE:
+ {
+ unsigned int sp1 = 0, sp2 = 0;
+ if (dstmd) {
+ if (dstmd->state.soul_change_flag) {
+ if(sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ dstmd->state.soul_change_flag = 1;
+ sp2 = sstatus->max_sp * 3 /100;
+ status_heal(src, 0, sp2, 2);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ }
+ sp1 = sstatus->sp;
+ sp2 = tstatus->sp;
+ #ifdef RENEWAL
+ sp1 = sp1 / 2;
+ sp2 = sp2 / 2;
+ if( tsc && tsc->data[SC_EXTREMITYFIST2] )
+ sp1 = tstatus->sp;
+ #endif
+ status_set_sp(src, sp2, 3);
+ status_set_sp(bl, sp1, 3);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ // Slim Pitcher
+ case CR_SLIMPITCHER:
+ // Updated to block Slim Pitcher from working on barricades and guardian stones.
+ if( dstmd && (dstmd->class_ == MOBID_EMPERIUM || (dstmd->class_ >= MOBID_BARRICADE1 && dstmd->class_ <= MOBID_GUARIDAN_STONE2)) )
+ break;
+ if (potion_hp || potion_sp) {
+ int hp = potion_hp, sp = potion_sp;
+ hp = hp * (100 + (tstatus->vit<<1))/100;
+ sp = sp * (100 + (tstatus->int_<<1))/100;
+ if (dstsd) {
+ if (hp)
+ hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10 + pc_skillheal2_bonus(dstsd, skill_id))/100;
+ if (sp)
+ sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10 + pc_skillheal2_bonus(dstsd, skill_id))/100;
+ }
+ if( tsc && tsc->count ) {
+ if (tsc->data[SC_CRITICALWOUND]) {
+ hp -= hp * tsc->data[SC_CRITICALWOUND]->val2 / 100;
+ sp -= sp * tsc->data[SC_CRITICALWOUND]->val2 / 100;
+ }
+ if (tsc->data[SC_DEATHHURT]) {
+ hp -= hp * 20 / 100;
+ sp -= sp * 20 / 100;
+ }
+ if( tsc->data[SC_WATER_INSIGNIA] && tsc->data[SC_WATER_INSIGNIA]->val1 == 2) {
+ hp += hp / 10;
+ sp += sp / 10;
+ }
+ }
+ if(hp > 0)
+ clif_skill_nodamage(NULL,bl,AL_HEAL,hp,1);
+ if(sp > 0)
+ clif_skill_nodamage(NULL,bl,MG_SRECOVERY,sp,1);
+ status_heal(bl,hp,sp,0);
+ }
+ break;
+ // Full Chemical Protection
+ case CR_FULLPROTECTION:
+ {
+ unsigned int equip[] = {EQP_WEAPON, EQP_SHIELD, EQP_ARMOR, EQP_HEAD_TOP};
+ int i, s = 0, skilltime = skill_get_time(skill_id,skill_lv);
+
+ for (i=0 ; i<4; i++) {
+ if( bl->type != BL_PC || ( dstsd && pc_checkequip(dstsd,equip[i]) < 0 ) )
+ continue;
+ sc_start(bl,(sc_type)(SC_CP_WEAPON + i),100,skill_lv,skilltime);
+ s++;
+ }
+ if( sd && !s ){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock(); // Don't consume item requirements
+ return 0;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case RG_CLEANER: //AppleGirl
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+
+ case CG_LONGINGFREEDOM:
+ {
+ if (tsc && !tsce && (tsce=tsc->data[SC_DANCING]) && tsce->val4
+ && (tsce->val1&0xFFFF) != CG_MOONLIT) //Can't use Longing for Freedom while under Moonlight Petals. [Skotlex]
+ {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ }
+ }
+ break;
+
+ case CG_TAROTCARD:
+ {
+ int eff, count = -1;
+ if( rnd() % 100 > skill_lv * 8 || (dstmd && ((dstmd->guardian_data && dstmd->class_ == MOBID_EMPERIUM) || mob_is_battleground(dstmd))) )
+ {
+ if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+
+ map_freeblock_unlock();
+ return 0;
+ }
+ status_zap(src,0,skill_db[skill_get_index(skill_id)].sp[skill_lv]); // consume sp only if succeeded [Inkfish]
+ do {
+ eff = rnd() % 14;
+ clif_specialeffect(bl, 523 + eff, AREA);
+ switch (eff)
+ {
+ case 0: // heals SP to 0
+ status_percent_damage(src, bl, 0, 100, false);
+ break;
+ case 1: // matk halved
+ sc_start(bl,SC_INCMATKRATE,100,-50,skill_get_time2(skill_id,skill_lv));
+ break;
+ case 2: // all buffs removed
+ status_change_clear_buffs(bl,1);
+ break;
+ case 3: // 1000 damage, random armor destroyed
+ {
+ int where[] = { EQP_ARMOR, EQP_SHIELD, EQP_HELM, EQP_SHOES, EQP_GARMENT };
+ status_fix_damage(src, bl, 1000, 0);
+ clif_damage(src,bl,tick,0,0,1000,0,0,0);
+ if( !status_isdead(bl) )
+ skill_break_equip(bl, where[rnd()%5], 10000, BCT_ENEMY);
+ }
+ break;
+ case 4: // atk halved
+ sc_start(bl,SC_INCATKRATE,100,-50,skill_get_time2(skill_id,skill_lv));
+ break;
+ case 5: // 2000HP heal, random teleported
+ status_heal(src, 2000, 0, 0);
+ if( !map_flag_vs(bl->m) )
+ unit_warp(bl, -1,-1,-1, CLR_TELEPORT);
+ break;
+ case 6: // random 2 other effects
+ if (count == -1)
+ count = 3;
+ else
+ count++; //Should not retrigger this one.
+ break;
+ case 7: // stop freeze or stoned
+ {
+ enum sc_type sc[] = { SC_STOP, SC_FREEZE, SC_STONE };
+ sc_start(bl,sc[rnd()%3],100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ }
+ break;
+ case 8: // curse coma and poison
+ sc_start(bl,SC_COMA,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_CURSE,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_POISON,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case 9: // confusion
+ sc_start(bl,SC_CONFUSION,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+ case 10: // 6666 damage, atk matk halved, cursed
+ status_fix_damage(src, bl, 6666, 0);
+ clif_damage(src,bl,tick,0,0,6666,0,0,0);
+ sc_start(bl,SC_INCATKRATE,100,-50,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_INCMATKRATE,100,-50,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_CURSE,skill_lv,100,skill_get_time2(skill_id,skill_lv));
+ break;
+ case 11: // 4444 damage
+ status_fix_damage(src, bl, 4444, 0);
+ clif_damage(src,bl,tick,0,0,4444,0,0,0);
+ break;
+ case 12: // stun
+ sc_start(bl,SC_STUN,100,skill_lv,5000);
+ break;
+ case 13: // atk,matk,hit,flee,def reduced
+ sc_start(bl,SC_INCATKRATE,100,-20,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_INCMATKRATE,100,-20,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_INCHITRATE,100,-20,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_INCFLEERATE,100,-20,skill_get_time2(skill_id,skill_lv));
+ sc_start(bl,SC_INCDEFRATE,100,-20,skill_get_time2(skill_id,skill_lv));
+ break;
+ default:
+ break;
+ }
+ } while ((--count) > 0);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case SL_ALCHEMIST:
+ case SL_ASSASIN:
+ case SL_BARDDANCER:
+ case SL_BLACKSMITH:
+ case SL_CRUSADER:
+ case SL_HUNTER:
+ case SL_KNIGHT:
+ case SL_MONK:
+ case SL_PRIEST:
+ case SL_ROGUE:
+ case SL_SAGE:
+ case SL_SOULLINKER:
+ case SL_STAR:
+ case SL_SUPERNOVICE:
+ case SL_WIZARD:
+ //NOTE: here, 'type' has the value of the associated MAPID, not of the SC_SPIRIT constant.
+ if (sd && !(dstsd && (dstsd->class_&MAPID_UPPERMASK) == type)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if (skill_id == SL_SUPERNOVICE && dstsd && dstsd->die_counter && !(rnd()%100))
+ { //Erase death count 1% of the casts
+ dstsd->die_counter = 0;
+ pc_setglobalreg(dstsd,"PC_DIE_COUNTER", 0);
+ clif_specialeffect(bl, 0x152, AREA);
+ //SC_SPIRIT invokes status_calc_pc for us.
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start4(bl,SC_SPIRIT,100,skill_lv,skill_id,0,0,skill_get_time(skill_id,skill_lv)));
+ sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA,skill_lv));
+ break;
+ case SL_HIGH:
+ if (sd && !(dstsd && (dstsd->class_&JOBL_UPPER) && !(dstsd->class_&JOBL_2) && dstsd->status.base_level < 70)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start4(bl,type,100,skill_lv,skill_id,0,0,skill_get_time(skill_id,skill_lv)));
+ sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA,skill_lv));
+ break;
+
+ case SL_SWOO:
+ if (tsce) {
+ if(sd)
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,10000,8);
+ status_change_end(bl, SC_SWOO, INVALID_TIMER);
+ break;
+ }
+ case SL_SKA: // [marquis007]
+ case SL_SKE:
+ if (sd && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ status_change_start(src,SC_STUN,10000,skill_lv,0,0,0,500,10);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ if (skill_id == SL_SKE)
+ sc_start(src,SC_SMA,100,skill_lv,skill_get_time(SL_SMA,skill_lv));
+ break;
+
+ // New guild skills [Celest]
+ case GD_BATTLEORDER:
+ if(flag&1) {
+ if (status_get_guild_id(src) == status_get_guild_id(bl))
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv));
+ } else if (status_get_guild_id(src)) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub, src,
+ skill_get_splash(skill_id, skill_lv), BL_PC,
+ src,skill_id,skill_lv,tick, flag|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ if (sd)
+ guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
+ }
+ break;
+ case GD_REGENERATION:
+ if(flag&1) {
+ if (status_get_guild_id(src) == status_get_guild_id(bl))
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id, skill_lv));
+ } else if (status_get_guild_id(src)) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub, src,
+ skill_get_splash(skill_id, skill_lv), BL_PC,
+ src,skill_id,skill_lv,tick, flag|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ if (sd)
+ guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
+ }
+ break;
+ case GD_RESTORE:
+ if(flag&1) {
+ if (status_get_guild_id(src) == status_get_guild_id(bl))
+ clif_skill_nodamage(src,bl,AL_HEAL,status_percent_heal(bl,90,90),1);
+ } else if (status_get_guild_id(src)) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub, src,
+ skill_get_splash(skill_id, skill_lv), BL_PC,
+ src,skill_id,skill_lv,tick, flag|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ if (sd)
+ guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
+ }
+ break;
+ case GD_EMERGENCYCALL:
+ {
+ int dx[9]={-1, 1, 0, 0,-1, 1,-1, 1, 0};
+ int dy[9]={ 0, 0, 1,-1, 1,-1,-1, 1, 0};
+ int j = 0;
+ struct guild *g = NULL;
+ // i don't know if it actually summons in a circle, but oh well. ;P
+ g = sd?sd->state.gmaster_flag:guild_search(status_get_guild_id(src));
+ if (!g)
+ break;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ for(i = 0; i < g->max_member; i++, j++) {
+ if (j>8) j=0;
+ if ((dstsd = g->member[i].sd) != NULL && sd != dstsd && !dstsd->state.autotrade && !pc_isdead(dstsd)) {
+ if (map[dstsd->bl.m].flag.nowarp && !map_flag_gvg2(dstsd->bl.m))
+ continue;
+ if(map_getcell(src->m,src->x+dx[j],src->y+dy[j],CELL_CHKNOREACH))
+ dx[j] = dy[j] = 0;
+ pc_setpos(dstsd, map_id2index(src->m), src->x+dx[j], src->y+dy[j], CLR_RESPAWN);
+ }
+ }
+ if (sd)
+ guild_block_skill(sd,skill_get_time2(skill_id,skill_lv));
+ }
+ break;
+
+ case SG_FEEL:
+ //AuronX reported you CAN memorize the same map as all three. [Skotlex]
+ if (sd) {
+ if(!sd->feel_map[skill_lv-1].index)
+ clif_feel_req(sd->fd,sd, skill_lv);
+ else
+ clif_feel_info(sd, skill_lv-1, 1);
+ }
+ break;
+
+ case SG_HATE:
+ if (sd) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if (!pc_set_hate_mob(sd, skill_lv-1, bl))
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+
+ case GS_GLITTERING:
+ if(sd) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if(rnd()%100 < (20+10*skill_lv))
+ pc_addspiritball(sd,skill_get_time(skill_id,skill_lv),10);
+ else if(sd->spiritball > 0)
+ pc_delspiritball(sd,1,0);
+ }
+ break;
+
+ case GS_CRACKER:
+ /* per official standards, this skill works on players and mobs. */
+ if (sd && (dstsd || dstmd))
+ {
+ i =65 -5*distance_bl(src,bl); //Base rate
+ if (i < 30) i = 30;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ sc_start(bl,SC_STUN, i,skill_lv,skill_get_time2(skill_id,skill_lv));
+ }
+ break;
+
+ case AM_CALLHOMUN: //[orn]
+ if (sd && !merc_call_homunculus(sd))
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+
+ case AM_REST:
+ if (sd) {
+ if (merc_hom_vaporize(sd,1))
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+
+ case HAMI_CASTLE: //[orn]
+ if(rnd()%100 < 20*skill_lv && src != bl)
+ {
+ int x,y;
+ x = src->x;
+ y = src->y;
+ if (hd)
+ skill_blockhomun_start(hd, skill_id, skill_get_time2(skill_id,skill_lv));
+
+ if (unit_movepos(src,bl->x,bl->y,0,0)) {
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1); // Homunc
+ clif_slide(src,bl->x,bl->y) ;
+ if (unit_movepos(bl,x,y,0,0))
+ {
+ clif_skill_nodamage(bl,bl,skill_id,skill_lv,1); // Master
+ clif_slide(bl,x,y) ;
+ }
+
+ //TODO: Shouldn't also players and the like switch targets?
+ map_foreachinrange(skill_chastle_mob_changetarget,src,
+ AREA_SIZE, BL_MOB, bl, src);
+ }
+ }
+ // Failed
+ else if (hd && hd->master)
+ clif_skill_fail(hd->master, skill_id, USESKILL_FAIL_LEVEL, 0);
+ else if (sd)
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ break;
+ case HVAN_CHAOTIC: //[orn]
+ {
+ static const int per[5][2]={{20,50},{50,60},{25,75},{60,64},{34,67}};
+ int r = rnd()%100;
+ i = (skill_lv-1)%5;
+ if(r<per[i][0]) //Self
+ bl = src;
+ else if(r<per[i][1]) //Master
+ bl = battle_get_master(src);
+ else //Enemy
+ bl = map_id2bl(battle_gettarget(src));
+
+ if (!bl) bl = src;
+ i = skill_calc_heal(src, bl, skill_id, 1+rnd()%skill_lv, true);
+ //Eh? why double skill packet?
+ clif_skill_nodamage(src,bl,AL_HEAL,i,1);
+ clif_skill_nodamage(src,bl,skill_id,i,1);
+ status_heal(bl, i, 0, 0);
+ }
+ break;
+ //Homun single-target support skills [orn]
+ case HAMI_BLOODLUST:
+ case HFLI_FLEET:
+ case HFLI_SPEED:
+ case HLIF_CHANGE:
+ case MH_ANGRIFFS_MODUS:
+ case MH_GOLDENE_FERSE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ if (hd)
+ skill_blockhomun_start(hd, skill_id, skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case NPC_DRAGONFEAR:
+ if (flag&1) {
+ const enum sc_type sc[] = { SC_STUN, SC_SILENCE, SC_CONFUSION, SC_BLEEDING };
+ int j;
+ j = i = rnd()%ARRAYLENGTH(sc);
+ while ( !sc_start(bl,sc[i],100,skill_lv,skill_get_time2(skill_id,i+1)) ) {
+ i++;
+ if ( i == ARRAYLENGTH(sc) )
+ i = 0;
+ if (i == j)
+ break;
+ }
+ break;
+ }
+ case NPC_WIDEBLEEDING:
+ case NPC_WIDECONFUSE:
+ case NPC_WIDECURSE:
+ case NPC_WIDEFREEZE:
+ case NPC_WIDESLEEP:
+ case NPC_WIDESILENCE:
+ case NPC_WIDESTONE:
+ case NPC_WIDESTUN:
+ case NPC_SLOWCAST:
+ case NPC_WIDEHELLDIGNITY:
+ if (flag&1)
+ sc_start(bl,type,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ else {
+ skill_area_temp[2] = 0; //For SD_PREAMBLE
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub, bl,
+ skill_get_splash(skill_id, skill_lv),BL_CHAR,
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|SD_PREAMBLE|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+ case NPC_WIDESOULDRAIN:
+ if (flag&1)
+ status_percent_damage(src,bl,0,((skill_lv-1)%5+1)*20,false);
+ else {
+ skill_area_temp[2] = 0; //For SD_PREAMBLE
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub, bl,
+ skill_get_splash(skill_id, skill_lv),BL_CHAR,
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|SD_PREAMBLE|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+ case ALL_PARTYFLEE:
+ if( sd && !(flag&1) )
+ {
+ if( !sd->status.party_id )
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ }
+ else
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ break;
+ case NPC_TALK:
+ case ALL_WEWISH:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+ case ALL_BUYING_STORE:
+ if( sd )
+ {// players only, skill allows 5 buying slots
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, buyingstore_setup(sd, MAX_BUYINGSTORE_SLOTS));
+ }
+ break;
+ case RK_ENCHANTBLADE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,// formula not confirmed
+ sc_start2(bl,type,100,skill_lv,100+20*skill_lv/*+sstatus->int_/2+status_get_lv(bl)/10*/,skill_get_time(skill_id,skill_lv)));
+ break;
+ case RK_DRAGONHOWLING:
+ if( flag&1)
+ sc_start(bl,type,50 + 6 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
+ else
+ {
+ skill_area_temp[2] = 0;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub, src,
+ skill_get_splash(skill_id,skill_lv),BL_CHAR,
+ src,skill_id,skill_lv,tick,flag|BCT_ENEMY|SD_PREAMBLE|1,
+ skill_castend_nodamage_id);
+ }
+ break;
+ case RK_IGNITIONBREAK:
+ case LG_EARTHDRIVE:
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ i = skill_get_splash(skill_id,skill_lv);
+ if( skill_id == LG_EARTHDRIVE ) {
+ int dummy = 1;
+ map_foreachinarea(skill_cell_overlap, src->m, src->x-i, src->y-i, src->x+i, src->y+i, BL_SKILL, LG_EARTHDRIVE, &dummy, src);
+ }
+ map_foreachinrange(skill_area_sub, bl,i,BL_CHAR,
+ src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ break;
+ case RK_STONEHARDSKIN:
+ if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 4 )
+ {
+ int heal = sstatus->hp / 4; // 25% HP
+ if( status_charge(bl,heal,0) )
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start2(bl,type,100,skill_lv,heal,skill_get_time(skill_id,skill_lv)));
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+ case RK_REFRESH:
+ if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 8 )
+ {
+ int heal = status_get_max_hp(bl) * 25 / 100;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ status_heal(bl,heal,0,1);
+ status_change_clear_buffs(bl,4);
+ }
+ break;
+
+ case RK_MILLENNIUMSHIELD:
+ if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 9 )
+ {
+ short shields = (rnd()%100<50) ? 4 : ((rnd()%100<80) ? 3 : 2);
+ sc_start4(bl,type,100,skill_lv,shields,1000,0,skill_get_time(skill_id,skill_lv));
+ clif_millenniumshield(sd,shields);
+ clif_skill_nodamage(src,bl,skill_id,1,1);
+ }
+ break;
+
+ case RK_GIANTGROWTH:
+ case RK_VITALITYACTIVATION:
+ case RK_ABUNDANCE:
+ case RK_CRUSHSTRIKE:
+ if( sd )
+ {
+ int lv = 1; // RK_GIANTGROWTH
+ if( skill_id == RK_VITALITYACTIVATION )
+ lv = 2;
+ else if( skill_id == RK_ABUNDANCE )
+ lv = 6;
+ else if( skill_id == RK_CRUSHSTRIKE )
+ lv = 7;
+ if( pc_checkskill(sd,RK_RUNEMASTERY) >= lv )
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ }
+ break;
+
+ case RK_FIGHTINGSPIRIT:
+ if( flag&1 ) {
+ if( src == bl )
+ sc_start2(bl,type,100,skill_area_temp[5],10*(sd?pc_checkskill(sd,RK_RUNEMASTERY):10),skill_get_time(skill_id,skill_lv));
+ else
+ sc_start(bl,type,100,skill_area_temp[5]/4,skill_get_time(skill_id,skill_lv));
+ } else if( sd && pc_checkskill(sd,RK_RUNEMASTERY) >= 5 ) {
+ if( sd->status.party_id ) {
+ i = party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,BCT_PARTY,skill_area_sub_count);
+ skill_area_temp[5] = 7 * i; // ATK
+ party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,flag|BCT_PARTY|1,skill_castend_nodamage_id);
+ } else
+ sc_start2(bl,type,100,7,5,skill_get_time(skill_id,skill_lv));
+ }
+ clif_skill_nodamage(src,bl,skill_id,1,1);
+ break;
+ /**
+ * Guilotine Cross
+ **/
+ case GC_ROLLINGCUTTER:
+ {
+ short count = 1;
+ skill_area_temp[2] = 0;
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|SD_PREAMBLE|SD_SPLASH|1,skill_castend_damage_id);
+ if( tsc && tsc->data[SC_ROLLINGCUTTER] )
+ { // Every time the skill is casted the status change is reseted adding a counter.
+ count += (short)tsc->data[SC_ROLLINGCUTTER]->val1;
+ if( count > 10 )
+ count = 10; // Max coounter
+ status_change_end(bl, SC_ROLLINGCUTTER, INVALID_TIMER);
+ }
+ sc_start(bl,SC_ROLLINGCUTTER,100,count,skill_get_time(skill_id,skill_lv));
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ }
+ break;
+
+ case GC_WEAPONBLOCKING:
+ if( tsc && tsc->data[SC_WEAPONBLOCKING] )
+ status_change_end(bl, SC_WEAPONBLOCKING, INVALID_TIMER);
+ else
+ sc_start(bl,SC_WEAPONBLOCKING,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+
+ case GC_CREATENEWPOISON:
+ if( sd )
+ {
+ clif_skill_produce_mix_list(sd,skill_id,25);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ }
+ break;
+
+ case GC_POISONINGWEAPON:
+ if( sd ) {
+ clif_poison_list(sd,skill_lv);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case GC_ANTIDOTE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if( tsc )
+ {
+ status_change_end(bl, SC_PARALYSE, INVALID_TIMER);
+ status_change_end(bl, SC_PYREXIA, INVALID_TIMER);
+ status_change_end(bl, SC_DEATHHURT, INVALID_TIMER);
+ status_change_end(bl, SC_LEECHESEND, INVALID_TIMER);
+ status_change_end(bl, SC_VENOMBLEED, INVALID_TIMER);
+ status_change_end(bl, SC_MAGICMUSHROOM, INVALID_TIMER);
+ status_change_end(bl, SC_TOXIN, INVALID_TIMER);
+ status_change_end(bl, SC_OBLIVIONCURSE, INVALID_TIMER);
+ }
+ break;
+
+ case GC_PHANTOMMENACE:
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,
+ src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ break;
+
+ case GC_HALLUCINATIONWALK:
+ {
+ int heal = status_get_max_hp(bl) / 10;
+ if( status_get_hp(bl) < heal ) { // if you haven't enough HP skill fails.
+ if( sd ) clif_skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0);
+ break;
+ }
+ if( !status_charge(bl,heal,0) )
+ {
+ if( sd ) clif_skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ }
+ break;
+ /**
+ * Arch Bishop
+ **/
+ case AB_ANCILLA:
+ if( sd ) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_produce_mix(sd, skill_id, ITEMID_ANCILLA, 0, 0, 0, 1);
+ }
+ break;
+
+ case AB_CLEMENTIA:
+ case AB_CANTO:
+ {
+ int bless_lv = pc_checkskill(sd,AL_BLESSING) + (sd->status.job_level / 10);
+ int agi_lv = pc_checkskill(sd,AL_INCAGI) + (sd->status.job_level / 10);
+ if( sd == NULL || sd->status.party_id == 0 || flag&1 )
+ clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(bl,type,100,
+ (skill_id == AB_CLEMENTIA)? bless_lv : (skill_id == AB_CANTO)? agi_lv : skill_lv, skill_get_time(skill_id,skill_lv)));
+ else if( sd )
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ }
+ break;
+
+ case AB_PRAEFATIO:
+ if( sd == NULL || sd->status.party_id == 0 || flag&1 )
+ clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start4(bl, type, 100, skill_lv, 0, 0, 1, skill_get_time(skill_id, skill_lv)));
+ else if( sd )
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ break;
+
+ case AB_CHEAL:
+ if( sd == NULL || sd->status.party_id == 0 || flag&1 )
+ {
+ if( sd && tstatus && !battle_check_undead(tstatus->race, tstatus->def_ele) )
+ {
+ i = skill_calc_heal(src, bl, AL_HEAL, pc_checkskill(sd, AL_HEAL), true);
+
+ if( (dstsd && pc_ismadogear(dstsd)) || status_isimmune(bl))
+ i = 0; // Should heal by 0 or won't do anything?? in iRO it breaks the healing to members.. [malufett]
+
+ clif_skill_nodamage(bl, bl, skill_id, i, 1);
+ if( tsc && tsc->data[SC_AKAITSUKI] && i )
+ i = ~i + 1;
+ status_heal(bl, i, 0, 0);
+ }
+ }
+ else if( sd )
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ break;
+
+ case AB_ORATIO:
+ if( flag&1 )
+ sc_start(bl, type, 40 + 5 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv));
+ else
+ {
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv), BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ }
+ break;
+
+ case AB_LAUDAAGNUS:
+ if( flag&1 || sd == NULL ) {
+ if( tsc && (tsc->data[SC_FREEZE] || tsc->data[SC_STONE] || tsc->data[SC_BLIND] ||
+ tsc->data[SC_BURNING] || tsc->data[SC_FREEZING] || tsc->data[SC_CRYSTALIZE])) {
+ // Success Chance: (40 + 10 * Skill Level) %
+ if( rnd()%100 > 40+10*skill_lv ) break;
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ status_change_end(bl, SC_BLIND, INVALID_TIMER);
+ status_change_end(bl, SC_BURNING, INVALID_TIMER);
+ status_change_end(bl, SC_FREEZING, INVALID_TIMER);
+ status_change_end(bl, SC_CRYSTALIZE, INVALID_TIMER);
+ }else //Success rate only applies to the curing effect and not stat bonus. Bonus status only applies to non infected targets
+ clif_skill_nodamage(bl, bl, skill_id, skill_lv,
+ sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)));
+ } else if( sd )
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv),
+ src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ break;
+
+ case AB_LAUDARAMUS:
+ if( flag&1 || sd == NULL ) {
+ if( tsc && (tsc->data[SC_SLEEP] || tsc->data[SC_STUN] || tsc->data[SC_MANDRAGORA] || tsc->data[SC_SILENCE]) ){
+ // Success Chance: (40 + 10 * Skill Level) %
+ if( rnd()%100 > 40+10*skill_lv ) break;
+ status_change_end(bl, SC_SLEEP, INVALID_TIMER);
+ status_change_end(bl, SC_STUN, INVALID_TIMER);
+ status_change_end(bl, SC_MANDRAGORA, INVALID_TIMER);
+ status_change_end(bl, SC_SILENCE, INVALID_TIMER);
+ }else // Success rate only applies to the curing effect and not stat bonus. Bonus status only applies to non infected targets
+ clif_skill_nodamage(bl, bl, skill_id, skill_lv,
+ sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)));
+ } else if( sd )
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv),
+ src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ break;
+
+ case AB_CLEARANCE:
+ if( flag&1 || (i = skill_get_splash(skill_id, skill_lv)) < 1 )
+ { //As of the behavior in official server Clearance is just a super version of Dispell skill. [Jobbie]
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if((dstsd && (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) || rnd()%100 >= 30 + 10 * skill_lv)
+ {
+ if (sd)
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if(status_isimmune(bl) || !tsc || !tsc->count)
+ break;
+ for(i=0;i<SC_MAX;i++)
+ {
+ if (!tsc->data[i])
+ continue;
+ switch (i) {
+ case SC_WEIGHT50: case SC_WEIGHT90: case SC_HALLUCINATION:
+ 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: case SC_COMBO:
+ case SC_STRFOOD: case SC_AGIFOOD: case SC_VITFOOD:
+ case SC_INTFOOD: case SC_DEXFOOD: case SC_LUKFOOD:
+ case SC_HITFOOD: case SC_FLEEFOOD: case SC_BATKFOOD:
+ case SC_WATKFOOD: case SC_MATKFOOD: case SC_DANCING:
+ case SC_SPIRIT: case SC_AUTOBERSERK:
+ case SC_CARTBOOST: case SC_MELTDOWN: case SC_SAFETYWALL:
+ case SC_SMA: case SC_SPEEDUP0: case SC_NOCHAT:
+ case SC_ANKLE: case SC_SPIDERWEB: case SC_JAILED:
+ case SC_ITEMBOOST: case SC_EXPBOOST: case SC_LIFEINSURANCE:
+ case SC_BOSSMAPINFO: case SC_PNEUMA: case SC_AUTOSPELL:
+ case SC_INCHITRATE: case SC_INCATKRATE: case SC_NEN:
+ case SC_READYSTORM: case SC_READYDOWN: case SC_READYTURN:
+ case SC_READYCOUNTER:case SC_DODGE: case SC_WARM:
+ case SC_SPEEDUP1: case SC_AUTOTRADE: case SC_CRITICALWOUND:
+ case SC_JEXPBOOST: case SC_INVINCIBLE: case SC_INVINCIBLEOFF:
+ case SC_HELLPOWER: case SC_MANU_ATK: case SC_MANU_DEF:
+ case SC_SPL_ATK: case SC_SPL_DEF: case SC_MANU_MATK:
+ case SC_SPL_MATK: case SC_RICHMANKIM: case SC_ETERNALCHAOS:
+ case SC_DRUMBATTLE: case SC_NIBELUNGEN: case SC_ROKISWEIL:
+ case SC_INTOABYSS: case SC_SIEGFRIED: case SC_WHISTLE:
+ case SC_ASSNCROS: case SC_POEMBRAGI: case SC_APPLEIDUN:
+ case SC_HUMMING: case SC_DONTFORGETME: case SC_FORTUNE:
+ case SC_SERVICE4U: case SC_FOOD_STR_CASH: case SC_FOOD_AGI_CASH:
+ case SC_FOOD_VIT_CASH: case SC_FOOD_DEX_CASH: case SC_FOOD_INT_CASH:
+ case SC_FOOD_LUK_CASH: case SC_ELECTRICSHOCKER: case SC_BITE:
+ case SC__STRIPACCESSORY: case SC__ENERVATION: case SC__GROOMY:
+ case SC__IGNORANCE: case SC__LAZINESS: case SC__UNLUCKY:
+ case SC__WEAKNESS: //case SC_SAVAGE_STEAK: case SC_COCKTAIL_WARG_BLOOD:
+ case SC_MAGNETICFIELD://case SC_MINOR_BBQ: case SC_SIROMA_ICE_TEA:
+ //case SC_DROCERA_HERB_STEAMED: case SC_PUTTI_TAILS_NOODLES:
+ case SC_NEUTRALBARRIER_MASTER: case SC_NEUTRALBARRIER:
+ case SC_STEALTHFIELD_MASTER: case SC_STEALTHFIELD:
+ case SC_LEADERSHIP: case SC_GLORYWOUNDS: case SC_SOULCOLD:
+ case SC_HAWKEYES: case SC_GUILDAURA: case SC_PUSH_CART:
+ case SC_PARTYFLEE: case SC_GT_REVITALIZE:
+ case SC_RAISINGDRAGON: case SC_GT_ENERGYGAIN: case SC_GT_CHANGE:
+#ifdef RENEWAL
+ case SC_EXTREMITYFIST2:
+#endif
+ continue;
+ case SC_ASSUMPTIO:
+ if( bl->type == BL_MOB )
+ continue;
+ break;
+ }
+ if(i==SC_BERSERK || i==SC_SATURDAYNIGHTFEVER) tsc->data[i]->val2=0; //Mark a dispelled berserk to avoid setting hp to 100 by setting hp penalty to 0.
+ status_change_end(bl,(sc_type)i,INVALID_TIMER);
+ }
+ break;
+ }
+ map_foreachinrange(skill_area_sub, bl, i, BL_CHAR, src, skill_id, skill_lv, tick, flag|1, skill_castend_damage_id);
+ break;
+
+ case AB_SILENTIUM:
+ // Should the level of Lex Divina be equivalent to the level of Silentium or should the highest level learned be used? [LimitLine]
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id, skill_lv), BL_CHAR,
+ src, PR_LEXDIVINA, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ break;
+ /**
+ * Warlock
+ **/
+ case WL_STASIS:
+ if( flag&1 )
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ else
+ {
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id, skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,(map_flag_vs(src->m)?BCT_ALL:BCT_ENEMY|BCT_SELF)|flag|1,skill_castend_nodamage_id);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ }
+ break;
+
+ case WL_WHITEIMPRISON:
+ if( (src == bl || battle_check_target(src, bl, BCT_ENEMY)) && !is_boss(bl) )// Should not work with bosses.
+ {
+ int rate = ( sd? sd->status.job_level : 50 ) / 4;
+
+ if( src == bl ) rate = 100; // Success Chance: On self, 100%
+ else if(bl->type == BL_PC) rate += 20 + 10 * skill_lv; // On Players, (20 + 10 * Skill Level) %
+ else rate += 40 + 10 * skill_lv; // On Monsters, (40 + 10 * Skill Level) %
+
+ if( sd )
+ skill_blockpc_start(sd,skill_id,4000);
+
+ if( !(tsc && tsc->data[type]) ){
+ i = sc_start2(bl,type,rate,skill_lv,src->id,(src == bl)?5000:(bl->type == BL_PC)?skill_get_time(skill_id,skill_lv):skill_get_time2(skill_id, skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,i);
+ if( !i )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ }else
+ if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_TOTARGET,0);
+ break;
+
+ case WL_FROSTMISTY:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_CHAR|BL_SKILL,src,skill_id,skill_lv,tick,flag|BCT_ENEMY,skill_castend_damage_id);
+ break;
+
+ case WL_JACKFROST:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_foreachinshootrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_CHAR|BL_SKILL,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ break;
+
+ case WL_MARSHOFABYSS:
+ // Should marsh of abyss still apply half reduction to players after the 28/10 patch? [LimitLine]
+ clif_skill_nodamage(src, bl, skill_id, skill_lv,
+ sc_start4(bl, type, 100, skill_lv, status_get_int(src), sd ? sd->status.job_level : 50, 0,
+ skill_get_time(skill_id, skill_lv)));
+ break;
+
+ case WL_SIENNAEXECRATE:
+ if( status_isimmune(bl) || !tsc )
+ break;
+
+ if( flag&1 ) {
+ if( bl->id == skill_area_temp[1] )
+ break; // Already work on this target
+
+ if( tsc && tsc->data[SC_STONE] )
+ status_change_end(bl,SC_STONE,INVALID_TIMER);
+ else
+ status_change_start(bl,SC_STONE,10000,skill_lv,0,0,1000,skill_get_time(skill_id, skill_lv),2);
+ } else {
+ int rate = 40 + 8 * skill_lv + ( sd? sd->status.job_level : 50 ) / 4;
+ // IroWiki says Rate should be reduced by target stats, but currently unknown
+ if( rnd()%100 < rate ) { // Success on First Target
+ if( !tsc->data[SC_STONE] )
+ rate = status_change_start(bl,SC_STONE,10000,skill_lv,0,0,1000,skill_get_time(skill_id, skill_lv),2);
+ else {
+ rate = 1;
+ status_change_end(bl,SC_STONE,INVALID_TIMER);
+ }
+
+ if( rate ) {
+ skill_area_temp[1] = bl->id;
+ map_foreachinrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_nodamage_id);
+ }
+ // Doesn't send failure packet if it fails on defense.
+ }
+ else if( sd ) // Failure on Rate
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ break;
+
+ case WL_SUMMONFB:
+ case WL_SUMMONBL:
+ case WL_SUMMONWB:
+ case WL_SUMMONSTONE:
+ {
+ short element = 0, sctype = 0, pos = -1;
+ struct status_change *sc = status_get_sc(src);
+ if( !sc ) break;
+
+ for( i = SC_SPHERE_1; i <= SC_SPHERE_5; i++ )
+ {
+ if( !sctype && !sc->data[i] )
+ sctype = i; // Take the free SC
+ if( sc->data[i] )
+ pos = max(sc->data[i]->val2,pos);
+ }
+
+ if( !sctype )
+ {
+ if( sd ) // No free slots to put SC
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_SUMMON,0);
+ break;
+ }
+
+ pos++; // Used in val2 for SC. Indicates the order of this ball
+ switch( skill_id )
+ { // Set val1. The SC element for this ball
+ case WL_SUMMONFB: element = WLS_FIRE; break;
+ case WL_SUMMONBL: element = WLS_WIND; break;
+ case WL_SUMMONWB: element = WLS_WATER; break;
+ case WL_SUMMONSTONE: element = WLS_STONE; break;
+ }
+
+ sc_start4(src,sctype,100,element,pos,skill_lv,0,skill_get_time(skill_id,skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,0,0);
+ }
+ break;
+
+ case WL_READING_SB:
+ if( sd ) {
+ struct status_change *sc = status_get_sc(bl);
+
+ for( i = SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++)
+ if( sc && !sc->data[i] )
+ break;
+ if( i == SC_MAXSPELLBOOK ) {
+ clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_READING, 0);
+ break;
+ }
+
+ sc_start(bl, SC_STOP, 100, skill_lv, INVALID_TIMER); //Can't move while selecting a spellbook.
+ clif_spellbook_list(sd);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ }
+ break;
+ /**
+ * Ranger
+ **/
+ case RA_FEARBREEZE:
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)));
+ break;
+
+ case RA_WUGMASTERY:
+ if( sd ) {
+ if( !pc_iswug(sd) )
+ pc_setoption(sd,sd->sc.option|OPTION_WUG);
+ else
+ pc_setoption(sd,sd->sc.option&~OPTION_WUG);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case RA_WUGRIDER:
+ if( sd ) {
+ if( !pc_isridingwug(sd) && pc_iswug(sd) ) {
+ pc_setoption(sd,sd->sc.option&~OPTION_WUG);
+ pc_setoption(sd,sd->sc.option|OPTION_WUGRIDER);
+ } else if( pc_isridingwug(sd) ) {
+ pc_setoption(sd,sd->sc.option&~OPTION_WUGRIDER);
+ pc_setoption(sd,sd->sc.option|OPTION_WUG);
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case RA_WUGDASH:
+ if( tsce ) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,status_change_end(bl, type, INVALID_TIMER));
+ map_freeblock_unlock();
+ return 0;
+ }
+ if( sd && pc_isridingwug(sd) ) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start4(bl,type,100,skill_lv,unit_getdir(bl),0,0,1));
+ clif_walkok(sd);
+ }
+ break;
+
+ case RA_SENSITIVEKEEN:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR|BL_SKILL,src,skill_id,skill_lv,tick,flag|BCT_ENEMY,skill_castend_damage_id);
+ break;
+ /**
+ * Mechanic
+ **/
+ case NC_F_SIDESLIDE:
+ case NC_B_SIDESLIDE:
+ {
+ uint8 dir = (skill_id == NC_F_SIDESLIDE) ? (unit_getdir(src)+4)%8 : unit_getdir(src);
+ skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),dir,0x1);
+ clif_slide(src,src->x,src->y);
+ clif_fixpos(src); //Aegis sent this packet
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case NC_SELFDESTRUCTION:
+ if( sd ) {
+ if( pc_ismadogear(sd) )
+ pc_setmadogear(sd, 0);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ skill_castend_damage_id(src, src, skill_id, skill_lv, tick, flag);
+ status_set_sp(src, 0, 0);
+ }
+ break;
+
+ case NC_ANALYZE:
+ clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv,
+ sc_start(bl,type, 30 + 12 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv)));
+ if( sd ) pc_overheat(sd,1);
+ break;
+
+ case NC_MAGNETICFIELD:
+ if( (i = sc_start2(bl,type,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv))) )
+ {
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),splash_target(src),src,skill_id,skill_lv,tick,flag|BCT_ENEMY|SD_SPLASH|1,skill_castend_damage_id);;
+ clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,6);
+ if (sd) pc_overheat(sd,1);
+ }
+ clif_skill_nodamage(src,src,skill_id,skill_lv,i);
+ break;
+
+ case NC_REPAIR:
+ if( sd )
+ {
+ int heal;
+ if( dstsd && pc_ismadogear(dstsd) )
+ {
+ heal = dstsd->status.max_hp * (3+3*skill_lv) / 100;
+ status_heal(bl,heal,0,2);
+ } else {
+ heal = sd->status.max_hp * (3+3*skill_lv) / 100;
+ status_heal(src,heal,0,2);
+ }
+
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, heal);
+ }
+ break;
+
+ case NC_DISJOINT:
+ {
+ if( bl->type != BL_MOB ) break;
+ md = map_id2md(bl->id);
+ if( md && md->class_ >= MOBID_SILVERSNIPER && md->class_ <= MOBID_MAGICDECOY_WIND )
+ status_kill(bl);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ }
+ break;
+ case SC_AUTOSHADOWSPELL:
+ if( sd ) {
+ if( sd->status.skill[sd->reproduceskill_id].id || sd->status.skill[sd->cloneskill_id].id ) {
+ sc_start(src,SC_STOP,100,skill_lv,-1);// The skill_lv is stored in val1 used in skill_select_menu to determine the used skill lvl [Xazax]
+ clif_autoshadowspell_list(sd);
+ clif_skill_nodamage(src,bl,skill_id,1,1);
+ }
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_IMITATION_SKILL_NONE,0);
+ }
+ break;
+
+ case SC_SHADOWFORM:
+ if( sd && dstsd && src != bl && !dstsd->shadowform_id ) {
+ if( clif_skill_nodamage(src,bl,skill_id,skill_lv,sc_start4(src,type,100,skill_lv,bl->id,4+skill_lv,0,skill_get_time(skill_id, skill_lv))) )
+ dstsd->shadowform_id = src->id;
+ }
+ else if( sd )
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ break;
+
+ case SC_BODYPAINT:
+ if( flag&1 ) {
+ if( tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING] ||
+ tsc->data[SC_CHASEWALK] || tsc->data[SC_CLOAKINGEXCEED] ||
+ tsc->data[SC__INVISIBILITY]) ) {
+ status_change_end(bl, SC_HIDING, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
+ status_change_end(bl, SC_CHASEWALK, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
+ status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER);
+
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ sc_start(bl,SC_BLIND,53 + 2 * skill_lv,skill_lv,skill_get_time(skill_id,skill_lv));
+ }
+ } else {
+ clif_skill_nodamage(src, bl, skill_id, 0, 1);
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ }
+ break;
+
+ case SC_ENERVATION:
+ case SC_GROOMY:
+ case SC_LAZINESS:
+ case SC_UNLUCKY:
+ case SC_WEAKNESS:
+ if( !(tsc && tsc->data[type]) ) {
+ //((rand(myDEX / 12, myDEX / 4) + myJobLevel + 10 * skLevel) + myLevel / 10) - (targetLevel / 10 + targetLUK / 10 + (targetMaxWeight - targetWeight) / 1000 + rand(targetAGI / 6, targetAGI / 3))
+ int rate = rnd_value(sstatus->dex/12,sstatus->dex/4) + 10*skill_lv + (sd?sd->status.job_level:0) + status_get_lv(src)/10
+ - status_get_lv(bl)/10 - tstatus->luk/10 - (dstsd?(dstsd->max_weight-dstsd->weight)/10000:0) - rnd_value(tstatus->agi/6,tstatus->agi/3);
+ rate = cap_value(rate, skill_lv+sstatus->dex/20, 100);
+ clif_skill_nodamage(src,bl,skill_id,0,sc_start(bl,type,rate,skill_lv,skill_get_time(skill_id,skill_lv)));
+ } else if( sd )
+ clif_skill_fail(sd,skill_id,0,0);
+ break;
+
+ case SC_IGNORANCE:
+ if( !(tsc && tsc->data[type]) ) {
+ int rate = rnd_value(sstatus->dex/12,sstatus->dex/4) + 10*skill_lv + (sd?sd->status.job_level:0) + status_get_lv(src)/10
+ - status_get_lv(bl)/10 - tstatus->luk/10 - (dstsd?(dstsd->max_weight-dstsd->weight)/10000:0) - rnd_value(tstatus->agi/6,tstatus->agi/3);
+ rate = cap_value(rate, skill_lv+sstatus->dex/20, 100);
+ if (clif_skill_nodamage(src,bl,skill_id,0,sc_start(bl,type,rate,skill_lv,skill_get_time(skill_id,skill_lv)))) {
+ int sp = 200 * skill_lv;
+ if( dstmd ) sp = dstmd->level * 2;
+ if( status_zap(bl,0,sp) )
+ status_heal(src,0,sp/2,3);
+ }
+ else if( sd ) clif_skill_fail(sd,skill_id,0,0);
+ } else if( sd )
+ clif_skill_fail(sd,skill_id,0,0);
+ break;
+
+ case LG_TRAMPLE:
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ map_foreachinrange(skill_destroy_trap,bl,skill_get_splash(skill_id,skill_lv),BL_SKILL,tick);
+ break;
+
+ case LG_REFLECTDAMAGE:
+ if( tsc && tsc->data[type] )
+ status_change_end(bl,type,INVALID_TIMER);
+ else
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ break;
+
+ case LG_SHIELDSPELL:
+ if( flag&1 ) {
+ int duration = (sd) ? sd->bonus.shieldmdef * 2000 : 10000;
+ sc_start(bl,SC_SILENCE,100,skill_lv,duration);
+ } else if( sd ) {
+ int opt = skill_lv;
+ int rate = rnd()%100;
+ int val, brate;
+ switch( skill_lv ) {
+ case 1:
+ {
+ struct item_data *shield_data = sd->inventory_data[sd->equip_index[EQI_HAND_L]];
+ if( !shield_data || shield_data->type != IT_ARMOR ) { // No shield?
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ break;
+ }
+ brate = shield_data->def * 10;
+ if( rate < 50 )
+ opt = 1;
+ else if( rate < 75 )
+ opt = 2;
+ else
+ opt = 3;
+
+ switch( opt ) {
+ case 1:
+ sc_start(bl,SC_SHIELDSPELL_DEF,100,opt,-1);
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( rate < brate )
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ status_change_end(bl,SC_SHIELDSPELL_DEF,INVALID_TIMER);
+ break;
+ case 2:
+ val = shield_data->def / 10; // % Reflected damage.
+ sc_start2(bl,SC_SHIELDSPELL_DEF,brate,opt,val,shield_data->def * 1000);
+ break;
+ case 3:
+ val = shield_data->def; // Attack increase.
+ sc_start2(bl,SC_SHIELDSPELL_DEF,brate,opt,val,shield_data->def * 3000);
+ break;
+ }
+ }
+ break;
+
+ case 2:
+ brate = sd->bonus.shieldmdef * 20;
+ if( rate < 30 )
+ opt = 1;
+ else if( rate < 60 )
+ opt = 2;
+ else
+ opt = 3;
+ switch( opt ) {
+ case 1:
+ sc_start(bl,SC_SHIELDSPELL_MDEF,100,opt,-1);
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( rate < brate )
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|2,skill_castend_damage_id);
+ status_change_end(bl,SC_SHIELDSPELL_MDEF,INVALID_TIMER);
+ break;
+ case 2:
+ sc_start(bl,SC_SHIELDSPELL_MDEF,100,opt,-1);
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( rate < brate )
+ map_foreachinrange(skill_area_sub,src,skill_get_splash(skill_id,skill_lv),BL_CHAR,src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_nodamage_id);
+ break;
+ case 3:
+ if( sc_start(bl,SC_SHIELDSPELL_MDEF,brate,opt,sd->bonus.shieldmdef * 30000) )
+ clif_skill_nodamage(src,bl,PR_MAGNIFICAT,skill_lv,
+ sc_start(bl,SC_MAGNIFICAT,100,1,sd->bonus.shieldmdef * 30000));
+ break;
+ }
+ break;
+
+ case 3:
+ {
+ struct item *it = &sd->status.inventory[sd->equip_index[EQI_HAND_L]];
+ if( !it ) { // No shield?
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ brate = it->refine * 5;
+ if( rate < 25 )
+ opt = 1;
+ else if( rate < 50 )
+ opt = 2;
+ else
+ opt = 3;
+ switch( opt ) {
+ case 1:
+ val = 105 * it->refine / 10;
+ sc_start2(bl,SC_SHIELDSPELL_REF,brate,opt,val,skill_get_time(skill_id,skill_lv));
+ break;
+ case 2: case 3:
+ if( rate < brate )
+ {
+ val = sstatus->max_hp * (11 + it->refine) / 100;
+ status_heal(bl, val, 0, 3);
+ }
+ break;
+ /*case 3:
+ // Full protection. I need confirm what effect should be here. Moved to case 2 to until we got it.
+ break;*/
+ }
+ }
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case LG_PIETY:
+ if( flag&1 )
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ else {
+ skill_area_temp[2] = 0;
+ map_foreachinrange(skill_area_sub,bl,skill_get_splash(skill_id,skill_lv),BL_PC,src,skill_id,skill_lv,tick,flag|SD_PREAMBLE|BCT_PARTY|BCT_SELF|1,skill_castend_nodamage_id);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case LG_INSPIRATION:
+ if( sd && !map[sd->bl.m].flag.noexppenalty && sd->status.base_level != MAX_LEVEL ) {
+ sd->status.base_exp -= min(sd->status.base_exp, pc_nextbaseexp(sd) * 1 / 100); // 1% penalty.
+ sd->status.job_exp -= min(sd->status.job_exp, pc_nextjobexp(sd) * 1 / 100);
+ clif_updatestatus(sd,SP_BASEEXP);
+ clif_updatestatus(sd,SP_JOBEXP);
+ }
+ clif_skill_nodamage(bl,src,skill_id,skill_lv,
+ sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)));
+ break;
+ case SR_CURSEDCIRCLE:
+ if( flag&1 ) {
+ if( is_boss(bl) ) break;
+ if( sc_start2(bl, type, 100, skill_lv, src->id, skill_get_time(skill_id, skill_lv))) {
+ if( bl->type == BL_MOB )
+ mob_unlocktarget((TBL_MOB*)bl,gettick());
+ unit_stop_attack(bl);
+ clif_bladestop(src, bl->id, 1);
+ map_freeblock_unlock();
+ return 1;
+ }
+ } else {
+ int count = 0;
+ clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ count = map_forcountinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv), (sd)?sd->spiritball_old:15, // Assume 15 spiritballs in non-charactors
+ BL_CHAR, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ if( sd ) pc_delspiritball(sd, count, 0);
+ clif_skill_nodamage(src, src, skill_id, skill_lv,
+ sc_start2(src, SC_CURSEDCIRCLE_ATKER, 100, skill_lv, count, skill_get_time(skill_id,skill_lv)));
+ }
+ break;
+
+ case SR_RAISINGDRAGON:
+ if( sd ) {
+ short max = 5 + skill_lv;
+ sc_start(bl, SC_EXPLOSIONSPIRITS, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ for( i = 0; i < max; i++ ) // Don't call more than max available spheres.
+ pc_addspiritball(sd, skill_get_time(skill_id, skill_lv), max);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, sc_start(bl, type, 100, skill_lv,skill_get_time(skill_id, skill_lv)));
+ }
+ break;
+
+ case SR_ASSIMILATEPOWER:
+ if( flag&1 ) {
+ i = 0;
+ if( dstsd && dstsd->spiritball && (sd == dstsd || map_flag_vs(src->m)) && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER )
+ {
+ i = dstsd->spiritball; //1%sp per spiritball.
+ pc_delspiritball(dstsd, dstsd->spiritball, 0);
+ }
+ if( i ) status_percent_heal(src, 0, i);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, i ? 1:0);
+ } else {
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|BCT_SELF|SD_SPLASH|1, skill_castend_nodamage_id);
+ }
+ break;
+
+ case SR_POWERVELOCITY:
+ if( !dstsd )
+ break;
+ if( sd && dstsd->spiritball <= 5 ) {
+ for(i = 0; i <= 5; i++) {
+ pc_addspiritball(dstsd, skill_get_time(MO_CALLSPIRITS, pc_checkskill(sd,MO_CALLSPIRITS)), i);
+ pc_delspiritball(sd, sd->spiritball, 0);
+ }
+ }
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ break;
+
+ case SR_GENTLETOUCH_CURE:
+ {
+ int heal;
+
+ if( status_isimmune(bl) )
+ {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,0);
+ break;
+ }
+
+ heal = 120 * skill_lv + status_get_max_hp(bl) * (2 + skill_lv) / 100;
+ status_heal(bl, heal, 0, 0);
+
+ if( (tsc && tsc->opt1) && (rnd()%100 < ((skill_lv * 5) + (status_get_dex(src) + status_get_lv(src)) / 4) - (1 + (rnd() % 10))) )
+ {
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ status_change_end(bl, SC_STUN, INVALID_TIMER);
+ status_change_end(bl, SC_POISON, INVALID_TIMER);
+ status_change_end(bl, SC_SILENCE, INVALID_TIMER);
+ status_change_end(bl, SC_BLIND, INVALID_TIMER);
+ status_change_end(bl, SC_HALLUCINATION, INVALID_TIMER);
+ status_change_end(bl, SC_BURNING, INVALID_TIMER);
+ status_change_end(bl, SC_FREEZING, INVALID_TIMER);
+ }
+
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+ case SR_GENTLETOUCH_CHANGE:
+ case SR_GENTLETOUCH_REVITALIZE:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(bl,type,100,skill_lv,src->id,skill_get_time(skill_id,skill_lv)));
+ break;
+ case WA_SWING_DANCE:
+ case WA_MOONLIT_SERENADE:
+ if( sd == NULL || sd->status.party_id == 0 || (flag & 1) )
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ else if( sd ) { // Only shows effects on caster.
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ }
+ break;
+
+ case WA_SYMPHONY_OF_LOVER:
+ case MI_RUSH_WINDMILL:
+ case MI_ECHOSONG:
+ if( sd == NULL || sd->status.party_id == 0 || (flag & 1) )
+ sc_start4(bl,type,100,skill_lv,6*skill_lv,(sd?pc_checkskill(sd,WM_LESSON):0),(sd?sd->status.job_level:0),skill_get_time(skill_id,skill_lv));
+ else if( sd ) { // Only shows effects on caster.
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ party_foreachsamemap(skill_area_sub, sd, skill_get_splash(skill_id, skill_lv), src, skill_id, skill_lv, tick, flag|BCT_PARTY|1, skill_castend_nodamage_id);
+ }
+ break;
+
+ case MI_HARMONIZE:
+ if( src != bl )
+ clif_skill_nodamage(src, src, skill_id, skill_lv, sc_start(src, type, 100, skill_lv, skill_get_time(skill_id,skill_lv)));
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id,skill_lv)));
+ break;
+
+ case WM_DEADHILLHERE:
+ if( bl->type == BL_PC ) {
+ if( !status_isdead(bl) )
+ break;
+
+ if( rnd()%100 < 88 + 2 * skill_lv ) {
+ int heal = tstatus->sp;
+ if( heal <= 0 )
+ heal = 1;
+ tstatus->hp = heal;
+ tstatus->sp -= tstatus->sp * ( 120 - 20 * skill_lv ) / 100;
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ pc_revive((TBL_PC*)bl,heal,0);
+ clif_resurrection(bl,1);
+ }
+ }
+ break;
+
+ case WM_SIRCLEOFNATURE:
+ flag |= BCT_SELF|BCT_PARTY|BCT_GUILD;
+ case WM_VOICEOFSIREN:
+ if( skill_id != WM_SIRCLEOFNATURE )
+ flag &= ~BCT_SELF;
+ if( flag&1 ) {
+ sc_start2(bl,type,(skill_id==WM_VOICEOFSIREN)?20+10*skill_lv:100,skill_lv,(skill_id==WM_VOICEOFSIREN)?src->id:0,skill_get_time(skill_id,skill_lv));
+ } else {
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),(skill_id==WM_VOICEOFSIREN)?BL_CHAR|BL_SKILL:BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case WM_GLOOMYDAY:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ if( dstsd && ( pc_checkskill(dstsd,KN_BRANDISHSPEAR) || pc_checkskill(dstsd,LK_SPIRALPIERCE) ||
+ pc_checkskill(dstsd,CR_SHIELDCHARGE) || pc_checkskill(dstsd,CR_SHIELDBOOMERANG) ||
+ pc_checkskill(dstsd,PA_SHIELDCHAIN) || pc_checkskill(dstsd,LG_SHIELDPRESS) ) )
+ {
+ sc_start(bl,SC_GLOOMYDAY_SK,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+ }
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ break;
+
+ case WM_SATURDAY_NIGHT_FEVER:
+ if( flag&1 ) { // Affect to all targets arround the caster and caster too.
+ if( !(tsc && tsc->data[type]) )
+ sc_start(bl, type, 100, skill_lv,skill_get_time(skill_id, skill_lv));
+ } else if( flag&2 ) {
+ if( src->id != bl->id && battle_check_target(src,bl,BCT_ENEMY) > 0 )
+ status_fix_damage(src,bl,9999,clif_damage(src,bl,tick,0,0,9999,0,0,0));
+ } else if( sd ) {
+ short chance = sstatus->int_/6 + sd->status.job_level/5 + skill_lv*4;
+ if( !sd->status.party_id || (rnd()%100 > chance)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_HELPER,0);
+ break;
+ }
+ if( map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id,skill_lv),
+ BL_PC, src, skill_id, skill_lv, tick, BCT_ENEMY, skill_area_sub_count) > 7 )
+ flag |= 2;
+ else
+ flag |= 1;
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|BCT_SELF, skill_castend_nodamage_id);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv,
+ sc_start(src,SC_STOP,100,skill_lv,skill_get_time2(skill_id,skill_lv)));
+ if( flag&2 ) // Dealed here to prevent conflicts
+ status_fix_damage(src,bl,9999,clif_damage(src,bl,tick,0,0,9999,0,0,0));
+ }
+ break;
+
+ case WM_SONG_OF_MANA:
+ case WM_DANCE_WITH_WUG:
+ case WM_LERADS_DEW:
+ if( flag&1 ) { // These affect to to all party members near the caster.
+ struct status_change *sc = status_get_sc(src);
+ if( sc && sc->data[type] ) {
+ sc_start2(bl,type,100,skill_lv,sc->data[type]->val2,skill_get_time(skill_id,skill_lv));
+ }
+ } else if( sd ) {
+ short lv = (short)skill_lv;
+ int count = skill_check_pc_partner(sd,skill_id,&lv,skill_get_splash(skill_id,skill_lv),1);
+ if( sc_start2(bl,type,100,skill_lv,count,skill_get_time(skill_id,skill_lv)) )
+ party_foreachsamemap(skill_area_sub,sd,skill_get_splash(skill_id,skill_lv),src,skill_id,skill_lv,tick,flag|BCT_PARTY|1,skill_castend_nodamage_id);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+
+ }
+ break;
+
+ case WM_MELODYOFSINK:
+ case WM_BEYOND_OF_WARCRY:
+ case WM_UNLIMITED_HUMMING_VOICE:
+ if( flag&1 ) {
+ sc_start2(bl,type,100,skill_lv,skill_area_temp[0],skill_get_time(skill_id,skill_lv));
+ } else { // These affect to all targets arround the caster.
+ short lv = (short)skill_lv;
+ skill_area_temp[0] = (sd) ? skill_check_pc_partner(sd,skill_id,&lv,skill_get_splash(skill_id,skill_lv),1) : 50; // 50% chance in non BL_PC (clones).
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),BL_PC, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case WM_RANDOMIZESPELL: {
+ int improv_skill_id = 0, improv_skill_lv;
+ do {
+ i = rnd() % MAX_SKILL_IMPROVISE_DB;
+ improv_skill_id = skill_improvise_db[i].skill_id;
+ } while( improv_skill_id == 0 || rnd()%10000 >= skill_improvise_db[i].per );
+ improv_skill_lv = 4 + skill_lv;
+ clif_skill_nodamage (src, bl, skill_id, skill_lv, 1);
+
+ if( sd ) {
+ sd->state.abra_flag = 2;
+ sd->skillitem = improv_skill_id;
+ sd->skillitemlv = improv_skill_lv;
+ clif_item_skill(sd, improv_skill_id, improv_skill_lv);
+ } else {
+ struct unit_data *ud = unit_bl2ud(src);
+ int inf = skill_get_inf(improv_skill_id);
+ int target_id = 0;
+ if (!ud) break;
+ if (inf&INF_SELF_SKILL || inf&INF_SUPPORT_SKILL) {
+ if (src->type == BL_PET)
+ bl = (struct block_list*)((TBL_PET*)src)->msd;
+ if (!bl) bl = src;
+ unit_skilluse_id(src, bl->id, improv_skill_id, improv_skill_lv);
+ } else {
+ if (ud->target)
+ target_id = ud->target;
+ else switch (src->type) {
+ case BL_MOB: target_id = ((TBL_MOB*)src)->target_id; break;
+ case BL_PET: target_id = ((TBL_PET*)src)->target_id; break;
+ }
+ if (!target_id)
+ break;
+ if (skill_get_casttype(improv_skill_id) == CAST_GROUND) {
+ bl = map_id2bl(target_id);
+ if (!bl) bl = src;
+ unit_skilluse_pos(src, bl->x, bl->y, improv_skill_id, improv_skill_lv);
+ } else
+ unit_skilluse_id(src, target_id, improv_skill_id, improv_skill_lv);
+ }
+ }
+ }
+ break;
+
+
+ case RETURN_TO_ELDICASTES:
+ case ALL_GUARDIAN_RECALL:
+ if( sd )
+ {
+ short x, y; // Destiny position.
+ unsigned short mapindex;
+
+ if( skill_id == RETURN_TO_ELDICASTES)
+ {
+ x = 198;
+ y = 187;
+ mapindex = mapindex_name2id(MAP_DICASTES);
+ }
+ else
+ {
+ x = 44;
+ y = 151;
+ mapindex = mapindex_name2id(MAP_MORA);
+ }
+
+ if(!mapindex)
+ { //Given map not found?
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ pc_setpos(sd, mapindex, x, y, CLR_TELEPORT);
+ }
+ break;
+
+ case GM_SANDMAN:
+ if( tsc ) {
+ if( tsc->opt1 == OPT1_SLEEP )
+ tsc->opt1 = 0;
+ else
+ tsc->opt1 = OPT1_SLEEP;
+ clif_changeoption(bl);
+ clif_skill_nodamage (src, bl, skill_id, skill_lv, 1);
+ }
+ break;
+
+ case SO_ARRULLO:
+ {
+ // [(15 + 5 * Skill Level) + ( Caster’s INT / 5 ) + ( Caster’s Job Level / 5 ) - ( Target’s INT / 6 ) - ( Target’s LUK / 10 )] %
+ int rate = (15 + 5 * skill_lv) + status_get_int(src)/5 + (sd)?sd->status.job_level:0;
+ rate -= status_get_int(bl)/6 - status_get_luk(bl)/10;
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ sc_start2(bl, type, rate, skill_lv, 1, skill_get_time(skill_id, skill_lv));
+ }
+ break;
+
+ case WM_LULLABY_DEEPSLEEP:
+ if( flag&1 ){
+ //[(Skill Level x 4) + (Voice Lessons Skill Level x 2) + (Caster’s Base Level / 15) + (Caster’s Job Level / 5)] %
+ int rate = (4 * skill_lv) + ( (sd) ? pc_checkskill(sd,WM_LESSON)*2 + sd->status.job_level/5 : 0 ) + status_get_lv(src) / 15;
+ if( bl != src )
+ sc_start(bl,type,rate,skill_lv,skill_get_time(skill_id,skill_lv));
+ }else {
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ALL|1, skill_castend_nodamage_id);
+ }
+ break;
+
+ case SO_SUMMON_AGNI:
+ case SO_SUMMON_AQUA:
+ case SO_SUMMON_VENTUS:
+ case SO_SUMMON_TERA:
+ if( sd ) {
+ int elemental_class = skill_get_elemental_type(skill_id,skill_lv);
+
+ // Remove previous elemental fisrt.
+ if( sd->ed )
+ elemental_delete(sd->ed,0);
+
+ // Summoning the new one.
+ if( !elemental_create(sd,elemental_class,skill_get_time(skill_id,skill_lv)) ) {
+ clif_skill_fail(sd,skill_id,0,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case SO_EL_CONTROL:
+ if( sd ) {
+ int mode = EL_MODE_PASSIVE; // Standard mode.
+
+ if( !sd->ed ) break;
+
+ if( skill_lv == 4 ) {// At level 4 delete elementals.
+ elemental_delete(sd->ed, 0);
+ break;
+ }
+ switch( skill_lv ) {// Select mode bassed on skill level used.
+ case 2: mode = EL_MODE_ASSIST; break;
+ case 3: mode = EL_MODE_AGGRESSIVE; break;
+ }
+ if( !elemental_change_mode(sd->ed,mode) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case SO_EL_ACTION:
+ if( sd ) {
+ int duration = 3000;
+ if( !sd->ed ) break;
+ sd->skill_id_old = skill_id;
+ elemental_action(sd->ed, bl, tick);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ switch(sd->ed->db->class_){
+ case 2115:case 2124:
+ case 2118:case 2121:
+ duration = 6000;
+ break;
+ case 2116:case 2119:
+ case 2122:case 2125:
+ duration = 9000;
+ break;
+ }
+ skill_blockpc_start(sd, skill_id, duration);
+ }
+ break;
+
+ case SO_EL_CURE:
+ if( sd ) {
+ struct elemental_data *ed = sd->ed;
+ int s_hp = sd->battle_status.hp * 10 / 100, s_sp = sd->battle_status.sp * 10 / 100;
+ int e_hp, e_sp;
+
+ if( !ed ) break;
+ if( !status_charge(&sd->bl,s_hp,s_sp) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ e_hp = ed->battle_status.max_hp * 10 / 100;
+ e_sp = ed->battle_status.max_sp * 10 / 100;
+ status_heal(&ed->bl,e_hp,e_sp,3);
+ clif_skill_nodamage(src,&ed->bl,skill_id,skill_lv,1);
+ }
+ break;
+
+ case GN_CHANGEMATERIAL:
+ case SO_EL_ANALYSIS:
+ if( sd ) {
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ clif_skill_itemlistwindow(sd,skill_id,skill_lv);
+ }
+ break;
+
+ case GN_BLOOD_SUCKER:
+ {
+ struct status_change *sc = status_get_sc(src);
+
+ if( sc && sc->bs_counter < skill_get_maxcount( skill_id , skill_lv) ) {
+ if( tsc && tsc->data[type] ){
+ (sc->bs_counter)--;
+ status_change_end(src, type, INVALID_TIMER); // the first one cancels and the last one will take effect resetting the timer
+ }
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ sc_start2(bl, type, 100, skill_lv, src->id, skill_get_time(skill_id,skill_lv));
+ (sc->bs_counter)++;
+ } else if( sd ) {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ break;
+ }
+ }
+ break;
+
+ case GN_MANDRAGORA:
+ if( flag&1 ) {
+ if ( clif_skill_nodamage(bl, src, skill_id, skill_lv,
+ sc_start(bl, type, 25 + 10 * skill_lv, skill_lv, skill_get_time(skill_id, skill_lv))) )
+ status_zap(bl, 0, status_get_max_sp(bl) * (25 + 5 * skill_lv) / 100);
+ } else
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ break;
+
+ case GN_SLINGITEM:
+ if( sd ) {
+ short ammo_id;
+ i = sd->equip_index[EQI_AMMO];
+ if( i <= 0 )
+ break; // No ammo.
+ ammo_id = sd->inventory_data[i]->nameid;
+ if( ammo_id <= 0 )
+ break;
+ sd->itemid = ammo_id;
+ if( itemdb_is_GNbomb(ammo_id) ) {
+ if(battle_check_target(src,bl,BCT_ENEMY) > 0) {// Only attack if the target is an enemy.
+ if( ammo_id == 13263 )
+ map_foreachincell(skill_area_sub,bl->m,bl->x,bl->y,BL_CHAR,src,GN_SLINGITEM_RANGEMELEEATK,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ else
+ skill_attack(BF_WEAPON,src,src,bl,GN_SLINGITEM_RANGEMELEEATK,skill_lv,tick,flag);
+ } else //Otherwise, it fails, shows animation and removes items.
+ clif_skill_fail(sd,GN_SLINGITEM_RANGEMELEEATK,0xa,0);
+ } else if( itemdb_is_GNthrowable(ammo_id) ){
+ struct script_code *script = sd->inventory_data[i]->script;
+ if( !script )
+ break;
+ if( dstsd )
+ run_script(script,0,dstsd->bl.id,fake_nd->bl.id);
+ else
+ run_script(script,0,src->id,0);
+ }
+ }
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);// This packet is received twice actually, I think it is to show the animation.
+ break;
+
+ case GN_MIX_COOKING:
+ case GN_MAKEBOMB:
+ case GN_S_PHARMACY:
+ if( sd ) {
+ int qty = 1;
+ sd->skill_id_old = skill_id;
+ sd->skill_lv_old = skill_lv;
+ if( skill_id != GN_S_PHARMACY && skill_lv > 1 )
+ qty = 10;
+ clif_cooking_list(sd,(skill_id - GN_MIX_COOKING) + 27,skill_id,qty,skill_id==GN_MAKEBOMB?5:6);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ }
+ break;
+ case EL_CIRCLE_OF_FIRE:
+ case EL_PYROTECHNIC:
+ case EL_HEATER:
+ case EL_TROPIC:
+ case EL_AQUAPLAY:
+ case EL_COOLER:
+ case EL_CHILLY_AIR:
+ case EL_GUST:
+ case EL_BLAST:
+ case EL_WILD_STORM:
+ case EL_PETROLOGY:
+ case EL_CURSED_SOIL:
+ case EL_UPHEAVAL:
+ case EL_FIRE_CLOAK:
+ case EL_WATER_DROP:
+ case EL_WIND_CURTAIN:
+ case EL_SOLID_SKIN:
+ case EL_STONE_SHIELD:
+ case EL_WIND_STEP: {
+ struct elemental_data *ele = BL_CAST(BL_ELEM, src);
+ if( ele ) {
+ sc_type type2 = type-1;
+ struct status_change *sc = status_get_sc(&ele->bl);
+
+ if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
+ elemental_clean_single_effect(ele, skill_id);
+ } else {
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ clif_skill_damage(src, ( skill_id == EL_GUST || skill_id == EL_BLAST || skill_id == EL_WILD_STORM )?src:bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ if( skill_id == EL_WIND_STEP ) // There aren't teleport, just push the master away.
+ skill_blown(src,bl,(rnd()%skill_get_blewcount(skill_id,skill_lv))+1,rand()%8,0);
+ sc_start(src,type2,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ }
+ }
+ }
+ break;
+
+ case EL_FIRE_MANTLE:
+ case EL_WATER_BARRIER:
+ case EL_ZEPHYR:
+ case EL_POWER_OF_GAIA:
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ clif_skill_damage(src, bl, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ skill_unitsetting(src,skill_id,skill_lv,bl->x,bl->y,0);
+ break;
+
+ case EL_WATER_SCREEN: {
+ struct elemental_data *ele = BL_CAST(BL_ELEM, src);
+ if( ele ) {
+ struct status_change *sc = status_get_sc(&ele->bl);
+ sc_type type2 = type-1;
+
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
+ elemental_clean_single_effect(ele, skill_id);
+ } else {
+ // This not heals at the end.
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ sc_start(src,type2,100,skill_lv,skill_get_time(skill_id,skill_lv));
+ sc_start(bl,type,100,src->id,skill_get_time(skill_id,skill_lv));
+ }
+ }
+ }
+ break;
+
+ case KO_KAHU_ENTEN:
+ case KO_HYOUHU_HUBUKI:
+ case KO_KAZEHU_SEIRAN:
+ case KO_DOHU_KOUKAI:
+ if(sd) {
+ int ttype = skill_get_ele(skill_id, skill_lv);
+ clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+ pc_add_talisman(sd, skill_get_time(skill_id, skill_lv), 10, ttype);
+ }
+ break;
+
+ case KO_ZANZOU:
+ if(sd){
+ struct mob_data *md;
+
+ md = mob_once_spawn_sub(src, src->m, src->x, src->y, status_get_name(src), 2308, "", SZ_SMALL, AI_NONE);
+ if( md )
+ {
+ md->master_id = src->id;
+ md->special_state.ai = AI_ZANZOU;
+ if( md->deletetimer != INVALID_TIMER )
+ delete_timer(md->deletetimer, mob_timer_delete);
+ md->deletetimer = add_timer (gettick() + skill_get_time(skill_id, skill_lv), mob_timer_delete, md->bl.id, 0);
+ mob_spawn( md );
+ pc_setinvincibletimer(sd,500);// unlock target lock
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ skill_blown(src,bl,skill_get_blewcount(skill_id,skill_lv),unit_getdir(bl),0);
+ }
+ }
+ break;
+
+ case KO_KYOUGAKU:
+ if( dstsd && tsc && !tsc->data[type] && rand()%100 < tstatus->int_/2 ){
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ }else if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+
+ case KO_JYUSATSU:
+ if( dstsd && tsc && !tsc->data[type] &&
+ rand()%100 < ((45+5*skill_lv) + skill_lv*5 - status_get_int(bl)/2) ){//[(Base chance of success) + (Skill Level x 5) - (int / 2)]%.
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ status_change_start(bl,type,10000,skill_lv,0,0,0,skill_get_time(skill_id,skill_lv),1));
+ status_zap(bl, tstatus->max_hp*skill_lv*5/100 , 0);
+ if( status_get_lv(bl) <= status_get_lv(src) )
+ status_change_start(bl,SC_COMA,10,skill_lv,0,src->id,0,0,0);
+ }else if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+
+ case KO_GENWAKU:
+ if ( !map_flag_gvg(src->m) && ( dstsd || dstmd ) && battle_check_target(src,bl,BCT_ENEMY) > 0 ) {
+ int x = src->x, y = src->y;
+
+ if( sd && rnd()%100 > ((45+5*skill_lv) - status_get_int(bl)/10) ){//[(Base chance of success) - (Intelligence Objectives / 10)]%.
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+
+ if (unit_movepos(src,bl->x,bl->y,0,0)) {
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ clif_slide(src,bl->x,bl->y) ;
+ sc_start(src,SC_CONFUSION,80,skill_lv,skill_get_time(skill_id,skill_lv));
+ if (unit_movepos(bl,x,y,0,0))
+ {
+ clif_skill_damage(bl,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, -1, 6);
+ if( bl->type == BL_PC && pc_issit((TBL_PC*)bl))
+ clif_sitting(bl); //Avoid sitting sync problem
+ clif_slide(bl,x,y) ;
+ sc_start(bl,SC_CONFUSION,80,skill_lv,skill_get_time(skill_id,skill_lv));
+ }
+ }
+ }
+ break;
+
+ case OB_AKAITSUKI:
+ case OB_OBOROGENSOU:
+ if( sd && ( (skill_id == OB_OBOROGENSOU && bl->type == BL_MOB) // This skill does not work on monsters.
+ || is_boss(bl) ) ){ // Does not work on Boss monsters.
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ case KO_IZAYOI:
+ case OB_ZANGETSU:
+ case KG_KYOMU:
+ case KG_KAGEMUSYA:
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start(bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ break;
+
+ case KG_KAGEHUMI:
+ if( flag&1 ){
+ if(tsc && ( tsc->option&(OPTION_CLOAK|OPTION_HIDE) ||
+ tsc->data[SC_CAMOUFLAGE] || tsc->data[SC__SHADOWFORM] ||
+ tsc->data[SC_MARIONETTE] || tsc->data[SC_HARMONIZE])){
+ sc_start(src, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ status_change_end(bl, SC_HIDING, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
+ status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER);
+ status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER);
+ status_change_end(bl, SC_MARIONETTE, INVALID_TIMER);
+ status_change_end(bl, SC_HARMONIZE, INVALID_TIMER);
+ }
+ if( skill_area_temp[2] == 1 ){
+ clif_skill_damage(src,src,tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ sc_start(src, SC_STOP, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ }
+ }else{
+ skill_area_temp[2] = 0;
+ map_foreachinrange(skill_area_sub, bl, skill_get_splash(skill_id, skill_lv), splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY|SD_SPLASH|1, skill_castend_nodamage_id);
+ }
+ break;
+
+ case MH_SILENT_BREEZE: {
+ struct status_change *ssc = status_get_sc(src);
+ struct block_list *m_bl = battle_get_master(src);
+ const enum sc_type scs[] = {
+ SC_MANDRAGORA, SC_HARMONIZE, SC_DEEPSLEEP, SC_VOICEOFSIREN, SC_SLEEP, SC_CONFUSION, SC_HALLUCINATION
+ };
+ int heal;
+ if(tsc){
+ for (i = 0; i < ARRAYLENGTH(scs); i++) {
+ if (tsc->data[scs[i]]) status_change_end(bl, scs[i], INVALID_TIMER);
+ }
+ if (!tsc->data[SC_SILENCE]) //put inavoidable silence on target
+ status_change_start(bl, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
+ }
+ heal = status_get_matk_min(src)*4;
+ status_heal(bl, heal, 0, 7);
+
+ //now inflict silence on everyone
+ if(ssc && !ssc->data[SC_SILENCE]) //put inavoidable silence on homun
+ status_change_start(src, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
+ if(m_bl){
+ struct status_change *msc = status_get_sc(m_bl);
+ if(msc && !msc->data[SC_SILENCE]) //put inavoidable silence on master
+ status_change_start(m_bl, SC_SILENCE, 100, skill_lv, 0,0,0, skill_get_time(skill_id, skill_lv),1|2|8);
+ }
+ if (hd)
+ skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+ }
+ break;
+ case MH_OVERED_BOOST:
+ if (hd){
+ struct block_list *s_bl = battle_get_master(src);
+ if(hd->homunculus.hunger>50) //reduce hunger
+ hd->homunculus.hunger = hd->homunculus.hunger/2;
+ else
+ hd->homunculus.hunger = min(1,hd->homunculus.hunger);
+ if(s_bl && s_bl->type==BL_PC){
+ status_set_sp(s_bl,status_get_max_sp(s_bl)/2,0); //master drain 50% sp
+ clif_send_homdata(((TBL_PC *)s_bl), SP_HUNGRY, hd->homunculus.hunger); //refresh hunger info
+ sc_start(s_bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)); //gene bonus
+ }
+ sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+ }
+ break;
+ case MH_GRANITIC_ARMOR:
+ case MH_PYROCLASTIC: {
+ struct block_list *s_bl = battle_get_master(src);
+ if(s_bl) sc_start2(s_bl, type, 100, skill_lv, hd->homunculus.level, skill_get_time(skill_id, skill_lv)); //start on master
+ sc_start2(bl, type, 100, skill_lv, hd->homunculus.level, skill_get_time(skill_id, skill_lv));
+ if (hd) skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+ }
+ break;
+
+ case MH_LIGHT_OF_REGENE:
+ if(hd){
+ hd->homunculus.intimacy = 251; //change to neutral (can't be cast if < 750)
+ if(sd) clif_send_homdata(sd, SP_INTIMATE, hd->homunculus.intimacy); //refresh intimacy info
+ }
+ //don't break need to start status and start block timer
+ case MH_STYLE_CHANGE:
+ case MH_MAGMA_FLOW:
+ case MH_PAIN_KILLER:
+ sc_start(bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ if (hd)
+ skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+ break;
+ case MH_SUMMON_LEGION:
+ {
+ int summons[5] = {1004, 1303, 1303, 1994, 1994};
+ int qty[5] = {3 , 3 , 4 , 4 , 5};
+ struct mob_data *md;
+ int i;
+
+ for(i=0; i<qty[skill_lv - 1]; i++){ //easy way
+ md = mob_once_spawn_sub(src, src->m, src->x, src->y, status_get_name(src), summons[skill_lv - 1], "", SZ_SMALL, AI_ATTACK);
+ if (md) {
+ md->master_id = src->id;
+ if (md->deletetimer != INVALID_TIMER)
+ delete_timer(md->deletetimer, mob_timer_delete);
+ md->deletetimer = add_timer(gettick() + skill_get_time(skill_id, skill_lv), mob_timer_delete, md->bl.id, 0);
+ mob_spawn(md); //Now it is ready for spawning.
+ sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_ASSIST, 0, 60000);
+ }
+ }
+ if (hd)
+ skill_blockhomun_start(hd, skill_id, skill_get_cooldown(skill_id, skill_lv));
+ }
+ break;
+ default:
+ ShowWarning("skill_castend_nodamage_id: Unknown skill used:%d\n",skill_id);
+ clif_skill_nodamage(src,bl,skill_id,skill_lv,1);
+ map_freeblock_unlock();
+ return 1;
+ }
+
+ if(skill_id != SR_CURSEDCIRCLE){
+ struct status_change *sc = status_get_sc(src);
+ if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] )//Should only remove after the skill had been casted.
+ status_change_end(src,SC_CURSEDCIRCLE_ATKER,INVALID_TIMER);
+ }
+
+ if (dstmd) { //Mob skill event for no damage skills (damage ones are handled in battle_calc_damage) [Skotlex]
+ mob_log_damage(dstmd, src, 0); //Log interaction (counts as 'attacker' for the exp bonus)
+ mobskill_event(dstmd, src, tick, MSC_SKILLUSED|(skill_id<<16));
+ }
+
+ if( sd && !(flag&1) )
+ {// ensure that the skill last-cast tick is recorded
+ sd->canskill_tick = gettick();
+
+ if( sd->state.arrow_atk )
+ {// consume arrow on last invocation to this skill.
+ battle_consume_ammo(sd, skill_id, skill_lv);
+ }
+ skill_onskillusage(sd, bl, skill_id, tick);
+ // perform skill requirement consumption
+ skill_consume_requirement(sd,skill_id,skill_lv,2);
+ }
+
+ map_freeblock_unlock();
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct block_list *target, *src;
+ struct map_session_data *sd;
+ struct mob_data *md;
+ struct unit_data *ud;
+ struct status_change *sc = NULL;
+ int inf,inf2,flag = 0;
+
+ src = map_id2bl(id);
+ if( src == NULL )
+ {
+ ShowDebug("skill_castend_id: src == NULL (tid=%d, id=%d)\n", tid, id);
+ return 0;// not found
+ }
+
+ ud = unit_bl2ud(src);
+ if( ud == NULL )
+ {
+ ShowDebug("skill_castend_id: ud == NULL (tid=%d, id=%d)\n", tid, id);
+ return 0;// ???
+ }
+
+ sd = BL_CAST(BL_PC, src);
+ md = BL_CAST(BL_MOB, src);
+
+ if( src->prev == NULL ) {
+ ud->skilltimer = INVALID_TIMER;
+ return 0;
+ }
+
+ if(ud->skill_id != SA_CASTCANCEL && ud->skill_id != SO_SPELLFIST) {// otherwise handled in unit_skillcastcancel()
+ if( ud->skilltimer != tid ) {
+ ShowError("skill_castend_id: Timer mismatch %d!=%d!\n", ud->skilltimer, tid);
+ ud->skilltimer = INVALID_TIMER;
+ return 0;
+ }
+
+ if( sd && ud->skilltimer != INVALID_TIMER && (pc_checkskill(sd,SA_FREECAST) > 0 || ud->skill_id == LG_EXEEDBREAK) )
+ {// restore original walk speed
+ ud->skilltimer = INVALID_TIMER;
+ status_calc_bl(&sd->bl, SCB_SPEED);
+ }
+
+ ud->skilltimer = INVALID_TIMER;
+ }
+
+ if (ud->skilltarget == id)
+ target = src;
+ else
+ target = map_id2bl(ud->skilltarget);
+
+ // Use a do so that you can break out of it when the skill fails.
+ do {
+ if(!target || target->prev==NULL) break;
+
+ if(src->m != target->m || status_isdead(src)) break;
+
+ switch (ud->skill_id) {
+ //These should become skill_castend_pos
+ case WE_CALLPARTNER:
+ if(sd) clif_callpartner(sd);
+ case WE_CALLPARENT:
+ case WE_CALLBABY:
+ case AM_RESURRECTHOMUN:
+ case PF_SPIDERWEB:
+ //Find a random spot to place the skill. [Skotlex]
+ inf2 = skill_get_splash(ud->skill_id, ud->skill_lv);
+ ud->skillx = target->x + inf2;
+ ud->skilly = target->y + inf2;
+ if (inf2 && !map_random_dir(target, &ud->skillx, &ud->skilly)) {
+ ud->skillx = target->x;
+ ud->skilly = target->y;
+ }
+ ud->skilltimer=tid;
+ return skill_castend_pos(tid,tick,id,data);
+ case GN_WALLOFTHORN:
+ ud->skillx = target->x;
+ ud->skilly = target->y;
+ ud->skilltimer = tid;
+ return skill_castend_pos(tid,tick,id,data);
+ }
+
+ if(ud->skill_id == RG_BACKSTAP) {
+ uint8 dir = map_calc_dir(src,target->x,target->y),t_dir = unit_getdir(target);
+ if(check_distance_bl(src, target, 0) || map_check_dir(dir,t_dir)) {
+ break;
+ }
+ }
+
+ if( ud->skill_id == PR_TURNUNDEAD )
+ {
+ struct status_data *tstatus = status_get_status_data(target);
+ if( !battle_check_undead(tstatus->race, tstatus->def_ele) )
+ break;
+ }
+
+ if( ud->skill_id == RA_WUGSTRIKE ){
+ if( !path_search(NULL,src->m,src->x,src->y,target->x,target->y,1,CELL_CHKNOREACH))
+ break;
+ }
+
+ if( ud->skill_id == PR_LEXDIVINA || ud->skill_id == MER_LEXDIVINA )
+ {
+ sc = status_get_sc(target);
+ if( battle_check_target(src,target, BCT_ENEMY) <= 0 && (!sc || !sc->data[SC_SILENCE]) )
+ { //If it's not an enemy, and not silenced, you can't use the skill on them. [Skotlex]
+ clif_skill_nodamage (src, target, ud->skill_id, ud->skill_lv, 0);
+ break;
+ }
+ }
+ else
+ { // Check target validity.
+ inf = skill_get_inf(ud->skill_id);
+ inf2 = skill_get_inf2(ud->skill_id);
+
+ if(inf&INF_ATTACK_SKILL ||
+ (inf&INF_SELF_SKILL && inf2&INF2_NO_TARGET_SELF) //Combo skills
+ ) // Casted through combo.
+ inf = BCT_ENEMY; //Offensive skill.
+ else if(inf2&INF2_NO_ENEMY)
+ inf = BCT_NOENEMY;
+ else
+ inf = 0;
+
+ if(inf2 & (INF2_PARTY_ONLY|INF2_GUILD_ONLY) && src != target)
+ {
+ inf |=
+ (inf2&INF2_PARTY_ONLY?BCT_PARTY:0)|
+ (inf2&INF2_GUILD_ONLY?BCT_GUILD:0);
+ //Remove neutral targets (but allow enemy if skill is designed to be so)
+ inf &= ~BCT_NEUTRAL;
+ }
+
+ if( ud->skill_id >= SL_SKE && ud->skill_id <= SL_SKA && target->type == BL_MOB )
+ {
+ if( ((TBL_MOB*)target)->class_ == MOBID_EMPERIUM )
+ break;
+ }
+ else if (inf && battle_check_target(src, target, inf) <= 0){
+ if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+
+ if(inf&BCT_ENEMY && (sc = status_get_sc(target)) &&
+ sc->data[SC_FOGWALL] &&
+ rnd() % 100 < 75) { //Fogwall makes all offensive-type targetted skills fail at 75%
+ if (sd) clif_skill_fail(sd, ud->skill_id, USESKILL_FAIL_LEVEL, 0);
+ break;
+ }
+ }
+
+ //Avoid doing double checks for instant-cast skills.
+ if (tid != INVALID_TIMER && !status_check_skilluse(src, target, ud->skill_id, 1))
+ break;
+
+ if(md) {
+ md->last_thinktime=tick +MIN_MOBTHINKTIME;
+ if(md->skill_idx >= 0 && md->db->skill[md->skill_idx].emotion >= 0)
+ clif_emotion(src, md->db->skill[md->skill_idx].emotion);
+ }
+
+ if(src != target && battle_config.skill_add_range &&
+ !check_distance_bl(src, target, skill_get_range2(src,ud->skill_id,ud->skill_lv)+battle_config.skill_add_range))
+ {
+ if (sd) {
+ clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
+ if(battle_config.skill_out_range_consume) //Consume items anyway. [Skotlex]
+ skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,3);
+ }
+ break;
+ }
+
+ if( sd )
+ {
+ if( !skill_check_condition_castend(sd, ud->skill_id, ud->skill_lv) )
+ break;
+ else
+ skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,1);
+ }
+#ifdef OFFICIAL_WALKPATH
+ if( !path_search_long(NULL, src->m, src->x, src->y, target->x, target->y, CELL_CHKWALL) )
+ break;
+#endif
+ if( (src->type == BL_MER || src->type == BL_HOM) && !skill_check_condition_mercenary(src, ud->skill_id, ud->skill_lv, 1) )
+ break;
+
+ if (ud->state.running && ud->skill_id == TK_JUMPKICK)
+ {
+ ud->state.running = 0;
+ status_change_end(src, SC_RUN, INVALID_TIMER);
+ flag = 1;
+ }
+
+ if (ud->walktimer != INVALID_TIMER && ud->skill_id != TK_RUN && ud->skill_id != RA_WUGDASH)
+ unit_stop_walking(src,1);
+
+ if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
+ ud->canact_tick = tick + skill_delayfix(src, ud->skill_id, ud->skill_lv); //Tests show wings don't overwrite the delay but skill scrolls do. [Inkfish]
+ if (sd) { //Cooldown application
+ int i, cooldown = skill_get_cooldown(ud->skill_id, ud->skill_lv);
+ for (i = 0; i < ARRAYLENGTH(sd->skillcooldown) && sd->skillcooldown[i].id; i++) { // Increases/Decreases cooldown of a skill by item/card bonuses.
+ if (sd->skillcooldown[i].id == ud->skill_id){
+ cooldown += sd->skillcooldown[i].val;
+ break;
+ }
+ }
+ if(cooldown)
+ skill_blockpc_start(sd, ud->skill_id, cooldown);
+ }
+ if( battle_config.display_status_timers && sd )
+ clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skill_id, ud->skill_lv), 0, 0, 0);
+ if( sd )
+ {
+ switch( ud->skill_id )
+ {
+ case GS_DESPERADO:
+ sd->canequip_tick = tick + skill_get_time(ud->skill_id, ud->skill_lv);
+ break;
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ if( (sc = status_get_sc(src)) && sc->data[SC_STRIPSHIELD] )
+ {
+ const struct TimerData *timer = get_timer(sc->data[SC_STRIPSHIELD]->timer);
+ if( timer && timer->func == status_change_timer && DIFF_TICK(timer->tick,gettick()+skill_get_time(ud->skill_id, ud->skill_lv)) > 0 )
+ break;
+ }
+ sc_start2(src, SC_STRIPSHIELD, 100, 0, 1, skill_get_time(ud->skill_id, ud->skill_lv));
+ break;
+ }
+ }
+ if (skill_get_state(ud->skill_id) != ST_MOVE_ENABLE)
+ unit_set_walkdelay(src, tick, battle_config.default_walk_delay+skill_get_walkdelay(ud->skill_id, ud->skill_lv), 1);
+
+ if(battle_config.skill_log && battle_config.skill_log&src->type)
+ ShowInfo("Type %d, ID %d skill castend id [id =%d, lv=%d, target ID %d]\n",
+ src->type, src->id, ud->skill_id, ud->skill_lv, target->id);
+
+ map_freeblock_lock();
+
+ // SC_MAGICPOWER needs to switch states before any damage is actually dealt
+ skill_toggle_magicpower(src, ud->skill_id);
+ if( ud->skill_id != RA_CAMOUFLAGE ) // only normal attack and auto cast skills benefit from its bonuses
+ status_change_end(src,SC_CAMOUFLAGE, INVALID_TIMER);
+
+ if (skill_get_casttype(ud->skill_id) == CAST_NODAMAGE)
+ skill_castend_nodamage_id(src,target,ud->skill_id,ud->skill_lv,tick,flag);
+ else
+ skill_castend_damage_id(src,target,ud->skill_id,ud->skill_lv,tick,flag);
+
+ sc = status_get_sc(src);
+ if(sc && sc->count) {
+ if(sc->data[SC_SPIRIT] &&
+ sc->data[SC_SPIRIT]->val2 == SL_WIZARD &&
+ sc->data[SC_SPIRIT]->val3 == ud->skill_id &&
+ ud->skill_id != WZ_WATERBALL)
+ sc->data[SC_SPIRIT]->val3 = 0; //Clear bounced spell check.
+
+ if( sc->data[SC_DANCING] && skill_get_inf2(ud->skill_id)&INF2_SONG_DANCE && sd )
+ skill_blockpc_start(sd,BD_ADAPTATION,3000);
+ }
+
+ if( sd && ud->skill_id != SA_ABRACADABRA && ud->skill_id != WM_RANDOMIZESPELL ) // they just set the data so leave it as it is.[Inkfish]
+ sd->skillitem = sd->skillitemlv = 0;
+
+ if (ud->skilltimer == INVALID_TIMER) {
+ if(md) md->skill_idx = -1;
+ else ud->skill_id = 0; //mobs can't clear this one as it is used for skill condition 'afterskill'
+ ud->skill_lv = ud->skilltarget = 0;
+ }
+ map_freeblock_unlock();
+ return 1;
+ } while(0);
+
+ //Skill failed.
+ if (ud->skill_id == MO_EXTREMITYFIST && sd && !(sc && sc->data[SC_FOGWALL]))
+ { //When Asura fails... (except when it fails from Fog of Wall)
+ //Consume SP/spheres
+ skill_consume_requirement(sd,ud->skill_id, ud->skill_lv,1);
+ status_set_sp(src, 0, 0);
+ sc = &sd->sc;
+ if (sc->count)
+ { //End states
+ status_change_end(src, SC_EXPLOSIONSPIRITS, INVALID_TIMER);
+ status_change_end(src, SC_BLADESTOP, INVALID_TIMER);
+#ifdef RENEWAL
+ sc_start(src, SC_EXTREMITYFIST2, 100, ud->skill_lv, skill_get_time(ud->skill_id, ud->skill_lv));
+#endif
+ }
+ if (target && target->m == src->m)
+ { //Move character to target anyway.
+ if (unit_movepos(src, src->x+3, src->y+3, 1, 1))
+ { //Display movement + animation.
+ clif_slide(src,src->x,src->y);
+ clif_skill_damage(src,target,tick,sd->battle_status.amotion,0,0,1,ud->skill_id, ud->skill_lv, 5);
+ }
+ clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
+ }
+ }
+
+ ud->skill_id = ud->skill_lv = ud->skilltarget = 0;
+ if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
+ ud->canact_tick = tick;
+ //You can't place a skill failed packet here because it would be
+ //sent in ALL cases, even cases where skill_check_condition fails
+ //which would lead to double 'skill failed' messages u.u [Skotlex]
+ if(sd)
+ sd->skillitem = sd->skillitemlv = 0;
+ else if(md)
+ md->skill_idx = -1;
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_castend_pos(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct block_list* src = map_id2bl(id);
+ int maxcount;
+ struct map_session_data *sd;
+ struct unit_data *ud = unit_bl2ud(src);
+ struct mob_data *md;
+
+ nullpo_ret(ud);
+
+ sd = BL_CAST(BL_PC , src);
+ md = BL_CAST(BL_MOB, src);
+
+ if( src->prev == NULL ) {
+ ud->skilltimer = INVALID_TIMER;
+ return 0;
+ }
+
+ if( ud->skilltimer != tid )
+ {
+ ShowError("skill_castend_pos: Timer mismatch %d!=%d\n", ud->skilltimer, tid);
+ ud->skilltimer = INVALID_TIMER;
+ return 0;
+ }
+
+ if( sd && ud->skilltimer != INVALID_TIMER && ( pc_checkskill(sd,SA_FREECAST) > 0 || ud->skill_id == LG_EXEEDBREAK ) )
+ {// restore original walk speed
+ ud->skilltimer = INVALID_TIMER;
+ status_calc_bl(&sd->bl, SCB_SPEED);
+ }
+ ud->skilltimer = INVALID_TIMER;
+
+ do {
+ if( status_isdead(src) )
+ break;
+
+ if( !(src->type&battle_config.skill_reiteration) &&
+ skill_get_unit_flag(ud->skill_id)&UF_NOREITERATION &&
+ skill_check_unit_range(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv)
+ )
+ {
+ if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if( src->type&battle_config.skill_nofootset &&
+ skill_get_unit_flag(ud->skill_id)&UF_NOFOOTSET &&
+ skill_check_unit_range2(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv)
+ )
+ {
+ if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ if( src->type&battle_config.land_skill_limit &&
+ (maxcount = skill_get_maxcount(ud->skill_id, ud->skill_lv)) > 0
+ ) {
+ int i;
+ for(i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i] && maxcount;i++) {
+ if(ud->skillunit[i]->skill_id == ud->skill_id)
+ maxcount--;
+ }
+ if( maxcount == 0 )
+ {
+ if (sd) clif_skill_fail(sd,ud->skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ }
+
+ if(tid != INVALID_TIMER)
+ { //Avoid double checks on instant cast skills. [Skotlex]
+ if (!status_check_skilluse(src, NULL, ud->skill_id, 1))
+ break;
+ if(battle_config.skill_add_range &&
+ !check_distance_blxy(src, ud->skillx, ud->skilly, skill_get_range2(src,ud->skill_id,ud->skill_lv)+battle_config.skill_add_range)) {
+ if (sd && battle_config.skill_out_range_consume) //Consume items anyway.
+ skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,3);
+ break;
+ }
+ }
+
+ if( sd )
+ {
+ if( !skill_check_condition_castend(sd, ud->skill_id, ud->skill_lv) )
+ break;
+ else
+ skill_consume_requirement(sd,ud->skill_id,ud->skill_lv,1);
+ }
+
+ if( (src->type == BL_MER || src->type == BL_HOM) && !skill_check_condition_mercenary(src, ud->skill_id, ud->skill_lv, 1) )
+ break;
+
+ if(md) {
+ md->last_thinktime=tick +MIN_MOBTHINKTIME;
+ if(md->skill_idx >= 0 && md->db->skill[md->skill_idx].emotion >= 0)
+ clif_emotion(src, md->db->skill[md->skill_idx].emotion);
+ }
+
+ if(battle_config.skill_log && battle_config.skill_log&src->type)
+ ShowInfo("Type %d, ID %d skill castend pos [id =%d, lv=%d, (%d,%d)]\n",
+ src->type, src->id, ud->skill_id, ud->skill_lv, ud->skillx, ud->skilly);
+
+ if (ud->walktimer != INVALID_TIMER)
+ unit_stop_walking(src,1);
+
+ if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
+ ud->canact_tick = tick + skill_delayfix(src, ud->skill_id, ud->skill_lv);
+ if (sd) { //Cooldown application
+ int i, cooldown = skill_get_cooldown(ud->skill_id, ud->skill_lv);
+ for (i = 0; i < ARRAYLENGTH(sd->skillcooldown) && sd->skillcooldown[i].id; i++) { // Increases/Decreases cooldown of a skill by item/card bonuses.
+ if (sd->skillcooldown[i].id == ud->skill_id){
+ cooldown += sd->skillcooldown[i].val;
+ break;
+ }
+ }
+ if(cooldown)
+ skill_blockpc_start(sd, ud->skill_id, cooldown);
+ }
+ if( battle_config.display_status_timers && sd )
+ clif_status_change(src, SI_ACTIONDELAY, 1, skill_delayfix(src, ud->skill_id, ud->skill_lv), 0, 0, 0);
+// if( sd )
+// {
+// switch( ud->skill_id )
+// {
+// case ????:
+// sd->canequip_tick = tick + ????;
+// break;
+// }
+// }
+ unit_set_walkdelay(src, tick, battle_config.default_walk_delay+skill_get_walkdelay(ud->skill_id, ud->skill_lv), 1);
+ status_change_end(src,SC_CAMOUFLAGE, INVALID_TIMER);// only normal attack and auto cast skills benefit from its bonuses
+ map_freeblock_lock();
+ skill_castend_pos2(src,ud->skillx,ud->skilly,ud->skill_id,ud->skill_lv,tick,0);
+
+ if( sd && sd->skillitem != AL_WARP ) // Warp-Portal thru items will clear data in skill_castend_map. [Inkfish]
+ sd->skillitem = sd->skillitemlv = 0;
+
+ if (ud->skilltimer == INVALID_TIMER) {
+ if (md) md->skill_idx = -1;
+ else ud->skill_id = 0; //Non mobs can't clear this one as it is used for skill condition 'afterskill'
+ ud->skill_lv = ud->skillx = ud->skilly = 0;
+ }
+
+ map_freeblock_unlock();
+ return 1;
+ } while(0);
+
+ if( !sd || sd->skillitem != ud->skill_id || skill_get_delay(ud->skill_id,ud->skill_lv) )
+ ud->canact_tick = tick;
+ ud->skill_id = ud->skill_lv = 0;
+ if(sd)
+ sd->skillitem = sd->skillitemlv = 0;
+ else if(md)
+ md->skill_idx = -1;
+ return 0;
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
+{
+ struct map_session_data* sd;
+ struct status_change* sc;
+ struct status_change_entry *sce;
+ struct skill_unit_group* sg;
+ enum sc_type type;
+ int i;
+
+ //if(skill_lv <= 0) return 0;
+ if(skill_id > 0 && !skill_lv) return 0; // celest
+
+ nullpo_ret(src);
+
+ if(status_isdead(src))
+ return 0;
+
+ sd = BL_CAST(BL_PC, src);
+
+ sc = status_get_sc(src);
+ type = status_skill2sc(skill_id);
+ sce = (sc && type != -1)?sc->data[type]:NULL;
+
+ switch (skill_id) { //Skill effect.
+ case WZ_METEOR:
+ case MO_BODYRELOCATION:
+ case CR_CULTIVATION:
+ case HW_GANBANTEIN:
+ case LG_EARTHDRIVE:
+ break; //Effect is displayed on respective switch case.
+ default:
+ if(skill_get_inf(skill_id)&INF_SELF_SKILL)
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ else
+ clif_skill_poseffect(src,skill_id,skill_lv,x,y,tick);
+ }
+
+ // SC_MAGICPOWER needs to switch states before any damage is actually dealt
+ skill_toggle_magicpower(src, skill_id);
+
+ switch(skill_id)
+ {
+ case PR_BENEDICTIO:
+ skill_area_temp[1] = src->id;
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea(skill_area_sub,
+ src->m, x-i, y-i, x+i, y+i, BL_PC,
+ src, skill_id, skill_lv, tick, flag|BCT_ALL|1,
+ skill_castend_nodamage_id);
+ map_foreachinarea(skill_area_sub,
+ src->m, x-i, y-i, x+i, y+i, BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ break;
+
+ case BS_HAMMERFALL:
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea (skill_area_sub,
+ src->m, x-i, y-i, x+i, y+i, BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|2,
+ skill_castend_nodamage_id);
+ break;
+
+ case HT_DETECTING:
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea( status_change_timer_sub,
+ src->m, x-i, y-i, x+i,y+i,BL_CHAR,
+ src,NULL,SC_SIGHT,tick);
+ if(battle_config.traps_setting&1)
+ map_foreachinarea( skill_reveal_trap,
+ src->m, x-i, y-i, x+i,y+i,BL_SKILL);
+ break;
+
+ case SR_RIDEINLIGHTNING:
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea(skill_area_sub, src->m, x-i, y-i, x+i, y+i, BL_CHAR,
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_damage_id);
+ break;
+
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+ { //Does not consumes if the skill is already active. [Skotlex]
+ struct skill_unit_group *sg;
+ if ((sg= skill_locate_element_field(src)) != NULL && ( sg->skill_id == SA_VOLCANO || sg->skill_id == SA_DELUGE || sg->skill_id == SA_VIOLENTGALE ))
+ {
+ if (sg->limit - DIFF_TICK(gettick(), sg->tick) > 0)
+ {
+ skill_unitsetting(src,skill_id,skill_lv,x,y,0);
+ return 0; // not to consume items
+ }
+ else
+ sg->limit = 0; //Disable it.
+ }
+ skill_unitsetting(src,skill_id,skill_lv,x,y,0);
+ break;
+ }
+ case MG_SAFETYWALL:
+ case MG_FIREWALL:
+ case MG_THUNDERSTORM:
+
+ case AL_PNEUMA:
+ case WZ_ICEWALL:
+ case WZ_FIREPILLAR:
+ case WZ_QUAGMIRE:
+ case WZ_VERMILION:
+ case WZ_STORMGUST:
+ case WZ_HEAVENDRIVE:
+ case PR_SANCTUARY:
+ case PR_MAGNUS:
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS:
+ case HT_SKIDTRAP:
+ case MA_SKIDTRAP:
+ case HT_LANDMINE:
+ case MA_LANDMINE:
+ case HT_ANKLESNARE:
+ case HT_SHOCKWAVE:
+ case HT_SANDMAN:
+ case MA_SANDMAN:
+ case HT_FLASHER:
+ case HT_FREEZINGTRAP:
+ case MA_FREEZINGTRAP:
+ case HT_BLASTMINE:
+ case HT_CLAYMORETRAP:
+ case AS_VENOMDUST:
+ case AM_DEMONSTRATION:
+ case PF_FOGWALL:
+ case PF_SPIDERWEB:
+ case HT_TALKIEBOX:
+ case WE_CALLPARTNER:
+ case WE_CALLPARENT:
+ case WE_CALLBABY:
+ case AC_SHOWER: //Ground-placed skill implementation.
+ case MA_SHOWER:
+ case SA_LANDPROTECTOR:
+ 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:
+ case GS_DESPERADO:
+ case NJ_KAENSIN:
+ case NJ_BAKUENRYU:
+ case NJ_SUITON:
+ case NJ_HYOUSYOURAKU:
+ case NJ_RAIGEKISAI:
+ case NJ_KAMAITACHI:
+#ifdef RENEWAL
+ case NJ_HUUMA:
+#endif
+ case NPC_EVILLAND:
+ case RA_ELECTRICSHOCKER:
+ case RA_CLUSTERBOMB:
+ case RA_MAGENTATRAP:
+ case RA_COBALTTRAP:
+ case RA_MAIZETRAP:
+ case RA_VERDURETRAP:
+ case RA_FIRINGTRAP:
+ case RA_ICEBOUNDTRAP:
+ case SC_MANHOLE:
+ case SC_DIMENSIONDOOR:
+ case SC_CHAOSPANIC:
+ case SC_MAELSTROM:
+ case WM_REVERBERATION:
+ case WM_SEVERE_RAINSTORM:
+ case WM_POEMOFNETHERWORLD:
+ case SO_PSYCHIC_WAVE:
+ case SO_VACUUM_EXTREME:
+ case GN_WALLOFTHORN:
+ case GN_THORNS_TRAP:
+ case GN_DEMONIC_FIRE:
+ case GN_HELLS_PLANT:
+ case SO_EARTHGRAVE:
+ case SO_DIAMONDDUST:
+ case SO_FIRE_INSIGNIA:
+ case SO_WATER_INSIGNIA:
+ case SO_WIND_INSIGNIA:
+ case SO_EARTH_INSIGNIA:
+ case KO_HUUMARANKA:
+ case KO_MUCHANAGE:
+ case KO_BAKURETSU:
+ case KO_ZENKAI:
+ case MH_LAVA_SLIDE:
+ case MH_VOLCANIC_ASH:
+ case MH_POISON_MIST:
+ case MH_STEINWAND:
+ case MH_XENO_SLASHER:
+ flag|=1;//Set flag to 1 to prevent deleting ammo (it will be deleted on group-delete).
+ case GS_GROUNDDRIFT: //Ammo should be deleted right away.
+ skill_unitsetting(src,skill_id,skill_lv,x,y,0);
+ break;
+ case RG_GRAFFITI: /* Graffiti [Valaris] */
+ skill_clear_unitgroup(src);
+ skill_unitsetting(src,skill_id,skill_lv,x,y,0);
+ flag|=1;
+ break;
+ case HP_BASILICA:
+ if( sc->data[SC_BASILICA] )
+ status_change_end(src, SC_BASILICA, INVALID_TIMER); // Cancel Basilica
+ else
+ { // Create Basilica. Start SC on caster. Unit timer start SC on others.
+ skill_clear_unitgroup(src);
+ if( skill_unitsetting(src,skill_id,skill_lv,x,y,0) )
+ sc_start4(src,type,100,skill_lv,0,0,src->id,skill_get_time(skill_id,skill_lv));
+ flag|=1;
+ }
+ break;
+ case CG_HERMODE:
+ skill_clear_unitgroup(src);
+ if ((sg = skill_unitsetting(src,skill_id,skill_lv,x,y,0)))
+ sc_start4(src,SC_DANCING,100,
+ skill_id,0,skill_lv,sg->group_id,skill_get_time(skill_id,skill_lv));
+ flag|=1;
+ break;
+ case RG_CLEANER: // [Valaris]
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea(skill_graffitiremover,src->m,x-i,y-i,x+i,y+i,BL_SKILL);
+ break;
+
+ case SO_WARMER:
+ flag|= 8;
+ case SO_CLOUD_KILL:
+ skill_unitsetting(src,skill_id,skill_lv,x,y,0);
+ break;
+
+ case WZ_METEOR: {
+ int area = skill_get_splash(skill_id, skill_lv);
+ short tmpx = 0, tmpy = 0, x1 = 0, y1 = 0;
+
+ for( i = 0; i < 2 + (skill_lv>>1); i++ ) {
+ // Creates a random Cell in the Splash Area
+ tmpx = x - area + rnd()%(area * 2 + 1);
+ tmpy = y - area + rnd()%(area * 2 + 1);
+
+ if( i == 0 && path_search_long(NULL, src->m, src->x, src->y, tmpx, tmpy, CELL_CHKWALL) )
+ clif_skill_poseffect(src,skill_id,skill_lv,tmpx,tmpy,tick);
+
+ if( i > 0 )
+ skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skill_id,skill_lv,(x1<<16)|y1,0);
+
+ x1 = tmpx;
+ y1 = tmpy;
+ }
+
+ skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skill_id,skill_lv,-1,0);
+ }
+ break;
+
+ case AL_WARP:
+ if(sd)
+ {
+ clif_skill_warppoint(sd, skill_id, skill_lv, sd->status.save_point.map,
+ (skill_lv >= 2) ? sd->status.memo_point[0].map : 0,
+ (skill_lv >= 3) ? sd->status.memo_point[1].map : 0,
+ (skill_lv >= 4) ? sd->status.memo_point[2].map : 0
+ );
+ }
+ if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] ) //Should only remove after the skill has been casted.
+ status_change_end(src,SC_CURSEDCIRCLE_ATKER,INVALID_TIMER);
+ return 0; // not to consume item.
+
+ case MO_BODYRELOCATION:
+ if (unit_movepos(src, x, y, 1, 1)) {
+#if PACKETVER >= 20111005
+ clif_snap(src, src->x, src->y);
+#else
+ clif_skill_poseffect(src,skill_id,skill_lv,src->x,src->y,tick);
+#endif
+ if (sd)
+ skill_blockpc_start (sd, MO_EXTREMITYFIST, 2000);
+ }
+ break;
+ case NJ_SHADOWJUMP:
+ if( !map_flag_gvg(src->m) && !map[src->m].flag.battleground ) { //You don't move on GVG grounds.
+ unit_movepos(src, x, y, 1, 0);
+ clif_slide(src,x,y);
+ }
+ status_change_end(src, SC_HIDING, INVALID_TIMER);
+ break;
+ case AM_SPHEREMINE:
+ case AM_CANNIBALIZE:
+ {
+ int summons[5] = { 1589, 1579, 1575, 1555, 1590 };
+ //int summons[5] = { 1020, 1068, 1118, 1500, 1368 };
+ int class_ = skill_id==AM_SPHEREMINE?1142:summons[skill_lv-1];
+ struct mob_data *md;
+
+ // Correct info, don't change any of this! [celest]
+ md = mob_once_spawn_sub(src, src->m, x, y, status_get_name(src), class_, "", SZ_SMALL, AI_NONE);
+ if (md) {
+ md->master_id = src->id;
+ md->special_state.ai = (skill_id == AM_SPHEREMINE) ? AI_SPHERE : AI_FLORA;
+ if( md->deletetimer != INVALID_TIMER )
+ delete_timer(md->deletetimer, mob_timer_delete);
+ md->deletetimer = add_timer (gettick() + skill_get_time(skill_id,skill_lv), mob_timer_delete, md->bl.id, 0);
+ mob_spawn (md); //Now it is ready for spawning.
+ }
+ }
+ break;
+
+ // Slim Pitcher [Celest]
+ case CR_SLIMPITCHER:
+ if (sd) {
+ int i = skill_lv%11 - 1;
+ int j = pc_search_inventory(sd,skill_db[skill_id].itemid[i]);
+ if( j < 0 || skill_db[skill_id].itemid[i] <= 0 || sd->inventory_data[j] == NULL || sd->status.inventory[j].amount < skill_db[skill_id].amount[i] )
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+ potion_flag = 1;
+ potion_hp = 0;
+ potion_sp = 0;
+ run_script(sd->inventory_data[j]->script,0,sd->bl.id,0);
+ potion_flag = 0;
+ //Apply skill bonuses
+ i = pc_checkskill(sd,CR_SLIMPITCHER)*10
+ + pc_checkskill(sd,AM_POTIONPITCHER)*10
+ + pc_checkskill(sd,AM_LEARNINGPOTION)*5
+ + pc_skillheal_bonus(sd, skill_id);
+
+ potion_hp = potion_hp * (100+i)/100;
+ potion_sp = potion_sp * (100+i)/100;
+
+ if(potion_hp > 0 || potion_sp > 0) {
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea(skill_area_sub,
+ src->m,x-i,y-i,x+i,y+i,BL_CHAR,
+ src,skill_id,skill_lv,tick,flag|BCT_PARTY|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ }
+ } else {
+ int i = skill_lv%11 - 1;
+ struct item_data *item;
+ i = skill_db[skill_id].itemid[i];
+ item = itemdb_search(i);
+ potion_flag = 1;
+ potion_hp = 0;
+ potion_sp = 0;
+ run_script(item->script,0,src->id,0);
+ potion_flag = 0;
+ i = skill_get_max(CR_SLIMPITCHER)*10;
+
+ potion_hp = potion_hp * (100+i)/100;
+ potion_sp = potion_sp * (100+i)/100;
+
+ if(potion_hp > 0 || potion_sp > 0) {
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea(skill_area_sub,
+ src->m,x-i,y-i,x+i,y+i,BL_CHAR,
+ src,skill_id,skill_lv,tick,flag|BCT_PARTY|BCT_GUILD|1,
+ skill_castend_nodamage_id);
+ }
+ }
+ break;
+
+ case HW_GANBANTEIN:
+ if (rnd()%100 < 80) {
+ int dummy = 1;
+ clif_skill_poseffect(src,skill_id,skill_lv,x,y,tick);
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea(skill_cell_overlap, src->m, x-i, y-i, x+i, y+i, BL_SKILL, HW_GANBANTEIN, &dummy, src);
+ } else {
+ if (sd) clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+ break;
+
+ case HW_GRAVITATION:
+ if ((sg = skill_unitsetting(src,skill_id,skill_lv,x,y,0)))
+ sc_start4(src,type,100,skill_lv,0,BCT_SELF,sg->group_id,skill_get_time(skill_id,skill_lv));
+ flag|=1;
+ break;
+
+ // Plant Cultivation [Celest]
+ case CR_CULTIVATION:
+ if (sd) {
+ if( map_count_oncell(src->m,x,y,BL_CHAR) > 0 )
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+ clif_skill_poseffect(src,skill_id,skill_lv,x,y,tick);
+ if (rnd()%100 < 50) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ } else {
+ TBL_MOB* md = mob_once_spawn_sub(src, src->m, x, y, "--ja--",(skill_lv < 2 ? 1084+rnd()%2 : 1078+rnd()%6),"", SZ_SMALL, AI_NONE);
+ int i;
+ if (!md) break;
+ if ((i = skill_get_time(skill_id, skill_lv)) > 0)
+ {
+ if( md->deletetimer != INVALID_TIMER )
+ delete_timer(md->deletetimer, mob_timer_delete);
+ md->deletetimer = add_timer (tick + i, mob_timer_delete, md->bl.id, 0);
+ }
+ mob_spawn (md);
+ }
+ }
+ break;
+
+ case SG_SUN_WARM:
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ skill_clear_unitgroup(src);
+ if ((sg = skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0)))
+ sc_start4(src,type,100,skill_lv,0,0,sg->group_id,skill_get_time(skill_id,skill_lv));
+ flag|=1;
+ break;
+
+ case PA_GOSPEL:
+ if (sce && sce->val4 == BCT_SELF)
+ {
+ status_change_end(src, SC_GOSPEL, INVALID_TIMER);
+ return 0;
+ }
+ else
+ {
+ sg = skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0);
+ if (!sg) break;
+ if (sce)
+ status_change_end(src, type, INVALID_TIMER); //Was under someone else's Gospel. [Skotlex]
+ sc_start4(src,type,100,skill_lv,0,sg->group_id,BCT_SELF,skill_get_time(skill_id,skill_lv));
+ clif_skill_poseffect(src, skill_id, skill_lv, 0, 0, tick); // PA_GOSPEL music packet
+ }
+ break;
+ case NJ_TATAMIGAESHI:
+ if (skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0))
+ sc_start(src,type,100,skill_lv,skill_get_time2(skill_id,skill_lv));
+ break;
+
+ case AM_RESURRECTHOMUN: //[orn]
+ if (sd)
+ {
+ if (!merc_resurrect_homunculus(sd, 20*skill_lv, x, y))
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ break;
+ }
+ }
+ break;
+
+ case RK_WINDCUTTER:
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ case NC_COLDSLOWER:
+ case NC_ARMSCANNON:
+ case RK_DRAGONBREATH:
+ i = skill_get_splash(skill_id,skill_lv);
+ map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,splash_target(src),
+ src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ break;
+
+ case SO_ARRULLO:
+ i = skill_get_splash(skill_id,skill_lv);
+ map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,splash_target(src),
+ src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill_castend_nodamage_id);
+ break;
+ /**
+ * Guilotine Cross
+ **/
+ case GC_POISONSMOKE:
+ if( !(sc && sc->data[SC_POISONINGWEAPON]) ) {
+ if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_GC_POISONINGWEAPON,0);
+ return 0;
+ }
+ clif_skill_damage(src,src,tick,status_get_amotion(src),0,-30000,1,skill_id,skill_lv,6);
+ skill_unitsetting(src, skill_id, skill_lv, x, y, flag);
+ //status_change_end(src,SC_POISONINGWEAPON,INVALID_TIMER); // 08/31/2011 - When using poison smoke, you no longer lose the poisoning weapon effect.
+ break;
+ /**
+ * Arch Bishop
+ **/
+ case AB_EPICLESIS:
+ if( (sg = skill_unitsetting(src, skill_id, skill_lv, x, y, 0)) ) {
+ i = sg->unit->range;
+ map_foreachinarea(skill_area_sub, src->m, x - i, y - i, x + i, y + i, BL_CHAR, src, ALL_RESURRECTION, 1, tick, flag|BCT_NOENEMY|1,skill_castend_nodamage_id);
+ }
+ break;
+ /**
+ * Warlock
+ **/
+ case WL_COMET:
+ if( sc ) {
+ sc->comet_x = x;
+ sc->comet_y = y;
+ }
+ i = skill_get_splash(skill_id,skill_lv);
+ map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,splash_target(src),src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ break;
+
+ case WL_EARTHSTRAIN:
+ {
+ int i, wave = skill_lv + 4, dir = map_calc_dir(src,x,y);
+ int sx = x = src->x, sy = y = src->y; // Store first caster's location to avoid glitch on unit setting
+
+ for( i = 1; i <= wave; i++ )
+ {
+ switch( dir ){
+ case 0: case 1: case 7: sy = y + i; break;
+ case 3: case 4: case 5: sy = y - i; break;
+ case 2: sx = x - i; break;
+ case 6: sx = x + i; break;
+ }
+ skill_addtimerskill(src,gettick() + (150 * i),0,sx,sy,skill_id,skill_lv,dir,flag&2);
+ }
+ }
+ break;
+ /**
+ * Ranger
+ **/
+ case RA_DETONATOR:
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea(skill_detonator, src->m, x-i, y-i, x+i, y+i, BL_SKILL, src);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skill_id, skill_lv, 6);
+ break;
+ /**
+ * Mechanic
+ **/
+ case NC_NEUTRALBARRIER:
+ case NC_STEALTHFIELD:
+ skill_clear_unitgroup(src); // To remove previous skills - cannot used combined
+ if( (sg = skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0)) != NULL ) {
+ sc_start2(src,skill_id == NC_NEUTRALBARRIER ? SC_NEUTRALBARRIER_MASTER : SC_STEALTHFIELD_MASTER,100,skill_lv,sg->group_id,skill_get_time(skill_id,skill_lv));
+ if( sd ) pc_overheat(sd,1);
+ }
+ break;
+
+ case NC_SILVERSNIPER:
+ {
+ int class_ = 2042;
+ struct mob_data *md;
+
+ md = mob_once_spawn_sub(src, src->m, x, y, status_get_name(src), class_, "", SZ_SMALL, AI_NONE);
+ if( md )
+ {
+ md->master_id = src->id;
+ md->special_state.ai = AI_FLORA;
+ if( md->deletetimer != INVALID_TIMER )
+ delete_timer(md->deletetimer, mob_timer_delete);
+ md->deletetimer = add_timer (gettick() + skill_get_time(skill_id, skill_lv), mob_timer_delete, md->bl.id, 0);
+ mob_spawn( md );
+ }
+ }
+ break;
+
+ case NC_MAGICDECOY:
+ if( sd ) clif_magicdecoy_list(sd,skill_lv,x,y);
+ break;
+
+ case SC_FEINTBOMB:
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ skill_unitsetting(src,skill_id,skill_lv,x,y,0); // Set bomb on current Position
+ if( skill_blown(src,src,6,unit_getdir(src),0) )
+ skill_castend_nodamage_id(src,src,TF_HIDING,1,tick,0);
+ break;
+
+ case LG_OVERBRAND:
+ {
+ int width;//according to data from irowiki it actually is a square
+ for( width = 0; width < 7; width++ )
+ for( i = 0; i < 7; i++ )
+ map_foreachincell(skill_area_sub, src->m, x-2+i, y-2+width, splash_target(src), src, LG_OVERBRAND_BRANDISH, skill_lv, tick, flag|BCT_ENEMY,skill_castend_damage_id);
+ for( width = 0; width < 7; width++ )
+ for( i = 0; i < 7; i++ )
+ map_foreachincell(skill_area_sub, src->m, x-2+i, y-2+width, splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY,skill_castend_damage_id);
+ }
+ break;
+
+ case LG_BANDING:
+ if( sc && sc->data[SC_BANDING] )
+ status_change_end(src,SC_BANDING,INVALID_TIMER);
+ else if( (sg = skill_unitsetting(src,skill_id,skill_lv,src->x,src->y,0)) != NULL ) {
+ sc_start4(src,SC_BANDING,100,skill_lv,0,0,sg->group_id,skill_get_time(skill_id,skill_lv));
+ if( sd ) pc_banding(sd,skill_lv);
+ }
+ clif_skill_nodamage(src,src,skill_id,skill_lv,1);
+ break;
+
+ case LG_RAYOFGENESIS:
+ if( status_charge(src,status_get_max_hp(src)*3*skill_lv / 100,0) ) {
+ i = skill_get_splash(skill_id,skill_lv);
+ map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,splash_target(src),
+ src,skill_id,skill_lv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ } else if( sd )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL,0);
+ break;
+
+ case WM_DOMINION_IMPULSE:
+ i = skill_get_splash(skill_id, skill_lv);
+ map_foreachinarea( skill_ative_reverberation,
+ src->m, x-i, y-i, x+i,y+i,BL_SKILL);
+ break;
+
+ case WM_GREAT_ECHO:
+ flag|=1; // Should counsume 1 item per skill usage.
+ map_foreachinrange(skill_area_sub, src, skill_get_splash(skill_id,skill_lv),splash_target(src), src, skill_id, skill_lv, tick, flag|BCT_ENEMY, skill_castend_damage_id);
+ break;
+ case GN_CRAZYWEED: {
+ int area = skill_get_splash(GN_CRAZYWEED_ATK, skill_lv);
+ short x1 = 0, y1 = 0;
+
+ for( i = 0; i < 3 + (skill_lv/2); i++ ) {
+ x1 = x - area + rnd()%(area * 2 + 1);
+ y1 = y - area + rnd()%(area * 2 + 1);
+ skill_addtimerskill(src,tick+i*150,0,x1,y1,GN_CRAZYWEED_ATK,skill_lv,-1,0);
+ }
+ }
+ break;
+ case GN_FIRE_EXPANSION: {
+ int i;
+ struct unit_data *ud = unit_bl2ud(src);
+
+ if( !ud ) break;
+
+ for( i = 0; i < MAX_SKILLUNITGROUP && ud->skillunit[i]; i ++ ) {
+ if( ud->skillunit[i]->skill_id == GN_DEMONIC_FIRE &&
+ distance_xy(x, y, ud->skillunit[i]->unit->bl.x, ud->skillunit[i]->unit->bl.y) < 4 ) {
+ switch( skill_lv ) {
+ case 3:
+ ud->skillunit[i]->unit_id = UNT_FIRE_EXPANSION_SMOKE_POWDER;
+ clif_changetraplook(&ud->skillunit[i]->unit->bl, UNT_FIRE_EXPANSION_SMOKE_POWDER);
+ break;
+ case 4:
+ ud->skillunit[i]->unit_id = UNT_FIRE_EXPANSION_TEAR_GAS;
+ clif_changetraplook(&ud->skillunit[i]->unit->bl, UNT_FIRE_EXPANSION_TEAR_GAS);
+ break;
+ case 5:
+ map_foreachinarea(skill_area_sub, src->m,
+ ud->skillunit[i]->unit->bl.x - 3, ud->skillunit[i]->unit->bl.y - 3,
+ ud->skillunit[i]->unit->bl.x + 3, ud->skillunit[i]->unit->bl.y + 3, BL_CHAR,
+ src, CR_ACIDDEMONSTRATION, sd ? pc_checkskill(sd, CR_ACIDDEMONSTRATION) : skill_lv, tick, flag|BCT_ENEMY|1|SD_LEVEL, skill_castend_damage_id);
+ skill_delunit(ud->skillunit[i]->unit);
+ break;
+ default:
+ ud->skillunit[i]->unit->val2 = skill_lv;
+ ud->skillunit[i]->unit->group->val2 = skill_lv;
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case SO_FIREWALK:
+ case SO_ELECTRICWALK:
+ if( sc && sc->data[type] )
+ status_change_end(src,type,INVALID_TIMER);
+ clif_skill_nodamage(src, src ,skill_id, skill_lv,
+ sc_start2(src, type, 100, skill_id, skill_lv, skill_get_time(skill_id, skill_lv)));
+ break;
+
+ case SC_BLOODYLUST: //set in another group so instance will move if recasted
+ flag |= 33;
+ skill_unitsetting(src, skill_id, skill_lv, x, y, 0);
+ break;
+
+ case KO_MAKIBISHI:
+ for( i = 0; i < (skill_lv+2); i++ ) {
+ x = src->x - 1 + rnd()%3;
+ y = src->y - 1 + rnd()%3;
+ skill_unitsetting(src,skill_id,skill_lv,x,y,0);
+ }
+ break;
+
+ default:
+ ShowWarning("skill_castend_pos2: Unknown skill used:%d\n",skill_id);
+ return 1;
+ }
+
+ if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] ) //Should only remove after the skill has been casted.
+ status_change_end(src,SC_CURSEDCIRCLE_ATKER,INVALID_TIMER);
+
+ if( sd )
+ {// ensure that the skill last-cast tick is recorded
+ sd->canskill_tick = gettick();
+
+ if( sd->state.arrow_atk && !(flag&1) )
+ {// consume arrow if this is a ground skill
+ battle_consume_ammo(sd, skill_id, skill_lv);
+ }
+
+ // perform skill requirement consumption
+ skill_consume_requirement(sd,skill_id,skill_lv,2);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_castend_map (struct map_session_data *sd, uint16 skill_id, const char *map)
+{
+ nullpo_ret(sd);
+
+//Simplify skill_failed code.
+#define skill_failed(sd) { sd->menuskill_id = sd->menuskill_val = 0; }
+ if(skill_id != sd->menuskill_id)
+ return 0;
+
+ if( sd->bl.prev == NULL || pc_isdead(sd) ) {
+ skill_failed(sd);
+ return 0;
+ }
+
+ if( ( sd->sc.opt1 && sd->sc.opt1 != OPT1_BURNING ) || sd->sc.option&OPTION_HIDE ) {
+ skill_failed(sd);
+ return 0;
+ }
+ if(sd->sc.count && (
+ sd->sc.data[SC_SILENCE] ||
+ sd->sc.data[SC_ROKISWEIL] ||
+ sd->sc.data[SC_AUTOCOUNTER] ||
+ sd->sc.data[SC_STEELBODY] ||
+ (sd->sc.data[SC_DANCING] && skill_id < RK_ENCHANTBLADE && !pc_checkskill(sd, WM_LESSON)) ||
+ sd->sc.data[SC_BERSERK] || sd->sc.data[SC__BLOODYLUST] ||
+ sd->sc.data[SC_BASILICA] ||
+ sd->sc.data[SC_MARIONETTE] ||
+ sd->sc.data[SC_WHITEIMPRISON] ||
+ (sd->sc.data[SC_STASIS] && skill_block_check(&sd->bl, SC_STASIS, skill_id)) ||
+ (sd->sc.data[SC_KAGEHUMI] && skill_block_check(&sd->bl, SC_KAGEHUMI, skill_id)) ||
+ sd->sc.data[SC_OBLIVIONCURSE] ||
+ sd->sc.data[SC__MANHOLE] ||
+ (sd->sc.data[SC_ASH] && rnd()%2) //50% fail chance under ASH
+ )) {
+ skill_failed(sd);
+ return 0;
+ }
+
+ pc_stop_attack(sd);
+ pc_stop_walking(sd,0);
+
+ if(battle_config.skill_log && battle_config.skill_log&BL_PC)
+ ShowInfo("PC %d skill castend skill =%d map=%s\n",sd->bl.id,skill_id,map);
+
+ if(strcmp(map,"cancel")==0) {
+ skill_failed(sd);
+ return 0;
+ }
+
+ switch(skill_id)
+ {
+ case AL_TELEPORT:
+ if(strcmp(map,"Random")==0)
+ pc_randomwarp(sd,CLR_TELEPORT);
+ else if (sd->menuskill_val > 1) //Need lv2 to be able to warp here.
+ pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,CLR_TELEPORT);
+ break;
+
+ case AL_WARP:
+ {
+ const struct point *p[4];
+ struct skill_unit_group *group;
+ int i, lv, wx, wy;
+ int maxcount=0;
+ int x,y;
+ unsigned short mapindex;
+
+ mapindex = mapindex_name2id((char*)map);
+ if(!mapindex) { //Given map not found?
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ skill_failed(sd);
+ return 0;
+ }
+ p[0] = &sd->status.save_point;
+ p[1] = &sd->status.memo_point[0];
+ p[2] = &sd->status.memo_point[1];
+ p[3] = &sd->status.memo_point[2];
+
+ if((maxcount = skill_get_maxcount(skill_id, sd->menuskill_val)) > 0) {
+ for(i=0;i<MAX_SKILLUNITGROUP && sd->ud.skillunit[i] && maxcount;i++) {
+ if(sd->ud.skillunit[i]->skill_id == skill_id)
+ maxcount--;
+ }
+ if(!maxcount) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ skill_failed(sd);
+ return 0;
+ }
+ }
+
+ lv = sd->skillitem==skill_id?sd->skillitemlv:pc_checkskill(sd,skill_id);
+ wx = sd->menuskill_val>>16;
+ wy = sd->menuskill_val&0xffff;
+
+ if( lv <= 0 ) return 0;
+ if( lv > 4 ) lv = 4; // crash prevention
+
+ // check if the chosen map exists in the memo list
+ ARR_FIND( 0, lv, i, mapindex == p[i]->map );
+ if( i < lv ) {
+ x=p[i]->x;
+ y=p[i]->y;
+ } else {
+ skill_failed(sd);
+ return 0;
+ }
+
+ if(!skill_check_condition_castend(sd, sd->menuskill_id, lv))
+ { // This checks versus skill_id/skill_lv...
+ skill_failed(sd);
+ return 0;
+ }
+
+ skill_consume_requirement(sd,sd->menuskill_id,lv,2);
+ sd->skillitem = sd->skillitemlv = 0; // Clear data that's skipped in 'skill_castend_pos' [Inkfish]
+
+ if((group=skill_unitsetting(&sd->bl,skill_id,lv,wx,wy,0))==NULL) {
+ skill_failed(sd);
+ return 0;
+ }
+
+ group->val1 = (group->val1<<16)|(short)0;
+ // record the destination coordinates
+ group->val2 = (x<<16)|y;
+ group->val3 = mapindex;
+ }
+ break;
+ }
+
+ sd->menuskill_id = sd->menuskill_val = 0;
+ return 0;
+#undef skill_failed
+}
+
+/// transforms 'target' skill unit into dissonance (if conditions are met)
+static int skill_dance_overlap_sub(struct block_list* bl, va_list ap)
+{
+ struct skill_unit* target = (struct skill_unit*)bl;
+ struct skill_unit* src = va_arg(ap, struct skill_unit*);
+ int flag = va_arg(ap, int);
+
+ if (src == target)
+ return 0;
+ if (!target->group || !(target->group->state.song_dance&0x1))
+ return 0;
+ if (!(target->val2 & src->val2 & ~UF_ENSEMBLE)) //They don't match (song + dance) is valid.
+ return 0;
+
+ if (flag) //Set dissonance
+ target->val2 |= UF_ENSEMBLE; //Add ensemble to signal this unit is overlapping.
+ else //Remove dissonance
+ target->val2 &= ~UF_ENSEMBLE;
+
+ clif_skill_setunit(target); //Update look of affected cell.
+
+ return 1;
+}
+
+//Does the song/dance overlapping -> dissonance check. [Skotlex]
+//When flag is 0, this unit is about to be removed, cancel the dissonance effect
+//When 1, this unit has been positioned, so start the cancel effect.
+int skill_dance_overlap(struct skill_unit* unit, int flag)
+{
+ if (!unit || !unit->group || !(unit->group->state.song_dance&0x1))
+ return 0;
+ if (!flag && !(unit->val2&UF_ENSEMBLE))
+ return 0; //Nothing to remove, this unit is not overlapped.
+
+ if (unit->val1 != unit->group->skill_id)
+ { //Reset state
+ unit->val1 = unit->group->skill_id;
+ unit->val2 &= ~UF_ENSEMBLE;
+ }
+
+ return map_foreachincell(skill_dance_overlap_sub, unit->bl.m,unit->bl.x,unit->bl.y,BL_SKILL, unit,flag);
+}
+
+/*==========================================
+ * Converts this group information so that it is handled as a Dissonance or Ugly Dance cell.
+ * Flag: 0 - Convert, 1 - Revert.
+ *------------------------------------------*/
+static bool skill_dance_switch(struct skill_unit* unit, int flag)
+{
+ static int prevflag = 1; // by default the backup is empty
+ static struct skill_unit_group backup;
+ struct skill_unit_group* group = unit->group;
+
+ // val2&UF_ENSEMBLE is a hack to indicate dissonance
+ if ( !(group->state.song_dance&0x1 && unit->val2&UF_ENSEMBLE) )
+ return false;
+
+ if( flag == prevflag )
+ {// protection against attempts to read an empty backup / write to a full backup
+ ShowError("skill_dance_switch: Attempted to %s (skill_id=%d, skill_lv=%d, src_id=%d).\n",
+ flag ? "read an empty backup" : "write to a full backup",
+ group->skill_id, group->skill_lv, group->src_id);
+ return false;
+ }
+ prevflag = flag;
+
+ if( !flag )
+ { //Transform
+ uint16 skill_id = unit->val2&UF_SONG ? BA_DISSONANCE : DC_UGLYDANCE;
+
+ // backup
+ backup.skill_id = group->skill_id;
+ backup.skill_lv = group->skill_lv;
+ backup.unit_id = group->unit_id;
+ backup.target_flag = group->target_flag;
+ backup.bl_flag = group->bl_flag;
+ backup.interval = group->interval;
+
+ // replace
+ group->skill_id = skill_id;
+ group->skill_lv = 1;
+ group->unit_id = skill_get_unit_id(skill_id,0);
+ group->target_flag = skill_get_unit_target(skill_id);
+ group->bl_flag = skill_get_unit_bl_target(skill_id);
+ group->interval = skill_get_unit_interval(skill_id);
+ }
+ else
+ { //Restore
+ group->skill_id = backup.skill_id;
+ group->skill_lv = backup.skill_lv;
+ group->unit_id = backup.unit_id;
+ group->target_flag = backup.target_flag;
+ group->bl_flag = backup.bl_flag;
+ group->interval = backup.interval;
+ }
+
+ return true;
+}
+/**
+ * Upon Ice Wall cast it checks all nearby mobs to find any who may be blocked by the IW
+ **/
+static int skill_icewall_block(struct block_list *bl,va_list ap) {
+ struct block_list *target = NULL;
+ struct mob_data *md = ((TBL_MOB*)bl);
+
+ nullpo_ret(bl);
+ nullpo_ret(md);
+ if( !md->target_id || ( target = map_id2bl(md->target_id) ) == NULL )
+ return 0;
+
+ if( path_search_long(NULL,bl->m,bl->x,bl->y,target->x,target->y,CELL_CHKICEWALL) )
+ return 0;
+
+ if( !check_distance_bl(bl, target, status_get_range(bl) ) ) {
+ mob_unlocktarget(md,gettick());
+ mob_stop_walking(md,1);
+ }
+
+ return 0;
+}
+/*==========================================
+ * Initializes and sets a ground skill.
+ * flag&1 is used to determine when the skill 'morphs' (Warp portal becomes active, or Fire Pillar becomes active)
+ *------------------------------------------*/
+struct skill_unit_group* skill_unitsetting (struct block_list *src, uint16 skill_id, uint16 skill_lv, int16 x, int16 y, int flag)
+{
+ struct skill_unit_group *group;
+ int i,limit,val1=0,val2=0,val3=0;
+ int target,interval,range,unit_flag,req_item=0;
+ struct s_skill_unit_layout *layout;
+ struct map_session_data *sd;
+ struct status_data *status;
+ struct status_change *sc;
+ int active_flag=1;
+ int subunt=0;
+
+ nullpo_retr(NULL, src);
+
+ limit = skill_get_time(skill_id,skill_lv);
+ range = skill_get_unit_range(skill_id,skill_lv);
+ interval = skill_get_unit_interval(skill_id);
+ target = skill_get_unit_target(skill_id);
+ unit_flag = skill_get_unit_flag(skill_id);
+ layout = skill_get_unit_layout(skill_id,skill_lv,src,x,y);
+
+ sd = BL_CAST(BL_PC, src);
+ status = status_get_status_data(src);
+ sc = status_get_sc(src); // for traps, firewall and fogwall - celest
+
+ switch( skill_id ) {
+ case MH_STEINWAND:
+ val2 = 4 + skill_lv; //nb of attack blocked
+ break;
+ case MG_SAFETYWALL:
+ #ifdef RENEWAL
+ /**
+ * According to data provided in RE, SW life is equal to 3 times caster's health
+ **/
+ val2 = status_get_max_hp(src) * 3;
+ #else
+ val2 = skill_lv+1;
+ #endif
+ break;
+ case MG_FIREWALL:
+ if(sc && sc->data[SC_VIOLENTGALE])
+ limit = limit*3/2;
+ val2=4+skill_lv;
+ break;
+
+ case AL_WARP:
+ val1=skill_lv+6;
+ if(!(flag&1))
+ limit=2000;
+ else // previous implementation (not used anymore)
+ { //Warp Portal morphing to active mode, extract relevant data from src. [Skotlex]
+ if( src->type != BL_SKILL ) return NULL;
+ group = ((TBL_SKILL*)src)->group;
+ src = map_id2bl(group->src_id);
+ if( !src ) return NULL;
+ val2 = group->val2; //Copy the (x,y) position you warp to
+ val3 = group->val3; //as well as the mapindex to warp to.
+ }
+ break;
+ case HP_BASILICA:
+ val1 = src->id; // Store caster id.
+ break;
+
+ case PR_SANCTUARY:
+ case NPC_EVILLAND:
+ val1=(skill_lv+3)*2;
+ break;
+
+ case WZ_FIREPILLAR:
+ if((flag&1)!=0)
+ limit=1000;
+ val1=skill_lv+2;
+ break;
+ case WZ_QUAGMIRE: //The target changes to "all" if used in a gvg map. [Skotlex]
+ case AM_DEMONSTRATION:
+ case GN_HELLS_PLANT:
+ if (map_flag_vs(src->m) && battle_config.vs_traps_bctall
+ && (src->type&battle_config.vs_traps_bctall))
+ target = BCT_ALL;
+ break;
+ case HT_SHOCKWAVE:
+ val1=skill_lv*15+10;
+ case HT_SANDMAN:
+ case MA_SANDMAN:
+ case HT_CLAYMORETRAP:
+ case HT_SKIDTRAP:
+ case MA_SKIDTRAP:
+ case HT_LANDMINE:
+ case MA_LANDMINE:
+ case HT_ANKLESNARE:
+ case HT_FLASHER:
+ case HT_FREEZINGTRAP:
+ case MA_FREEZINGTRAP:
+ case HT_BLASTMINE:
+ /**
+ * Ranger
+ **/
+ case RA_ELECTRICSHOCKER:
+ case RA_CLUSTERBOMB:
+ case RA_MAGENTATRAP:
+ case RA_COBALTTRAP:
+ case RA_MAIZETRAP:
+ case RA_VERDURETRAP:
+ case RA_FIRINGTRAP:
+ case RA_ICEBOUNDTRAP:
+ {
+ struct skill_condition req = skill_get_requirement(sd,skill_id,skill_lv);
+ ARR_FIND(0, MAX_SKILL_ITEM_REQUIRE, i, req.itemid[i] && (req.itemid[i] == ITEMID_TRAP || req.itemid[i] == ITEMID_TRAP_ALLOY));
+ if( req.itemid[i] )
+ req_item = req.itemid[i];
+ if( map_flag_gvg(src->m) || map[src->m].flag.battleground )
+ limit *= 4; // longer trap times in WOE [celest]
+ if( battle_config.vs_traps_bctall && map_flag_vs(src->m) && (src->type&battle_config.vs_traps_bctall) )
+ target = BCT_ALL;
+ }
+ break;
+
+ case SA_LANDPROTECTOR:
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+ {
+ struct skill_unit_group *old_sg;
+ if ((old_sg = skill_locate_element_field(src)) != NULL)
+ { //HelloKitty confirmed that these are interchangeable,
+ //so you can change element and not consume gemstones.
+ if ((
+ old_sg->skill_id == SA_VOLCANO ||
+ old_sg->skill_id == SA_DELUGE ||
+ old_sg->skill_id == SA_VIOLENTGALE
+ ) && old_sg->limit > 0)
+ { //Use the previous limit (minus the elapsed time) [Skotlex]
+ limit = old_sg->limit - DIFF_TICK(gettick(), old_sg->tick);
+ if (limit < 0) //This can happen...
+ limit = skill_get_time(skill_id,skill_lv);
+ }
+ skill_clear_group(src,1);
+ }
+ break;
+ }
+
+ case BA_DISSONANCE:
+ case DC_UGLYDANCE:
+ val1 = 10; //FIXME: This value is not used anywhere, what is it for? [Skotlex]
+ break;
+ case BA_WHISTLE:
+ val1 = skill_lv +status->agi/10; // Flee increase
+ val2 = ((skill_lv+1)/2)+status->luk/10; // Perfect dodge increase
+ if(sd){
+ val1 += pc_checkskill(sd,BA_MUSICALLESSON);
+ val2 += pc_checkskill(sd,BA_MUSICALLESSON);
+ }
+ break;
+ case DC_HUMMING:
+ val1 = 2*skill_lv+status->dex/10; // Hit increase
+ #ifdef RENEWAL
+ val1 *= 2;
+ #endif
+ if(sd)
+ val1 += pc_checkskill(sd,DC_DANCINGLESSON);
+ break;
+ case BA_POEMBRAGI:
+ val1 = 3*skill_lv+status->dex/10; // Casting time reduction
+ //For some reason at level 10 the base delay reduction is 50%.
+ val2 = (skill_lv<10?3*skill_lv:50)+status->int_/5; // After-cast delay reduction
+ if(sd){
+ val1 += 2*pc_checkskill(sd,BA_MUSICALLESSON);
+ val2 += 2*pc_checkskill(sd,BA_MUSICALLESSON);
+ }
+ break;
+ case DC_DONTFORGETME:
+ val1 = status->dex/10 + 3*skill_lv + 5; // ASPD decrease
+ val2 = status->agi/10 + 3*skill_lv + 5; // Movement speed adjustment.
+ if(sd){
+ val1 += pc_checkskill(sd,DC_DANCINGLESSON);
+ val2 += pc_checkskill(sd,DC_DANCINGLESSON);
+ }
+ break;
+ case BA_APPLEIDUN:
+ val1 = 5+2*skill_lv+status->vit/10; // MaxHP percent increase
+ if(sd)
+ val1 += pc_checkskill(sd,BA_MUSICALLESSON);
+ break;
+ case DC_SERVICEFORYOU:
+ val1 = 15+skill_lv+(status->int_/10); // MaxSP percent increase TO-DO: this INT bonus value is guessed
+ val2 = 20+3*skill_lv+(status->int_/10); // SP cost reduction
+ if(sd){
+ val1 += pc_checkskill(sd,DC_DANCINGLESSON); //TO-DO This bonus value is guessed
+ val2 += pc_checkskill(sd,DC_DANCINGLESSON); //TO-DO Should be half this value
+ }
+ break;
+ case BA_ASSASSINCROSS:
+ val1 = 100+(10*skill_lv)+(status->agi/10); // ASPD increase
+ if(sd)
+ val1 += 5*pc_checkskill(sd,BA_MUSICALLESSON);
+ break;
+ case DC_FORTUNEKISS:
+ val1 = 10+skill_lv+(status->luk/10); // Critical increase
+ if(sd)
+ val1 += pc_checkskill(sd,DC_DANCINGLESSON);
+ val1*=10; //Because every 10 crit is an actual cri point.
+ break;
+ case BD_DRUMBATTLEFIELD:
+ #ifdef RENEWAL
+ val1 = (skill_lv+5)*25; //Watk increase
+ val2 = skill_lv*10; //Def increase
+ #else
+ val1 = (skill_lv+1)*25; //Watk increase
+ val2 = (skill_lv+1)*2; //Def increase
+ #endif
+ break;
+ case BD_RINGNIBELUNGEN:
+ val1 = (skill_lv+2)*25; //Watk increase
+ break;
+ case BD_RICHMANKIM:
+ val1 = 25 + 11*skill_lv; //Exp increase bonus.
+ break;
+ case BD_SIEGFRIED:
+ val1 = 55 + skill_lv*5; //Elemental Resistance
+ val2 = skill_lv*10; //Status ailment resistance
+ break;
+ case WE_CALLPARTNER:
+ if (sd) val1 = sd->status.partner_id;
+ break;
+ case WE_CALLPARENT:
+ if (sd) {
+ val1 = sd->status.father;
+ val2 = sd->status.mother;
+ }
+ break;
+ case WE_CALLBABY:
+ if (sd) val1 = sd->status.child;
+ break;
+ case NJ_KAENSIN:
+ skill_clear_group(src, 1); //Delete previous Kaensins/Suitons
+ val2 = (skill_lv+1)/2 + 4;
+ break;
+ case NJ_SUITON:
+ skill_clear_group(src, 1);
+ break;
+
+ case GS_GROUNDDRIFT:
+ {
+ int element[5]={ELE_WIND,ELE_DARK,ELE_POISON,ELE_WATER,ELE_FIRE};
+
+ val1 = status->rhw.ele;
+ if (!val1)
+ val1=element[rnd()%5];
+
+ switch (val1)
+ {
+ case ELE_FIRE:
+ subunt++;
+ case ELE_WATER:
+ subunt++;
+ case ELE_POISON:
+ subunt++;
+ case ELE_DARK:
+ subunt++;
+ case ELE_WIND:
+ break;
+ default:
+ subunt=rnd()%5;
+ break;
+ }
+
+ break;
+ }
+ case GC_POISONSMOKE:
+ if( !(sc && sc->data[SC_POISONINGWEAPON]) )
+ return NULL;
+ val2 = sc->data[SC_POISONINGWEAPON]->val2; // Type of Poison
+ val3 = sc->data[SC_POISONINGWEAPON]->val1;
+ limit = 4000 + 2000 * skill_lv;
+ break;
+ case GD_LEADERSHIP:
+ case GD_GLORYWOUNDS:
+ case GD_SOULCOLD:
+ case GD_HAWKEYES:
+ limit = 1000000;//it doesn't matter
+ break;
+ case LG_BANDING:
+ limit = -1;
+ break;
+ case WM_REVERBERATION:
+ interval = limit;
+ val2 = 1;
+ case WM_POEMOFNETHERWORLD: // Can't be placed on top of Land Protector.
+ if( map_getcell(src->m, x, y, CELL_CHKLANDPROTECTOR) )
+ return NULL;
+ break;
+ case SO_CLOUD_KILL:
+ skill_clear_group(src, 4);
+ break;
+ case SO_WARMER:
+ skill_clear_group(src, 8);
+ break;
+ case SO_VACUUM_EXTREME:
+ range++;
+
+ break;
+ case SC_BLOODYLUST:
+ skill_clear_group(src, 32);
+ break;
+ case GN_WALLOFTHORN:
+ if( flag&1 )
+ limit = 3000;
+ val3 = (x<<16)|y;
+ break;
+ case KO_ZENKAI:
+ if( sd ){
+ ARR_FIND(1, 6, i, sd->talisman[i] > 0);
+ if( i < 5 ){
+ val1 = sd->talisman[i]; // no. of aura
+ val2 = i; // aura type
+ limit += val1 * 1000;
+ subunt = i - 1;
+ pc_del_talisman(sd, sd->talisman[i], i);
+ }
+ }
+ break;
+ }
+
+ nullpo_retr(NULL, group=skill_initunitgroup(src,layout->count,skill_id,skill_lv,skill_get_unit_id(skill_id,flag&1)+subunt, limit, interval));
+ group->val1=val1;
+ group->val2=val2;
+ group->val3=val3;
+ group->target_flag=target;
+ group->bl_flag= skill_get_unit_bl_target(skill_id);
+ group->state.ammo_consume = (sd && sd->state.arrow_atk && skill_id != GS_GROUNDDRIFT); //Store if this skill needs to consume ammo.
+ group->state.song_dance = (unit_flag&(UF_DANCE|UF_SONG)?1:0)|(unit_flag&UF_ENSEMBLE?2:0); //Signals if this is a song/dance/duet
+ group->state.guildaura = ( skill_id >= GD_LEADERSHIP && skill_id <= GD_HAWKEYES )?1:0;
+ group->item_id = req_item;
+ //if tick is greater than current, do not invoke onplace function just yet. [Skotlex]
+ if (DIFF_TICK(group->tick, gettick()) > SKILLUNITTIMER_INTERVAL)
+ active_flag = 0;
+
+ if(skill_id==HT_TALKIEBOX || skill_id==RG_GRAFFITI){
+ group->valstr=(char *) aMalloc(MESSAGE_SIZE*sizeof(char));
+ if (sd)
+ safestrncpy(group->valstr, sd->message, MESSAGE_SIZE);
+ else //Eh... we have to write something here... even though mobs shouldn't use this. [Skotlex]
+ safestrncpy(group->valstr, "Boo!", MESSAGE_SIZE);
+ }
+
+ if (group->state.song_dance) {
+ if(sd){
+ sd->skill_id_dance = skill_id;
+ sd->skill_lv_dance = skill_lv;
+ }
+ if (
+ sc_start4(src, SC_DANCING, 100, skill_id, group->group_id, skill_lv,
+ (group->state.song_dance&2?BCT_SELF:0), limit+1000) &&
+ sd && group->state.song_dance&2 && skill_id != CG_HERMODE //Hermod is a encore with a warp!
+ )
+ skill_check_pc_partner(sd, skill_id, &skill_lv, 1, 1);
+ }
+
+ limit = group->limit;
+ for( i = 0; i < layout->count; i++ )
+ {
+ struct skill_unit *unit;
+ int ux = x + layout->dx[i];
+ int uy = y + layout->dy[i];
+ int val1 = skill_lv;
+ int val2 = 0;
+ int alive = 1;
+
+ if( !group->state.song_dance && !map_getcell(src->m,ux,uy,CELL_CHKREACH) )
+ continue; // don't place skill units on walls (except for songs/dances/encores)
+ if( battle_config.skill_wall_check && skill_get_unit_flag(skill_id)&UF_PATHCHECK && !path_search_long(NULL,src->m,ux,uy,x,y,CELL_CHKWALL) )
+ continue; // no path between cell and center of casting.
+
+ switch( skill_id )
+ {
+ case MG_FIREWALL:
+ case NJ_KAENSIN:
+ val2=group->val2;
+ break;
+ case WZ_ICEWALL:
+ val1 = (skill_lv <= 1) ? 500 : 200 + 200*skill_lv;
+ val2 = map_getcell(src->m, ux, uy, CELL_GETTYPE);
+ break;
+ case HT_LANDMINE:
+ case MA_LANDMINE:
+ case HT_ANKLESNARE:
+ case HT_SHOCKWAVE:
+ case HT_SANDMAN:
+ case MA_SANDMAN:
+ case HT_FLASHER:
+ case HT_FREEZINGTRAP:
+ case MA_FREEZINGTRAP:
+ case HT_TALKIEBOX:
+ case HT_SKIDTRAP:
+ case MA_SKIDTRAP:
+ case HT_CLAYMORETRAP:
+ case HT_BLASTMINE:
+ /**
+ * Ranger
+ **/
+ case RA_ELECTRICSHOCKER:
+ case RA_CLUSTERBOMB:
+ case RA_MAGENTATRAP:
+ case RA_COBALTTRAP:
+ case RA_MAIZETRAP:
+ case RA_VERDURETRAP:
+ case RA_FIRINGTRAP:
+ case RA_ICEBOUNDTRAP:
+ val1 = 3500;
+ break;
+ case GS_DESPERADO:
+ val1 = abs(layout->dx[i]);
+ val2 = abs(layout->dy[i]);
+ if (val1 < 2 || val2 < 2) { //Nearby cross, linear decrease with no diagonals
+ if (val2 > val1) val1 = val2;
+ if (val1) val1--;
+ val1 = 36 -12*val1;
+ } else //Diagonal edges
+ val1 = 28 -4*val1 -4*val2;
+ if (val1 < 1) val1 = 1;
+ val2 = 0;
+ break;
+ case WM_REVERBERATION:
+ val1 = 1 + skill_lv;
+ break;
+ case GN_WALLOFTHORN:
+ val1 = 1000 * skill_lv; // Need official value. [LimitLine]
+ break;
+ default:
+ if (group->state.song_dance&0x1)
+ val2 = unit_flag&(UF_DANCE|UF_SONG); //Store whether this is a song/dance
+ break;
+ }
+ if (skill_get_unit_flag(skill_id) & UF_RANGEDSINGLEUNIT && i == (layout->count / 2))
+ val2 |= UF_RANGEDSINGLEUNIT; // center.
+
+ if( range <= 0 )
+ map_foreachincell(skill_cell_overlap,src->m,ux,uy,BL_SKILL,skill_id, &alive, src);
+ if( !alive )
+ continue;
+
+ nullpo_retr(NULL, unit=skill_initunit(group,i,ux,uy,val1,val2));
+ unit->limit=limit;
+ unit->range=range;
+
+ if (skill_id == PF_FOGWALL && alive == 2)
+ { //Double duration of cells on top of Deluge/Suiton
+ unit->limit *= 2;
+ group->limit = unit->limit;
+ }
+
+ // execute on all targets standing on this cell
+ if (range==0 && active_flag)
+ map_foreachincell(skill_unit_effect,unit->bl.m,unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),1);
+ }
+
+ if (!group->alive_count)
+ { //No cells? Something that was blocked completely by Land Protector?
+ skill_delunitgroup(group);
+ return NULL;
+ }
+
+ //success, unit created.
+ switch( skill_id ) {
+ case WZ_ICEWALL:
+ map_foreachinrange(skill_icewall_block, src, AREA_SIZE, BL_MOB);
+ break;
+ case NJ_TATAMIGAESHI: //Store number of tiles.
+ group->val1 = group->alive_count;
+ break;
+ }
+
+ return group;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+void ext_skill_unit_onplace(struct skill_unit *src, struct block_list *bl, unsigned int tick){skill_unit_onplace(src, bl, tick);}
+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 status_change *sc;
+ struct status_change_entry *sce;
+ enum sc_type type;
+ uint16 skill_id;
+
+ nullpo_ret(src);
+ nullpo_ret(bl);
+
+ if(bl->prev==NULL || !src->alive || status_isdead(bl))
+ return 0;
+
+ nullpo_ret(sg=src->group);
+ nullpo_ret(ss=map_id2bl(sg->src_id));
+
+ if( skill_get_type(sg->skill_id) == BF_MAGIC && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR) && sg->skill_id != SA_LANDPROTECTOR )
+ return 0; //AoE skills are ineffective. [Skotlex]
+
+ sc = status_get_sc(bl);
+
+ if (sc && sc->option&OPTION_HIDE && sg->skill_id != WZ_HEAVENDRIVE && sg->skill_id != WL_EARTHSTRAIN )
+ return 0; //Hidden characters are immune to AoE skills except to these. [Skotlex]
+
+ type = status_skill2sc(sg->skill_id);
+ sce = (sc && type != -1)?sc->data[type]:NULL;
+ skill_id = sg->skill_id; //In case the group is deleted, we need to return the correct skill id, still.
+ switch (sg->unit_id)
+ {
+ case UNT_SPIDERWEB:
+ if( sc && sc->data[SC_SPIDERWEB] && sc->data[SC_SPIDERWEB]->val1 > 0 )
+ { // If you are fiberlocked and can't move, it will only increase your fireweakness level. [Inkfish]
+ sc->data[SC_SPIDERWEB]->val2++;
+ break;
+ }
+ else if( sc )
+ {
+ int sec = skill_get_time2(sg->skill_id,sg->skill_lv);
+ if( status_change_start(bl,type,10000,sg->skill_lv,1,sg->group_id,0,sec,8) )
+ {
+ const struct TimerData* td = sc->data[type]?get_timer(sc->data[type]->timer):NULL;
+ if( td )
+ sec = DIFF_TICK(td->tick, tick);
+ map_moveblock(bl, src->bl.x, src->bl.y, tick);
+ clif_fixpos(bl);
+ sg->val2 = bl->id;
+ }
+ else
+ sec = 3000; //Couldn't trap it?
+ sg->limit = DIFF_TICK(tick,sg->tick)+sec;
+ }
+ break;
+ case UNT_SAFETYWALL:
+ if (!sce)
+ sc_start4(bl,type,100,sg->skill_lv,sg->skill_id,sg->group_id,0,sg->limit);
+ break;
+
+ case UNT_PNEUMA:
+ case UNT_CHAOSPANIC:
+ case UNT_MAELSTROM:
+ if (!sce)
+ sc_start4(bl,type,100,sg->skill_lv,sg->group_id,0,0,sg->limit);
+ break;
+ case UNT_BLOODYLUST:
+ if (sg->src_id == bl->id)
+ break; //Does not affect the caster.
+ if (!sce) {
+ TBL_PC *sd = BL_CAST(BL_PC, bl); //prevent fullheal exploit
+ if (sd && sd->bloodylust_tick && DIFF_TICK(gettick(), sd->bloodylust_tick) < skill_get_time2(SC_BLOODYLUST, 1))
+ clif_skill_nodamage(&src->bl,bl,sg->skill_id,sg->skill_lv,
+ sc_start4(bl, type, 100, sg->skill_lv, 1, 0, 0, skill_get_time(LK_BERSERK, sg->skill_lv)));
+ else {
+ if (sd) sd->bloodylust_tick = gettick();
+ clif_skill_nodamage(&src->bl,bl,sg->skill_id,sg->skill_lv,
+ sc_start4(bl, type, 100, sg->skill_lv, 0, 0, 0, skill_get_time(LK_BERSERK, sg->skill_lv)));
+ }
+ }
+ break;
+
+ case UNT_WARP_WAITING: {
+ int working = sg->val1&0xffff;
+
+ if(bl->type==BL_PC && !working){
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ if((!sd->chatID || battle_config.chat_warpportal)
+ && sd->ud.to_x == src->bl.x && sd->ud.to_y == src->bl.y)
+ {
+ int x = sg->val2>>16;
+ int y = sg->val2&0xffff;
+ int count = sg->val1>>16;
+ unsigned short m = sg->val3;
+
+ if( --count <= 0 )
+ skill_delunitgroup(sg);
+
+ if ( map_mapindex2mapid(sg->val3) == sd->bl.m && x == sd->bl.x && y == sd->bl.y )
+ working = 1;/* we break it because officials break it, lovely stuff. */
+
+ sg->val1 = (count<<16)|working;
+
+ pc_setpos(sd,m,x,y,CLR_TELEPORT);
+ }
+ } else if(bl->type == BL_MOB && battle_config.mob_warp&2) {
+ int16 m = map_mapindex2mapid(sg->val3);
+ if (m < 0) break; //Map not available on this map-server.
+ unit_warp(bl,m,sg->val2>>16,sg->val2&0xffff,CLR_TELEPORT);
+ }
+ }
+ break;
+
+ case UNT_QUAGMIRE:
+ if( !sce && battle_check_target(&sg->unit->bl,bl,sg->target_flag) > 0 )
+ sc_start4(bl,type,100,sg->skill_lv,sg->group_id,0,0,sg->limit);
+ break;
+
+ case UNT_VOLCANO:
+ case UNT_DELUGE:
+ case UNT_VIOLENTGALE:
+ if(!sce)
+ sc_start(bl,type,100,sg->skill_lv,sg->limit);
+ break;
+
+ case UNT_SUITON:
+ if(!sce)
+ sc_start4(bl,type,100,sg->skill_lv,
+ map_flag_vs(bl->m) || battle_check_target(&src->bl,bl,BCT_ENEMY)>0?1:0, //Send val3 =1 to reduce agi.
+ 0,0,sg->limit);
+ break;
+
+ case UNT_HERMODE:
+ if (sg->src_id!=bl->id && battle_check_target(&src->bl,bl,BCT_PARTY|BCT_GUILD) > 0)
+ status_change_clear_buffs(bl,1); //Should dispell only allies.
+ case UNT_RICHMANKIM:
+ case UNT_ETERNALCHAOS:
+ case UNT_DRUMBATTLEFIELD:
+ case UNT_RINGNIBELUNGEN:
+ case UNT_ROKISWEIL:
+ case UNT_INTOABYSS:
+ case UNT_SIEGFRIED:
+ //Needed to check when a dancer/bard leaves their ensemble area.
+ if (sg->src_id==bl->id && !(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_BARDDANCER))
+ return skill_id;
+ if (!sce)
+ sc_start4(bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit);
+ break;
+ case UNT_WHISTLE:
+ case UNT_ASSASSINCROSS:
+ case UNT_POEMBRAGI:
+ case UNT_APPLEIDUN:
+ case UNT_HUMMING:
+ case UNT_DONTFORGETME:
+ case UNT_FORTUNEKISS:
+ case UNT_SERVICEFORYOU:
+ if (sg->src_id==bl->id && !(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_BARDDANCER))
+ return 0;
+
+ if (!sc) return 0;
+ if (!sce)
+ sc_start4(bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit);
+ else if (sce->val4 == 1) {
+ //Readjust timers since the effect will not last long.
+ sce->val4 = 0;
+ delete_timer(sce->timer, status_change_timer);
+ sce->timer = add_timer(tick+sg->limit, status_change_timer, bl->id, type);
+ }
+ break;
+
+ case UNT_FOGWALL:
+ if (!sce)
+ {
+ sc_start4(bl, type, 100, sg->skill_lv, sg->val1, sg->val2, sg->group_id, sg->limit);
+ if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0)
+ skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, BF_MISC, ATK_DEF, tick);
+ }
+ break;
+
+ case UNT_GRAVITATION:
+ if (!sce)
+ sc_start4(bl,type,100,sg->skill_lv,0,BCT_ENEMY,sg->group_id,sg->limit);
+ break;
+
+// officially, icewall has no problems existing on occupied cells [ultramage]
+// case UNT_ICEWALL: //Destroy the cell. [Skotlex]
+// src->val1 = 0;
+// if(src->limit + sg->tick > tick + 700)
+// src->limit = DIFF_TICK(tick+700,sg->tick);
+// break;
+
+ case UNT_MOONLIT:
+ //Knockback out of area if affected char isn't in Moonlit effect
+ if (sc && sc->data[SC_DANCING] && (sc->data[SC_DANCING]->val1&0xFFFF) == CG_MOONLIT)
+ break;
+ if (ss == bl) //Also needed to prevent infinite loop crash.
+ break;
+ skill_blown(ss,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),unit_getdir(bl),0);
+ break;
+
+ case UNT_WALLOFTHORN:
+ if( status_get_mode(bl)&MD_BOSS )
+ break; // iRO Wiki says that this skill don't affect to Boss monsters.
+ if( map_flag_vs(bl->m) || bl->id == src->bl.id || battle_check_target(&src->bl,bl, BCT_ENEMY) == 1 )
+ skill_attack(skill_get_type(sg->skill_id), ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ break;
+
+ case UNT_VOLCANIC_ASH:
+ if (!sce)
+ sc_start(bl, SC_ASH, 100, sg->skill_lv, skill_get_time(MH_VOLCANIC_ASH, sg->skill_lv));
+ break;
+
+ case UNT_GD_LEADERSHIP:
+ case UNT_GD_GLORYWOUNDS:
+ case UNT_GD_SOULCOLD:
+ case UNT_GD_HAWKEYES:
+ if ( !sce )
+ sc_start4(bl,type,100,sg->skill_lv,0,0,0,1000);
+ break;
+ }
+ return skill_id;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, unsigned int tick)
+{
+ struct skill_unit_group *sg;
+ struct block_list *ss;
+ TBL_PC* tsd;
+ struct status_data *tstatus;
+ struct status_change *tsc;
+ struct skill_unit_group_tickset *ts;
+ enum sc_type type;
+ uint16 skill_id;
+ int diff=0;
+
+ nullpo_ret(src);
+ nullpo_ret(bl);
+
+ if (bl->prev==NULL || !src->alive || status_isdead(bl))
+ return 0;
+
+ nullpo_ret(sg=src->group);
+ nullpo_ret(ss=map_id2bl(sg->src_id));
+ tsd = BL_CAST(BL_PC, bl);
+ tsc = status_get_sc(bl);
+
+ if ( tsc && tsc->data[SC_HOVERING] )
+ return 0; //Under hovering characters are immune to trap and ground target skills.
+
+ tstatus = status_get_status_data(bl);
+ type = status_skill2sc(sg->skill_id);
+ skill_id = sg->skill_id;
+
+ if (sg->interval == -1) {
+ switch (sg->unit_id) {
+ case UNT_ANKLESNARE: //These happen when a trap is splash-triggered by multiple targets on the same cell.
+ case UNT_FIREPILLAR_ACTIVE:
+ case UNT_ELECTRICSHOCKER:
+ case UNT_MANHOLE:
+ return 0;
+ default:
+ ShowError("skill_unit_onplace_timer: interval error (unit id %x)\n", sg->unit_id);
+ return 0;
+ }
+ }
+
+ if ((ts = skill_unitgrouptickset_search(bl,sg,tick)))
+ { //Not all have it, eg: Traps don't have it even though they can be hit by Heaven's Drive [Skotlex]
+ diff = DIFF_TICK(tick,ts->tick);
+ if (diff < 0)
+ return 0;
+ ts->tick = tick+sg->interval;
+
+ if ((skill_id==CR_GRANDCROSS || skill_id==NPC_GRANDDARKNESS) && !battle_config.gx_allhit)
+ ts->tick += sg->interval*(map_count_oncell(bl->m,bl->x,bl->y,BL_CHAR)-1);
+ }
+
+ switch (sg->unit_id)
+ {
+ case UNT_FIREWALL:
+ case UNT_KAEN:
+ {
+ int count=0;
+ const int x = bl->x, y = bl->y;
+
+ if( sg->skill_id == GN_WALLOFTHORN && !map_flag_vs(bl->m) )
+ break;
+
+ //Take into account these hit more times than the timer interval can handle.
+ do
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0);
+ while(--src->val2 && x == bl->x && y == bl->y &&
+ ++count < SKILLUNITTIMER_INTERVAL/sg->interval && !status_isdead(bl));
+
+ if (src->val2<=0)
+ skill_delunit(src);
+ }
+ break;
+
+ case UNT_SANCTUARY:
+ if( battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race==RC_DEMON )
+ { //Only damage enemies with offensive Sanctuary. [Skotlex]
+ if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 && skill_attack(BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0) )
+ sg->val1 -= 2; // reduce healing count if this was meant for damaging [hekate]
+ }
+ else
+ {
+ int heal = skill_calc_heal(ss,bl,sg->skill_id,sg->skill_lv,true);
+ struct mob_data *md = BL_CAST(BL_MOB, bl);
+#ifdef RENEWAL
+ if( md && md->class_ == MOBID_EMPERIUM )
+ break;
+#endif
+ if( md && mob_is_battleground(md) )
+ break;
+ if( tstatus->hp >= tstatus->max_hp )
+ break;
+ if( status_isimmune(bl) )
+ heal = 0;
+ clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
+ if( tsc && tsc->data[SC_AKAITSUKI] && heal )
+ heal = ~heal + 1;
+ status_heal(bl, heal, 0, 0);
+ if( diff >= 500 )
+ sg->val1--;
+ }
+ if( sg->val1 <= 0 )
+ skill_delunitgroup(sg);
+ break;
+
+ case UNT_EVILLAND:
+ //Will heal demon and undead element monsters, but not players.
+ if ((bl->type == BL_PC) || (!battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race!=RC_DEMON))
+ { //Damage enemies
+ if(battle_check_target(&src->bl,bl,BCT_ENEMY)>0)
+ skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ } else {
+ int heal = skill_calc_heal(ss,bl,sg->skill_id,sg->skill_lv,true);
+ if (tstatus->hp >= tstatus->max_hp)
+ break;
+ if (status_isimmune(bl))
+ heal = 0;
+ clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
+ status_heal(bl, heal, 0, 0);
+ }
+ break;
+
+ case UNT_MAGNUS:
+ if (!battle_check_undead(tstatus->race,tstatus->def_ele) && tstatus->race!=RC_DEMON)
+ break;
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+
+ case UNT_DUMMYSKILL:
+ switch (sg->skill_id)
+ {
+ case SG_SUN_WARM: //SG skills [Komurka]
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ {
+ int count = 0;
+ const int x = bl->x, y = bl->y;
+
+ //If target isn't knocked back it should hit every "interval" ms [Playtester]
+ do
+ {
+ if( bl->type == BL_PC )
+ status_zap(bl, 0, 15); // sp damage to players
+ else // mobs
+ if( status_charge(ss, 0, 2) ) // costs 2 SP per hit
+ {
+ if( !skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0) )
+ status_charge(ss, 0, 8); //costs additional 8 SP if miss
+ }
+ else
+ { //should end when out of sp.
+ sg->limit = DIFF_TICK(tick,sg->tick);
+ break;
+ }
+ } while( x == bl->x && y == bl->y &&
+ ++count < SKILLUNITTIMER_INTERVAL/sg->interval && !status_isdead(bl) );
+ }
+ break;
+ /**
+ * The storm gust counter was dropped in renewal
+ **/
+ #ifndef RENEWAL
+ case WZ_STORMGUST: //SG counter does not reset per stormgust. IE: One hit from a SG and two hits from another will freeze you.
+ if (tsc)
+ tsc->sg_counter++; //SG hit counter.
+ if (skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0) <= 0 && tsc)
+ tsc->sg_counter=0; //Attack absorbed.
+ break;
+ #endif
+ case GS_DESPERADO:
+ if (rnd()%100 < src->val1)
+ skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+ case GN_CRAZYWEED_ATK:
+ if( bl->type == BL_SKILL ){
+ struct skill_unit *su = (struct skill_unit *)bl;
+ if( su && !(skill_get_inf2(su->group->skill_id)&INF2_TRAP) )
+ break;
+ }
+ default:
+ skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ }
+ break;
+
+ case UNT_FIREPILLAR_WAITING:
+ skill_unitsetting(ss,sg->skill_id,sg->skill_lv,src->bl.x,src->bl.y,1);
+ skill_delunit(src);
+ break;
+
+ case UNT_SKIDTRAP:
+ {
+ skill_blown(&src->bl,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),unit_getdir(bl),0);
+ sg->unit_id = UNT_USED_TRAPS;
+ clif_changetraplook(&src->bl, UNT_USED_TRAPS);
+ sg->limit=DIFF_TICK(tick,sg->tick)+1500;
+ }
+ break;
+
+ case UNT_ANKLESNARE:
+ case UNT_MANHOLE:
+ if( sg->val2 == 0 && tsc && (sg->unit_id == UNT_ANKLESNARE || bl->id != sg->src_id) ) {
+ int sec = skill_get_time2(sg->skill_id,sg->skill_lv);
+ if( status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,sec, 8) ) {
+ const struct TimerData* td = tsc->data[type]?get_timer(tsc->data[type]->timer):NULL;
+ if( td )
+ sec = DIFF_TICK(td->tick, tick);
+ unit_movepos(bl, src->bl.x, src->bl.y, 0, 0);
+ clif_fixpos(bl);
+ sg->val2 = bl->id;
+ } else
+ sec = 3000; //Couldn't trap it?
+ if( sg->unit_id == UNT_ANKLESNARE ) {
+ clif_skillunit_update(&src->bl);
+ /**
+ * If you're snared from a trap that was invisible this makes the trap be
+ * visible again -- being you stepped on it (w/o this the trap remains invisible and you go "WTF WHY I CANT MOVE")
+ * bugreport:3961
+ **/
+ clif_changetraplook(&src->bl, UNT_ANKLESNARE);
+ }
+ sg->limit = DIFF_TICK(tick,sg->tick)+sec;
+ sg->interval = -1;
+ src->range = 0;
+ }
+ break;
+
+ case UNT_ELECTRICSHOCKER:
+ if( bl->id != ss->id ) {
+ if( status_get_mode(bl)&MD_BOSS )
+ break;
+ if( status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id, sg->skill_lv), 8) ) {
+
+ map_moveblock(bl, src->bl.x, src->bl.y, tick);
+ clif_fixpos(bl);
+
+ }
+
+ map_foreachinrange(skill_trap_splash, &src->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl, tick);
+ sg->unit_id = UNT_USED_TRAPS; //Changed ID so it does not invoke a for each in area again.
+ }
+ break;
+
+ case UNT_VENOMDUST:
+ if(tsc && !tsc->data[type])
+ status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),0);
+ break;
+
+
+ case UNT_MAGENTATRAP:
+ case UNT_COBALTTRAP:
+ case UNT_MAIZETRAP:
+ case UNT_VERDURETRAP:
+ if( bl->type == BL_PC )// it won't work on players
+ break;
+ case UNT_FIRINGTRAP:
+ case UNT_ICEBOUNDTRAP:
+ case UNT_CLUSTERBOMB:
+ if( bl->id == ss->id )// it won't trigger on caster
+ break;
+ case UNT_LANDMINE:
+ case UNT_CLAYMORETRAP:
+ case UNT_BLASTMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_FREEZINGTRAP:
+ case UNT_FIREPILLAR_ACTIVE:
+ map_foreachinrange(skill_trap_splash,&src->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl,tick);
+ if (sg->unit_id != UNT_FIREPILLAR_ACTIVE)
+ clif_changetraplook(&src->bl, sg->unit_id==UNT_LANDMINE?UNT_FIREPILLAR_ACTIVE:UNT_USED_TRAPS);
+ sg->limit=DIFF_TICK(tick,sg->tick)+1500 +
+ (sg->unit_id== UNT_CLUSTERBOMB || sg->unit_id== UNT_ICEBOUNDTRAP?1000:0);// Cluster Bomb/Icebound has 1s to disappear once activated.
+ sg->unit_id = UNT_USED_TRAPS; //Changed ID so it does not invoke a for each in area again.
+ break;
+
+ case UNT_TALKIEBOX:
+ if (sg->src_id == bl->id)
+ break;
+ if (sg->val2 == 0){
+ clif_talkiebox(&src->bl, sg->valstr);
+ sg->unit_id = UNT_USED_TRAPS;
+ clif_changetraplook(&src->bl, UNT_USED_TRAPS);
+ sg->limit = DIFF_TICK(tick, sg->tick) + 5000;
+ sg->val2 = -1;
+ }
+ break;
+
+ case UNT_LULLABY:
+ if (ss->id == bl->id)
+ break;
+ skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, ATK_DEF, tick);
+ break;
+
+ case UNT_UGLYDANCE: //Ugly Dance [Skotlex]
+ if (ss->id != bl->id)
+ skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, ATK_DEF, tick);
+ break;
+
+ case UNT_DISSONANCE:
+ skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ break;
+
+ case UNT_APPLEIDUN: //Apple of Idun [Skotlex]
+ {
+ int heal;
+#ifdef RENEWAL
+ struct mob_data *md = BL_CAST(BL_MOB, bl);
+ if( md && md->class_ == MOBID_EMPERIUM )
+ break;
+#endif
+ if( sg->src_id == bl->id && !(tsc && tsc->data[SC_SPIRIT] && tsc->data[SC_SPIRIT]->val2 == SL_BARDDANCER) )
+ break; // affects self only when soullinked
+ heal = skill_calc_heal(ss,bl,sg->skill_id, sg->skill_lv, true);
+ if( tsc->data[SC_AKAITSUKI] && heal )
+ heal = ~heal + 1;
+ clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1);
+ status_heal(bl, heal, 0, 0);
+ break;
+ }
+
+ case UNT_TATAMIGAESHI:
+ case UNT_DEMONSTRATION:
+ skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+
+ case UNT_GOSPEL:
+ if (rnd()%100 > sg->skill_lv*10 || ss == bl)
+ break;
+ if (battle_check_target(ss,bl,BCT_PARTY)>0)
+ { // Support Effect only on party, not guild
+ int heal;
+ int i = rnd()%13; // Positive buff count
+ int time = skill_get_time2(sg->skill_id, sg->skill_lv); //Duration
+ switch (i)
+ {
+ case 0: // Heal 1~9999 HP
+ heal = rnd() %9999+1;
+ clif_skill_nodamage(ss,bl,AL_HEAL,heal,1);
+ status_heal(bl,heal,0,0);
+ break;
+ case 1: // End all negative status
+ status_change_clear_buffs(bl,6);
+ if (tsd) clif_gospel_info(tsd, 0x15);
+ break;
+ case 2: // Immunity to all status
+ sc_start(bl,SC_SCRESIST,100,100,time);
+ if (tsd) clif_gospel_info(tsd, 0x16);
+ break;
+ case 3: // MaxHP +100%
+ sc_start(bl,SC_INCMHPRATE,100,100,time);
+ if (tsd) clif_gospel_info(tsd, 0x17);
+ break;
+ case 4: // MaxSP +100%
+ sc_start(bl,SC_INCMSPRATE,100,100,time);
+ if (tsd) clif_gospel_info(tsd, 0x18);
+ break;
+ case 5: // All stats +20
+ sc_start(bl,SC_INCALLSTATUS,100,20,time);
+ if (tsd) clif_gospel_info(tsd, 0x19);
+ break;
+ case 6: // Level 10 Blessing
+ sc_start(bl,SC_BLESSING,100,10,time);
+ break;
+ case 7: // Level 10 Increase AGI
+ sc_start(bl,SC_INCREASEAGI,100,10,time);
+ break;
+ case 8: // Enchant weapon with Holy element
+ sc_start(bl,SC_ASPERSIO,100,1,time);
+ if (tsd) clif_gospel_info(tsd, 0x1c);
+ break;
+ case 9: // Enchant armor with Holy element
+ sc_start(bl,SC_BENEDICTIO,100,1,time);
+ if (tsd) clif_gospel_info(tsd, 0x1d);
+ break;
+ case 10: // DEF +25%
+ sc_start(bl,SC_INCDEFRATE,100,25,time);
+ if (tsd) clif_gospel_info(tsd, 0x1e);
+ break;
+ case 11: // ATK +100%
+ sc_start(bl,SC_INCATKRATE,100,100,time);
+ if (tsd) clif_gospel_info(tsd, 0x1f);
+ break;
+ case 12: // HIT/Flee +50
+ sc_start(bl,SC_INCHIT,100,50,time);
+ sc_start(bl,SC_INCFLEE,100,50,time);
+ if (tsd) clif_gospel_info(tsd, 0x20);
+ break;
+ }
+ }
+ else if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0)
+ { // Offensive Effect
+ int i = rnd()%9; // Negative buff count
+ int time = skill_get_time2(sg->skill_id, sg->skill_lv);
+ switch (i)
+ {
+ case 0: // Deal 1~9999 damage
+ skill_attack(BF_MISC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+ case 1: // Curse
+ sc_start(bl,SC_CURSE,100,1,time);
+ break;
+ case 2: // Blind
+ sc_start(bl,SC_BLIND,100,1,time);
+ break;
+ case 3: // Poison
+ sc_start(bl,SC_POISON,100,1,time);
+ break;
+ case 4: // Level 10 Provoke
+ sc_start(bl,SC_PROVOKE,100,10,time);
+ break;
+ case 5: // DEF -100%
+ sc_start(bl,SC_INCDEFRATE,100,-100,time);
+ break;
+ case 6: // ATK -100%
+ sc_start(bl,SC_INCATKRATE,100,-100,time);
+ break;
+ case 7: // Flee -100%
+ sc_start(bl,SC_INCFLEERATE,100,-100,time);
+ break;
+ case 8: // Speed/ASPD -25%
+ sc_start4(bl,SC_GOSPEL,100,1,0,0,BCT_ENEMY,time);
+ break;
+ }
+ }
+ break;
+
+ case UNT_BASILICA:
+ {
+ int i = battle_check_target(&src->bl, bl, BCT_ENEMY);
+ if( i > 0 && !(status_get_mode(bl)&MD_BOSS) )
+ { // knock-back any enemy except Boss
+ skill_blown(&src->bl, bl, 2, unit_getdir(bl), 0);
+ clif_fixpos(bl);
+ }
+
+ if( sg->src_id != bl->id && i <= 0 )
+ sc_start4(bl, type, 100, 0, 0, 0, src->bl.id, sg->interval + 100);
+ }
+ break;
+
+ case UNT_GRAVITATION:
+ case UNT_EARTHSTRAIN:
+ case UNT_FIREWALK:
+ case UNT_ELECTRICWALK:
+ case UNT_PSYCHIC_WAVE:
+ skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+
+ case UNT_GROUNDDRIFT_WIND:
+ case UNT_GROUNDDRIFT_DARK:
+ case UNT_GROUNDDRIFT_POISON:
+ case UNT_GROUNDDRIFT_WATER:
+ case UNT_GROUNDDRIFT_FIRE:
+ map_foreachinrange(skill_trap_splash,&src->bl,
+ skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag,
+ &src->bl,tick);
+ sg->unit_id = UNT_USED_TRAPS;
+ //clif_changetraplook(&src->bl, UNT_FIREPILLAR_ACTIVE);
+ sg->limit=DIFF_TICK(tick,sg->tick)+1500;
+ break;
+ /**
+ * 3rd stuff
+ **/
+ case UNT_POISONSMOKE:
+ if( battle_check_target(ss,bl,BCT_ENEMY) > 0 && !(tsc && tsc->data[sg->val2]) && rnd()%100 < 20 )
+ sc_start(bl,sg->val2,100,sg->val3,skill_get_time2(GC_POISONINGWEAPON, 1));
+ break;
+
+ case UNT_EPICLESIS:
+ if( bl->type == BL_PC && !battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race != RC_DEMON )
+ {
+ if( ++sg->val2 % 3 == 0 ) {
+ int hp, sp;
+ switch( sg->skill_lv )
+ {
+ case 1: case 2: hp = 3; sp = 2; break;
+ case 3: case 4: hp = 4; sp = 3; break;
+ case 5: default: hp = 5; sp = 4; break;
+ }
+ hp = tstatus->max_hp * hp / 100;
+ sp = tstatus->max_sp * sp / 100;
+ status_heal(bl, hp, sp, 2);
+ sc_start(bl, type, 100, sg->skill_lv, (sg->interval * 3) + 100);
+ }
+ // Reveal hidden players every 5 seconds.
+ if( sg->val2 % 5 == 0 ) {
+ // TODO: check if other hidden status can be removed.
+ status_change_end(bl,SC_HIDING,INVALID_TIMER);
+ status_change_end(bl,SC_CLOAKING,INVALID_TIMER);
+ }
+ }
+ /* Enable this if kRO fix the current skill. Currently no damage on undead and demon monster. [Jobbie]
+ else if( battle_check_target(ss, bl, BCT_ENEMY) > 0 && battle_check_undead(tstatus->race, tstatus->def_ele) )
+ skill_castend_damage_id(&src->bl, bl, sg->skill_id, sg->skill_lv, 0, 0);*/
+ break;
+
+ case UNT_STEALTHFIELD:
+ if( bl->id == sg->src_id )
+ break; // Dont work on Self (video shows that)
+ case UNT_NEUTRALBARRIER:
+ sc_start(bl,type,100,sg->skill_lv,sg->interval + 100);
+ break;
+
+ case UNT_DIMENSIONDOOR:
+ if( tsd && !map[bl->m].flag.noteleport )
+ pc_randomwarp(tsd,3);
+ else if( bl->type == BL_MOB && battle_config.mob_warp&8 )
+ unit_warp(bl,-1,-1,-1,3);
+ break;
+
+ case UNT_REVERBERATION:
+ clif_changetraplook(&src->bl,UNT_USED_TRAPS);
+ map_foreachinrange(skill_trap_splash,&src->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &src->bl,tick);
+ sg->limit = DIFF_TICK(tick,sg->tick)+1000;
+ sg->unit_id = UNT_USED_TRAPS;
+ break;
+
+ case UNT_SEVERE_RAINSTORM:
+ if( battle_check_target(&src->bl, bl, BCT_ENEMY) )
+ skill_attack(BF_WEAPON,ss,&src->bl,bl,WM_SEVERE_RAINSTORM_MELEE,sg->skill_lv,tick,0);
+ break;
+ case UNT_NETHERWORLD:
+ if( !(status_get_mode(bl)&MD_BOSS) && ss != bl && battle_check_target(&src->bl, bl, BCT_PARTY) ) {
+ if( !(tsc && tsc->data[type]) ){
+ sc_start(bl, type, 100, sg->skill_lv, skill_get_time2(sg->skill_id,sg->skill_lv));
+ sg->limit = DIFF_TICK(tick,sg->tick);
+ sg->unit_id = UNT_USED_TRAPS;
+ }
+ }
+ break;
+ case UNT_THORNS_TRAP:
+ if( tsc ) {
+ if( !sg->val2 ) {
+ int sec = skill_get_time2(sg->skill_id, sg->skill_lv);
+ if( sc_start(bl, type, 100, sg->skill_lv, sec) ) {
+ const struct TimerData* td = tsc->data[type]?get_timer(tsc->data[type]->timer):NULL;
+ if( td )
+ sec = DIFF_TICK(td->tick, tick);
+ ///map_moveblock(bl, src->bl.x, src->bl.y, tick); // in official server it doesn't behave like this. [malufett]
+ clif_fixpos(bl);
+ sg->val2 = bl->id;
+ } else
+ sec = 3000; // Couldn't trap it?
+ sg->limit = DIFF_TICK(tick, sg->tick) + sec;
+ } else if( tsc->data[SC_THORNSTRAP] && bl->id == sg->val2 )
+ skill_attack(skill_get_type(GN_THORNS_TRAP), ss, ss, bl, sg->skill_id, sg->skill_lv, tick, SD_LEVEL|SD_ANIMATION);
+ }
+ break;
+
+ case UNT_DEMONIC_FIRE: {
+ TBL_PC* sd = BL_CAST(BL_PC, ss);
+ switch( sg->val2 ) {
+ case 1:
+ case 2:
+ default:
+ sc_start(bl, SC_BURNING, 4 + 4 * sg->skill_lv, sg->skill_lv,
+ skill_get_time2(sg->skill_id, sg->skill_lv));
+ skill_attack(skill_get_type(sg->skill_id), ss, &src->bl, bl,
+ sg->skill_id, sg->skill_lv + 10 * sg->val2, tick, 0);
+ break;
+ case 3:
+ skill_attack(skill_get_type(CR_ACIDDEMONSTRATION), ss, &src->bl, bl,
+ CR_ACIDDEMONSTRATION, sd ? pc_checkskill(sd, CR_ACIDDEMONSTRATION) : sg->skill_lv, tick, 0);
+ break;
+
+ }
+ }
+ break;
+
+ case UNT_FIRE_EXPANSION_SMOKE_POWDER:
+ sc_start(bl, status_skill2sc(GN_FIRE_EXPANSION_SMOKE_POWDER), 100, sg->skill_lv, 1000);
+ break;
+
+ case UNT_FIRE_EXPANSION_TEAR_GAS:
+ sc_start(bl, status_skill2sc(GN_FIRE_EXPANSION_TEAR_GAS), 100, sg->skill_lv, 1000);
+ break;
+
+ case UNT_HELLS_PLANT:
+ if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 )
+ skill_attack(skill_get_type(GN_HELLS_PLANT_ATK), ss, &src->bl, bl, GN_HELLS_PLANT_ATK, sg->skill_lv, tick, 0);
+ if( ss != bl) //The caster is the only one who can step on the Plants, without destroying them
+ sg->limit = DIFF_TICK(tick, sg->tick) + 100;
+ break;
+
+ case UNT_CLOUD_KILL:
+ if(tsc && !tsc->data[type])
+ status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),8);
+ skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+
+ case UNT_WARMER:
+ if( bl->type == BL_PC && !battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race != RC_DEMON ) {
+ int hp = 125 * sg->skill_lv; // Officially is 125 * skill_lv.
+ struct status_change *ssc = status_get_sc(ss);
+ if( ssc && ssc->data[SC_HEATER_OPTION] )
+ hp += hp * ssc->data[SC_HEATER_OPTION]->val3 / 100;
+ if( tstatus->hp != tstatus->max_hp )
+ clif_skill_nodamage(&src->bl, bl, AL_HEAL, hp, 0);
+ if( tsc && tsc->data[SC_AKAITSUKI] && hp )
+ hp = ~hp + 1;
+ status_heal(bl, hp, 0, 0);
+ sc_start(bl, SC_WARMER, 100, sg->skill_lv, skill_get_time2(sg->skill_id,sg->skill_lv));
+ }
+ break;
+
+ case UNT_FIRE_INSIGNIA:
+ case UNT_WATER_INSIGNIA:
+ case UNT_WIND_INSIGNIA:
+ case UNT_EARTH_INSIGNIA:
+ case UNT_ZEPHYR:
+ sc_start(bl,type, 100, sg->skill_lv, sg->interval);
+ if (sg->unit_id != UNT_ZEPHYR && !battle_check_undead(tstatus->race, tstatus->def_ele)) {
+ int hp = tstatus->max_hp / 100; //+1% each 5s
+ if ((sg->val3) % 5) { //each 5s
+ if (tstatus->def_ele == skill_get_ele(sg->skill_id,sg->skill_lv)){
+ status_heal(bl, hp, 0, 2);
+ } else if((sg->unit_id == UNT_FIRE_INSIGNIA && tstatus->def_ele == ELE_EARTH)
+ ||(sg->unit_id == UNT_WATER_INSIGNIA && tstatus->def_ele == ELE_FIRE)
+ ||(sg->unit_id == UNT_WIND_INSIGNIA && tstatus->def_ele == ELE_WATER)
+ ||(sg->unit_id == UNT_EARTH_INSIGNIA && tstatus->def_ele == ELE_WIND)
+ ){
+ status_heal(bl, -hp, 0, 0);
+ }
+ }
+ sg->val3++; //timer
+ if (sg->val3 > 5) sg->val3 = 0;
+ }
+ break;
+
+ case UNT_VACUUM_EXTREME:
+ {// TODO: official behavior in gvg area. [malufett]
+ int sec = sg->limit - DIFF_TICK(tick, sg->tick);
+ int range = skill_get_unit_range(sg->skill_id, sg->skill_lv);
+
+ if( tsc && !tsc->data[type] &&
+ distance_xy(src->bl.x, src->bl.y, bl->x, bl->y) <= range)// don't consider outer bounderies
+ sc_start(bl, type, 100, sg->skill_lv, sec);
+
+ if( unit_is_walking(bl) && // wait until target stop walking
+ ( tsc && tsc->data[type] && tsc->data[type]->val4 >= tsc->data[type]->val3-range ))
+ break;
+
+ if( tsc && ( !tsc->data[type] || (tsc->data[type] && tsc->data[type]->val4 < 1 ) ) )
+ break;
+
+ if( unit_is_walking(bl) &&
+ distance_xy(src->bl.x, src->bl.y, bl->x, bl->y) > range )// going outside of boundaries? then force it to stop
+ unit_stop_walking(bl,1);
+
+ if( !unit_is_walking(bl) &&
+ distance_xy(src->bl.x, src->bl.y, bl->x, bl->y) <= range && // only snap if the target is inside the range or
+ src->bl.x != bl->x && src->bl.y != bl->y){// diagonal position parallel to VE's center
+ unit_movepos(bl, src->bl.x, src->bl.y, 0, 0);
+ clif_fixpos(bl);
+ }
+ }
+ break;
+
+ case UNT_FIRE_MANTLE:
+ if( battle_check_target(&src->bl, bl, BCT_ENEMY) )
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+
+ case UNT_ZENKAI_WATER:
+ case UNT_ZENKAI_LAND:
+ case UNT_ZENKAI_FIRE:
+ case UNT_ZENKAI_WIND:
+ if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 ){
+ switch( sg->unit_id ){
+ case UNT_ZENKAI_WATER:
+ sc_start(bl, SC_CRYSTALIZE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ sc_start(bl, SC_FREEZE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ sc_start(bl, SC_FREEZING, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ case UNT_ZENKAI_LAND:
+ sc_start(bl, SC_STONE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ sc_start(bl, SC_POISON, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ case UNT_ZENKAI_FIRE:
+ sc_start(bl, SC_BURNING, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ case UNT_ZENKAI_WIND:
+ sc_start(bl, SC_SILENCE, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ sc_start(bl, SC_SLEEP, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ sc_start(bl, SC_DEEPSLEEP, sg->val1*5, sg->skill_lv, skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ }
+ }else
+ sc_start2(bl,type,100,sg->val1,sg->val2,skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+
+ case UNT_MAKIBISHI:
+ skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ sg->limit = DIFF_TICK(tick, sg->tick);
+ sg->unit_id = UNT_USED_TRAPS;
+ break;
+
+ case UNT_LAVA_SLIDE:
+ skill_attack(BF_WEAPON, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ if(++sg->val1 > 4) //after 5 stop hit and destroy me
+ sg->limit = DIFF_TICK(tick, sg->tick);
+ break;
+ case UNT_POISON_MIST:
+ skill_attack(BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0);
+ status_change_start(bl, SC_BLIND, rnd() % 100 > sg->skill_lv * 10, sg->skill_lv, sg->skill_id, 0, 0, skill_get_time2(sg->skill_id, sg->skill_lv), 2|8);
+ break;
+ }
+
+ if (bl->type == BL_MOB && ss != bl)
+ mobskill_event((TBL_MOB*)bl, ss, tick, MSC_SKILLUSED|(skill_id<<16));
+
+ return skill_id;
+}
+/*==========================================
+ * Triggered when a char steps out of a skill cell
+ *------------------------------------------*/
+int skill_unit_onout (struct skill_unit *src, struct block_list *bl, unsigned int tick)
+{
+ struct skill_unit_group *sg;
+ struct status_change *sc;
+ struct status_change_entry *sce;
+ enum sc_type type;
+
+ nullpo_ret(src);
+ nullpo_ret(bl);
+ nullpo_ret(sg=src->group);
+ sc = status_get_sc(bl);
+ type = status_skill2sc(sg->skill_id);
+ sce = (sc && type != -1)?sc->data[type]:NULL;
+
+ if( bl->prev==NULL ||
+ (status_isdead(bl) && sg->unit_id != UNT_ANKLESNARE && sg->unit_id != UNT_SPIDERWEB) ) //Need to delete the trap if the source died.
+ return 0;
+
+ switch(sg->unit_id){
+ case UNT_SAFETYWALL:
+ case UNT_PNEUMA:
+ case UNT_EPICLESIS://Arch Bishop
+ case UNT_NEUTRALBARRIER:
+ case UNT_STEALTHFIELD:
+ if (sce)
+ status_change_end(bl, type, INVALID_TIMER);
+ break;
+
+ case UNT_BASILICA:
+ if( sce && sce->val4 == src->bl.id )
+ status_change_end(bl, type, INVALID_TIMER);
+ break;
+ case UNT_HERMODE: //Clear Hermode if the owner moved.
+ if (sce && sce->val3 == BCT_SELF && sce->val4 == sg->group_id)
+ status_change_end(bl, type, INVALID_TIMER);
+ break;
+
+ case UNT_SPIDERWEB:
+ {
+ struct block_list *target = map_id2bl(sg->val2);
+ if (target && target==bl)
+ {
+ if (sce && sce->val3 == sg->group_id)
+ status_change_end(bl, type, INVALID_TIMER);
+ sg->limit = DIFF_TICK(tick,sg->tick)+1000;
+ }
+ break;
+ }
+ }
+ return sg->skill_id;
+}
+
+/*==========================================
+ * Triggered when a char steps out of a skill group (entirely) [Skotlex]
+ *------------------------------------------*/
+static int skill_unit_onleft (uint16 skill_id, struct block_list *bl, unsigned int tick)
+{
+ struct status_change *sc;
+ struct status_change_entry *sce;
+ enum sc_type type;
+
+ sc = status_get_sc(bl);
+ if (sc && !sc->count)
+ sc = NULL;
+
+ type = status_skill2sc(skill_id);
+ sce = (sc && type != -1)?sc->data[type]:NULL;
+
+ switch (skill_id)
+ {
+ case WZ_QUAGMIRE:
+ if (bl->type==BL_MOB)
+ break;
+ if (sce)
+ status_change_end(bl, type, INVALID_TIMER);
+ 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:
+ if(sc && sc->data[SC_DANCING] && (sc->data[SC_DANCING]->val1&0xFFFF) == skill_id)
+ { //Check if you just stepped out of your ensemble skill to cancel dancing. [Skotlex]
+ //We don't check for SC_LONGING because someone could always have knocked you back and out of the song/dance.
+ //FIXME: This code is not perfect, it doesn't checks for the real ensemble's owner,
+ //it only checks if you are doing the same ensemble. So if there's two chars doing an ensemble
+ //which overlaps, by stepping outside of the other parther's ensemble will cause you to cancel
+ //your own. Let's pray that scenario is pretty unlikely and noone will complain too much about it.
+ status_change_end(bl, SC_DANCING, INVALID_TIMER);
+ }
+ case MH_STEINWAND:
+ case MG_SAFETYWALL:
+ case AL_PNEUMA:
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+ case CG_HERMODE:
+ case HW_GRAVITATION:
+ case NJ_SUITON:
+ case SC_MAELSTROM:
+ case EL_WATER_BARRIER:
+ case EL_ZEPHYR:
+ case EL_POWER_OF_GAIA:
+ case SO_FIRE_INSIGNIA:
+ case SO_WATER_INSIGNIA:
+ case SO_WIND_INSIGNIA:
+ case SO_EARTH_INSIGNIA:
+ if (sce)
+ status_change_end(bl, type, INVALID_TIMER);
+ break;
+ case SC_BLOODYLUST:
+ if (sce) {
+ status_change_end(bl, type, INVALID_TIMER);
+ status_set_sp(bl, 0, 0); //set sp to 0 when quitting zone
+ }
+ break;
+
+ case BA_POEMBRAGI:
+ case BA_WHISTLE:
+ case BA_ASSASSINCROSS:
+ case BA_APPLEIDUN:
+ case DC_HUMMING:
+ case DC_DONTFORGETME:
+ case DC_FORTUNEKISS:
+ case DC_SERVICEFORYOU:
+ if (sce)
+ {
+ delete_timer(sce->timer, status_change_timer);
+ //NOTE: It'd be nice if we could get the skill_lv for a more accurate extra time, but alas...
+ //not possible on our current implementation.
+ sce->val4 = 1; //Store the fact that this is a "reduced" duration effect.
+ sce->timer = add_timer(tick+skill_get_time2(skill_id,1), status_change_timer, bl->id, type);
+ }
+ break;
+ case PF_FOGWALL:
+ if (sce)
+ {
+ status_change_end(bl, type, INVALID_TIMER);
+ if ((sce=sc->data[SC_BLIND]))
+ {
+ if (bl->type == BL_PC) //Players get blind ended inmediately, others have it still for 30 secs. [Skotlex]
+ status_change_end(bl, SC_BLIND, INVALID_TIMER);
+ else {
+ delete_timer(sce->timer, status_change_timer);
+ sce->timer = add_timer(30000+tick, status_change_timer, bl->id, SC_BLIND);
+ }
+ }
+ }
+ break;
+ case GD_LEADERSHIP:
+ case GD_GLORYWOUNDS:
+ case GD_SOULCOLD:
+ case GD_HAWKEYES:
+ if( !(sce && sce->val4) )
+ status_change_end(bl, type, INVALID_TIMER);
+ break;
+ }
+
+ return skill_id;
+}
+
+/*==========================================
+ * Invoked when a unit cell has been placed/removed/deleted.
+ * flag values:
+ * flag&1: Invoke onplace function (otherwise invoke onout)
+ * flag&4: Invoke a onleft call (the unit might be scheduled for deletion)
+ *------------------------------------------*/
+static int skill_unit_effect (struct block_list* bl, va_list ap)
+{
+ struct skill_unit* unit = va_arg(ap,struct skill_unit*);
+ struct skill_unit_group* group = unit->group;
+ unsigned int tick = va_arg(ap,unsigned int);
+ unsigned int flag = va_arg(ap,unsigned int);
+ uint16 skill_id;
+ bool dissonance;
+
+ if( (!unit->alive && !(flag&4)) || bl->prev == NULL )
+ return 0;
+
+ nullpo_ret(group);
+
+ dissonance = skill_dance_switch(unit, 0);
+
+ //Necessary in case the group is deleted after calling on_place/on_out [Skotlex]
+ skill_id = group->skill_id;
+ //Target-type check.
+ if( !(group->bl_flag&bl->type && battle_check_target(&unit->bl,bl,group->target_flag)>0) && (flag&4) ) {
+ if( group->state.song_dance&0x1 || (group->src_id == bl->id && group->state.song_dance&0x2) )
+ skill_unit_onleft(skill_id, bl, tick);//Ensemble check to terminate it.
+ } else {
+ if( flag&1 )
+ skill_unit_onplace(unit,bl,tick);
+ else
+ skill_unit_onout(unit,bl,tick);
+
+ if( flag&4 )
+ skill_unit_onleft(skill_id, bl, tick);
+ }
+
+ if( dissonance ) skill_dance_switch(unit, 1);
+
+ 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_ret(src);
+ nullpo_ret(sg=src->group);
+
+ switch( sg->unit_id ) {
+ case UNT_BLASTMINE:
+ case UNT_SKIDTRAP:
+ case UNT_LANDMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_CLAYMORETRAP:
+ case UNT_FREEZINGTRAP:
+ case UNT_TALKIEBOX:
+ case UNT_ANKLESNARE:
+ case UNT_ICEWALL:
+ case UNT_REVERBERATION:
+ case UNT_WALLOFTHORN:
+ src->val1-=damage;
+ break;
+ default:
+ damage = 0;
+ break;
+ }
+ return damage;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static int skill_check_condition_char_sub (struct block_list *bl, va_list ap)
+{
+ int *c, skill_id;
+ struct block_list *src;
+ struct map_session_data *sd;
+ struct map_session_data *tsd;
+ int *p_sd; //Contains the list of characters found.
+
+ nullpo_ret(bl);
+ nullpo_ret(tsd=(struct map_session_data*)bl);
+ nullpo_ret(src=va_arg(ap,struct block_list *));
+ nullpo_ret(sd=(struct map_session_data*)src);
+
+ c=va_arg(ap,int *);
+ p_sd = va_arg(ap, int *);
+ skill_id = va_arg(ap,int);
+
+ if ( ((skill_id != PR_BENEDICTIO && *c >=1) || *c >=2) && !(skill_get_inf2(skill_id)&INF2_CHORUS_SKILL) )
+ return 0; //Partner found for ensembles, or the two companions for Benedictio. [Skotlex]
+
+ if (bl == src)
+ return 0;
+
+ if(pc_isdead(tsd))
+ return 0;
+
+ if (tsd->sc.data[SC_SILENCE] || ( tsd->sc.opt1 && tsd->sc.opt1 != OPT1_BURNING ))
+ return 0;
+
+ if( skill_get_inf2(skill_id)&INF2_CHORUS_SKILL ) {
+ if( tsd->status.party_id == sd->status.party_id && (tsd->class_&MAPID_THIRDMASK) == MAPID_MINSTRELWANDERER )
+ p_sd[(*c)++] = tsd->bl.id;
+ return 1;
+ } else {
+
+ switch(skill_id) {
+ case PR_BENEDICTIO: {
+ uint8 dir = map_calc_dir(&sd->bl,tsd->bl.x,tsd->bl.y);
+ dir = (unit_getdir(&sd->bl) + dir)%8; //This adjusts dir to account for the direction the sd is facing.
+ if ((tsd->class_&MAPID_BASEMASK) == MAPID_ACOLYTE && (dir == 2 || dir == 6) //Must be standing to the left/right of Priest.
+ && sd->status.sp >= 10)
+ p_sd[(*c)++]=tsd->bl.id;
+ return 1;
+ }
+ case AB_ADORAMUS:
+ // Adoramus does not consume Blue Gemstone when there is at least 1 Priest class next to the caster
+ if( (tsd->class_&MAPID_UPPERMASK) == MAPID_PRIEST )
+ p_sd[(*c)++] = tsd->bl.id;
+ return 1;
+ case WL_COMET:
+ // Comet does not consume Red Gemstones when there is at least 1 Warlock class next to the caster
+ if( ( sd->class_&MAPID_THIRDMASK ) == MAPID_WARLOCK )
+ p_sd[(*c)++] = tsd->bl.id;
+ return 1;
+ case LG_RAYOFGENESIS:
+ if( tsd->status.party_id == sd->status.party_id && (tsd->class_&MAPID_THIRDMASK) == MAPID_ROYAL_GUARD &&
+ tsd->sc.data[SC_BANDING] )
+ p_sd[(*c)++] = tsd->bl.id;
+ return 1;
+ default: //Warning: Assuming Ensemble Dance/Songs for code speed. [Skotlex]
+ {
+ uint16 skill_lv;
+ if(pc_issit(tsd) || !unit_can_move(&tsd->bl))
+ return 0;
+ if (sd->status.sex != tsd->status.sex &&
+ (tsd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER &&
+ (skill_lv = pc_checkskill(tsd, skill_id)) > 0 &&
+ (tsd->weapontype1==W_MUSICAL || tsd->weapontype1==W_WHIP) &&
+ sd->status.party_id && tsd->status.party_id &&
+ sd->status.party_id == tsd->status.party_id &&
+ !tsd->sc.data[SC_DANCING])
+ {
+ p_sd[(*c)++]=tsd->bl.id;
+ return skill_lv;
+ } else {
+ return 0;
+ }
+ }
+ break;
+ }
+
+ }
+ return 0;
+}
+
+/*==========================================
+ * Checks and stores partners for ensemble skills [Skotlex]
+ *------------------------------------------*/
+int skill_check_pc_partner (struct map_session_data *sd, uint16 skill_id, short* skill_lv, int range, int cast_flag)
+{
+ static int c=0;
+ static int p_sd[2] = { 0, 0 };
+ int i;
+ bool is_chorus = ( skill_get_inf2(skill_id)&INF2_CHORUS_SKILL );
+
+ if (!battle_config.player_skill_partner_check || pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL))
+ return is_chorus ? MAX_PARTY : 99; //As if there were infinite partners.
+
+ if (cast_flag) { //Execute the skill on the partners.
+ struct map_session_data* tsd;
+ switch (skill_id) {
+ case PR_BENEDICTIO:
+ for (i = 0; i < c; i++) {
+ if ((tsd = map_id2sd(p_sd[i])) != NULL)
+ status_charge(&tsd->bl, 0, 10);
+ }
+ return c;
+ case AB_ADORAMUS:
+ if( c > 0 && (tsd = map_id2sd(p_sd[0])) != NULL ) {
+ i = 2 * (*skill_lv);
+ status_charge(&tsd->bl, 0, i);
+ }
+ break;
+ case WM_GREAT_ECHO:
+ for( i = 0; i < c; i++ ) {
+ if( (tsd = map_id2sd(p_sd[i])) != NULL )
+ status_zap(&tsd->bl,0,skill_get_sp(skill_id,*skill_lv)/c);
+ }
+ break;
+ default: //Warning: Assuming Ensemble skills here (for speed)
+ if( is_chorus )
+ break;//Chorus skills are not to be parsed as ensambles
+ if (c > 0 && sd->sc.data[SC_DANCING] && (tsd = map_id2sd(p_sd[0])) != NULL) {
+ sd->sc.data[SC_DANCING]->val4 = tsd->bl.id;
+ sc_start4(&tsd->bl,SC_DANCING,100,skill_id,sd->sc.data[SC_DANCING]->val2,*skill_lv,sd->bl.id,skill_get_time(skill_id,*skill_lv)+1000);
+ clif_skill_nodamage(&tsd->bl, &sd->bl, skill_id, *skill_lv, 1);
+ tsd->skill_id_dance = skill_id;
+ tsd->skill_lv_dance = *skill_lv;
+ }
+ return c;
+ }
+ }
+
+ //Else: new search for partners.
+ c = 0;
+ memset (p_sd, 0, sizeof(p_sd));
+ if( is_chorus )
+ i = party_foreachsamemap(skill_check_condition_char_sub,sd,AREA_SIZE,&sd->bl, &c, &p_sd, skill_id, *skill_lv);
+ else
+ i = map_foreachinrange(skill_check_condition_char_sub, &sd->bl, range, BL_PC, &sd->bl, &c, &p_sd, skill_id);
+
+ if ( skill_id != PR_BENEDICTIO && skill_id != AB_ADORAMUS && skill_id != WL_COMET ) //Apply the average lv to encore skills.
+ *skill_lv = (i+(*skill_lv))/(c+1); //I know c should be one, but this shows how it could be used for the average of n partners.
+ return c;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static int skill_check_condition_mob_master_sub (struct block_list *bl, va_list ap)
+{
+ int *c,src_id,mob_class,skill;
+ struct mob_data *md;
+
+ md=(struct mob_data*)bl;
+ src_id=va_arg(ap,int);
+ mob_class=va_arg(ap,int);
+ skill=va_arg(ap,int);
+ c=va_arg(ap,int *);
+
+ if( md->master_id != src_id || md->special_state.ai != (unsigned)(skill == AM_SPHEREMINE?2:skill == KO_ZANZOU?4:3) )
+ return 0; //Non alchemist summoned mobs have nothing to do here.
+
+ if(md->class_==mob_class)
+ (*c)++;
+
+ return 1;
+}
+
+/*==========================================
+ * Determines if a given skill should be made to consume ammo
+ * when used by the player. [Skotlex]
+ *------------------------------------------*/
+int skill_isammotype (struct map_session_data *sd, int skill)
+{
+ return (
+ battle_config.arrow_decrement==2 &&
+ (sd->status.weapon == W_BOW || (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)) &&
+ skill != HT_PHANTASMIC &&
+ skill_get_type(skill) == BF_WEAPON &&
+ !(skill_get_nk(skill)&NK_NO_DAMAGE) &&
+ !skill_get_spiritball(skill,1) //Assume spirit spheres are used as ammo instead.
+ );
+}
+
+int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv) {
+ struct status_data *status;
+ struct status_change *sc;
+ struct skill_condition require;
+ int i;
+
+ nullpo_ret(sd);
+
+ if (sd->chatID) return 0;
+
+ if( pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->skillitem != skill_id )
+ { //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex]
+ sd->state.arrow_atk = skill_get_ammotype(skill_id)?1:0; //Need to do arrow state check.
+ sd->spiritball_old = sd->spiritball; //Need to do Spiritball check.
+ return 1;
+ }
+
+ switch( sd->menuskill_id ) {
+ case AM_PHARMACY:
+ switch( skill_id ) {
+ case AM_PHARMACY:
+ case AC_MAKINGARROW:
+ case BS_REPAIRWEAPON:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ return 0;
+ }
+ break;
+ case GN_MIX_COOKING:
+ case GN_MAKEBOMB:
+ case GN_S_PHARMACY:
+ case GN_CHANGEMATERIAL:
+ if( sd->menuskill_id != skill_id )
+ return 0;
+ break;
+ }
+ status = &sd->battle_status;
+ sc = &sd->sc;
+ if( !sc->count )
+ sc = NULL;
+
+ if( sd->skillitem == skill_id )
+ {
+ if( sd->state.abra_flag ) // Hocus-Pocus was used. [Inkfish]
+ sd->state.abra_flag = 0;
+ else
+ { // When a target was selected, consume items that were skipped in pc_use_item [Skotlex]
+ if( (i = sd->itemindex) == -1 ||
+ sd->status.inventory[i].nameid != sd->itemid ||
+ sd->inventory_data[i] == NULL ||
+ !sd->inventory_data[i]->flag.delay_consume ||
+ sd->status.inventory[i].amount < 1
+ )
+ { //Something went wrong, item exploit?
+ sd->itemid = sd->itemindex = -1;
+ return 0;
+ }
+ //Consume
+ sd->itemid = sd->itemindex = -1;
+ if( skill_id == WZ_EARTHSPIKE && sc && sc->data[SC_EARTHSCROLL] && rnd()%100 > sc->data[SC_EARTHSCROLL]->val2 ) // [marquis007]
+ ; //Do not consume item.
+ else if( sd->status.inventory[i].expire_time == 0 )
+ pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME); // Rental usable items are not consumed until expiration
+ }
+ return 1;
+ }
+
+ if( pc_is90overweight(sd) )
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_WEIGHTOVER,0);
+ return 0;
+ }
+
+ if( sc && ( sc->data[SC__SHADOWFORM] || sc->data[SC__IGNORANCE] ) )
+ return 0;
+
+ switch( skill_id ) { // Turn off check.
+ case BS_MAXIMIZE: case NV_TRICKDEAD: case TF_HIDING: case AS_CLOAKING: case CR_AUTOGUARD:
+ case ML_AUTOGUARD: case CR_DEFENDER: case ML_DEFENDER: case ST_CHASEWALK: case PA_GOSPEL:
+ case CR_SHRINK: case TK_RUN: case GS_GATLINGFEVER: case TK_READYCOUNTER: case TK_READYDOWN:
+ case TK_READYSTORM: case TK_READYTURN: case SG_FUSION: case RA_WUGDASH: case KO_YAMIKUMO:
+ if( sc && sc->data[status_skill2sc(skill_id)] )
+ return 1;
+ }
+
+ // Check the skills that can be used while mounted on a warg
+ if( pc_isridingwug(sd) ) {
+ switch( skill_id ) {
+ 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_SPRINGTRAP: case RA_DETONATOR: case RA_CLUSTERBOMB:
+ case HT_TALKIEBOX: case RA_FIRINGTRAP: case RA_ICEBOUNDTRAP:
+ case RA_WUGDASH: case RA_WUGRIDER: case RA_WUGSTRIKE:
+ break;
+ default: // in official there is no message.
+ return 0;
+ }
+
+ }
+ if( pc_ismadogear(sd) ) {
+ switch( skill_id ) { //None Mado skills are unusable when Mado is equipped. [Jobbie]
+ case BS_REPAIRWEAPON: case WS_MELTDOWN:
+ case BS_HAMMERFALL: case WS_CARTBOOST:
+ case BS_ADRENALINE: case WS_WEAPONREFINE:
+ case BS_WEAPONPERFECT: case WS_CARTTERMINATION:
+ case BS_OVERTHRUST: case WS_OVERTHRUSTMAX:
+ case BS_MAXIMIZE: case NC_AXEBOOMERANG:
+ case BS_ADRENALINE2: case NC_POWERSWING:
+ case BS_UNFAIRLYTRICK: case NC_AXETORNADO:
+ case BS_GREED:
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ default: //Only Mechanic exlcusive skill can be used.
+ break;
+ }
+ }
+ if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL )
+ return 0;
+
+ require = skill_get_requirement(sd,skill_id,skill_lv);
+
+ //Can only update state when weapon/arrow info is checked.
+ sd->state.arrow_atk = require.ammo?1:0;
+
+ // perform skill-specific checks (and actions)
+ switch( skill_id ) {
+ case SO_SPELLFIST:
+ if(sd->skill_id_old != MG_FIREBOLT && sd->skill_id_old != MG_COLDBOLT && sd->skill_id_old != MG_LIGHTNINGBOLT){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ case SA_CASTCANCEL:
+ if(sd->ud.skilltimer == INVALID_TIMER) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case AL_WARP:
+ if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza]
+ char output[128]; sprintf(output, msg_txt(365), skill_get_name(AL_WARP));
+ clif_displaymessage(sd->fd, output); //"Duel: Can't use %s in duel."
+ return 0;
+ }
+ break;
+ case MO_CALLSPIRITS:
+ if(sc && sc->data[SC_RAISINGDRAGON])
+ skill_lv += sc->data[SC_RAISINGDRAGON]->val1;
+ if(sd->spiritball >= skill_lv) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case MO_FINGEROFFENSIVE:
+ case GS_FLING:
+ case SR_RAMPAGEBLASTER:
+ case SR_RIDEINLIGHTNING:
+ if( sd->spiritball > 0 && sd->spiritball < require.spiritball )
+ sd->spiritball_old = require.spiritball = sd->spiritball;
+ else
+ sd->spiritball_old = require.spiritball;
+ break;
+ case MO_CHAINCOMBO:
+ if(!sc)
+ return 0;
+ if(sc->data[SC_BLADESTOP])
+ break;
+ if(sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_TRIPLEATTACK)
+ break;
+ return 0;
+ case MO_COMBOFINISH:
+ if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_CHAINCOMBO))
+ return 0;
+ break;
+ case CH_TIGERFIST:
+ if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_COMBOFINISH))
+ return 0;
+ break;
+ case CH_CHAINCRUSH:
+ if(!(sc && sc->data[SC_COMBO]))
+ return 0;
+ if(sc->data[SC_COMBO]->val1 != MO_COMBOFINISH && sc->data[SC_COMBO]->val1 != CH_TIGERFIST)
+ return 0;
+ break;
+ case MO_EXTREMITYFIST:
+ // if(sc && sc->data[SC_EXTREMITYFIST]) //To disable Asura during the 5 min skill block uncomment this...
+ // return 0;
+ if( sc && (sc->data[SC_BLADESTOP] || sc->data[SC_CURSEDCIRCLE_ATKER]) )
+ break;
+ if( sc && sc->data[SC_COMBO] )
+ {
+ switch(sc->data[SC_COMBO]->val1) {
+ case MO_COMBOFINISH:
+ case CH_TIGERFIST:
+ case CH_CHAINCRUSH:
+ break;
+ default:
+ return 0;
+ }
+ }
+ else if( !unit_can_move(&sd->bl) )
+ { //Placed here as ST_MOVE_ENABLE should not apply if rooted or on a combo. [Skotlex]
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case TK_MISSION:
+ if( (sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON )
+ {// Cannot be used by Non-Taekwon classes
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case TK_READYCOUNTER:
+ case TK_READYDOWN:
+ case TK_READYSTORM:
+ case TK_READYTURN:
+ case TK_JUMPKICK:
+ if( (sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER )
+ {// Soul Linkers cannot use this skill
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case TK_TURNKICK:
+ case TK_STORMKICK:
+ case TK_DOWNKICK:
+ case TK_COUNTER:
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER)
+ return 0; //Anti-Soul Linker check in case you job-changed with Stances active.
+ if(!(sc && sc->data[SC_COMBO]) || sc->data[SC_COMBO]->val1 == TK_JUMPKICK)
+ return 0; //Combo needs to be ready
+
+ if (sc->data[SC_COMBO]->val3) { //Kick chain
+ //Do not repeat a kick.
+ if (sc->data[SC_COMBO]->val3 != skill_id)
+ break;
+ status_change_end(&sd->bl, SC_COMBO, INVALID_TIMER);
+ return 0;
+ }
+ if(sc->data[SC_COMBO]->val1 != skill_id && !( sd && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON) )) { //Cancel combo wait.
+ unit_cancel_combo(&sd->bl);
+ return 0;
+ }
+ break; //Combo ready.
+ case BD_ADAPTATION:
+ {
+ int time;
+ if(!(sc && sc->data[SC_DANCING]))
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ time = 1000*(sc->data[SC_DANCING]->val3>>16);
+ if (skill_get_time(
+ (sc->data[SC_DANCING]->val1&0xFFFF), //Dance Skill ID
+ (sc->data[SC_DANCING]->val1>>16)) //Dance Skill LV
+ - time < skill_get_time2(skill_id,skill_lv))
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ }
+ break;
+
+ case PR_BENEDICTIO:
+ if (skill_check_pc_partner(sd, skill_id, &skill_lv, 1, 0) < 2)
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case SL_SMA:
+ if(!(sc && sc->data[SC_SMA]))
+ return 0;
+ break;
+
+ case HT_POWER:
+ if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_id))
+ return 0;
+ break;
+
+ case CG_HERMODE:
+ if(!npc_check_areanpc(1,sd->bl.m,sd->bl.x,sd->bl.y,skill_get_splash(skill_id, skill_lv)))
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case CG_MOONLIT: //Check there's no wall in the range+1 area around the caster. [Skotlex]
+ {
+ int i,x,y,range = skill_get_splash(skill_id, skill_lv)+1;
+ int size = range*2+1;
+ for (i=0;i<size*size;i++) {
+ x = sd->bl.x+(i%size-range);
+ y = sd->bl.y+(i/size-range);
+ if (map_getcell(sd->bl.m,x,y,CELL_CHKWALL)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ }
+ }
+ break;
+ case PR_REDEMPTIO:
+ {
+ int exp;
+ if( ((exp = pc_nextbaseexp(sd)) > 0 && get_percentage(sd->status.base_exp, exp) < 1) ||
+ ((exp = pc_nextjobexp(sd)) > 0 && get_percentage(sd->status.job_exp, exp) < 1)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0); //Not enough exp.
+ return 0;
+ }
+ break;
+ }
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ if (!party_skill_check(sd, sd->status.party_id, skill_id, skill_lv))
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case SG_SUN_WARM:
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ if (sc && sc->data[SC_MIRACLE])
+ break;
+ i = skill_id-SG_SUN_WARM;
+ if (sd->bl.m == sd->feel_map[i].m)
+ break;
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ break;
+ case SG_SUN_COMFORT:
+ case SG_MOON_COMFORT:
+ case SG_STAR_COMFORT:
+ if (sc && sc->data[SC_MIRACLE])
+ break;
+ i = skill_id-SG_SUN_COMFORT;
+ if (sd->bl.m == sd->feel_map[i].m &&
+ (battle_config.allow_skill_without_day || sg_info[i].day_func()))
+ break;
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ case SG_FUSION:
+ if (sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_STAR)
+ break;
+ //Auron insists we should implement SP consumption when you are not Soul Linked. [Skotlex]
+ //Only invoke on skill begin cast (instant cast skill). [Kevin]
+ if( require.sp > 0 )
+ {
+ if (status->sp < (unsigned int)require.sp)
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_SP_INSUFFICIENT,0);
+ else
+ status_zap(&sd->bl, 0, require.sp);
+ }
+ return 0;
+ case GD_BATTLEORDER:
+ case GD_REGENERATION:
+ case GD_RESTORE:
+ if (!map_flag_gvg2(sd->bl.m)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ case GD_EMERGENCYCALL:
+ // other checks were already done in skillnotok()
+ if (!sd->status.guild_id || !sd->state.gmaster_flag)
+ return 0;
+ break;
+
+ case GS_GLITTERING:
+ if(sd->spiritball >= 10) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case NJ_ISSEN:
+#ifdef RENEWAL
+ if (status->hp < (status->hp/100)) {
+#else
+ if (status->hp < 2) {
+#endif
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ case NJ_BUNSINJYUTSU:
+ if (!(sc && sc->data[SC_NEN])) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case NJ_ZENYNAGE:
+ case KO_MUCHANAGE:
+ if(sd->status.zeny < require.zeny) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_MONEY,0);
+ return 0;
+ }
+ break;
+ case PF_HPCONVERSION:
+ if (status->sp == status->max_sp)
+ return 0; //Unusable when at full SP.
+ break;
+ case AM_CALLHOMUN: //Can't summon if a hom is already out
+ if (sd->status.hom_id && sd->hd && !sd->hd->homunculus.vaporize) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case AM_REST: //Can't vapo homun if you don't have an active homunc or it's hp is < 80%
+ if (!merc_is_hom_active(sd->hd) || sd->hd->battle_status.hp < (sd->hd->battle_status.max_hp*80/100))
+ {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Arch Bishop
+ **/
+ case AB_ANCILLA:
+ {
+ int count = 0;
+ for( i = 0; i < MAX_INVENTORY; i ++ )
+ if( sd->status.inventory[i].nameid == ITEMID_ANCILLA )
+ count += sd->status.inventory[i].amount;
+ if( count >= 3 ) {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_ANCILLA_NUMOVER, 0);
+ return 0;
+ }
+ }
+ break;
+ /**
+ * Keeping as a note:
+ * Bug Report #17 provides a link to a sep-2011 changelog that shows this requirement was removed
+ **/
+ //case AB_LAUDAAGNUS:
+ //case AB_LAUDARAMUS:
+ // if( !sd->status.party_id ) {
+ // clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ // return 0;
+ // }
+ // break;
+
+ case AB_ADORAMUS:
+ /**
+ * Warlock
+ **/
+ case WL_COMET:
+ if( skill_check_pc_partner(sd,skill_id,&skill_lv,1,0) <= 0 && ((i = pc_search_inventory(sd,require.itemid[0])) < 0 || sd->status.inventory[i].amount < require.amount[0]) )
+ {
+ //clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_ITEM,require.amount[0],require.itemid[0]);
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case WL_SUMMONFB:
+ case WL_SUMMONBL:
+ case WL_SUMMONWB:
+ case WL_SUMMONSTONE:
+ if( sc )
+ {
+ ARR_FIND(SC_SPHERE_1,SC_SPHERE_5+1,i,!sc->data[i]);
+ if( i == SC_SPHERE_5+1 )
+ { // No more free slots
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_SUMMON,0);
+ return 0;
+ }
+ }
+ break;
+ /**
+ * Guilotine Cross
+ **/
+ case GC_HALLUCINATIONWALK:
+ if( sc && (sc->data[SC_HALLUCINATIONWALK] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY]) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case GC_COUNTERSLASH:
+ case GC_WEAPONCRUSH:
+ if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == GC_WEAPONBLOCKING) ) {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_GC_WEAPONBLOCKING, 0);
+ return 0;
+ }
+ break;
+ /**
+ * Ranger
+ **/
+ case RA_WUGMASTERY:
+ if( pc_isfalcon(sd) || pc_isridingwug(sd) || sd->sc.data[SC__GROOMY]) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case RA_WUGSTRIKE:
+ if( !pc_iswug(sd) && !pc_isridingwug(sd) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case RA_WUGRIDER:
+ if( pc_isfalcon(sd) || ( !pc_isridingwug(sd) && !pc_iswug(sd) ) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case RA_WUGDASH:
+ if(!pc_isridingwug(sd)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Royal Guard
+ **/
+ case LG_BANDING:
+ if( sc && sc->data[SC_INSPIRATION] ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case LG_PRESTIGE:
+ if( sc && (sc->data[SC_BANDING] || sc->data[SC_INSPIRATION]) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case LG_RAGEBURST:
+ if( sd->spiritball == 0 ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_SKILLINTERVAL,0);
+ return 0;
+ }
+ sd->spiritball_old = require.spiritball = sd->spiritball;
+ break;
+ case LG_RAYOFGENESIS:
+ if( sc && sc->data[SC_INSPIRATION] )
+ return 1; // Don't check for partner.
+ if( !(sc && sc->data[SC_BANDING]) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL,0);
+ return 0;
+ } else if( skill_check_pc_partner(sd,skill_id,&skill_lv,skill_get_range(skill_id,skill_lv),0) < 1 )
+ return 0; // Just fails, no msg here.
+ break;
+ case LG_HESPERUSLIT:
+ if( !sc || !sc->data[SC_BANDING] ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case SR_FALLENEMPIRE:
+ if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == SR_DRAGONCOMBO) )
+ return 0;
+ break;
+
+ case SR_CRESCENTELBOW:
+ if( sc && sc->data[SC_CRESCENTELBOW] ) {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_DUPLICATE, 0);
+ return 0;
+ }
+ break;
+ case SR_CURSEDCIRCLE:
+ if (map_flag_gvg(sd->bl.m)) {
+ if (map_foreachinrange(mob_count_sub, &sd->bl, skill_get_splash(skill_id, skill_lv), BL_MOB,
+ MOBID_EMPERIUM, MOBID_GUARIDAN_STONE1, MOBID_GUARIDAN_STONE2)) {
+ char output[128];
+ sprintf(output, "You're too close to a stone or emperium to do this skill");
+ clif_colormes(sd, COLOR_RED, output);
+ return 0;
+ }
+ }
+ if( sd->spiritball > 0 )
+ sd->spiritball_old = require.spiritball = sd->spiritball;
+ else {
+ clif_skill_fail(sd,skill_id,0,0);
+ return 0;
+ }
+ break;
+ case SR_GATEOFHELL:
+ if( sd->spiritball > 0 )
+ sd->spiritball_old = require.spiritball;
+ break;
+ case SC_MANHOLE:
+ case SC_DIMENSIONDOOR:
+ if( sc && sc->data[SC_MAGNETICFIELD] ) {
+ clif_skill_fail(sd,skill_id,0,0);
+ return 0;
+ }
+ break;
+ case WM_GREAT_ECHO: {
+ int count;
+ count = skill_check_pc_partner(sd, skill_id, &skill_lv, skill_get_splash(skill_id,skill_lv), 0);
+ if( count < 1 ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_NEED_HELPER,0);
+ return 0;
+ } else
+ require.sp -= require.sp * 20 * count / 100; // -20% each W/M in the party.
+ }
+ break;
+ case SO_FIREWALK:
+ case SO_ELECTRICWALK: // Can't be casted until you've walked all cells.
+ if( sc && sc->data[SC_PROPERTYWALK] &&
+ sc->data[SC_PROPERTYWALK]->val3 < skill_get_maxcount(sc->data[SC_PROPERTYWALK]->val1,sc->data[SC_PROPERTYWALK]->val2) ) {
+ clif_skill_fail(sd,skill_id,0x0,0);
+ return 0;
+ }
+ break;
+ case SO_EL_CONTROL:
+ if( !sd->status.ele_id || !sd->ed ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case RETURN_TO_ELDICASTES:
+ if( pc_ismadogear(sd) ) { //Cannot be used if Mado is equipped.
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case LG_REFLECTDAMAGE:
+ case CR_REFLECTSHIELD:
+ if( sc && sc->data[SC_KYOMU] && rand()%100 < 30){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case KO_KAHU_ENTEN:
+ case KO_HYOUHU_HUBUKI:
+ case KO_KAZEHU_SEIRAN:
+ case KO_DOHU_KOUKAI:
+ {
+ int ttype = skill_get_ele(skill_id, skill_lv);
+ ARR_FIND(1, 5, i, sd->talisman[i] > 0 && i != ttype);
+ if( (i < 5 && i != ttype) || sd->talisman[ttype] >= 10 ){
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ return 0;
+ }
+ }
+ break;
+ case KO_KAIHOU:
+ case KO_ZENKAI:
+ ARR_FIND(1, 6, i, sd->talisman[i] > 0);
+ if( i > 4 ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ }
+
+ switch(require.state) {
+ case ST_HIDING:
+ if(!(sc && sc->option&OPTION_HIDE)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_CLOAKING:
+ if(!pc_iscloaking(sd)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_HIDDEN:
+ if(!pc_ishiding(sd)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_RIDING:
+ if(!pc_isriding(sd)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_FALCON:
+ if(!pc_isfalcon(sd)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_CARTBOOST:
+ if(!(sc && sc->data[SC_CARTBOOST])) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ case ST_CART:
+ if(!pc_iscarton(sd)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_SHIELD:
+ if(sd->status.shield <= 0) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_SIGHT:
+ if(!(sc && sc->data[SC_SIGHT])) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_EXPLOSIONSPIRITS:
+ if(!(sc && sc->data[SC_EXPLOSIONSPIRITS])) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_RECOV_WEIGHT_RATE:
+ if(battle_config.natural_heal_weight_rate <= 100 && sd->weight*100/sd->max_weight >= (unsigned int)battle_config.natural_heal_weight_rate) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_MOVE_ENABLE:
+ if (sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill_id)
+ sd->ud.canmove_tick = gettick(); //When using a combo, cancel the can't move delay to enable the skill. [Skotlex]
+
+ if (!unit_can_move(&sd->bl)) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case ST_WATER:
+ if (sc && (sc->data[SC_DELUGE] || sc->data[SC_SUITON]))
+ break;
+ if (map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKWATER))
+ break;
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ /**
+ * Rune Knight
+ **/
+ case ST_RIDINGDRAGON:
+ if( !pc_isridingdragon(sd) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Wug
+ **/
+ case ST_WUG:
+ if( !pc_iswug(sd) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Riding Wug
+ **/
+ case ST_RIDINGWUG:
+ if( !pc_isridingwug(sd) ){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Mechanic
+ **/
+ case ST_MADO:
+ if( !pc_ismadogear(sd) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Sorcerer
+ **/
+ case ST_ELEMENTALSPIRIT:
+ if(!sd->ed) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_EL_SUMMON,0);
+ return 0;
+ }
+ break;
+ case ST_POISONINGWEAPON:
+ if (!(sc && sc->data[SC_POISONINGWEAPON])) {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_GC_POISONINGWEAPON, 0);
+ return 0;
+ }
+ break;
+ case ST_ROLLINGCUTTER:
+ if (!(sc && sc->data[SC_ROLLINGCUTTER])) {
+ clif_skill_fail(sd, skill_id, USESKILL_FAIL_CONDITION, 0);
+ return 0;
+ }
+ break;
+ case ST_MH_FIGHTING:
+ if (!(sc && sc->data[SC_STYLE_CHANGE] && sc->data[SC_STYLE_CHANGE]->val2 == MH_MD_FIGHTING)){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ case ST_MH_GRAPPLING:
+ if (!(sc && sc->data[SC_STYLE_CHANGE] && sc->data[SC_STYLE_CHANGE]->val2 == MH_MD_GRAPPLING)){
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ }
+
+ if(require.mhp > 0 && get_percentage(status->hp, status->max_hp) > require.mhp) {
+ //mhp is the max-hp-requirement, that is,
+ //you must have this % or less of HP to cast it.
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0);
+ return 0;
+ }
+
+ if( require.weapon && !pc_check_weapontype(sd,require.weapon) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0);
+ return 0;
+ }
+
+ if( require.sp > 0 && status->sp < (unsigned int)require.sp) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_SP_INSUFFICIENT,0);
+ return 0;
+ }
+
+ if( require.zeny > 0 && sd->status.zeny < require.zeny ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_MONEY,0);
+ return 0;
+ }
+
+ if( require.spiritball > 0 && sd->spiritball < require.spiritball) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_SPIRITS,require.spiritball);
+ return 0;
+ }
+
+ return 1;
+}
+
+int skill_check_condition_castend(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv)
+{
+ struct skill_condition require;
+ struct status_data *status;
+ int i;
+ int index[MAX_SKILL_ITEM_REQUIRE];
+
+ nullpo_ret(sd);
+
+ if( sd->chatID )
+ return 0;
+
+ if( pc_has_permission(sd, PC_PERM_SKILL_UNCONDITIONAL) && sd->skillitem != skill_id ) {
+ //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex]
+ sd->state.arrow_atk = skill_get_ammotype(skill_id)?1:0; //Need to do arrow state check.
+ sd->spiritball_old = sd->spiritball; //Need to do Spiritball check.
+ return 1;
+ }
+
+ switch( sd->menuskill_id ) { // Cast start or cast end??
+ case AM_PHARMACY:
+ switch( skill_id ) {
+ case AM_PHARMACY:
+ case AC_MAKINGARROW:
+ case BS_REPAIRWEAPON:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ return 0;
+ }
+ break;
+ case GN_MIX_COOKING:
+ case GN_MAKEBOMB:
+ case GN_S_PHARMACY:
+ case GN_CHANGEMATERIAL:
+ if( sd->menuskill_id != skill_id )
+ return 0;
+ break;
+ }
+
+ if( sd->skillitem == skill_id ) // Casting finished (Item skill or Hocus-Pocus)
+ return 1;
+
+ if( pc_is90overweight(sd) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_WEIGHTOVER,0);
+ return 0;
+ }
+
+ // perform skill-specific checks (and actions)
+ switch( skill_id ) {
+ case PR_BENEDICTIO:
+ skill_check_pc_partner(sd, skill_id, &skill_lv, 1, 1);
+ break;
+ case AM_CANNIBALIZE:
+ case AM_SPHEREMINE: {
+ int c=0;
+ int summons[5] = { 1589, 1579, 1575, 1555, 1590 };
+ //int summons[5] = { 1020, 1068, 1118, 1500, 1368 };
+ int maxcount = (skill_id==AM_CANNIBALIZE)? 6-skill_lv : skill_get_maxcount(skill_id,skill_lv);
+ int mob_class = (skill_id==AM_CANNIBALIZE)? summons[skill_lv-1] :1142;
+ if(battle_config.land_skill_limit && maxcount>0 && (battle_config.land_skill_limit&BL_PC)) {
+ i = map_foreachinmap(skill_check_condition_mob_master_sub ,sd->bl.m, BL_MOB, sd->bl.id, mob_class, skill_id, &c);
+ if(c >= maxcount ||
+ (skill_id==AM_CANNIBALIZE && c != i && battle_config.summon_flora&2))
+ { //Fails when: exceed max limit. There are other plant types already out.
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ }
+ break;
+ }
+ case NC_SILVERSNIPER:
+ case NC_MAGICDECOY: {
+ int c = 0, j;
+ int maxcount = skill_get_maxcount(skill_id,skill_lv);
+ int mob_class = 2042;
+ if( skill_id == NC_MAGICDECOY )
+ mob_class = 2043;
+
+ if( battle_config.land_skill_limit && maxcount > 0 && ( battle_config.land_skill_limit&BL_PC ) ) {
+ if( skill_id == NC_MAGICDECOY ) {
+ for( j = mob_class; j <= 2046; j++ )
+ map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, j, skill_id, &c);
+ } else
+ map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, mob_class, skill_id, &c);
+ if( c >= maxcount ) {
+ clif_skill_fail(sd , skill_id, USESKILL_FAIL_LEVEL, 0);
+ return 0;
+ }
+ }
+ }
+ break;
+ case KO_ZANZOU: {
+ int c = 0;
+ i = map_foreachinmap(skill_check_condition_mob_master_sub, sd->bl.m, BL_MOB, sd->bl.id, 2308, skill_id, &c);
+ if( c >= skill_get_maxcount(skill_id,skill_lv) || c != i)
+ {
+ clif_skill_fail(sd , skill_id, USESKILL_FAIL_LEVEL, 0);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ status = &sd->battle_status;
+
+ require = skill_get_requirement(sd,skill_id,skill_lv);
+
+ if( require.hp > 0 && status->hp <= (unsigned int)require.hp) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_HP_INSUFFICIENT,0);
+ return 0;
+ }
+
+ if( require.weapon && !pc_check_weapontype(sd,require.weapon) ) {
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0);
+ return 0;
+ }
+
+ if( require.ammo ) { //Skill requires stuff equipped in the arrow slot.
+ if((i=sd->equip_index[EQI_AMMO]) < 0 || !sd->inventory_data[i] ) {
+ clif_arrow_fail(sd,0);
+ return 0;
+ } else if( sd->status.inventory[i].amount < require.ammo_qty ) {
+ char e_msg[100];
+ sprintf(e_msg,"Skill Failed. [%s] requires %dx %s.",
+ skill_get_desc(skill_id),
+ require.ammo_qty,
+ itemdb_jname(sd->status.inventory[i].nameid));
+ clif_colormes(sd,COLOR_RED,e_msg);
+ return 0;
+ }
+ if (!(require.ammo&1<<sd->inventory_data[i]->look)) { //Ammo type check. Send the "wrong weapon type" message
+ //which is the closest we have to wrong ammo type. [Skotlex]
+ clif_arrow_fail(sd,0); //Haplo suggested we just send the equip-arrows message instead. [Skotlex]
+ //clif_skill_fail(sd,skill_id,USESKILL_FAIL_THIS_WEAPON,0);
+ return 0;
+ }
+ }
+
+ for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; ++i ) {
+ if( !require.itemid[i] )
+ continue;
+ index[i] = pc_search_inventory(sd,require.itemid[i]);
+ if( index[i] < 0 || sd->status.inventory[index[i]].amount < require.amount[i] ) {
+ if( require.itemid[i] == ITEMID_RED_GEMSTONE )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_REDJAMSTONE,0);// red gemstone required
+ else if( require.itemid[i] == ITEMID_BLUE_GEMSTONE )
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_BLUEJAMSTONE,0);// blue gemstone required
+ else
+ clif_skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+// type&2: consume items (after skill was used)
+// type&1: consume the others (before skill was used)
+int skill_consume_requirement( struct map_session_data *sd, uint16 skill_id, uint16 skill_lv, short type)
+{
+ struct skill_condition req;
+
+ nullpo_ret(sd);
+
+ req = skill_get_requirement(sd,skill_id,skill_lv);
+
+ if( type&1 )
+ {
+ if( skill_id == CG_TAROTCARD || sd->state.autocast )
+ req.sp = 0; // TarotCard will consume sp in skill_cast_nodamage_id [Inkfish]
+ if(req.hp || req.sp)
+ status_zap(&sd->bl, req.hp, req.sp);
+
+ if(req.spiritball > 0)
+ pc_delspiritball(sd,req.spiritball,0);
+
+ if(req.zeny > 0)
+ {
+ if( skill_id == NJ_ZENYNAGE )
+ req.zeny = 0; //Zeny is reduced on skill_attack.
+ if( sd->status.zeny < req.zeny )
+ req.zeny = sd->status.zeny;
+ pc_payzeny(sd,req.zeny,LOG_TYPE_CONSUME,NULL);
+ }
+ }
+
+ if( type&2 )
+ {
+ struct status_change *sc = &sd->sc;
+ int n,i;
+
+ if( !sc->count )
+ sc = NULL;
+
+ for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; ++i )
+ {
+ if( !req.itemid[i] )
+ continue;
+
+ if( itemid_isgemstone(req.itemid[i]) && skill_id != HW_GANBANTEIN && sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_WIZARD )
+ continue; //Gemstones are checked, but not substracted from inventory.
+
+ switch( skill_id ){
+ case SA_SEISMICWEAPON:
+ if( sc && sc->data[SC_UPHEAVAL_OPTION] && rnd()%100 < 50 )
+ continue;
+ break;
+ case SA_FLAMELAUNCHER:
+ case SA_VOLCANO:
+ if( sc && sc->data[SC_TROPIC_OPTION] && rnd()%100 < 50 )
+ continue;
+ break;
+ case SA_FROSTWEAPON:
+ case SA_DELUGE:
+ if( sc && sc->data[SC_CHILLY_AIR_OPTION] && rnd()%100 < 50 )
+ continue;
+ break;
+ case SA_LIGHTNINGLOADER:
+ case SA_VIOLENTGALE:
+ if( sc && sc->data[SC_WILD_STORM_OPTION] && rnd()%100 < 50 )
+ continue;
+ break;
+ }
+
+ if( (n = pc_search_inventory(sd,req.itemid[i])) >= 0 )
+ pc_delitem(sd,n,req.amount[i],0,1,LOG_TYPE_CONSUME);
+ }
+ }
+
+ return 1;
+}
+
+struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 skill_id, uint16 skill_lv)
+{
+ struct skill_condition req;
+ struct status_data *status;
+ struct status_change *sc;
+ int i,hp_rate,sp_rate, sp_skill_rate_bonus = 100;
+ uint16 idx;
+
+ memset(&req,0,sizeof(req));
+
+ if( !sd )
+ return req;
+
+ if( sd->skillitem == skill_id )
+ return req; // Item skills and Hocus-Pocus don't have requirements.[Inkfish]
+
+ sc = &sd->sc;
+ if( !sc->count )
+ sc = NULL;
+
+ switch( skill_id )
+ { // Turn off check.
+ case BS_MAXIMIZE: case NV_TRICKDEAD: case TF_HIDING: case AS_CLOAKING: case CR_AUTOGUARD:
+ case ML_AUTOGUARD: case CR_DEFENDER: case ML_DEFENDER: case ST_CHASEWALK: case PA_GOSPEL:
+ case CR_SHRINK: case TK_RUN: case GS_GATLINGFEVER: case TK_READYCOUNTER: case TK_READYDOWN:
+ case TK_READYSTORM: case TK_READYTURN: case SG_FUSION: case KO_YAMIKUMO:
+ if( sc && sc->data[status_skill2sc(skill_id)] )
+ return req;
+ }
+
+ idx = skill_get_index(skill_id);
+ if( idx == 0 ) // invalid skill id
+ return req;
+ if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL )
+ return req;
+
+ status = &sd->battle_status;
+
+ req.hp = skill_db[idx].hp[skill_lv-1];
+ hp_rate = skill_db[idx].hp_rate[skill_lv-1];
+ if(hp_rate > 0)
+ req.hp += (status->hp * hp_rate)/100;
+ else
+ req.hp += (status->max_hp * (-hp_rate))/100;
+
+ req.sp = skill_db[idx].sp[skill_lv-1];
+ if((sd->skill_id_old == BD_ENCORE) && skill_id == sd->skill_id_dance)
+ req.sp /= 2;
+ sp_rate = skill_db[idx].sp_rate[skill_lv-1];
+ if(sp_rate > 0)
+ req.sp += (status->sp * sp_rate)/100;
+ else
+ req.sp += (status->max_sp * (-sp_rate))/100;
+ if( sd->dsprate != 100 )
+ req.sp = req.sp * sd->dsprate / 100;
+
+ ARR_FIND(0, ARRAYLENGTH(sd->skillusesprate), i, sd->skillusesprate[i].id == skill_id);
+ if( i < ARRAYLENGTH(sd->skillusesprate) )
+ sp_skill_rate_bonus += sd->skillusesprate[i].val;
+ ARR_FIND(0, ARRAYLENGTH(sd->skillusesp), i, sd->skillusesp[i].id == skill_id);
+ if( i < ARRAYLENGTH(sd->skillusesp) )
+ req.sp -= sd->skillusesp[i].val;
+
+ req.sp = cap_value(req.sp * sp_skill_rate_bonus / 100, 0, SHRT_MAX);
+
+ if( sc ) {
+ if( sc->data[SC__LAZINESS] )
+ req.sp += req.sp + sc->data[SC__LAZINESS]->val1 * 10;
+ if (sc->data[SC_UNLIMITEDHUMMINGVOICE])
+ req.sp += req.sp * sc->data[SC_UNLIMITEDHUMMINGVOICE]->val2 / 100;
+ if( sc->data[SC_RECOGNIZEDSPELL] )
+ req.sp += req.sp / 4;
+ }
+
+ req.zeny = skill_db[idx].zeny[skill_lv-1];
+
+ if( sc && sc->data[SC__UNLUCKY] )
+ req.zeny += sc->data[SC__UNLUCKY]->val1 * 500;
+
+ req.spiritball = skill_db[idx].spiritball[skill_lv-1];
+
+ req.state = skill_db[idx].state;
+
+ req.mhp = skill_db[idx].mhp[skill_lv-1];
+
+ req.weapon = skill_db[idx].weapon;
+
+ req.ammo_qty = skill_db[idx].ammo_qty[skill_lv-1];
+ if (req.ammo_qty)
+ req.ammo = skill_db[idx].ammo;
+
+ if (!req.ammo && skill_id && skill_isammotype(sd, skill_id))
+ { //Assume this skill is using the weapon, therefore it requires arrows.
+ req.ammo = 0xFFFFFFFF; //Enable use on all ammo types.
+ req.ammo_qty = 1;
+ }
+
+ for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++ ) {
+ if( (skill_id == AM_POTIONPITCHER || skill_id == CR_SLIMPITCHER || skill_id == CR_CULTIVATION) && i != skill_lv%11 - 1 )
+ continue;
+
+ switch( skill_id ) {
+ case AM_CALLHOMUN:
+ if (sd->status.hom_id) //Don't delete items when hom is already out.
+ continue;
+ break;
+ case NC_SHAPESHIFT:
+ if( i < 4 )
+ continue;
+ break;
+ case WZ_FIREPILLAR: // celest
+ if (skill_lv <= 5) // no gems required at level 1-5
+ continue;
+ break;
+ case AB_ADORAMUS:
+ if( itemid_isgemstone(skill_db[idx].itemid[i]) && skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 2) )
+ continue;
+ break;
+ case WL_COMET:
+ if( itemid_isgemstone(skill_db[idx].itemid[i]) && skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 0) )
+ continue;
+ break;
+ case GN_FIRE_EXPANSION:
+ if( i < 5 )
+ continue;
+ break;
+ case SO_SUMMON_AGNI:
+ case SO_SUMMON_AQUA:
+ case SO_SUMMON_VENTUS:
+ case SO_SUMMON_TERA:
+ case SO_WATER_INSIGNIA:
+ case SO_FIRE_INSIGNIA:
+ case SO_WIND_INSIGNIA:
+ case SO_EARTH_INSIGNIA:
+ if( i < 3 )
+ continue;
+ break;
+ }
+
+ req.itemid[i] = skill_db[idx].itemid[i];
+ req.amount[i] = skill_db[idx].amount[i];
+
+ if( itemid_isgemstone(req.itemid[i]) && skill_id != HW_GANBANTEIN )
+ {
+ if( sd->special_state.no_gemstone )
+ { //Make it substract 1 gem rather than skipping the cost.
+ if( --req.amount[i] < 1 )
+ req.itemid[i] = 0;
+ }
+ if(sc && sc->data[SC_INTOABYSS])
+ {
+ if( skill_id != SA_ABRACADABRA )
+ req.itemid[i] = req.amount[i] = 0;
+ else if( --req.amount[i] < 1 )
+ req.amount[i] = 1; // Hocus Pocus allways use at least 1 gem
+ }
+ }
+ if( skill_id >= HT_SKIDTRAP && skill_id <= HT_TALKIEBOX && pc_checkskill(sd, RA_RESEARCHTRAP) > 0){
+ int16 itIndex;
+ if( (itIndex = pc_search_inventory(sd,req.itemid[i])) < 0 || ( itIndex >= 0 && sd->status.inventory[itIndex].amount < req.amount[i] ) ){
+ req.itemid[i] = ITEMID_TRAP_ALLOY;
+ req.amount[i] = 1;
+ }
+ break;
+ }
+ }
+
+ /* requirements are level-dependent */
+ switch( skill_id ) {
+ case NC_SHAPESHIFT:
+ case GN_FIRE_EXPANSION:
+ case SO_SUMMON_AGNI:
+ case SO_SUMMON_AQUA:
+ case SO_SUMMON_VENTUS:
+ case SO_SUMMON_TERA:
+ case SO_WATER_INSIGNIA:
+ case SO_FIRE_INSIGNIA:
+ case SO_WIND_INSIGNIA:
+ case SO_EARTH_INSIGNIA:
+ req.itemid[skill_lv-1] = skill_db[idx].itemid[skill_lv-1];
+ req.amount[skill_lv-1] = skill_db[idx].amount[skill_lv-1];
+ break;
+ }
+
+ // Check for cost reductions due to skills & SCs
+ switch(skill_id) {
+ case MC_MAMMONITE:
+ if(pc_checkskill(sd,BS_UNFAIRLYTRICK)>0)
+ req.zeny -= req.zeny*10/100;
+ break;
+ case AL_HOLYLIGHT:
+ if(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_PRIEST)
+ req.sp *= 5;
+ break;
+ case SL_SMA:
+ case SL_STUN:
+ case SL_STIN:
+ {
+ int kaina_lv = pc_checkskill(sd,SL_KAINA);
+
+ if(kaina_lv==0 || sd->status.base_level<70)
+ break;
+ if(sd->status.base_level>=90)
+ req.sp -= req.sp*7*kaina_lv/100;
+ else if(sd->status.base_level>=80)
+ req.sp -= req.sp*5*kaina_lv/100;
+ else if(sd->status.base_level>=70)
+ req.sp -= req.sp*3*kaina_lv/100;
+ }
+ break;
+ case MO_TRIPLEATTACK:
+ case MO_CHAINCOMBO:
+ case MO_COMBOFINISH:
+ case CH_TIGERFIST:
+ case CH_CHAINCRUSH:
+ if(sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_MONK)
+ req.sp -= req.sp*25/100; //FIXME: Need real data. this is a custom value.
+ break;
+ case MO_BODYRELOCATION:
+ if( sc && sc->data[SC_EXPLOSIONSPIRITS] )
+ req.spiritball = 0;
+ break;
+ case MO_EXTREMITYFIST:
+ if( sc )
+ {
+ if( sc->data[SC_BLADESTOP] )
+ req.spiritball--;
+ else if( sc->data[SC_COMBO] )
+ {
+ switch( sc->data[SC_COMBO]->val1 )
+ {
+ case MO_COMBOFINISH:
+ req.spiritball = 4;
+ break;
+ case CH_TIGERFIST:
+ req.spiritball = 3;
+ break;
+ case CH_CHAINCRUSH: //It should consume whatever is left as long as it's at least 1.
+ req.spiritball = sd->spiritball?sd->spiritball:1;
+ break;
+ }
+ }else if( sc->data[SC_RAISINGDRAGON] && sd->spiritball > 5)
+ req.spiritball = sd->spiritball; // must consume all regardless of the amount required
+ }
+ break;
+ case SR_RAMPAGEBLASTER:
+ req.spiritball = sd->spiritball?sd->spiritball:15;
+ break;
+ case SR_GATEOFHELL:
+ if( sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == SR_FALLENEMPIRE )
+ req.sp -= req.sp * 10 / 100;
+ break;
+ case SO_SUMMON_AGNI:
+ case SO_SUMMON_AQUA:
+ case SO_SUMMON_VENTUS:
+ case SO_SUMMON_TERA:
+ req.sp -= req.sp * (5 + 5 * pc_checkskill(sd,SO_EL_SYMPATHY)) / 100;
+ break;
+ case SO_PSYCHIC_WAVE:
+ if( sc && sc->data[SC_BLAST_OPTION] )
+ req.sp += req.sp * 150 / 100;
+ break;
+ }
+
+ return req;
+}
+
+/*==========================================
+ * Does cast-time reductions based on dex, item bonuses and config setting
+ *------------------------------------------*/
+int skill_castfix (struct block_list *bl, uint16 skill_id, uint16 skill_lv) {
+ int time = skill_get_cast(skill_id, skill_lv);
+
+ nullpo_ret(bl);
+#ifndef RENEWAL_CAST
+ {
+ struct map_session_data *sd;
+
+ sd = BL_CAST(BL_PC, bl);
+
+ // calculate base cast time (reduced by dex)
+ if( !(skill_get_castnodex(skill_id, skill_lv)&1) ) {
+ int scale = battle_config.castrate_dex_scale - status_get_dex(bl);
+ if( scale > 0 ) // not instant cast
+ time = time * scale / battle_config.castrate_dex_scale;
+ else
+ return 0; // instant cast
+ }
+
+ // calculate cast time reduced by item/card bonuses
+ if( !(skill_get_castnodex(skill_id, skill_lv)&4) && sd )
+ {
+ int i;
+ if( sd->castrate != 100 )
+ time = time * sd->castrate / 100;
+ for( i = 0; i < ARRAYLENGTH(sd->skillcast) && sd->skillcast[i].id; i++ )
+ {
+ if( sd->skillcast[i].id == skill_id )
+ {
+ time+= time * sd->skillcast[i].val / 100;
+ break;
+ }
+ }
+ }
+
+ }
+#endif
+ // config cast time multiplier
+ if (battle_config.cast_rate != 100)
+ time = time * battle_config.cast_rate / 100;
+ // return final cast time
+ time = max(time, 0);
+
+// ShowInfo("Castime castfix = %d\n",time);
+ return time;
+}
+
+/*==========================================
+ * Does cast-time reductions based on sc data.
+ *------------------------------------------*/
+int skill_castfix_sc (struct block_list *bl, int time)
+{
+ struct status_change *sc = status_get_sc(bl);
+
+ if( time < 0 )
+ return 0;
+
+ if (sc && sc->count) {
+ if (sc->data[SC_SLOWCAST])
+ time += time * sc->data[SC_SLOWCAST]->val2 / 100;
+ if (sc->data[SC_PARALYSIS])
+ time += sc->data[SC_PARALYSIS]->val3;
+ if (sc->data[SC_SUFFRAGIUM]) {
+ time -= time * sc->data[SC_SUFFRAGIUM]->val2 / 100;
+ status_change_end(bl, SC_SUFFRAGIUM, INVALID_TIMER);
+ }
+ if (sc->data[SC_MEMORIZE]) {
+ time>>=1;
+ if ((--sc->data[SC_MEMORIZE]->val2) <= 0)
+ status_change_end(bl, SC_MEMORIZE, INVALID_TIMER);
+ }
+ if (sc->data[SC_POEMBRAGI])
+ time -= time * sc->data[SC_POEMBRAGI]->val2 / 100;
+ if (sc->data[SC_IZAYOI])
+ time -= time * 50 / 100;
+ }
+ time = max(time, 0);
+
+// ShowInfo("Castime castfix_sc = %d\n",time);
+ return time;
+}
+#ifdef RENEWAL_CAST
+int skill_vfcastfix (struct block_list *bl, double time, uint16 skill_id, uint16 skill_lv)
+{
+ struct status_change *sc = status_get_sc(bl);
+ struct map_session_data *sd = BL_CAST(BL_PC,bl);
+ int fixed = skill_get_fixed_cast(skill_id, skill_lv), fixcast_r = 0, varcast_r = 0, i = 0;
+
+ if( time < 0 )
+ return 0;
+
+ if( fixed == 0 ){
+ fixed = (int)time * 20 / 100; // fixed time
+ time = time * 80 / 100; // variable time
+ }else if( fixed < 0 ) // no fixed cast time
+ fixed = 0;
+
+ if(sd && !(skill_get_castnodex(skill_id, skill_lv)&4) ){ // Increases/Decreases fixed/variable cast time of a skill by item/card bonuses.
+ if( sd->bonus.varcastrate < 0 )
+ VARCAST_REDUCTION(sd->bonus.varcastrate);
+ for (i = 0; i < ARRAYLENGTH(sd->skillfixcast) && sd->skillfixcast[i].id; i++)
+ if (sd->skillfixcast[i].id == skill_id){ // bonus2 bSkillFixedCast
+ fixed += sd->skillfixcast[i].val;
+ break;
+ }
+ for( i = 0; i < ARRAYLENGTH(sd->skillvarcast) && sd->skillvarcast[i].id; i++ )
+ if( sd->skillvarcast[i].id == skill_id ){ // bonus2 bSkillVariableCast
+ time += sd->skillvarcast[i].val;
+ break;
+ }
+ for( i = 0; i < ARRAYLENGTH(sd->skillcast) && sd->skillcast[i].id; i++ )
+ if( sd->skillcast[i].id == skill_id ){ // bonus2 bVariableCastrate
+ if( (i=sd->skillcast[i].val) < 0)
+ VARCAST_REDUCTION(i);
+ break;
+ }
+ }
+
+ if (sc && sc->count && !(skill_get_castnodex(skill_id, skill_lv)&2) ) {
+ // All variable cast additive bonuses must come first
+ if (sc->data[SC_SLOWCAST])
+ VARCAST_REDUCTION(-sc->data[SC_SLOWCAST]->val2);
+
+ // Variable cast reduction bonuses
+ if (sc->data[SC_SUFFRAGIUM]) {
+ VARCAST_REDUCTION(sc->data[SC_SUFFRAGIUM]->val2);
+ status_change_end(bl, SC_SUFFRAGIUM, INVALID_TIMER);
+ }
+ if (sc->data[SC_MEMORIZE]) {
+ VARCAST_REDUCTION(50);
+ if ((--sc->data[SC_MEMORIZE]->val2) <= 0)
+ status_change_end(bl, SC_MEMORIZE, INVALID_TIMER);
+ }
+ if (sc->data[SC_POEMBRAGI])
+ VARCAST_REDUCTION(sc->data[SC_POEMBRAGI]->val2);
+ if (sc->data[SC_IZAYOI])
+ VARCAST_REDUCTION(50);
+ if (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 3 && (skill_get_ele(skill_id, skill_lv) == ELE_WATER))
+ VARCAST_REDUCTION(30); //Reduces 30% Variable Cast Time of Water spells.
+ // Fixed cast reduction bonuses
+ if( sc->data[SC__LAZINESS] )
+ fixcast_r = max(fixcast_r, sc->data[SC__LAZINESS]->val2);
+ if( sc->data[SC_SECRAMENT] )
+ fixcast_r = max(fixcast_r, sc->data[SC_SECRAMENT]->val2);
+ if( sd && ( skill_lv = pc_checkskill(sd, WL_RADIUS) ) && skill_id >= WL_WHITEIMPRISON && skill_id <= WL_FREEZE_SP )
+ fixcast_r = max(fixcast_r, 5 + skill_lv * 5);
+ // Fixed cast non percentage bonuses
+ if( sc->data[SC_MANDRAGORA] )
+ fixed += sc->data[SC_MANDRAGORA]->val1 * 1000 / 2;
+ if (sc->data[SC_IZAYOI] && (skill_id >= NJ_TOBIDOUGU && skill_id <= NJ_ISSEN))
+ fixed = 0;
+ if( sc->data[SC_GUST_OPTION] || sc->data[SC_BLAST_OPTION] || sc->data[SC_WILD_STORM_OPTION] )
+ fixed -= 1000;
+ }
+
+ if( sd && !(skill_get_castnodex(skill_id, skill_lv)&4) ){
+ VARCAST_REDUCTION( max(sd->bonus.varcastrate, 0) + max(i, 0) );
+ fixcast_r = max(fixcast_r, sd->bonus.fixcastrate) + min(sd->bonus.fixcastrate,0);
+ }
+
+ if( varcast_r < 0 ) // now compute overall factors
+ time = time * (1 - (float)varcast_r / 100);
+ if( !(skill_get_castnodex(skill_id, skill_lv)&1) )// reduction from status point
+ time = (1 - sqrt( ((float)(status_get_dex(bl)*2 + status_get_int(bl)) / battle_config.vcast_stat_scale) )) * time;
+ // underflow checking/capping
+ time = max(time, 0) + (1 - (float)min(fixcast_r, 100) / 100) * max(fixed,0);
+
+ return (int)time;
+}
+#endif
+
+/*==========================================
+ * Does delay reductions based on dex/agi, sc data, item bonuses, ...
+ *------------------------------------------*/
+int skill_delayfix (struct block_list *bl, uint16 skill_id, uint16 skill_lv)
+{
+ int delaynodex = skill_get_delaynodex(skill_id, skill_lv);
+ int time = skill_get_delay(skill_id, skill_lv);
+ struct map_session_data *sd;
+ struct status_change *sc = status_get_sc(bl);
+
+ nullpo_ret(bl);
+ sd = BL_CAST(BL_PC, bl);
+
+ if (skill_id == SA_ABRACADABRA || skill_id == WM_RANDOMIZESPELL)
+ return 0; //Will use picked skill's delay.
+
+ if (bl->type&battle_config.no_skill_delay)
+ return battle_config.min_skill_delay_limit;
+
+ if (time < 0)
+ time = -time + status_get_amotion(bl); // If set to <0, add to attack motion.
+
+ // Delay reductions
+ switch (skill_id) { //Monk combo skills have their delay reduced by agi/dex.
+ case MO_TRIPLEATTACK:
+ case MO_CHAINCOMBO:
+ case MO_COMBOFINISH:
+ case CH_TIGERFIST:
+ case CH_CHAINCRUSH:
+ case SR_DRAGONCOMBO:
+ case SR_FALLENEMPIRE:
+ time -= 4*status_get_agi(bl) - 2*status_get_dex(bl);
+ break;
+ case HP_BASILICA:
+ if( sc && !sc->data[SC_BASILICA] )
+ time = 0; // There is no Delay on Basilica creation, only on cancel
+ break;
+ default:
+ if (battle_config.delay_dependon_dex && !(delaynodex&1))
+ { // if skill delay is allowed to be reduced by dex
+ int scale = battle_config.castrate_dex_scale - status_get_dex(bl);
+ if (scale > 0)
+ time = time * scale / battle_config.castrate_dex_scale;
+ else //To be capped later to minimum.
+ time = 0;
+ }
+ if (battle_config.delay_dependon_agi && !(delaynodex&1))
+ { // if skill delay is allowed to be reduced by agi
+ int scale = battle_config.castrate_dex_scale - status_get_agi(bl);
+ if (scale > 0)
+ time = time * scale / battle_config.castrate_dex_scale;
+ else //To be capped later to minimum.
+ time = 0;
+ }
+ }
+
+ if ( sc && sc->data[SC_SPIRIT] )
+ {
+ switch (skill_id) {
+ case CR_SHIELDBOOMERANG:
+ if (sc->data[SC_SPIRIT]->val2 == SL_CRUSADER)
+ time /= 2;
+ break;
+ case AS_SONICBLOW:
+ if (!map_flag_gvg(bl->m) && !map[bl->m].flag.battleground && sc->data[SC_SPIRIT]->val2 == SL_ASSASIN)
+ time /= 2;
+ break;
+ }
+ }
+
+ if (!(delaynodex&2))
+ {
+ if (sc && sc->count) {
+ if (sc->data[SC_POEMBRAGI])
+ time -= time * sc->data[SC_POEMBRAGI]->val3 / 100;
+ if (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 3 && (skill_get_ele(skill_id, skill_lv) == ELE_WIND))
+ time /= 2; // After Delay of Wind element spells reduced by 50%.
+ }
+
+ }
+
+ if( !(delaynodex&4) && sd && sd->delayrate != 100 )
+ time = time * sd->delayrate / 100;
+
+ if (battle_config.delay_rate != 100)
+ time = time * battle_config.delay_rate / 100;
+
+ //min delay
+ time = max(time, status_get_amotion(bl)); // Delay can never be below amotion [Playtester]
+ time = max(time, battle_config.min_skill_delay_limit);
+
+// ShowInfo("Delay delayfix = %d\n",time);
+ return time;
+}
+
+/*=========================================
+ *
+ *-----------------------------------------*/
+struct square {
+ int val1[5];
+ int val2[5];
+};
+
+static void skill_brandishspear_first (struct square *tc, uint8 dir, int16 x, int16 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;
+ }
+
+}
+
+static void skill_brandishspear_dir (struct square* tc, uint8 dir, int are)
+{
+ int c;
+ nullpo_retv(tc);
+
+ for( c = 0; c < 5; c++ )
+ {
+ switch( dir )
+ {
+ case 0: tc->val2[c]+=are; break;
+ case 1: tc->val1[c]-=are; tc->val2[c]+=are; break;
+ case 2: tc->val1[c]-=are; break;
+ case 3: tc->val1[c]-=are; tc->val2[c]-=are; break;
+ case 4: tc->val2[c]-=are; break;
+ case 5: tc->val1[c]+=are; tc->val2[c]-=are; break;
+ case 6: tc->val1[c]+=are; break;
+ case 7: tc->val1[c]+=are; tc->val2[c]+=are; break;
+ }
+ }
+}
+
+void skill_brandishspear(struct block_list* src, struct block_list* bl, uint16 skill_id, uint16 skill_lv, unsigned int tick, int flag)
+{
+ int c,n=4;
+ uint8 dir = map_calc_dir(src,bl->x,bl->y);
+ struct square tc;
+ int x=bl->x,y=bl->y;
+ skill_brandishspear_first(&tc,dir,x,y);
+ skill_brandishspear_dir(&tc,dir,4);
+ skill_area_temp[1] = bl->id;
+
+ if(skill_lv > 9){
+ for(c=1;c<4;c++){
+ map_foreachincell(skill_area_sub,
+ bl->m,tc.val1[c],tc.val2[c],BL_CHAR,
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|n,
+ skill_castend_damage_id);
+ }
+ }
+ if(skill_lv > 6){
+ skill_brandishspear_dir(&tc,dir,-1);
+ n--;
+ }else{
+ skill_brandishspear_dir(&tc,dir,-2);
+ n-=2;
+ }
+
+ if(skill_lv > 3){
+ for(c=0;c<5;c++){
+ map_foreachincell(skill_area_sub,
+ bl->m,tc.val1[c],tc.val2[c],BL_CHAR,
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|n,
+ skill_castend_damage_id);
+ if(skill_lv > 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_foreachincell(skill_area_sub,
+ bl->m,tc.val1[c%5],tc.val2[c%5],BL_CHAR,
+ src,skill_id,skill_lv,tick, flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ }
+}
+
+/*==========================================
+ * Weapon Repair [Celest/DracoRPG]
+ *------------------------------------------*/
+void skill_repairweapon (struct map_session_data *sd, int idx) {
+ int material;
+ int materials[4] = { 1002, 998, 999, 756 };
+ struct item *item;
+ struct map_session_data *target_sd;
+
+ nullpo_retv(sd);
+
+ if ( !( target_sd = map_id2sd(sd->menuskill_val) ) ) //Failed....
+ return;
+
+ if( idx == 0xFFFF ) // No item selected ('Cancel' clicked)
+ return;
+ if( idx < 0 || idx >= MAX_INVENTORY )
+ return; //Invalid index??
+
+ item = &target_sd->status.inventory[idx];
+ if( item->nameid <= 0 || item->attribute == 0 )
+ return; //Again invalid item....
+
+ if( sd != target_sd && !battle_check_range(&sd->bl,&target_sd->bl, skill_get_range2(&sd->bl, sd->menuskill_id,sd->menuskill_val2) ) ){
+ clif_item_repaireffect(sd,idx,1);
+ return;
+ }
+
+ if ( target_sd->inventory_data[idx]->type == IT_WEAPON )
+ material = materials [ target_sd->inventory_data[idx]->wlv - 1 ]; // Lv1/2/3/4 weapons consume 1 Iron Ore/Iron/Steel/Rough Oridecon
+ else
+ material = materials [2]; // Armors consume 1 Steel
+ if ( pc_search_inventory(sd,material) < 0 ) {
+ clif_skill_fail(sd,sd->menuskill_id,USESKILL_FAIL_LEVEL,0);
+ return;
+ }
+
+ clif_skill_nodamage(&sd->bl,&target_sd->bl,sd->menuskill_id,1,1);
+
+ item->attribute = 0;/* clear broken state */
+
+ clif_equiplist(target_sd);
+
+ pc_delitem(sd,pc_search_inventory(sd,material),1,0,0,LOG_TYPE_CONSUME);
+
+ clif_item_repaireffect(sd,idx,0);
+
+ if( sd != target_sd )
+ clif_item_repaireffect(target_sd,idx,0);
+}
+
+/*==========================================
+ * Item Appraisal
+ *------------------------------------------*/
+void skill_identify (struct map_session_data *sd, int idx)
+{
+ int flag=1;
+
+ nullpo_retv(sd);
+
+ if(idx >= 0 && idx < MAX_INVENTORY) {
+ if(sd->status.inventory[idx].nameid > 0 && sd->status.inventory[idx].identify == 0 ){
+ flag=0;
+ sd->status.inventory[idx].identify=1;
+ }
+ }
+ clif_item_identified(sd,idx,flag);
+}
+
+/*==========================================
+ * Weapon Refine [Celest]
+ *------------------------------------------*/
+void skill_weaponrefine (struct map_session_data *sd, int idx)
+{
+ nullpo_retv(sd);
+
+ if (idx >= 0 && idx < MAX_INVENTORY)
+ {
+ int i = 0, ep = 0, per;
+ int material[5] = { 0, 1010, 1011, 984, 984 };
+ struct item *item;
+ struct item_data *ditem = sd->inventory_data[idx];
+ item = &sd->status.inventory[idx];
+
+ if(item->nameid > 0 && ditem->type == IT_WEAPON)
+ {
+ if( item->refine >= sd->menuskill_val
+ || item->refine >= 10 // if it's no longer refineable
+ || ditem->flag.no_refine // if the item isn't refinable
+ || (i = pc_search_inventory(sd, material [ditem->wlv])) < 0 )
+ {
+ clif_skill_fail(sd,sd->menuskill_id,USESKILL_FAIL_LEVEL,0);
+ return;
+ }
+
+ per = status_get_refine_chance(ditem->wlv, (int)item->refine);
+ per += (((signed int)sd->status.job_level)-50)/2; //Updated per the new kro descriptions. [Skotlex]
+
+ pc_delitem(sd, i, 1, 0, 0, LOG_TYPE_OTHER);
+ if (per > rnd() % 100) {
+ log_pick_pc(sd, LOG_TYPE_OTHER, -1, item);
+ item->refine++;
+ log_pick_pc(sd, LOG_TYPE_OTHER, 1, item);
+ if(item->equip) {
+ ep = item->equip;
+ pc_unequipitem(sd,idx,3);
+ }
+ clif_refine(sd->fd,0,idx,item->refine);
+ clif_delitem(sd,idx,1,3);
+ clif_additem(sd,idx,1,0);
+ if (ep)
+ pc_equipitem(sd,idx,ep);
+ clif_misceffect(&sd->bl,3);
+ if(item->refine == 10 &&
+ item->card[0] == CARD0_FORGE &&
+ (int)MakeDWord(item->card[2],item->card[3]) == sd->status.char_id)
+ { // Fame point system [DracoRPG]
+ switch(ditem->wlv){
+ case 1:
+ pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point
+ break;
+ case 2:
+ pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point
+ break;
+ case 3:
+ pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point
+ break;
+ }
+ }
+ } else {
+ item->refine = 0;
+ if(item->equip)
+ pc_unequipitem(sd,idx,3);
+ clif_refine(sd->fd,1,idx,item->refine);
+ pc_delitem(sd,idx,1,0,2, LOG_TYPE_OTHER);
+ clif_misceffect(&sd->bl,2);
+ clif_emotion(&sd->bl, E_OMG);
+ }
+ }
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_autospell (struct map_session_data *sd, uint16 skill_id)
+{
+ uint16 skill_lv;
+ int maxlv=1,lv;
+
+ nullpo_ret(sd);
+
+ skill_lv = sd->menuskill_val;
+ lv=pc_checkskill(sd,skill_id);
+
+ if(!skill_lv || !lv) return 0; // Player must learn the skill before doing auto-spell [Lance]
+
+ if(skill_id==MG_NAPALMBEAT) maxlv=3;
+ else if(skill_id==MG_COLDBOLT || skill_id==MG_FIREBOLT || skill_id==MG_LIGHTNINGBOLT){
+ if (sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_SAGE)
+ maxlv =10; //Soul Linker bonus. [Skotlex]
+ else if(skill_lv==2) maxlv=1;
+ else if(skill_lv==3) maxlv=2;
+ else if(skill_lv>=4) maxlv=3;
+ }
+ else if(skill_id==MG_SOULSTRIKE){
+ if(skill_lv==5) maxlv=1;
+ else if(skill_lv==6) maxlv=2;
+ else if(skill_lv>=7) maxlv=3;
+ }
+ else if(skill_id==MG_FIREBALL){
+ if(skill_lv==8) maxlv=1;
+ else if(skill_lv>=9) maxlv=2;
+ }
+ else if(skill_id==MG_FROSTDIVER) maxlv=1;
+ else return 0;
+
+ if(maxlv > lv)
+ maxlv = lv;
+
+ sc_start4(&sd->bl,SC_AUTOSPELL,100,skill_lv,skill_id,maxlv,0,
+ skill_get_time(SA_AUTOSPELL,skill_lv));
+ return 0;
+}
+
+/*==========================================
+ * Sitting skills functions.
+ *------------------------------------------*/
+static int skill_sit_count (struct block_list *bl, va_list ap)
+{
+ struct map_session_data *sd;
+ int type =va_arg(ap,int);
+ sd=(struct map_session_data*)bl;
+
+ if(!pc_issit(sd))
+ return 0;
+
+ if(type&1 && pc_checkskill(sd,RG_GANGSTER) > 0)
+ return 1;
+
+ if(type&2 && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0))
+ return 1;
+
+ return 0;
+}
+
+static int skill_sit_in (struct block_list *bl, va_list ap)
+{
+ struct map_session_data *sd;
+ int type =va_arg(ap,int);
+
+ sd=(struct map_session_data*)bl;
+
+ if(!pc_issit(sd))
+ return 0;
+
+ if(type&1 && pc_checkskill(sd,RG_GANGSTER) > 0)
+ sd->state.gangsterparadise=1;
+
+ if(type&2 && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0 ))
+ {
+ sd->state.rest=1;
+ status_calc_regen(bl, &sd->battle_status, &sd->regen);
+ status_calc_regen_rate(bl, &sd->regen, &sd->sc);
+ }
+
+ return 0;
+}
+
+static int skill_sit_out (struct block_list *bl, va_list ap)
+{
+ struct map_session_data *sd;
+ int type =va_arg(ap,int);
+ sd=(struct map_session_data*)bl;
+ if(sd->state.gangsterparadise && type&1)
+ sd->state.gangsterparadise=0;
+ if(sd->state.rest && type&2) {
+ sd->state.rest=0;
+ status_calc_regen(bl, &sd->battle_status, &sd->regen);
+ status_calc_regen_rate(bl, &sd->regen, &sd->sc);
+ }
+ return 0;
+}
+
+int skill_sit (struct map_session_data *sd, int type)
+{
+ int flag = 0;
+ int range = 0, lv;
+ nullpo_ret(sd);
+
+
+ if((lv = pc_checkskill(sd,RG_GANGSTER)) > 0) {
+ flag|=1;
+ range = skill_get_splash(RG_GANGSTER, lv);
+ }
+ if((lv = pc_checkskill(sd,TK_HPTIME)) > 0) {
+ flag|=2;
+ range = skill_get_splash(TK_HPTIME, lv);
+ }
+ else if ((lv = pc_checkskill(sd,TK_SPTIME)) > 0) {
+ flag|=2;
+ range = skill_get_splash(TK_SPTIME, lv);
+ }
+
+ if( type ) {
+ clif_status_load(&sd->bl,SI_SIT,1);
+ } else {
+ clif_status_load(&sd->bl,SI_SIT,0);
+ }
+
+ if (!flag) return 0;
+
+ if(type) {
+ if (map_foreachinrange(skill_sit_count,&sd->bl, range, BL_PC, flag) > 1)
+ map_foreachinrange(skill_sit_in,&sd->bl, range, BL_PC, flag);
+ } else {
+ if (map_foreachinrange(skill_sit_count,&sd->bl, range, BL_PC, flag) < 2)
+ map_foreachinrange(skill_sit_out,&sd->bl, range, BL_PC, flag);
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_frostjoke_scream (struct block_list *bl, va_list ap)
+{
+ struct block_list *src;
+ uint16 skill_id,skill_lv;
+ unsigned int tick;
+
+ nullpo_ret(bl);
+ nullpo_ret(src=va_arg(ap,struct block_list*));
+
+ skill_id=va_arg(ap,int);
+ skill_lv=va_arg(ap,int);
+ if(!skill_lv) return 0;
+ tick=va_arg(ap,unsigned int);
+
+ if (src == bl || status_isdead(bl))
+ return 0;
+ if (bl->type == BL_PC) {
+ struct map_session_data *sd = (struct map_session_data *)bl;
+ if ( sd && sd->sc.option&(OPTION_INVISIBLE|OPTION_MADOGEAR) )
+ return 0;//Frost Joke / Scream cannot target invisible or MADO Gear characters [Ind]
+ }
+ //It has been reported that Scream/Joke works the same regardless of woe-setting. [Skotlex]
+ if(battle_check_target(src,bl,BCT_ENEMY) > 0)
+ skill_additional_effect(src,bl,skill_id,skill_lv,BF_MISC,ATK_DEF,tick);
+ else if(battle_check_target(src,bl,BCT_PARTY) > 0 && rnd()%100 < 10)
+ skill_additional_effect(src,bl,skill_id,skill_lv,BF_MISC,ATK_DEF,tick);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static void skill_unitsetmapcell (struct skill_unit *src, uint16 skill_id, uint16 skill_lv, cell_t cell, bool flag)
+{
+ int range = skill_get_unit_range(skill_id,skill_lv);
+ int x,y;
+
+ for( y = src->bl.y - range; y <= src->bl.y + range; ++y )
+ for( x = src->bl.x - range; x <= src->bl.x + range; ++x )
+ map_setcell(src->bl.m, x, y, cell, flag);
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_attack_area (struct block_list *bl, va_list ap)
+{
+ struct block_list *src,*dsrc;
+ int atk_type,skill_id,skill_lv,flag,type;
+ unsigned int tick;
+
+ if(status_isdead(bl))
+ return 0;
+
+ atk_type = va_arg(ap,int);
+ src=va_arg(ap,struct block_list*);
+ dsrc=va_arg(ap,struct block_list*);
+ skill_id=va_arg(ap,int);
+ skill_lv=va_arg(ap,int);
+ tick=va_arg(ap,unsigned int);
+ flag=va_arg(ap,int);
+ type=va_arg(ap,int);
+
+
+ if (skill_area_temp[1] == bl->id) //This is the target of the skill, do a full attack and skip target checks.
+ return skill_attack(atk_type,src,dsrc,bl,skill_id,skill_lv,tick,flag);
+
+ if(battle_check_target(dsrc,bl,type) <= 0 ||
+ !status_check_skilluse(NULL, bl, skill_id, 2))
+ return 0;
+
+
+ switch (skill_id) {
+ case WZ_FROSTNOVA: //Skills that don't require the animation to be removed
+ case NPC_ACIDBREATH:
+ case NPC_DARKNESSBREATH:
+ case NPC_FIREBREATH:
+ case NPC_ICEBREATH:
+ case NPC_THUNDERBREATH:
+ return skill_attack(atk_type,src,dsrc,bl,skill_id,skill_lv,tick,flag);
+ default:
+ //Area-splash, disable skill animation.
+ return skill_attack(atk_type,src,dsrc,bl,skill_id,skill_lv,tick,flag|SD_ANIMATION);
+ }
+}
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_clear_group (struct block_list *bl, int flag)
+{
+ struct unit_data *ud = unit_bl2ud(bl);
+ struct skill_unit_group *group[MAX_SKILLUNITGROUP];
+ int i, count=0;
+
+ nullpo_ret(bl);
+ if (!ud) return 0;
+
+ //All groups to be deleted are first stored on an array since the array elements shift around when you delete them. [Skotlex]
+ for (i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i];i++)
+ {
+ switch (ud->skillunit[i]->skill_id) {
+ case SA_DELUGE:
+ case SA_VOLCANO:
+ case SA_VIOLENTGALE:
+ case SA_LANDPROTECTOR:
+ case NJ_SUITON:
+ case NJ_KAENSIN:
+ if (flag&1)
+ group[count++]= ud->skillunit[i];
+ break;
+ case SO_WARMER:
+ if( flag&8 )
+ group[count++]= ud->skillunit[i];
+ break;
+ case SC_BLOODYLUST:
+ if (flag & 32)
+ group[count++] = ud->skillunit[i];
+ break;
+ default:
+ if (flag&2 && skill_get_inf2(ud->skillunit[i]->skill_id)&INF2_TRAP)
+ group[count++]= ud->skillunit[i];
+ break;
+ }
+
+ }
+ for (i=0;i<count;i++)
+ skill_delunitgroup(group[i]);
+ return count;
+}
+
+/*==========================================
+ * Returns the first element field found [Skotlex]
+ *------------------------------------------*/
+struct skill_unit_group *skill_locate_element_field(struct block_list *bl)
+{
+ struct unit_data *ud = unit_bl2ud(bl);
+ int i;
+ nullpo_ret(bl);
+ if (!ud) return NULL;
+
+ for (i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i];i++) {
+ switch (ud->skillunit[i]->skill_id) {
+ case SA_DELUGE:
+ case SA_VOLCANO:
+ case SA_VIOLENTGALE:
+ case SA_LANDPROTECTOR:
+ case NJ_SUITON:
+ case SO_WARMER:
+ case SC_BLOODYLUST:
+ return ud->skillunit[i];
+ }
+ }
+ return NULL;
+}
+
+// for graffiti cleaner [Valaris]
+int skill_graffitiremover (struct block_list *bl, va_list ap)
+{
+ struct skill_unit *unit=NULL;
+
+ nullpo_ret(bl);
+ nullpo_ret(ap);
+
+ if(bl->type!=BL_SKILL || (unit=(struct skill_unit *)bl) == NULL)
+ return 0;
+
+ if((unit->group) && (unit->group->unit_id == UNT_GRAFFITI))
+ skill_delunit(unit);
+
+ return 0;
+}
+
+int skill_greed (struct block_list *bl, va_list ap)
+{
+ struct block_list *src;
+ struct map_session_data *sd=NULL;
+ struct flooritem_data *fitem=NULL;
+
+ nullpo_ret(bl);
+ nullpo_ret(src = va_arg(ap, struct block_list *));
+
+ if(src->type == BL_PC && (sd=(struct map_session_data *)src) && bl->type==BL_ITEM && (fitem=(struct flooritem_data *)bl))
+ pc_takeitem(sd, fitem);
+
+ return 0;
+}
+//For Ranger's Detonator [Jobbie/3CeAM]
+int skill_detonator(struct block_list *bl, va_list ap)
+{
+ struct skill_unit *unit=NULL;
+ struct block_list *src;
+ int unit_id;
+
+ nullpo_ret(bl);
+ nullpo_ret(ap);
+ src = va_arg(ap,struct block_list *);
+
+ if( bl->type != BL_SKILL || (unit = (struct skill_unit *)bl) == NULL || !unit->group )
+ return 0;
+ if( unit->group->src_id != src->id )
+ return 0;
+
+ unit_id = unit->group->unit_id;
+ switch( unit_id )
+ { //List of Hunter and Ranger Traps that can be detonate.
+ case UNT_BLASTMINE:
+ case UNT_SANDMAN:
+ case UNT_CLAYMORETRAP:
+ case UNT_TALKIEBOX:
+ case UNT_CLUSTERBOMB:
+ case UNT_FIRINGTRAP:
+ case UNT_ICEBOUNDTRAP:
+ if( unit_id == UNT_TALKIEBOX )
+ {
+ clif_talkiebox(bl,unit->group->valstr);
+ unit->group->val2 = -1;
+ }
+ else
+ map_foreachinrange(skill_trap_splash,bl,skill_get_splash(unit->group->skill_id,unit->group->skill_lv),unit->group->bl_flag,bl,unit->group->tick);
+
+ clif_changetraplook(bl,unit_id == UNT_FIRINGTRAP ? UNT_DUMMYSKILL : UNT_USED_TRAPS);
+ unit->group->unit_id = UNT_USED_TRAPS;
+ unit->group->limit = DIFF_TICK(gettick(),unit->group->tick) +
+ (unit_id == UNT_TALKIEBOX ? 5000 : (unit_id == UNT_CLUSTERBOMB || unit_id == UNT_ICEBOUNDTRAP? 2500 : 1500) );
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static int skill_cell_overlap(struct block_list *bl, va_list ap)
+{
+ uint16 skill_id;
+ int *alive;
+ struct skill_unit *unit;
+
+ skill_id = va_arg(ap,int);
+ alive = va_arg(ap,int *);
+ unit = (struct skill_unit *)bl;
+
+ if (unit == NULL || unit->group == NULL || (*alive) == 0)
+ return 0;
+
+ switch (skill_id) {
+ case SA_LANDPROTECTOR:
+ if( unit->group->skill_id == SA_LANDPROTECTOR ) {//Check for offensive Land Protector to delete both. [Skotlex]
+ (*alive) = 0;
+ skill_delunit(unit);
+ return 1;
+ }
+ if( !(skill_get_inf2(unit->group->skill_id)&(INF2_SONG_DANCE|INF2_TRAP)) ) { //It deletes everything except songs/dances and traps
+ skill_delunit(unit);
+ return 1;
+ }
+ break;
+ case HW_GANBANTEIN:
+ case LG_EARTHDRIVE:
+ if( !(unit->group->state.song_dance&0x1) ) {// Don't touch song/dance.
+ skill_delunit(unit);
+ return 1;
+ }
+ break;
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+// The official implementation makes them fail to appear when casted on top of ANYTHING
+// but I wonder if they didn't actually meant to fail when casted on top of each other?
+// hence, I leave the alternate implementation here, commented. [Skotlex]
+ if (unit->range <= 0)
+ {
+ (*alive) = 0;
+ return 1;
+ }
+/*
+ switch (unit->group->skill_id)
+ { //These cannot override each other.
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+ (*alive) = 0;
+ return 1;
+ }
+*/
+ break;
+ case PF_FOGWALL:
+ switch(unit->group->skill_id) {
+ case SA_VOLCANO: //Can't be placed on top of these
+ case SA_VIOLENTGALE:
+ (*alive) = 0;
+ return 1;
+ case SA_DELUGE:
+ case NJ_SUITON:
+ //Cheap 'hack' to notify the calling function that duration should be doubled [Skotlex]
+ (*alive) = 2;
+ break;
+ }
+ break;
+ case HP_BASILICA:
+ if (unit->group->skill_id == HP_BASILICA)
+ { //Basilica can't be placed on top of itself to avoid map-cell stacking problems. [Skotlex]
+ (*alive) = 0;
+ return 1;
+ }
+ break;
+ case GN_CRAZYWEED_ATK:
+ switch(unit->group->unit_id){ //TODO: look for other ground skills that are affected.
+ case UNT_WALLOFTHORN:
+ case UNT_THORNS_TRAP:
+ case UNT_BLOODYLUST:
+ case UNT_CHAOSPANIC:
+ case UNT_MAELSTROM:
+ case UNT_FIREPILLAR_ACTIVE:
+ case UNT_LANDPROTECTOR:
+ case UNT_VOLCANO:
+ case UNT_DELUGE:
+ case UNT_VIOLENTGALE:
+ case UNT_SAFETYWALL:
+ case UNT_PNEUMA:
+ skill_delunit(unit);
+ return 1;
+ }
+ break;
+ }
+
+ if (unit->group->skill_id == SA_LANDPROTECTOR && !(skill_get_inf2(skill_id)&(INF2_SONG_DANCE|INF2_TRAP))) { //It deletes everything except songs/dances/traps
+ (*alive) = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_chastle_mob_changetarget(struct block_list *bl,va_list ap)
+{
+ struct mob_data* md;
+ struct unit_data*ud = unit_bl2ud(bl);
+ struct block_list *from_bl;
+ struct block_list *to_bl;
+ md = (struct mob_data*)bl;
+ from_bl = va_arg(ap,struct block_list *);
+ to_bl = va_arg(ap,struct block_list *);
+
+ if(ud && ud->target == from_bl->id)
+ ud->target = to_bl->id;
+
+ if(md->bl.type == BL_MOB && md->target_id == from_bl->id)
+ md->target_id = to_bl->id;
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+static int skill_trap_splash (struct block_list *bl, va_list ap)
+{
+ struct block_list *src;
+ int tick;
+ struct skill_unit *unit;
+ struct skill_unit_group *sg;
+ struct block_list *ss;
+ src = va_arg(ap,struct block_list *);
+ unit = (struct skill_unit *)src;
+ tick = va_arg(ap,int);
+
+ if( !unit->alive || bl->prev == NULL )
+ return 0;
+
+ nullpo_ret(sg = unit->group);
+ nullpo_ret(ss = map_id2bl(sg->src_id));
+
+ if(battle_check_target(src,bl,sg->target_flag) <= 0)
+ return 0;
+
+ switch(sg->unit_id){
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ skill_additional_effect(ss,bl,sg->skill_id,sg->skill_lv,BF_MISC,ATK_DEF,tick);
+ break;
+ case UNT_GROUNDDRIFT_WIND:
+ if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1))
+ sc_start(bl,SC_STUN,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ case UNT_GROUNDDRIFT_DARK:
+ if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1))
+ sc_start(bl,SC_BLIND,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ case UNT_GROUNDDRIFT_POISON:
+ if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1))
+ sc_start(bl,SC_POISON,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ case UNT_GROUNDDRIFT_WATER:
+ if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1))
+ sc_start(bl,SC_FREEZE,5,sg->skill_lv,skill_get_time2(sg->skill_id, sg->skill_lv));
+ break;
+ case UNT_GROUNDDRIFT_FIRE:
+ if(skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1))
+ skill_blown(src,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv),-1,0);
+ break;
+ case UNT_ELECTRICSHOCKER:
+ clif_skill_damage(src,bl,tick,0,0,-30000,1,sg->skill_id,sg->skill_lv,5);
+ break;
+ case UNT_FIRINGTRAP:
+ case UNT_ICEBOUNDTRAP:
+ case UNT_CLUSTERBOMB:
+ if( ss != bl )
+ skill_attack(BF_MISC,ss,src,bl,sg->skill_id,sg->skill_lv,tick,sg->val1|SD_LEVEL);
+ break;
+ case UNT_MAGENTATRAP:
+ case UNT_COBALTTRAP:
+ case UNT_MAIZETRAP:
+ case UNT_VERDURETRAP:
+ if( bl->type != BL_PC && !is_boss(bl) )
+ sc_start2(bl,SC_ELEMENTALCHANGE,100,sg->skill_lv,skill_get_ele(sg->skill_id,sg->skill_lv),skill_get_time2(sg->skill_id,sg->skill_lv));
+ break;
+ case UNT_REVERBERATION:
+ skill_addtimerskill(ss,tick+50,bl->id,0,0,WM_REVERBERATION_MELEE,sg->skill_lv,BF_WEAPON,0); // for proper skill delay animation when use with Dominion Impulse
+ skill_addtimerskill(ss,tick+250,bl->id,0,0,WM_REVERBERATION_MAGIC,sg->skill_lv,BF_MAGIC,0);
+ break;
+ default:
+ skill_attack(skill_get_type(sg->skill_id),ss,src,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+ }
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_enchant_elemental_end (struct block_list *bl, int type)
+{
+ struct status_change *sc;
+ const enum sc_type scs[] = { SC_ENCPOISON, SC_ASPERSIO, SC_FIREWEAPON, SC_WATERWEAPON, SC_WINDWEAPON, SC_EARTHWEAPON, SC_SHADOWWEAPON, SC_GHOSTWEAPON, SC_ENCHANTARMS, SC_EXEEDBREAK };
+ int i;
+ nullpo_ret(bl);
+ nullpo_ret(sc= status_get_sc(bl));
+
+ if (!sc->count) return 0;
+
+ for (i = 0; i < ARRAYLENGTH(scs); i++)
+ if (type != scs[i] && sc->data[scs[i]])
+ status_change_end(bl, scs[i], INVALID_TIMER);
+
+ return 0;
+}
+
+bool skill_check_cloaking(struct block_list *bl, struct status_change_entry *sce)
+{
+ static int dx[] = { 0, 1, 0, -1, -1, 1, 1, -1};
+ static int dy[] = {-1, 0, 1, 0, -1, -1, 1, 1};
+ bool wall = true;
+
+ if( (bl->type == BL_PC && battle_config.pc_cloak_check_type&1)
+ || (bl->type != BL_PC && battle_config.monster_cloak_check_type&1) )
+ { //Check for walls.
+ int i;
+ ARR_FIND( 0, 8, i, map_getcell(bl->m, bl->x+dx[i], bl->y+dy[i], CELL_CHKNOPASS) != 0 );
+ if( i == 8 )
+ wall = false;
+ }
+
+ if( sce )
+ {
+ if( !wall )
+ {
+ if( sce->val1 < 3 ) //End cloaking.
+ status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
+ else
+ if( sce->val4&1 )
+ { //Remove wall bonus
+ sce->val4&=~1;
+ status_calc_bl(bl,SCB_SPEED);
+ }
+ }
+ else
+ {
+ if( !(sce->val4&1) )
+ { //Add wall speed bonus
+ sce->val4|=1;
+ status_calc_bl(bl,SCB_SPEED);
+ }
+ }
+ }
+
+ return wall;
+}
+bool skill_check_camouflage(struct block_list *bl, struct status_change_entry *sce)
+{
+ static int dx[] = { 0, 1, 0, -1, -1, 1, 1, -1};
+ static int dy[] = {-1, 0, 1, 0, -1, -1, 1, 1};
+ bool wall = true;
+
+ if( bl->type == BL_PC )
+ { //Check for walls.
+ int i;
+ ARR_FIND( 0, 8, i, map_getcell(bl->m, bl->x+dx[i], bl->y+dy[i], CELL_CHKNOPASS) != 0 );
+ if( i == 8 )
+ wall = false;
+ }
+
+ if( sce )
+ {
+ if( !wall )
+ {
+ if( sce->val1 < 3 ) //End camouflage.
+ status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER);
+ else
+ if( sce->val3&1 )
+ { //Remove wall bonus
+ sce->val3&=~1;
+ status_calc_bl(bl,SCB_SPEED);
+ }
+ }
+ }
+
+ return wall;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int x, int y, int val1, int val2)
+{
+ struct skill_unit *unit;
+
+ nullpo_retr(NULL, group);
+ nullpo_retr(NULL, group->unit); // crash-protection against poor coding
+ nullpo_retr(NULL, unit=&group->unit[idx]);
+
+ if(!unit->alive)
+ group->alive_count++;
+
+ unit->bl.id=map_get_new_object_id();
+ unit->bl.type=BL_SKILL;
+ unit->bl.m=group->map;
+ unit->bl.x=x;
+ unit->bl.y=y;
+ unit->group=group;
+ unit->alive=1;
+ unit->val1=val1;
+ unit->val2=val2;
+
+ idb_put(skillunit_db, unit->bl.id, unit);
+ map_addiddb(&unit->bl);
+ map_addblock(&unit->bl);
+
+ // perform oninit actions
+ switch (group->skill_id) {
+ case WZ_ICEWALL:
+ map_setgatcell(unit->bl.m,unit->bl.x,unit->bl.y,5);
+ clif_changemapcell(0,unit->bl.m,unit->bl.x,unit->bl.y,5,AREA);
+ skill_unitsetmapcell(unit,WZ_ICEWALL,group->skill_lv,CELL_ICEWALL,true);
+ map[unit->bl.m].icewall_num++;
+ break;
+ case SA_LANDPROTECTOR:
+ skill_unitsetmapcell(unit,SA_LANDPROTECTOR,group->skill_lv,CELL_LANDPROTECTOR,true);
+ break;
+ case HP_BASILICA:
+ skill_unitsetmapcell(unit,HP_BASILICA,group->skill_lv,CELL_BASILICA,true);
+ break;
+ case SC_MAELSTROM:
+ skill_unitsetmapcell(unit,SC_MAELSTROM,group->skill_lv,CELL_MAELSTROM,true);
+ break;
+ default:
+ if (group->state.song_dance&0x1) //Check for dissonance.
+ skill_dance_overlap(unit, 1);
+ break;
+ }
+
+ clif_skill_setunit(unit);
+
+ return unit;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_delunit (struct skill_unit* unit)
+{
+ struct skill_unit_group *group;
+
+ nullpo_ret(unit);
+ if( !unit->alive )
+ return 0;
+ unit->alive=0;
+
+ nullpo_ret(group=unit->group);
+
+ if( group->state.song_dance&0x1 ) //Cancel dissonance effect.
+ skill_dance_overlap(unit, 0);
+
+ // invoke onout event
+ if( !unit->range )
+ map_foreachincell(skill_unit_effect,unit->bl.m,unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),4);
+
+ // perform ondelete actions
+ switch (group->skill_id) {
+ case HT_ANKLESNARE: {
+ struct block_list* target = map_id2bl(group->val2);
+ if( target )
+ status_change_end(target, SC_ANKLE, INVALID_TIMER);
+ }
+ break;
+ case WZ_ICEWALL:
+ map_setgatcell(unit->bl.m,unit->bl.x,unit->bl.y,unit->val2);
+ clif_changemapcell(0,unit->bl.m,unit->bl.x,unit->bl.y,unit->val2,ALL_SAMEMAP); // hack to avoid clientside cell bug
+ skill_unitsetmapcell(unit,WZ_ICEWALL,group->skill_lv,CELL_ICEWALL,false);
+ map[unit->bl.m].icewall_num--;
+ break;
+ case SA_LANDPROTECTOR:
+ skill_unitsetmapcell(unit,SA_LANDPROTECTOR,group->skill_lv,CELL_LANDPROTECTOR,false);
+ break;
+ case HP_BASILICA:
+ skill_unitsetmapcell(unit,HP_BASILICA,group->skill_lv,CELL_BASILICA,false);
+ break;
+ case RA_ELECTRICSHOCKER: {
+ struct block_list* target = map_id2bl(group->val2);
+ if( target )
+ status_change_end(target, SC_ELECTRICSHOCKER, INVALID_TIMER);
+ }
+ break;
+ case SC_MAELSTROM:
+ skill_unitsetmapcell(unit,SC_MAELSTROM,group->skill_lv,CELL_MAELSTROM,false);
+ break;
+ case SC_MANHOLE: // Note : Removing the unit don't remove the status (official info)
+ if( group->val2 ) { // Someone Traped
+ struct status_change *tsc = status_get_sc( map_id2bl(group->val2));
+ if( tsc && tsc->data[SC__MANHOLE] )
+ tsc->data[SC__MANHOLE]->val4 = 0; // Remove the Unit ID
+ }
+ break;
+ }
+
+ clif_skill_delunit(unit);
+
+ unit->group=NULL;
+ map_delblock(&unit->bl); // don't free yet
+ map_deliddb(&unit->bl);
+ idb_remove(skillunit_db, unit->bl.id);
+ if(--group->alive_count==0)
+ skill_delunitgroup(group);
+
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------*/
+static DBMap* group_db = NULL;// int group_id -> struct skill_unit_group*
+
+/// Returns the target skill_unit_group or NULL if not found.
+struct skill_unit_group* skill_id2group(int group_id)
+{
+ return (struct skill_unit_group*)idb_get(group_db, group_id);
+}
+
+
+static int skill_unit_group_newid = MAX_SKILL_DB;
+
+/// Returns a new group_id that isn't being used in group_db.
+/// Fatal error if nothing is available.
+static int skill_get_new_group_id(void)
+{
+ if( skill_unit_group_newid >= MAX_SKILL_DB && skill_id2group(skill_unit_group_newid) == NULL )
+ return skill_unit_group_newid++;// available
+ {// find next id
+ int base_id = skill_unit_group_newid;
+ while( base_id != ++skill_unit_group_newid )
+ {
+ if( skill_unit_group_newid < MAX_SKILL_DB )
+ skill_unit_group_newid = MAX_SKILL_DB;
+ if( skill_id2group(skill_unit_group_newid) == NULL )
+ return skill_unit_group_newid++;// available
+ }
+ // full loop, nothing available
+ ShowFatalError("skill_get_new_group_id: All ids are taken. Exiting...");
+ exit(1);
+ }
+}
+
+struct skill_unit_group* skill_initunitgroup (struct block_list* src, int count, uint16 skill_id, uint16 skill_lv, int unit_id, int limit, int interval)
+{
+ struct unit_data* ud = unit_bl2ud( src );
+ struct skill_unit_group* group;
+ int i;
+
+ if(!(skill_id && skill_lv)) return 0;
+
+ nullpo_retr(NULL, src);
+ nullpo_retr(NULL, ud);
+
+ // find a free spot to store the new unit group
+ ARR_FIND( 0, MAX_SKILLUNITGROUP, i, ud->skillunit[i] == NULL );
+ if(i == MAX_SKILLUNITGROUP)
+ {
+ // array is full, make room by discarding oldest group
+ int j=0;
+ unsigned maxdiff=0,x,tick=gettick();
+ for(i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[i];i++)
+ if((x=DIFF_TICK(tick,ud->skillunit[i]->tick))>maxdiff){
+ maxdiff=x;
+ j=i;
+ }
+ skill_delunitgroup(ud->skillunit[j]);
+ //Since elements must have shifted, we use the last slot.
+ i = MAX_SKILLUNITGROUP-1;
+ }
+
+ group = ers_alloc(skill_unit_ers, struct skill_unit_group);
+ group->src_id = src->id;
+ group->party_id = status_get_party_id(src);
+ group->guild_id = status_get_guild_id(src);
+ group->bg_id = bg_team_get_id(src);
+ group->group_id = skill_get_new_group_id();
+ group->unit = (struct skill_unit *)aCalloc(count,sizeof(struct skill_unit));
+ group->unit_count = count;
+ group->alive_count = 0;
+ group->val1 = 0;
+ group->val2 = 0;
+ group->val3 = 0;
+ group->skill_id = skill_id;
+ group->skill_lv = skill_lv;
+ group->unit_id = unit_id;
+ group->map = src->m;
+ group->limit = limit;
+ group->interval = interval;
+ group->tick = gettick();
+ group->valstr = NULL;
+
+ ud->skillunit[i] = group;
+
+ if (skill_id == PR_SANCTUARY) //Sanctuary starts healing +1500ms after casted. [Skotlex]
+ group->tick += 1500;
+
+ idb_put(group_db, group->group_id, group);
+ return group;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_delunitgroup_(struct skill_unit_group *group, const char* file, int line, const char* func)
+{
+ struct block_list* src;
+ struct unit_data *ud;
+ int i,j;
+
+ if( group == NULL )
+ {
+ ShowDebug("skill_delunitgroup: group is NULL (source=%s:%d, %s)! Please report this! (#3504)\n", file, line, func);
+ return 0;
+ }
+
+ src=map_id2bl(group->src_id);
+ ud = unit_bl2ud(src);
+ if(!src || !ud) {
+ ShowError("skill_delunitgroup: Group's source not found! (src_id: %d skill_id: %d)\n", group->src_id, group->skill_id);
+ return 0;
+ }
+
+ if( !status_isdead(src) && ((TBL_PC*)src)->state.warping && !((TBL_PC*)src)->state.changemap ) {
+ switch( group->skill_id ) {
+ 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:
+ skill_usave_add(((TBL_PC*)src), group->skill_id, group->skill_lv);
+ break;
+ }
+ }
+
+ if (skill_get_unit_flag(group->skill_id)&(UF_DANCE|UF_SONG|UF_ENSEMBLE))
+ {
+ struct status_change* sc = status_get_sc(src);
+ if (sc && sc->data[SC_DANCING])
+ {
+ sc->data[SC_DANCING]->val2 = 0 ; //This prevents status_change_end attempting to redelete the group. [Skotlex]
+ status_change_end(src, SC_DANCING, INVALID_TIMER);
+ }
+ }
+
+ // end Gospel's status change on 'src'
+ // (needs to be done when the group is deleted by other means than skill deactivation)
+ if (group->unit_id == UNT_GOSPEL) {
+ struct status_change *sc = status_get_sc(src);
+ if(sc && sc->data[SC_GOSPEL]) {
+ sc->data[SC_GOSPEL]->val3 = 0; //Remove reference to this group. [Skotlex]
+ status_change_end(src, SC_GOSPEL, INVALID_TIMER);
+ }
+ }
+
+ switch( group->skill_id ) {
+ case SG_SUN_WARM:
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ {
+ struct status_change *sc = NULL;
+ if( (sc = status_get_sc(src)) != NULL && sc->data[SC_WARM] ) {
+ sc->data[SC_WARM]->val4 = 0;
+ status_change_end(src, SC_WARM, INVALID_TIMER);
+ }
+ }
+ break;
+ case NC_NEUTRALBARRIER:
+ {
+ struct status_change *sc = NULL;
+ if( (sc = status_get_sc(src)) != NULL && sc->data[SC_NEUTRALBARRIER_MASTER] ) {
+ sc->data[SC_NEUTRALBARRIER_MASTER]->val2 = 0;
+ status_change_end(src,SC_NEUTRALBARRIER_MASTER,INVALID_TIMER);
+ }
+ }
+ break;
+ case NC_STEALTHFIELD:
+ {
+ struct status_change *sc = NULL;
+ if( (sc = status_get_sc(src)) != NULL && sc->data[SC_STEALTHFIELD_MASTER] ) {
+ sc->data[SC_STEALTHFIELD_MASTER]->val2 = 0;
+ status_change_end(src,SC_STEALTHFIELD_MASTER,INVALID_TIMER);
+ }
+ }
+ break;
+ case LG_BANDING:
+ {
+ struct status_change *sc = NULL;
+ if( (sc = status_get_sc(src)) && sc->data[SC_BANDING] ) {
+ sc->data[SC_BANDING]->val4 = 0;
+ status_change_end(src,SC_BANDING,INVALID_TIMER);
+ }
+ }
+ break;
+ }
+
+ if (src->type==BL_PC && group->state.ammo_consume)
+ battle_consume_ammo((TBL_PC*)src, group->skill_id, group->skill_lv);
+
+ group->alive_count=0;
+
+ // remove all unit cells
+ if(group->unit != NULL)
+ for( i = 0; i < group->unit_count; i++ )
+ skill_delunit(&group->unit[i]);
+
+ // clear Talkie-box string
+ if( group->valstr != NULL )
+ {
+ aFree(group->valstr);
+ group->valstr = NULL;
+ }
+
+ idb_remove(group_db, group->group_id);
+ map_freeblock(&group->unit->bl); // schedules deallocation of whole array (HACK)
+ group->unit=NULL;
+ group->group_id=0;
+ group->unit_count=0;
+
+ // locate this group, swap with the last entry and delete it
+ ARR_FIND( 0, MAX_SKILLUNITGROUP, i, ud->skillunit[i] == group );
+ ARR_FIND( i, MAX_SKILLUNITGROUP, j, ud->skillunit[j] == NULL ); j--;
+ if( i < MAX_SKILLUNITGROUP )
+ {
+ ud->skillunit[i] = ud->skillunit[j];
+ ud->skillunit[j] = NULL;
+ ers_free(skill_unit_ers, group);
+ }
+ else
+ ShowError("skill_delunitgroup: Group not found! (src_id: %d skill_id: %d)\n", group->src_id, group->skill_id);
+
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_clear_unitgroup (struct block_list *src)
+{
+ struct unit_data *ud = unit_bl2ud(src);
+
+ nullpo_ret(ud);
+
+ while (ud->skillunit[0])
+ skill_delunitgroup(ud->skillunit[0]);
+
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+struct skill_unit_group_tickset *skill_unitgrouptickset_search (struct block_list *bl, struct skill_unit_group *group, int tick)
+{
+ int i,j=-1,k,s,id;
+ struct unit_data *ud;
+ struct skill_unit_group_tickset *set;
+
+ nullpo_ret(bl);
+ if (group->interval==-1)
+ return NULL;
+
+ ud = unit_bl2ud(bl);
+ if (!ud) return NULL;
+
+ set = ud->skillunittick;
+
+ if (skill_get_unit_flag(group->skill_id)&UF_NOOVERLAP)
+ id = s = group->skill_id;
+ else
+ id = s = group->group_id;
+
+ for (i=0; i<MAX_SKILLUNITGROUPTICKSET; i++) {
+ k = (i+s) % MAX_SKILLUNITGROUPTICKSET;
+ if (set[k].id == id)
+ return &set[k];
+ else if (j==-1 && (DIFF_TICK(tick,set[k].tick)>0 || set[k].id==0))
+ j=k;
+ }
+
+ if (j == -1) {
+ ShowWarning ("skill_unitgrouptickset_search: tickset is full\n");
+ j = id % MAX_SKILLUNITGROUPTICKSET;
+ }
+
+ set[j].id = id;
+ set[j].tick = tick;
+ return &set[j];
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_unit_timer_sub_onplace (struct block_list* bl, va_list ap)
+{
+ struct skill_unit* unit = va_arg(ap,struct skill_unit *);
+ struct skill_unit_group* group = unit->group;
+ unsigned int tick = va_arg(ap,unsigned int);
+
+ if( !unit->alive || bl->prev == NULL )
+ return 0;
+
+ nullpo_ret(group);
+
+ if( !(skill_get_inf2(group->skill_id)&(INF2_SONG_DANCE|INF2_TRAP|INF2_NOLP)) && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR) )
+ return 0; //AoE skills are ineffective. [Skotlex]
+
+ if( battle_check_target(&unit->bl,bl,group->target_flag) <= 0 )
+ return 0;
+
+ skill_unit_onplace_timer(unit,bl,tick);
+
+ return 1;
+}
+
+/**
+ * @see DBApply
+ */
+static int skill_unit_timer_sub(DBKey key, DBData *data, va_list ap)
+{
+ struct skill_unit* unit = db_data2ptr(data);
+ struct skill_unit_group* group = unit->group;
+ unsigned int tick = va_arg(ap,unsigned int);
+ bool dissonance;
+ struct block_list* bl = &unit->bl;
+
+ if( !unit->alive )
+ return 0;
+
+ nullpo_ret(group);
+
+ // check for expiration
+ if( !group->state.guildaura && (DIFF_TICK(tick,group->tick) >= group->limit || DIFF_TICK(tick,group->tick) >= unit->limit) )
+ {// skill unit expired (inlined from skill_unit_onlimit())
+ switch( group->unit_id )
+ {
+ case UNT_BLASTMINE:
+#ifdef RENEWAL
+ case UNT_CLAYMORETRAP:
+#endif
+ case UNT_GROUNDDRIFT_WIND:
+ case UNT_GROUNDDRIFT_DARK:
+ case UNT_GROUNDDRIFT_POISON:
+ case UNT_GROUNDDRIFT_WATER:
+ case UNT_GROUNDDRIFT_FIRE:
+ group->unit_id = UNT_USED_TRAPS;
+ //clif_changetraplook(bl, UNT_FIREPILLAR_ACTIVE);
+ group->limit=DIFF_TICK(tick+1500,group->tick);
+ unit->limit=DIFF_TICK(tick+1500,group->tick);
+ break;
+
+ case UNT_ANKLESNARE:
+ case UNT_ELECTRICSHOCKER:
+ if( group->val2 > 0 ) {
+ // Used Trap don't returns back to item
+ skill_delunit(unit);
+ break;
+ }
+ case UNT_SKIDTRAP:
+ case UNT_LANDMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_FREEZINGTRAP:
+#ifndef RENEWAL
+ case UNT_CLAYMORETRAP:
+#endif
+ case UNT_TALKIEBOX:
+ case UNT_CLUSTERBOMB:
+ case UNT_MAGENTATRAP:
+ case UNT_COBALTTRAP:
+ case UNT_MAIZETRAP:
+ case UNT_VERDURETRAP:
+ case UNT_FIRINGTRAP:
+ case UNT_ICEBOUNDTRAP:
+
+ {
+ struct block_list* src;
+ if( unit->val1 > 0 && (src = map_id2bl(group->src_id)) != NULL && src->type == BL_PC )
+ { // revert unit back into a trap
+ struct item item_tmp;
+ memset(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid = group->item_id?group->item_id:ITEMID_TRAP;
+ item_tmp.identify = 1;
+ map_addflooritem(&item_tmp,1,bl->m,bl->x,bl->y,0,0,0,0);
+ }
+ skill_delunit(unit);
+ }
+ break;
+
+ case UNT_WARP_ACTIVE:
+ // warp portal opens (morph to a UNT_WARP_WAITING cell)
+ group->unit_id = skill_get_unit_id(group->skill_id, 1); // UNT_WARP_WAITING
+ clif_changelook(&unit->bl, LOOK_BASE, group->unit_id);
+ // restart timers
+ group->limit = skill_get_time(group->skill_id,group->skill_lv);
+ unit->limit = skill_get_time(group->skill_id,group->skill_lv);
+ // apply effect to all units standing on it
+ map_foreachincell(skill_unit_effect,unit->bl.m,unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),1);
+ break;
+
+ case UNT_CALLFAMILY:
+ {
+ struct map_session_data *sd = NULL;
+ if(group->val1) {
+ sd = map_charid2sd(group->val1);
+ group->val1 = 0;
+ if (sd && !map[sd->bl.m].flag.nowarp)
+ pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,CLR_TELEPORT);
+ }
+ if(group->val2) {
+ sd = map_charid2sd(group->val2);
+ group->val2 = 0;
+ if (sd && !map[sd->bl.m].flag.nowarp)
+ pc_setpos(sd,map_id2index(unit->bl.m),unit->bl.x,unit->bl.y,CLR_TELEPORT);
+ }
+ skill_delunit(unit);
+ }
+ break;
+
+ case UNT_REVERBERATION:
+ if( unit->val1 <= 0 ) { // If it was deactivated.
+ skill_delunit(unit);
+ break;
+ }
+ clif_changetraplook(bl,UNT_USED_TRAPS);
+ map_foreachinrange(skill_trap_splash, bl, skill_get_splash(group->skill_id, group->skill_lv), group->bl_flag, bl, tick);
+ group->limit = DIFF_TICK(tick,group->tick)+1000;
+ unit->limit = DIFF_TICK(tick,group->tick)+1000;
+ group->unit_id = UNT_USED_TRAPS;
+ break;
+
+ case UNT_FEINTBOMB: {
+ struct block_list *src = map_id2bl(group->src_id);
+ if( src )
+ map_foreachinrange(skill_area_sub, &group->unit->bl, unit->range, splash_target(src), src, SC_FEINTBOMB, group->skill_lv, tick, BCT_ENEMY|SD_ANIMATION|1, skill_castend_damage_id);
+ skill_delunit(unit);
+ break;
+ }
+
+ case UNT_BANDING:
+ {
+ struct block_list *src = map_id2bl(group->src_id);
+ struct status_change *sc;
+ if( !src || (sc = status_get_sc(src)) == NULL || !sc->data[SC_BANDING] )
+ {
+ skill_delunit(unit);
+ break;
+ }
+ // This unit isn't removed while SC_BANDING is active.
+ group->limit = DIFF_TICK(tick+group->interval,group->tick);
+ unit->limit = DIFF_TICK(tick+group->interval,group->tick);
+ }
+ break;
+
+ default:
+ skill_delunit(unit);
+ }
+ }
+ else
+ {// skill unit is still active
+ switch( group->unit_id )
+ {
+ case UNT_ICEWALL:
+ // icewall loses 50 hp every second
+ unit->val1 -= SKILLUNITTIMER_INTERVAL/20; // trap's hp
+ if( unit->val1 <= 0 && unit->limit + group->tick > tick + 700 )
+ unit->limit = DIFF_TICK(tick+700,group->tick);
+ break;
+ case UNT_BLASTMINE:
+ case UNT_SKIDTRAP:
+ case UNT_LANDMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_CLAYMORETRAP:
+ case UNT_FREEZINGTRAP:
+ case UNT_TALKIEBOX:
+ case UNT_ANKLESNARE:
+ if( unit->val1 <= 0 ) {
+ if( group->unit_id == UNT_ANKLESNARE && group->val2 > 0 )
+ skill_delunit(unit);
+ else {
+ clif_changetraplook(bl, group->unit_id==UNT_LANDMINE?UNT_FIREPILLAR_ACTIVE:UNT_USED_TRAPS);
+ group->limit = DIFF_TICK(tick, group->tick) + 1500;
+ group->unit_id = UNT_USED_TRAPS;
+ }
+ }
+ break;
+ case UNT_REVERBERATION:
+ if( unit->val1 <= 0 ){
+ clif_changetraplook(bl,UNT_USED_TRAPS);
+ map_foreachinrange(skill_trap_splash, bl, skill_get_splash(group->skill_id, group->skill_lv), group->bl_flag, bl, tick);
+ group->limit = DIFF_TICK(tick,group->tick)+1000;
+ unit->limit = DIFF_TICK(tick,group->tick)+1000;
+ group->unit_id = UNT_USED_TRAPS;
+ }
+ break;
+ case UNT_WALLOFTHORN:
+ if( unit->val1 <= 0 ) {
+ group->unit_id = UNT_USED_TRAPS;
+ group->limit = DIFF_TICK(tick, group->tick) + 1500;
+ }
+ break;
+ }
+ }
+
+ //Don't continue if unit or even group is expired and has been deleted.
+ if( !group || !unit->alive )
+ return 0;
+
+ dissonance = skill_dance_switch(unit, 0);
+
+ if( unit->range >= 0 && group->interval != -1 )
+ {
+ if( battle_config.skill_wall_check )
+ map_foreachinshootrange(skill_unit_timer_sub_onplace, bl, unit->range, group->bl_flag, bl,tick);
+ else
+ map_foreachinrange(skill_unit_timer_sub_onplace, bl, unit->range, group->bl_flag, bl,tick);
+
+ if(unit->range == -1) //Unit disabled, but it should not be deleted yet.
+ group->unit_id = UNT_USED_TRAPS;
+
+ if( group->unit_id == UNT_TATAMIGAESHI )
+ {
+ unit->range = -1; //Disable processed cell.
+ if (--group->val1 <= 0) // number of live cells
+ { //All tiles were processed, disable skill.
+ group->target_flag=BCT_NOONE;
+ group->bl_flag= BL_NUL;
+ }
+ }
+ }
+
+ if( dissonance ) skill_dance_switch(unit, 1);
+
+ return 0;
+}
+/*==========================================
+ * Executes on all skill units every SKILLUNITTIMER_INTERVAL miliseconds.
+ *------------------------------------------*/
+int skill_unit_timer(int tid, unsigned int tick, int id, intptr_t data)
+{
+ map_freeblock_lock();
+
+ skillunit_db->foreach(skillunit_db, skill_unit_timer_sub, tick);
+
+ map_freeblock_unlock();
+
+ return 0;
+}
+
+static int skill_unit_temp[20]; // temporary storage for tracking skill unit skill ids as players move in/out of them
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_unit_move_sub (struct block_list* bl, va_list ap)
+{
+ struct skill_unit* unit = (struct skill_unit *)bl;
+ struct skill_unit_group* group = unit->group;
+
+ struct block_list* target = va_arg(ap,struct block_list*);
+ unsigned int tick = va_arg(ap,unsigned int);
+ int flag = va_arg(ap,int);
+
+ bool dissonance;
+ uint16 skill_id;
+ int i;
+
+ nullpo_ret(group);
+
+ if( !unit->alive || target->prev == NULL )
+ return 0;
+
+ if( flag&1 && ( unit->group->skill_id == PF_SPIDERWEB || unit->group->skill_id == GN_THORNS_TRAP ) )
+ return 0; // Fiberlock is never supposed to trigger on skill_unit_move. [Inkfish]
+
+ dissonance = skill_dance_switch(unit, 0);
+
+ //Necessary in case the group is deleted after calling on_place/on_out [Skotlex]
+ skill_id = unit->group->skill_id;
+
+ if( unit->group->interval != -1 && !(skill_get_unit_flag(skill_id)&UF_DUALMODE) && skill_id != BD_LULLABY ) //Lullaby is the exception, bugreport:411
+ { //Non-dualmode unit skills with a timer don't trigger when walking, so just return
+ if( dissonance ) skill_dance_switch(unit, 1);
+ return 0;
+ }
+
+ //Target-type check.
+ if( !(group->bl_flag&target->type && battle_check_target(&unit->bl,target,group->target_flag) > 0) )
+ {
+ if( group->src_id == target->id && group->state.song_dance&0x2 )
+ { //Ensemble check to see if they went out/in of the area [Skotlex]
+ if( flag&1 )
+ {
+ if( flag&2 )
+ { //Clear this skill id.
+ ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == skill_id );
+ if( i < ARRAYLENGTH(skill_unit_temp) )
+ skill_unit_temp[i] = 0;
+ }
+ }
+ else
+ {
+ if( flag&2 )
+ { //Store this skill id.
+ ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == 0 );
+ if( i < ARRAYLENGTH(skill_unit_temp) )
+ skill_unit_temp[i] = skill_id;
+ else
+ ShowError("skill_unit_move_sub: Reached limit of unit objects per cell!\n");
+ }
+
+ }
+
+ if( flag&4 )
+ skill_unit_onleft(skill_id,target,tick);
+ }
+
+ if( dissonance ) skill_dance_switch(unit, 1);
+
+ return 0;
+ }
+ else
+ {
+ if( flag&1 )
+ {
+ int result = skill_unit_onplace(unit,target,tick);
+ if( flag&2 && result )
+ { //Clear skill ids we have stored in onout.
+ ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == result );
+ if( i < ARRAYLENGTH(skill_unit_temp) )
+ skill_unit_temp[i] = 0;
+ }
+ }
+ else
+ {
+ int result = skill_unit_onout(unit,target,tick);
+ if( flag&2 && result )
+ { //Store this unit id.
+ ARR_FIND( 0, ARRAYLENGTH(skill_unit_temp), i, skill_unit_temp[i] == 0 );
+ if( i < ARRAYLENGTH(skill_unit_temp) )
+ skill_unit_temp[i] = skill_id;
+ else
+ ShowError("skill_unit_move_sub: Reached limit of unit objects per cell!\n");
+ }
+ }
+
+ //TODO: Normally, this is dangerous since the unit and group could be freed
+ //inside the onout/onplace functions. Currently it is safe because we know song/dance
+ //cells do not get deleted within them. [Skotlex]
+ if( dissonance ) skill_dance_switch(unit, 1);
+
+ if( flag&4 )
+ skill_unit_onleft(skill_id,target,tick);
+
+ return 1;
+ }
+}
+
+/*==========================================
+ * Invoked when a char has moved and unit cells must be invoked (onplace, onout, onleft)
+ * Flag values:
+ * flag&1: invoke skill_unit_onplace (otherwise invoke skill_unit_onout)
+ * flag&2: this function is being invoked twice as a bl moves, store in memory the affected
+ * units to figure out when they have left a group.
+ * flag&4: Force a onleft event (triggered when the bl is killed, for example)
+ *------------------------------------------*/
+int skill_unit_move (struct block_list *bl, unsigned int tick, int flag)
+{
+ nullpo_ret(bl);
+
+ if( bl->prev == NULL )
+ return 0;
+
+ if( flag&2 && !(flag&1) )
+ { //Onout, clear data
+ memset(skill_unit_temp, 0, sizeof(skill_unit_temp));
+ }
+
+ map_foreachincell(skill_unit_move_sub,bl->m,bl->x,bl->y,BL_SKILL,bl,tick,flag);
+
+ if( flag&2 && flag&1 )
+ { //Onplace, check any skill units you have left.
+ int i;
+ for( i = 0; i < ARRAYLENGTH(skill_unit_temp); i++ )
+ if( skill_unit_temp[i] )
+ skill_unit_onleft(skill_unit_temp[i], bl, tick);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_unit_move_unit_group (struct skill_unit_group *group, int16 m, int16 dx, int16 dy)
+{
+ int i,j;
+ unsigned int tick = gettick();
+ int *m_flag;
+ struct skill_unit *unit1;
+ struct skill_unit *unit2;
+
+ if (group == NULL)
+ return 0;
+ if (group->unit_count<=0)
+ return 0;
+ if (group->unit==NULL)
+ return 0;
+
+ if (skill_get_unit_flag(group->skill_id)&UF_ENSEMBLE)
+ return 0; //Ensembles may not be moved around.
+
+ if( group->unit_id == UNT_ICEWALL || group->unit_id == UNT_WALLOFTHORN )
+ return 0; //Icewalls and Wall of Thorns don't get knocked back
+
+ m_flag = (int *) aCalloc(group->unit_count, sizeof(int));
+ // m_flag
+ // 0: Neither of the following (skill_unit_onplace & skill_unit_onout are needed)
+ // 1: Unit will move to a slot that had another unit of the same group (skill_unit_onplace not needed)
+ // 2: Another unit from same group will end up positioned on this unit (skill_unit_onout not needed)
+ // 3: Both 1+2.
+ for(i=0;i<group->unit_count;i++){
+ unit1=&group->unit[i];
+ if (!unit1->alive || unit1->bl.m!=m)
+ continue;
+ for(j=0;j<group->unit_count;j++){
+ unit2=&group->unit[j];
+ if (!unit2->alive)
+ continue;
+ if (unit1->bl.x+dx==unit2->bl.x && unit1->bl.y+dy==unit2->bl.y){
+ m_flag[i] |= 0x1;
+ }
+ if (unit1->bl.x-dx==unit2->bl.x && unit1->bl.y-dy==unit2->bl.y){
+ m_flag[i] |= 0x2;
+ }
+ }
+ }
+ j = 0;
+ for (i=0;i<group->unit_count;i++) {
+ unit1=&group->unit[i];
+ if (!unit1->alive)
+ continue;
+ if (!(m_flag[i]&0x2)) {
+ if (group->state.song_dance&0x1) //Cancel dissonance effect.
+ skill_dance_overlap(unit1, 0);
+ map_foreachincell(skill_unit_effect,unit1->bl.m,unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,4);
+ }
+ //Move Cell using "smart" criteria (avoid useless moving around)
+ switch(m_flag[i])
+ {
+ case 0:
+ //Cell moves independently, safely move it.
+ map_moveblock(&unit1->bl, unit1->bl.x+dx, unit1->bl.y+dy, tick);
+ break;
+ case 1:
+ //Cell moves unto another cell, look for a replacement cell that won't collide
+ //and has no cell moving into it (flag == 2)
+ for(;j<group->unit_count;j++)
+ {
+ if(m_flag[j]!=2 || !group->unit[j].alive)
+ continue;
+ //Move to where this cell would had moved.
+ unit2 = &group->unit[j];
+ map_moveblock(&unit1->bl, unit2->bl.x+dx, unit2->bl.y+dy, tick);
+ j++; //Skip this cell as we have used it.
+ break;
+ }
+ break;
+ case 2:
+ case 3:
+ break; //Don't move the cell as a cell will end on this tile anyway.
+ }
+ if (!(m_flag[i]&0x2)) { //We only moved the cell in 0-1
+ if (group->state.song_dance&0x1) //Check for dissonance effect.
+ skill_dance_overlap(unit1, 1);
+ clif_skill_setunit(unit1);
+ map_foreachincell(skill_unit_effect,unit1->bl.m,unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,1);
+ }
+ }
+ aFree(m_flag);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_can_produce_mix (struct map_session_data *sd, int nameid, int trigger, int qty)
+{
+ int i,j;
+
+ nullpo_ret(sd);
+
+ if(nameid<=0)
+ return 0;
+
+ for(i=0;i<MAX_SKILL_PRODUCE_DB;i++){
+ if(skill_produce_db[i].nameid == nameid ){
+ if((j=skill_produce_db[i].req_skill)>0 &&
+ pc_checkskill(sd,j) < skill_produce_db[i].req_skill_lv)
+ continue; // must iterate again to check other skills that produce it. [malufett]
+ if( j > 0 && sd->menuskill_id > 0 && sd->menuskill_id != j )
+ continue; // special case
+ break;
+ }
+ }
+
+ if( i >= MAX_SKILL_PRODUCE_DB )
+ return 0;
+
+ if( pc_checkadditem(sd, nameid, qty) == ADDITEM_OVERAMOUNT )
+ {// cannot carry the produced stuff
+ return 0;
+ }
+
+ if(trigger>=0){
+ if(trigger>20) { // Non-weapon, non-food item (itemlv must match)
+ if(skill_produce_db[i].itemlv!=trigger)
+ return 0;
+ } else if(trigger>10) { // Food (any item level between 10 and 20 will do)
+ if(skill_produce_db[i].itemlv<=10 || skill_produce_db[i].itemlv>20)
+ return 0;
+ } else { // Weapon (itemlv must be higher or equal)
+ if(skill_produce_db[i].itemlv>trigger)
+ return 0;
+ }
+ }
+
+ for(j=0;j<MAX_PRODUCE_RESOURCE;j++){
+ int id,x,y;
+ if( (id=skill_produce_db[i].mat_id[j]) <= 0 )
+ continue;
+ if(skill_produce_db[i].mat_amount[j] <= 0) {
+ if(pc_search_inventory(sd,id) < 0)
+ return 0;
+ }
+ else {
+ for(y=0,x=0;y<MAX_INVENTORY;y++)
+ if( sd->status.inventory[y].nameid == id )
+ x+=sd->status.inventory[y].amount;
+ if(x<qty*skill_produce_db[i].mat_amount[j])
+ return 0;
+ }
+ }
+ return i+1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_produce_mix (struct map_session_data *sd, uint16 skill_id, int nameid, int slot1, int slot2, int slot3, int qty)
+{
+ int slot[3];
+ int i,sc,ele,idx,equip,wlv,make_per = 0,flag = 0,skill_lv = 0;
+ int num = -1; // exclude the recipe
+ struct status_data *status;
+ struct item_data* data;
+
+ nullpo_ret(sd);
+ status = status_get_status_data(&sd->bl);
+
+ if( sd->skill_id_old == skill_id )
+ skill_lv = sd->skill_lv_old;
+
+ if( !(idx=skill_can_produce_mix(sd,nameid,-1, qty)) )
+ return 0;
+ idx--;
+
+ if (qty < 1)
+ qty = 1;
+
+ if (!skill_id) //A skill can be specified for some override cases.
+ skill_id = skill_produce_db[idx].req_skill;
+
+ if( skill_id == GC_RESEARCHNEWPOISON )
+ skill_id = GC_CREATENEWPOISON;
+
+ slot[0]=slot1;
+ slot[1]=slot2;
+ slot[2]=slot3;
+
+ for(i=0,sc=0,ele=0;i<3;i++){ //Note that qty should always be one if you are using these!
+ int j;
+ if( slot[i]<=0 )
+ continue;
+ j = pc_search_inventory(sd,slot[i]);
+ if(j < 0)
+ continue;
+ if(slot[i]==1000){ /* Star Crumb */
+ pc_delitem(sd,j,1,1,0,LOG_TYPE_PRODUCE);
+ sc++;
+ }
+ if(slot[i]>=994 && slot[i]<=997 && ele==0){ /* Flame Heart . . . Great Nature */
+ static const int ele_table[4]={3,1,4,2};
+ pc_delitem(sd,j,1,1,0,LOG_TYPE_PRODUCE);
+ ele=ele_table[slot[i]-994];
+ }
+ }
+
+ if( skill_id == RK_RUNEMASTERY ) {
+ int temp_qty, skill_lv = pc_checkskill(sd,skill_id);
+ data = itemdb_search(nameid);
+
+ if( skill_lv == 10 ) temp_qty = 1 + rnd()%3;
+ else if( skill_lv > 5 ) temp_qty = 1 + rnd()%2;
+ else temp_qty = 1;
+
+ if (data->stack.inventory) {
+ for( i = 0; i < MAX_INVENTORY; i++ ) {
+ if( sd->status.inventory[i].nameid == nameid ) {
+ if( sd->status.inventory[i].amount >= data->stack.amount ) {
+ clif_msgtable(sd->fd,0x61b);
+ return 0;
+ } else {
+ /**
+ * the amount fits, say we got temp_qty 4 and 19 runes, we trim temp_qty to 1.
+ **/
+ if( temp_qty + sd->status.inventory[i].amount >= data->stack.amount )
+ temp_qty = data->stack.amount - sd->status.inventory[i].amount;
+ }
+ break;
+ }
+ }
+ }
+ qty = temp_qty;
+ }
+
+ for(i=0;i<MAX_PRODUCE_RESOURCE;i++){
+ int j,id,x;
+ if( (id=skill_produce_db[idx].mat_id[i]) <= 0 )
+ continue;
+ num++;
+ x=( skill_id == RK_RUNEMASTERY ? 1 : qty)*skill_produce_db[idx].mat_amount[i];
+ do{
+ int y=0;
+ j = pc_search_inventory(sd,id);
+
+ if(j >= 0){
+ y = sd->status.inventory[j].amount;
+ if(y>x)y=x;
+ pc_delitem(sd,j,y,0,0,LOG_TYPE_PRODUCE);
+ } else
+ ShowError("skill_produce_mix: material item error\n");
+
+ x-=y;
+ }while( j>=0 && x>0 );
+ }
+
+ if( (equip = (itemdb_isequip(nameid) && skill_id != GN_CHANGEMATERIAL && skill_id != GN_MAKEBOMB )) )
+ wlv = itemdb_wlv(nameid);
+ if(!equip) {
+ switch(skill_id){
+ case BS_IRON:
+ case BS_STEEL:
+ case BS_ENCHANTEDSTONE:
+ // Ores & Metals Refining - skill bonuses are straight from kRO website [DracoRPG]
+ i = pc_checkskill(sd,skill_id);
+ make_per = sd->status.job_level*20 + status->dex*10 + status->luk*10; //Base chance
+ switch(nameid){
+ case 998: // Iron
+ make_per += 4000+i*500; // Temper Iron bonus: +26/+32/+38/+44/+50
+ break;
+ case 999: // Steel
+ make_per += 3000+i*500; // Temper Steel bonus: +35/+40/+45/+50/+55
+ break;
+ case 1000: //Star Crumb
+ make_per = 100000; // Star Crumbs are 100% success crafting rate? (made 1000% so it succeeds even after penalties) [Skotlex]
+ break;
+ default: // Enchanted Stones
+ make_per += 1000+i*500; // Enchantedstone Craft bonus: +15/+20/+25/+30/+35
+ break;
+ }
+ break;
+ case ASC_CDP:
+ make_per = (2000 + 40*status->dex + 20*status->luk);
+ break;
+ case AL_HOLYWATER:
+ /**
+ * Arch Bishop
+ **/
+ case AB_ANCILLA:
+ make_per = 100000; //100% success
+ break;
+ case AM_PHARMACY: // Potion Preparation - reviewed with the help of various Ragnainfo sources [DracoRPG]
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ make_per = pc_checkskill(sd,AM_LEARNINGPOTION)*50
+ + pc_checkskill(sd,AM_PHARMACY)*300 + sd->status.job_level*20
+ + (status->int_/2)*10 + status->dex*10+status->luk*10;
+ if(merc_is_hom_active(sd->hd)) {//Player got a homun
+ int skill;
+ if((skill=merc_hom_checkskill(sd->hd,HVAN_INSTRUCT)) > 0) //His homun is a vanil with instruction change
+ make_per += skill*100; //+1% bonus per level
+ }
+ switch(nameid){
+ case 501: // Red Potion
+ case 503: // Yellow Potion
+ case 504: // White Potion
+ make_per += (1+rnd()%100)*10 + 2000;
+ break;
+ case 970: // Alcohol
+ make_per += (1+rnd()%100)*10 + 1000;
+ break;
+ case 7135: // Bottle Grenade
+ case 7136: // Acid Bottle
+ case 7137: // Plant Bottle
+ case 7138: // Marine Sphere Bottle
+ make_per += (1+rnd()%100)*10;
+ break;
+ case 546: // Condensed Yellow Potion
+ make_per -= (1+rnd()%50)*10;
+ break;
+ case 547: // Condensed White Potion
+ case 7139: // Glistening Coat
+ make_per -= (1+rnd()%100)*10;
+ break;
+ //Common items, recieve no bonus or penalty, listed just because they are commonly produced
+ case 505: // Blue Potion
+ case 545: // Condensed Red Potion
+ case 605: // Anodyne
+ case 606: // Aloevera
+ default:
+ break;
+ }
+ if(battle_config.pp_rate != 100)
+ make_per = make_per * battle_config.pp_rate / 100;
+ break;
+ case SA_CREATECON: // Elemental Converter Creation
+ make_per = 100000; // should be 100% success rate
+ break;
+ /**
+ * Rune Knight
+ **/
+ case RK_RUNEMASTERY:
+ {
+ int A = 100 * (51 + 2 * pc_checkskill(sd, skill_id));
+ int B = 100 * status->dex / 30 + 10 * (status->luk + sd->status.job_level);
+ int C = 100 * cap_value(sd->itemid,0,100); //itemid depend on makerune()
+ int D = 0;
+ switch (nameid) { //rune rank it_diff 9 craftable rune
+ case ITEMID_BERKANA:
+ D = -2000;
+ break; //Rank S
+ case ITEMID_NAUTHIZ:
+ case ITEMID_URUZ:
+ D = -1500;
+ break; //Rank A
+ case ITEMID_ISA:
+ case ITEMID_WYRD:
+ D = -1000;
+ break; //Rank B
+ case ITEMID_RAIDO:
+ case ITEMID_THURISAZ:
+ case ITEMID_HAGALAZ:
+ case ITEMID_OTHILA:
+ D = -500;
+ break; //Rank C
+ default: D = -1500;
+ break; //not specified =-15%
+ }
+ make_per = A + B + C + D;
+ break;
+ }
+ /**
+ * Guilotine Cross
+ **/
+ case GC_CREATENEWPOISON:
+ make_per = 3000 + 500 * pc_checkskill(sd,GC_RESEARCHNEWPOISON);
+ qty = 1+rnd()%pc_checkskill(sd,GC_RESEARCHNEWPOISON);
+ break;
+ case GN_CHANGEMATERIAL:
+ for(i=0; i<MAX_SKILL_PRODUCE_DB; i++)
+ if( skill_changematerial_db[i].itemid == nameid ){
+ make_per = skill_changematerial_db[i].rate * 10;
+ break;
+ }
+ break;
+ case GN_S_PHARMACY:
+ {
+ int difficulty = 0;
+
+ difficulty = (620 - 20 * skill_lv);// (620 - 20 * Skill Level)
+
+ make_per = status->int_ + status->dex/2 + status->luk + sd->status.job_level + (30+rnd()%120) + // (Caster?s INT) + (Caster?s DEX / 2) + (Caster?s LUK) + (Caster?s Job Level) + Random number between (30 ~ 150) +
+ (sd->status.base_level-100) + pc_checkskill(sd, AM_LEARNINGPOTION) + pc_checkskill(sd, CR_FULLPROTECTION)*(4+rnd()%6); // (Caster?s Base Level - 100) + (Potion Research x 5) + (Full Chemical Protection Skill Level) x (Random number between 4 ~ 10)
+
+ switch(nameid){// difficulty factor
+ case 12422: case 12425:
+ case 12428:
+ difficulty += 10;
+ break;
+ case 6212: case 12426:
+ difficulty += 15;
+ break;
+ case 13264: case 12423:
+ case 12427: case 12436:
+ difficulty += 20;
+ break;
+ case 6210: case 6211:
+ case 12437:
+ difficulty += 30;
+ break;
+ case 12424: case 12475:
+ difficulty += 40;
+ break;
+ }
+
+ if( make_per >= 400 && make_per > difficulty)
+ qty = 10;
+ else if( make_per >= 300 && make_per > difficulty)
+ qty = 7;
+ else if( make_per >= 100 && make_per > difficulty)
+ qty = 6;
+ else if( make_per >= 1 && make_per > difficulty)
+ qty = 5;
+ else
+ qty = 4;
+ make_per = 10000;
+ }
+ break;
+ case GN_MAKEBOMB:
+ case GN_MIX_COOKING:
+ {
+ int difficulty = 30 + rnd()%120; // Random number between (30 ~ 150)
+
+ make_per = sd->status.job_level / 4 + status->luk / 2 + status->dex / 3; // (Caster?s Job Level / 4) + (Caster?s LUK / 2) + (Caster?s DEX / 3)
+ qty = ~(5 + rnd()%5) + 1;
+
+ switch(nameid){// difficulty factor
+ case 13260:
+ difficulty += 5;
+ break;
+ case 13261: case 13262:
+ difficulty += 10;
+ break;
+ case 12429: case 12430: case 12431:
+ case 12432: case 12433: case 12434:
+ case 13263:
+ difficulty += 15;
+ break;
+ case 13264:
+ difficulty += 20;
+ break;
+ }
+
+ if( make_per >= 30 && make_per > difficulty)
+ qty = 10 + rnd()%2;
+ else if( make_per >= 10 && make_per > difficulty)
+ qty = 10;
+ else if( make_per == 10 && make_per > difficulty)
+ qty = 8;
+ else if( (make_per >= 50 || make_per < 30) && make_per < difficulty)
+ ;// Food/Bomb creation fails.
+ else if( make_per >= 30 && make_per < difficulty)
+ qty = 5;
+
+ if( qty < 0 || (skill_lv == 1 && make_per < difficulty)){
+ qty = ~qty + 1;
+ make_per = 0;
+ }else
+ make_per = 10000;
+ qty = (skill_lv > 1 ? qty : 1);
+ }
+ break;
+ default:
+ if (sd->menuskill_id == AM_PHARMACY &&
+ sd->menuskill_val > 10 && sd->menuskill_val <= 20)
+ { //Assume Cooking Dish
+ if (sd->menuskill_val >= 15) //Legendary Cooking Set.
+ make_per = 10000; //100% Success
+ else
+ make_per = 1200 * (sd->menuskill_val - 10)
+ + 20 * (sd->status.base_level + 1)
+ + 20 * (status->dex + 1)
+ + 100 * (rnd()%(30+5*(sd->cook_mastery/400) - (6+sd->cook_mastery/80)) + (6+sd->cook_mastery/80))
+ - 400 * (skill_produce_db[idx].itemlv - 11 + 1)
+ - 10 * (100 - status->luk + 1)
+ - 500 * (num - 1)
+ - 100 * (rnd()%4 + 1);
+ break;
+ }
+ make_per = 5000;
+ break;
+ }
+ } else { // Weapon Forging - skill bonuses are straight from kRO website, other things from a jRO calculator [DracoRPG]
+ make_per = 5000 + sd->status.job_level*20 + status->dex*10 + status->luk*10; // Base
+ make_per += pc_checkskill(sd,skill_id)*500; // Smithing skills bonus: +5/+10/+15
+ make_per += pc_checkskill(sd,BS_WEAPONRESEARCH)*100 +((wlv >= 3)? pc_checkskill(sd,BS_ORIDEOCON)*100:0); // Weaponry Research bonus: +1/+2/+3/+4/+5/+6/+7/+8/+9/+10, Oridecon Research bonus (custom): +1/+2/+3/+4/+5
+ make_per -= (ele?2000:0) + sc*1500 + (wlv>1?wlv*1000:0); // Element Stone: -20%, Star Crumb: -15% each, Weapon level malus: -0/-20/-30
+ if(pc_search_inventory(sd,989) > 0) make_per+= 1000; // Emperium Anvil: +10
+ else if(pc_search_inventory(sd,988) > 0) make_per+= 500; // Golden Anvil: +5
+ else if(pc_search_inventory(sd,987) > 0) make_per+= 300; // Oridecon Anvil: +3
+ else if(pc_search_inventory(sd,986) > 0) make_per+= 0; // Anvil: +0?
+ if(battle_config.wp_rate != 100)
+ make_per = make_per * battle_config.wp_rate / 100;
+ }
+
+ if (sd->class_&JOBL_BABY) //if it's a Baby Class
+ make_per = (make_per * 50) / 100; //Baby penalty is 50% (bugreport:4847)
+
+ if(make_per < 1) make_per = 1;
+
+
+ if(rnd()%10000 < make_per || qty > 1){ //Success, or crafting multiple items.
+ struct item tmp_item;
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid=nameid;
+ tmp_item.amount=1;
+ tmp_item.identify=1;
+ if(equip){
+ tmp_item.card[0]=CARD0_FORGE;
+ tmp_item.card[1]=((sc*5)<<8)+ele;
+ tmp_item.card[2]=GetWord(sd->status.char_id,0); // CharId
+ tmp_item.card[3]=GetWord(sd->status.char_id,1);
+ } else {
+ //Flag is only used on the end, so it can be used here. [Skotlex]
+ switch (skill_id) {
+ case BS_DAGGER:
+ case BS_SWORD:
+ case BS_TWOHANDSWORD:
+ case BS_AXE:
+ case BS_MACE:
+ case BS_KNUCKLE:
+ case BS_SPEAR:
+ flag = battle_config.produce_item_name_input&0x1;
+ break;
+ case AM_PHARMACY:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ flag = battle_config.produce_item_name_input&0x2;
+ break;
+ case AL_HOLYWATER:
+ /**
+ * Arch Bishop
+ **/
+ case AB_ANCILLA:
+ flag = battle_config.produce_item_name_input&0x8;
+ break;
+ case ASC_CDP:
+ flag = battle_config.produce_item_name_input&0x10;
+ break;
+ default:
+ flag = battle_config.produce_item_name_input&0x80;
+ break;
+ }
+ if (flag) {
+ tmp_item.card[0]=CARD0_CREATE;
+ tmp_item.card[1]=0;
+ tmp_item.card[2]=GetWord(sd->status.char_id,0); // CharId
+ tmp_item.card[3]=GetWord(sd->status.char_id,1);
+ }
+ }
+
+// if(log_config.produce > 0)
+// log_produce(sd,nameid,slot1,slot2,slot3,1);
+//TODO update PICKLOG
+
+ if(equip){
+ clif_produceeffect(sd,0,nameid);
+ clif_misceffect(&sd->bl,3);
+ if(itemdb_wlv(nameid) >= 3 && ((ele? 1 : 0) + sc) >= 3) // Fame point system [DracoRPG]
+ pc_addfame(sd,10); // Success to forge a lv3 weapon with 3 additional ingredients = +10 fame point
+ } else {
+ int fame = 0;
+ tmp_item.amount = 0;
+
+ for (i=0; i< qty; i++) { //Apply quantity modifiers.
+ if( (skill_id == GN_MIX_COOKING || skill_id == GN_MAKEBOMB || skill_id == GN_S_PHARMACY) && make_per > 1){
+ tmp_item.amount = qty;
+ break;
+ }
+ if (rnd()%10000 < make_per || qty == 1) { //Success
+ tmp_item.amount++;
+ if(nameid < 545 || nameid > 547)
+ continue;
+ if( skill_id != AM_PHARMACY &&
+ skill_id != AM_TWILIGHT1 &&
+ skill_id != AM_TWILIGHT2 &&
+ skill_id != AM_TWILIGHT3 )
+ continue;
+ //Add fame as needed.
+ switch(++sd->potion_success_counter) {
+ case 3:
+ fame+=1; // Success to prepare 3 Condensed Potions in a row
+ break;
+ case 5:
+ fame+=3; // Success to prepare 5 Condensed Potions in a row
+ break;
+ case 7:
+ fame+=10; // Success to prepare 7 Condensed Potions in a row
+ break;
+ case 10:
+ fame+=50; // Success to prepare 10 Condensed Potions in a row
+ sd->potion_success_counter = 0;
+ break;
+ }
+ } else //Failure
+ sd->potion_success_counter = 0;
+ }
+
+ if (fame)
+ pc_addfame(sd,fame);
+ //Visual effects and the like.
+ switch (skill_id) {
+ case AM_PHARMACY:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ case ASC_CDP:
+ clif_produceeffect(sd,2,nameid);
+ clif_misceffect(&sd->bl,5);
+ break;
+ case BS_IRON:
+ case BS_STEEL:
+ case BS_ENCHANTEDSTONE:
+ clif_produceeffect(sd,0,nameid);
+ clif_misceffect(&sd->bl,3);
+ break;
+ case RK_RUNEMASTERY:
+ case GC_CREATENEWPOISON:
+ clif_produceeffect(sd,2,nameid);
+ clif_misceffect(&sd->bl,5);
+ break;
+ default: //Those that don't require a skill?
+ if( skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20)
+ { //Cooking items.
+ clif_specialeffect(&sd->bl, 608, AREA);
+ if( sd->cook_mastery < 1999 )
+ pc_setglobalreg(sd, "COOK_MASTERY",sd->cook_mastery + ( 1 << ( (skill_produce_db[idx].itemlv - 11) / 2 ) ) * 5);
+ }
+ break;
+ }
+ }
+ if ( skill_id == GN_CHANGEMATERIAL && tmp_item.amount) { //Success
+ int j, k = 0;
+ for(i=0; i<MAX_SKILL_PRODUCE_DB; i++)
+ if( skill_changematerial_db[i].itemid == nameid ){
+ for(j=0; j<5; j++){
+ if( rnd()%1000 < skill_changematerial_db[i].qty_rate[j] ){
+ tmp_item.amount = qty * skill_changematerial_db[i].qty[j];
+ if((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ k++;
+ }
+ }
+ break;
+ }
+ if( k ){
+ clif_msg_skill(sd,skill_id,0x627);
+ return 1;
+ }
+ } else if (tmp_item.amount) { //Success
+ if((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ if( skill_id == GN_MIX_COOKING || skill_id == GN_MAKEBOMB || skill_id == GN_S_PHARMACY )
+ clif_msg_skill(sd,skill_id,0x627);
+ return 1;
+ }
+ }
+ //Failure
+// if(log_config.produce)
+// log_produce(sd,nameid,slot1,slot2,slot3,0);
+//TODO update PICKLOG
+
+ if(equip){
+ clif_produceeffect(sd,1,nameid);
+ clif_misceffect(&sd->bl,2);
+ } else {
+ switch (skill_id) {
+ case ASC_CDP: //25% Damage yourself, and display same effect as failed potion.
+ status_percent_damage(NULL, &sd->bl, -25, 0, true);
+ case AM_PHARMACY:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ clif_produceeffect(sd,3,nameid);
+ clif_misceffect(&sd->bl,6);
+ sd->potion_success_counter = 0; // Fame point system [DracoRPG]
+ break;
+ case BS_IRON:
+ case BS_STEEL:
+ case BS_ENCHANTEDSTONE:
+ clif_produceeffect(sd,1,nameid);
+ clif_misceffect(&sd->bl,2);
+ break;
+ case RK_RUNEMASTERY:
+ case GC_CREATENEWPOISON:
+ clif_produceeffect(sd,3,nameid);
+ clif_misceffect(&sd->bl,6);
+ break;
+ case GN_MIX_COOKING: {
+ struct item tmp_item;
+ const int compensation[5] = {13265, 13266, 13267, 12435, 13268};
+ int rate = rnd()%500;
+ memset(&tmp_item,0,sizeof(tmp_item));
+ if( rate < 50) i = 4;
+ else if( rate < 100) i = 2+rnd()%1;
+ else if( rate < 250 ) i = 1;
+ else if( rate < 500 ) i = 0;
+ tmp_item.nameid = compensation[i];
+ tmp_item.amount = qty;
+ tmp_item.identify = 1;
+ if( pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE) ) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ clif_msg_skill(sd,skill_id,0x628);
+ }
+ break;
+ case GN_MAKEBOMB:
+ case GN_S_PHARMACY:
+ case GN_CHANGEMATERIAL:
+ clif_msg_skill(sd,skill_id,0x628);
+ break;
+ default:
+ if( skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20 )
+ { //Cooking items.
+ clif_specialeffect(&sd->bl, 609, AREA);
+ if( sd->cook_mastery > 0 )
+ pc_setglobalreg(sd, "COOK_MASTERY", sd->cook_mastery - ( 1 << ((skill_produce_db[idx].itemlv - 11) / 2) ) - ( ( ( 1 << ((skill_produce_db[idx].itemlv - 11) / 2) ) >> 1 ) * 3 ));
+ }
+ }
+ }
+ return 0;
+}
+
+int skill_arrow_create (struct map_session_data *sd, int nameid)
+{
+ int i,j,flag,index=-1;
+ struct item tmp_item;
+
+ nullpo_ret(sd);
+
+ if(nameid <= 0)
+ return 1;
+
+ for(i=0;i<MAX_SKILL_ARROW_DB;i++)
+ if(nameid == skill_arrow_db[i].nameid) {
+ index = i;
+ break;
+ }
+
+ if(index < 0 || (j = pc_search_inventory(sd,nameid)) < 0)
+ return 1;
+
+ pc_delitem(sd,j,1,0,0,LOG_TYPE_PRODUCE);
+ for(i=0;i<MAX_ARROW_RESOURCE;i++) {
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.identify = 1;
+ tmp_item.nameid = skill_arrow_db[index].cre_id[i];
+ tmp_item.amount = skill_arrow_db[index].cre_amount[i];
+ if(battle_config.produce_item_name_input&0x4) {
+ tmp_item.card[0]=CARD0_CREATE;
+ tmp_item.card[1]=0;
+ tmp_item.card[2]=GetWord(sd->status.char_id,0); // CharId
+ tmp_item.card[3]=GetWord(sd->status.char_id,1);
+ }
+ if(tmp_item.nameid <= 0 || tmp_item.amount <= 0)
+ continue;
+ if((flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+
+ return 0;
+}
+int skill_poisoningweapon( struct map_session_data *sd, int nameid) {
+ sc_type type;
+ int chance, i;
+ nullpo_ret(sd);
+ if( nameid <= 0 || (i = pc_search_inventory(sd,nameid)) < 0 || pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME) ) {
+ clif_skill_fail(sd,GC_POISONINGWEAPON,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ switch( nameid )
+ { // t_lv used to take duration from skill_get_time2
+ case PO_PARALYSE: type = SC_PARALYSE; break;
+ case PO_PYREXIA: type = SC_PYREXIA; break;
+ case PO_DEATHHURT: type = SC_DEATHHURT; break;
+ case PO_LEECHESEND: type = SC_LEECHESEND; break;
+ case PO_VENOMBLEED: type = SC_VENOMBLEED; break;
+ case PO_TOXIN: type = SC_TOXIN; break;
+ case PO_MAGICMUSHROOM: type = SC_MAGICMUSHROOM; break;
+ case PO_OBLIVIONCURSE: type = SC_OBLIVIONCURSE; break;
+ default:
+ clif_skill_fail(sd,GC_POISONINGWEAPON,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+
+ chance = 2 + 2 * sd->menuskill_val; // 2 + 2 * skill_lv
+ sc_start4(&sd->bl, SC_POISONINGWEAPON, 100, pc_checkskill(sd, GC_RESEARCHNEWPOISON), //in Aegis it store the level of GC_RESEARCHNEWPOISON in val1
+ type, chance, 0, skill_get_time(GC_POISONINGWEAPON, sd->menuskill_val));
+
+ return 0;
+}
+
+static void skill_toggle_magicpower(struct block_list *bl, uint16 skill_id)
+{
+ struct status_change *sc = status_get_sc(bl);
+
+ // non-offensive and non-magic skills do not affect the status
+ if (skill_get_nk(skill_id)&NK_NO_DAMAGE || !(skill_get_type(skill_id)&BF_MAGIC))
+ return;
+
+ if (sc && sc->count && sc->data[SC_MAGICPOWER])
+ {
+ if (sc->data[SC_MAGICPOWER]->val4)
+ {
+ status_change_end(bl, SC_MAGICPOWER, INVALID_TIMER);
+ }
+ else
+ {
+ sc->data[SC_MAGICPOWER]->val4 = 1;
+ status_calc_bl(bl, status_sc2scb_flag(SC_MAGICPOWER));
+#ifndef RENEWAL
+ if(bl->type == BL_PC){// update current display.
+ clif_updatestatus(((TBL_PC *)bl),SP_MATK1);
+ clif_updatestatus(((TBL_PC *)bl),SP_MATK2);
+ }
+#endif
+ }
+ }
+}
+
+
+int skill_magicdecoy(struct map_session_data *sd, int nameid) {
+ int x, y, i, class_, skill;
+ struct mob_data *md;
+ nullpo_ret(sd);
+ skill = sd->menuskill_val;
+
+ if( nameid <= 0 || !itemdb_is_element(nameid) || (i = pc_search_inventory(sd,nameid)) < 0 || !skill || pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME) )
+ {
+ clif_skill_fail(sd,NC_MAGICDECOY,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+
+ // Spawn Position
+ pc_delitem(sd,i,1,0,0,LOG_TYPE_CONSUME);
+ x = sd->sc.comet_x;
+ y = sd->sc.comet_y;
+ sd->sc.comet_x = sd->sc.comet_y = 0;
+ sd->menuskill_val = 0;
+
+ class_ = (nameid == 990 || nameid == 991) ? 2043 + nameid - 990 : (nameid == 992) ? 2046 : 2045;
+
+
+ md = mob_once_spawn_sub(&sd->bl, sd->bl.m, x, y, sd->status.name, class_, "", SZ_SMALL, AI_NONE);
+ if( md ) {
+ md->master_id = sd->bl.id;
+ md->special_state.ai = AI_FLORA;
+ if( md->deletetimer != INVALID_TIMER )
+ delete_timer(md->deletetimer, mob_timer_delete);
+ md->deletetimer = add_timer (gettick() + skill_get_time(NC_MAGICDECOY,skill), mob_timer_delete, md->bl.id, 0);
+ mob_spawn(md);
+ md->status.matk_min = md->status.matk_max = 250 + (50 * skill);
+ }
+
+ return 0;
+}
+
+// Warlock Spellbooks. [LimitLine/3CeAM]
+int skill_spellbook (struct map_session_data *sd, int nameid) {
+ int i, max_preserve, skill_id, point;
+ struct status_change *sc;
+
+ nullpo_ret(sd);
+
+ sc = status_get_sc(&sd->bl);
+ status_change_end(&sd->bl, SC_STOP, INVALID_TIMER);
+
+ for(i=SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++) if( sc && !sc->data[i] ) break;
+ if( i > SC_MAXSPELLBOOK )
+ {
+ clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_READING, 0);
+ return 0;
+ }
+
+ ARR_FIND(0,MAX_SKILL_SPELLBOOK_DB,i,skill_spellbook_db[i].nameid == nameid); // Search for information of this item
+ if( i == MAX_SKILL_SPELLBOOK_DB ) return 0;
+
+ if( !pc_checkskill(sd, (skill_id = skill_spellbook_db[i].skill_id)) )
+ { // User don't know the skill
+ sc_start(&sd->bl, SC_SLEEP, 100, 1, skill_get_time(WL_READING_SB, pc_checkskill(sd,WL_READING_SB)));
+ clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_DIFFICULT_SLEEP, 0);
+ return 0;
+ }
+
+ max_preserve = 4 * pc_checkskill(sd, WL_FREEZE_SP) + status_get_int(&sd->bl) / 10 + sd->status.base_level / 10;
+ point = skill_spellbook_db[i].point;
+
+ if( sc && sc->data[SC_READING_SB] ){
+ if( (sc->data[SC_READING_SB]->val2 + point) > max_preserve )
+ {
+ clif_skill_fail(sd, WL_READING_SB, USESKILL_FAIL_SPELLBOOK_PRESERVATION_POINT, 0);
+ return 0;
+ }
+ for(i = SC_MAXSPELLBOOK; i >= SC_SPELLBOOK1; i--){ // This is how official saves spellbook. [malufett]
+ if( !sc->data[i] ){
+ sc->data[SC_READING_SB]->val2 += point; // increase points
+ sc_start4(&sd->bl, (sc_type)i, 100, skill_id, pc_checkskill(sd,skill_id), point, 0, INVALID_TIMER);
+ break;
+ }
+ }
+ }else{
+ sc_start2(&sd->bl, SC_READING_SB, 100, 0, point, INVALID_TIMER);
+ sc_start4(&sd->bl, SC_MAXSPELLBOOK, 100, skill_id, pc_checkskill(sd,skill_id), point, 0, INVALID_TIMER);
+ }
+
+ return 1;
+}
+int skill_select_menu(struct map_session_data *sd,uint16 skill_id) {
+ int id, lv, prob, aslvl = 0;
+ nullpo_ret(sd);
+
+ if (sd->sc.data[SC_STOP]) {
+ aslvl = sd->sc.data[SC_STOP]->val1;
+ status_change_end(&sd->bl,SC_STOP,INVALID_TIMER);
+ }
+
+ if( skill_id >= GS_GLITTERING || skill_get_type(skill_id) != BF_MAGIC ||
+ (id = sd->status.skill[skill_id].id) == 0 || sd->status.skill[skill_id].flag != SKILL_FLAG_PLAGIARIZED ) {
+ clif_skill_fail(sd,SC_AUTOSHADOWSPELL,0,0);
+ return 0;
+ }
+
+ lv = (aslvl + 1) / 2; // The level the skill will be autocasted
+ lv = min(lv,sd->status.skill[skill_id].lv);
+ prob = (aslvl == 10) ? 15 : (32 - 2 * aslvl); // Probability at level 10 was increased to 15.
+ sc_start4(&sd->bl,SC__AUTOSHADOWSPELL,100,id,lv,prob,0,skill_get_time(SC_AUTOSHADOWSPELL,aslvl));
+ return 0;
+}
+int skill_elementalanalysis(struct map_session_data* sd, int n, uint16 skill_lv, unsigned short* item_list) {
+ int i;
+
+ nullpo_ret(sd);
+ nullpo_ret(item_list);
+
+ if( n <= 0 )
+ return 1;
+
+ for( i = 0; i < n; i++ ) {
+ int nameid, add_amount, del_amount, idx, product, flag;
+ struct item tmp_item;
+
+ idx = item_list[i*2+0]-2;
+ del_amount = item_list[i*2+1];
+
+ if( skill_lv == 2 )
+ del_amount -= (del_amount % 10);
+ add_amount = (skill_lv == 1) ? del_amount * (5 + rnd()%5) : del_amount / 10 ;
+
+ if( (nameid = sd->status.inventory[idx].nameid) <= 0 || del_amount > sd->status.inventory[idx].amount ) {
+ clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+
+ switch( nameid ) {
+ // Level 1
+ case 994: product = 990; break; // Flame Heart -> Red Blood.
+ case 995: product = 991; break; // Mystic Frozen -> Crystal Blue.
+ case 996: product = 992; break; // Rough Wind -> Wind of Verdure.
+ case 997: product = 993; break; // Great Nature -> Green Live.
+ // Level 2
+ case 990: product = 994; break; // Red Blood -> Flame Heart.
+ case 991: product = 995; break; // Crystal Blue -> Mystic Frozen.
+ case 992: product = 996; break; // Wind of Verdure -> Rough Wind.
+ case 993: product = 997; break; // Green Live -> Great Nature.
+ default:
+ clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+
+ if( pc_delitem(sd,idx,del_amount,0,1,LOG_TYPE_CONSUME) ) {
+ clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+
+ if( skill_lv == 2 && rnd()%100 < 25 ) { // At level 2 have a fail chance. You loose your items if it fails.
+ clif_skill_fail(sd,SO_EL_ANALYSIS,USESKILL_FAIL_LEVEL,0);
+ return 1;
+ }
+
+
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = product;
+ tmp_item.amount = add_amount;
+ tmp_item.identify = 1;
+
+ if( tmp_item.amount ) {
+ if( (flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_CONSUME)) ) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+int skill_changematerial(struct map_session_data *sd, int n, unsigned short *item_list) {
+ int i, j, k, c, p = 0, nameid, amount;
+
+ nullpo_ret(sd);
+ nullpo_ret(item_list);
+
+ // Search for objects that can be created.
+ for( i = 0; i < MAX_SKILL_PRODUCE_DB; i++ ) {
+ if( skill_produce_db[i].itemlv == 26 ) {
+ p = 0;
+ do {
+ c = 0;
+ // Verification of overlap between the objects required and the list submitted.
+ for( j = 0; j < MAX_PRODUCE_RESOURCE; j++ ) {
+ if( skill_produce_db[i].mat_id[j] > 0 ) {
+ for( k = 0; k < n; k++ ) {
+ int idx = item_list[k*2+0]-2;
+ nameid = sd->status.inventory[idx].nameid;
+ amount = item_list[k*2+1];
+ if( nameid > 0 && sd->status.inventory[idx].identify == 0 ){
+ clif_msg_skill(sd,GN_CHANGEMATERIAL,0x62D);
+ return 0;
+ }
+ if( nameid == skill_produce_db[i].mat_id[j] && (amount-p*skill_produce_db[i].mat_amount[j]) >= skill_produce_db[i].mat_amount[j]
+ && (amount-p*skill_produce_db[i].mat_amount[j])%skill_produce_db[i].mat_amount[j] == 0 ) // must be in exact amount
+ c++; // match
+ }
+ }
+ else
+ break; // No more items required
+ }
+ p++;
+ } while(n == j && c == n);
+ p--;
+ if ( p > 0 ) {
+ skill_produce_mix(sd,GN_CHANGEMATERIAL,skill_produce_db[i].nameid,0,0,0,p);
+ return 1;
+ }
+ }
+ }
+
+ if( p == 0)
+ clif_msg_skill(sd,GN_CHANGEMATERIAL,0x623);
+
+ return 0;
+}
+/**
+ * for Royal Guard's LG_TRAMPLE
+ **/
+static int skill_destroy_trap( struct block_list *bl, va_list ap ) {
+ struct skill_unit *su = (struct skill_unit *)bl;
+ struct skill_unit_group *sg;
+ unsigned int tick;
+
+ nullpo_ret(su);
+ tick = va_arg(ap, unsigned int);
+
+ if (su->alive && (sg = su->group) && skill_get_inf2(sg->skill_id)&INF2_TRAP) {
+ switch( sg->unit_id ) {
+ case UNT_LANDMINE:
+ case UNT_CLAYMORETRAP:
+ case UNT_BLASTMINE:
+ case UNT_SHOCKWAVE:
+ case UNT_SANDMAN:
+ case UNT_FLASHER:
+ case UNT_FREEZINGTRAP:
+ case UNT_CLUSTERBOMB:
+ case UNT_FIRINGTRAP:
+ case UNT_ICEBOUNDTRAP:
+ map_foreachinrange(skill_trap_splash,&su->bl, skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, &su->bl,tick);
+ break;
+ }
+ // Traps aren't recovered.
+ skill_delunit(su);
+ }
+ return 0;
+}
+/*==========================================
+ *
+ *------------------------------------------*/
+int skill_blockpc_end(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct map_session_data *sd = map_id2sd(id);
+ struct skill_cd * cd = NULL;
+
+ if (data <= 0 || data >= MAX_SKILL)
+ return 0;
+ if (!sd) return 0;
+ if (sd->blockskill[data] != (0x1|(tid&0xFE))) return 0;
+
+ if( ( cd = idb_get(skillcd_db,sd->status.char_id) ) ) {
+ int i,cursor;
+ ARR_FIND( 0, cd->cursor+1, cursor, cd->skidx[cursor] == data );
+ cd->duration[cursor] = 0;
+ cd->skidx[cursor] = 0;
+ cd->nameid[cursor] = 0;
+ // compact the cool down list
+ for( i = 0, cursor = 0; i < cd->cursor; i++ ) {
+ if( cd->duration[i] == 0 )
+ continue;
+ if( cursor != i ) {
+ cd->duration[cursor] = cd->duration[i];
+ cd->skidx[cursor] = cd->skidx[i];
+ cd->nameid[cursor] = cd->nameid[i];
+ }
+ cursor++;
+ }
+ if( cursor == 0 )
+ idb_remove(skillcd_db,sd->status.char_id);
+ else
+ cd->cursor = cursor;
+ }
+
+ sd->blockskill[data] = 0;
+ return 1;
+}
+
+/**
+ * flags a singular skill as being blocked from persistent usage.
+ * @param sd the player the skill delay affects
+ * @param skill_id the skill which should be delayed
+ * @param tick the length of time the delay should last
+ * @param load whether this assignment is being loaded upon player login
+ * @return 0 if successful, -1 otherwise
+ */
+int skill_blockpc_start_(struct map_session_data *sd, uint16 skill_id, int tick, bool load)
+{
+ int oskill_id = skill_id;
+ struct skill_cd* cd = NULL;
+ uint16 idx = skill_get_index(skill_id);
+
+ nullpo_retr (-1, sd);
+
+ if (idx == 0)
+ return -1;
+
+ if (tick < 1) {
+ sd->blockskill[idx] = 0;
+ return -1;
+ }
+
+ if( battle_config.display_status_timers )
+ clif_skill_cooldown(sd, idx, tick);
+
+ if( !load )
+ {// not being loaded initially so ensure the skill delay is recorded
+ if( !(cd = idb_get(skillcd_db,sd->status.char_id)) )
+ {// create a new skill cooldown object for map storage
+ CREATE( cd, struct skill_cd, 1 );
+ idb_put( skillcd_db, sd->status.char_id, cd );
+ }
+
+ // record the skill duration in the database map
+ cd->duration[cd->cursor] = tick;
+ cd->skidx[cd->cursor] = idx;
+ cd->nameid[cd->cursor] = oskill_id;
+ cd->cursor++;
+ }
+
+ sd->blockskill[idx] = 0x1|(0xFE&add_timer(gettick()+tick,skill_blockpc_end,sd->bl.id,idx));
+ return 0;
+}
+
+int skill_blockhomun_end(int tid, unsigned int tick, int id, intptr_t data) //[orn]
+{
+ struct homun_data *hd = (TBL_HOM*) map_id2bl(id);
+ if (data <= 0 || data >= MAX_SKILL)
+ return 0;
+ if (hd) hd->blockskill[data] = 0;
+
+ return 1;
+}
+
+int skill_blockhomun_start(struct homun_data *hd, uint16 skill_id, int tick) //[orn]
+{
+ uint16 idx = skill_get_index(skill_id);
+ nullpo_retr (-1, hd);
+
+
+ if (idx == 0)
+ return -1;
+
+ if (tick < 1) {
+ hd->blockskill[idx] = 0;
+ return -1;
+ }
+ hd->blockskill[idx] = 1;
+ return add_timer(gettick() + tick, skill_blockhomun_end, hd->bl.id, idx);
+}
+
+int skill_blockmerc_end(int tid, unsigned int tick, int id, intptr_t data) //[orn]
+{
+ struct mercenary_data *md = (TBL_MER*)map_id2bl(id);
+ if( data <= 0 || data >= MAX_SKILL )
+ return 0;
+ if( md ) md->blockskill[data] = 0;
+
+ return 1;
+}
+
+int skill_blockmerc_start(struct mercenary_data *md, uint16 skill_id, int tick)
+{
+ uint16 idx = skill_get_index(skill_id);
+ nullpo_retr (-1, md);
+
+ if (idx == 0)
+ return -1;
+ if( tick < 1 )
+ {
+ md->blockskill[idx] = 0;
+ return -1;
+ }
+ md->blockskill[idx] = 1;
+ return add_timer(gettick() + tick, skill_blockmerc_end, md->bl.id, idx);
+}
+/**
+ * Adds a new skill unit entry for this player to recast after map load
+ **/
+void skill_usave_add(struct map_session_data * sd, uint16 skill_id, uint16 skill_lv) {
+ struct skill_usave * sus = NULL;
+
+ if( idb_exists(skillusave_db,sd->status.char_id) ) {
+ idb_remove(skillusave_db,sd->status.char_id);
+ }
+
+ CREATE( sus, struct skill_usave, 1 );
+ idb_put( skillusave_db, sd->status.char_id, sus );
+
+ sus->skill_id = skill_id;
+ sus->skill_lv = skill_lv;
+
+ return;
+}
+void skill_usave_trigger(struct map_session_data *sd) {
+ struct skill_usave * sus = NULL;
+
+ if( ! (sus = idb_get(skillusave_db,sd->status.char_id)) ) {
+ return;
+ }
+
+ skill_unitsetting(&sd->bl,sus->skill_id,sus->skill_lv,sd->bl.x,sd->bl.y,0);
+
+ idb_remove(skillusave_db,sd->status.char_id);
+
+ return;
+}
+/*
+ *
+ */
+int skill_split_str (char *str, char **val, int num)
+{
+ int i;
+
+ for( i = 0; i < num && str; i++ )
+ {
+ val[i] = str;
+ str = strchr(str,',');
+ if( str )
+ *str++=0;
+ }
+
+ return i;
+}
+/*
+ *
+ */
+int skill_split_atoi (char *str, int *val)
+{
+ int i, j, diff, step = 1;
+
+ for (i=0; i<MAX_SKILL_LEVEL; i++) {
+ if (!str) break;
+ val[i] = atoi(str);
+ str = strchr(str,':');
+ if (str)
+ *str++=0;
+ }
+ if(i==0) //No data found.
+ return 0;
+ if(i==1)
+ { //Single value, have the whole range have the same value.
+ for (; i < MAX_SKILL_LEVEL; i++)
+ val[i] = val[i-1];
+ return i;
+ }
+ //Check for linear change with increasing steps until we reach half of the data acquired.
+ for (step = 1; step <= i/2; step++)
+ {
+ diff = val[i-1] - val[i-step-1];
+ for(j = i-1; j >= step; j--)
+ if ((val[j]-val[j-step]) != diff)
+ break;
+
+ if (j>=step) //No match, try next step.
+ continue;
+
+ for(; i < MAX_SKILL_LEVEL; i++)
+ { //Apply linear increase
+ val[i] = val[i-step]+diff;
+ if (val[i] < 1 && val[i-1] >=0) //Check if we have switched from + to -, cap the decrease to 0 in said cases.
+ { val[i] = 1; diff = 0; step = 1; }
+ }
+ return i;
+ }
+ //Okay.. we can't figure this one out, just fill out the stuff with the previous value.
+ for (;i<MAX_SKILL_LEVEL; i++)
+ val[i] = val[i-1];
+ return i;
+}
+
+/*
+ *
+ */
+void skill_init_unit_layout (void)
+{
+ int i,j,size,pos = 0;
+
+ memset(skill_unit_layout,0,sizeof(skill_unit_layout));
+
+ // standard square layouts go first
+ for (i=0; i<=MAX_SQUARE_LAYOUT; i++) {
+ size = i*2+1;
+ skill_unit_layout[i].count = size*size;
+ for (j=0; j<size*size; j++) {
+ skill_unit_layout[i].dx[j] = (j%size-i);
+ skill_unit_layout[i].dy[j] = (j/size-i);
+ }
+ }
+
+ // afterwards add special ones
+ pos = i;
+ for (i=0;i<MAX_SKILL_DB;i++) {
+ if (!skill_db[i].unit_id[0] || skill_db[i].unit_layout_type[0] != -1)
+ continue;
+ if( i >= HM_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) {
+ int skill = i;
+
+ if( i >= EL_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) {
+ skill -= EL_SKILLRANGEMIN;
+ skill += EL_SKILLBASE;
+ }
+ if( skill == EL_FIRE_MANTLE ) {
+ static const int dx[] = {-1, 0, 1, 1, 1, 0,-1,-1};
+ static const int dy[] = { 1, 1, 1, 0,-1,-1,-1, 0};
+ skill_unit_layout[pos].count = 8;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ } else {
+ switch (i) {
+ case MG_FIREWALL:
+ case WZ_ICEWALL:
+ case WL_EARTHSTRAIN://Warlock
+ // these will be handled later
+ break;
+ case PR_SANCTUARY:
+ case NPC_EVILLAND: {
+ 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};
+ skill_unit_layout[pos].count = 21;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ 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};
+ skill_unit_layout[pos].count = 33;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case MH_POISON_MIST:
+ case AS_VENOMDUST: {
+ static const int dx[] = {-1, 0, 0, 0, 1};
+ static const int dy[] = { 0,-1, 0, 1, 0};
+ skill_unit_layout[pos].count = 5;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case CR_GRANDCROSS:
+ case NPC_GRANDDARKNESS: {
+ 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};
+ skill_unit_layout[pos].count = 29;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case PF_FOGWALL: {
+ static const int dx[] = {
+ -2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
+ static const int dy[] = {
+ -1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};
+ skill_unit_layout[pos].count = 15;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case PA_GOSPEL: {
+ 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};
+ skill_unit_layout[pos].count = 33;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case NJ_KAENSIN: {
+ static const int dx[] = {-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
+ static const int dy[] = { 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2};
+ skill_unit_layout[pos].count = 24;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case NJ_TATAMIGAESHI: {
+ //Level 1 (count 4, cross of 3x3)
+ static const int dx1[] = {-1, 1, 0, 0};
+ static const int dy1[] = { 0, 0,-1, 1};
+ //Level 2-3 (count 8, cross of 5x5)
+ static const int dx2[] = {-2,-1, 1, 2, 0, 0, 0, 0};
+ static const int dy2[] = { 0, 0, 0, 0,-2,-1, 1, 2};
+ //Level 4-5 (count 12, cross of 7x7
+ static const int dx3[] = {-3,-2,-1, 1, 2, 3, 0, 0, 0, 0, 0, 0};
+ static const int dy3[] = { 0, 0, 0, 0, 0, 0,-3,-2,-1, 1, 2, 3};
+ //lv1
+ j = 0;
+ skill_unit_layout[pos].count = 4;
+ memcpy(skill_unit_layout[pos].dx,dx1,sizeof(dx1));
+ memcpy(skill_unit_layout[pos].dy,dy1,sizeof(dy1));
+ skill_db[i].unit_layout_type[j] = pos;
+ //lv2/3
+ j++;
+ pos++;
+ skill_unit_layout[pos].count = 8;
+ memcpy(skill_unit_layout[pos].dx,dx2,sizeof(dx2));
+ memcpy(skill_unit_layout[pos].dy,dy2,sizeof(dy2));
+ skill_db[i].unit_layout_type[j] = pos;
+ skill_db[i].unit_layout_type[++j] = pos;
+ //lv4/5
+ j++;
+ pos++;
+ skill_unit_layout[pos].count = 12;
+ memcpy(skill_unit_layout[pos].dx,dx3,sizeof(dx3));
+ memcpy(skill_unit_layout[pos].dy,dy3,sizeof(dy3));
+ skill_db[i].unit_layout_type[j] = pos;
+ skill_db[i].unit_layout_type[++j] = pos;
+ //Fill in the rest using lv 5.
+ for (;j<MAX_SKILL_LEVEL;j++)
+ skill_db[i].unit_layout_type[j] = pos;
+ //Skip, this way the check below will fail and continue to the next skill.
+ pos++;
+ }
+ break;
+ case GN_WALLOFTHORN: {
+ static const int dx[] = {-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2, 2, 2, 1, 0};
+ static const int dy[] = { 2, 2, 1, 0,-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2};
+ skill_unit_layout[pos].count = 16;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ default:
+ ShowError("unknown unit layout at skill %d\n",i);
+ break;
+ }
+ }
+ if (!skill_unit_layout[pos].count)
+ continue;
+ for (j=0;j<MAX_SKILL_LEVEL;j++)
+ skill_db[i].unit_layout_type[j] = pos;
+ pos++;
+ }
+
+ // firewall and icewall have 8 layouts (direction-dependent)
+ firewall_unit_pos = pos;
+ for (i=0;i<8;i++) {
+ if (i&1) {
+ skill_unit_layout[pos].count = 5;
+ if (i&0x2) {
+ int dx[] = {-1,-1, 0, 0, 1};
+ int dy[] = { 1, 0, 0,-1,-1};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else {
+ int dx[] = { 1, 1 ,0, 0,-1};
+ int dy[] = { 1, 0, 0,-1,-1};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ } else {
+ skill_unit_layout[pos].count = 3;
+ if (i%4==0) {
+ int dx[] = {-1, 0, 1};
+ int dy[] = { 0, 0, 0};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else {
+ int dx[] = { 0, 0, 0};
+ int dy[] = {-1, 0, 1};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ }
+ pos++;
+ }
+ icewall_unit_pos = pos;
+ for (i=0;i<8;i++) {
+ skill_unit_layout[pos].count = 5;
+ if (i&1) {
+ if (i&0x2) {
+ int dx[] = {-2,-1, 0, 1, 2};
+ int dy[] = { 2, 1, 0,-1,-2};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else {
+ int dx[] = { 2, 1 ,0,-1,-2};
+ int dy[] = { 2, 1, 0,-1,-2};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ } else {
+ if (i%4==0) {
+ int dx[] = {-2,-1, 0, 1, 2};
+ int dy[] = { 0, 0, 0, 0, 0};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ } else {
+ int dx[] = { 0, 0, 0, 0, 0};
+ int dy[] = {-2,-1, 0, 1, 2};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ }
+ pos++;
+ }
+ earthstrain_unit_pos = pos;
+ for( i = 0; i < 8; i++ )
+ { // For each Direction
+ skill_unit_layout[pos].count = 15;
+ switch( i )
+ {
+ case 0: case 1: case 3: case 4: case 5: case 7:
+ {
+ int dx[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7};
+ int dy[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case 2:
+ case 6:
+ {
+ int dx[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int dy[] = {-7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7};
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ }
+ pos++;
+ }
+
+}
+
+int skill_block_check(struct block_list *bl, sc_type type , uint16 skill_id) {
+ int inf = 0;
+ struct status_change *sc = status_get_sc(bl);
+
+ if( !sc || !bl || !skill_id )
+ return 0; // Can do it
+
+ switch(type){
+ case SC_STASIS:
+ inf = skill_get_inf2(skill_id);
+ if( inf == INF2_SONG_DANCE || /*skill_get_inf2(skill_id) == INF2_CHORUS_SKILL ||*/ inf == INF2_SPIRIT_SKILL )
+ return 1; // Can't do it.
+ switch( skill_id )
+ {
+ case NV_FIRSTAID: case TF_HIDING: case AS_CLOAKING: case WZ_SIGHTRASHER:
+ case RG_STRIPWEAPON: case RG_STRIPSHIELD: case RG_STRIPARMOR: case WZ_METEOR:
+ case RG_STRIPHELM: case SC_STRIPACCESSARY: case ST_FULLSTRIP: case WZ_SIGHTBLASTER:
+ case ST_CHASEWALK: case SC_ENERVATION: case SC_GROOMY: case WZ_ICEWALL:
+ case SC_IGNORANCE: case SC_LAZINESS: case SC_UNLUCKY: case WZ_STORMGUST:
+ case SC_WEAKNESS: case AL_RUWACH: case AL_PNEUMA: case WZ_JUPITEL:
+ case AL_HEAL: case AL_BLESSING: case AL_INCAGI: case WZ_VERMILION:
+ case AL_TELEPORT: case AL_WARP: case AL_HOLYWATER: case WZ_EARTHSPIKE:
+ case AL_HOLYLIGHT: case PR_IMPOSITIO: case PR_ASPERSIO: case WZ_HEAVENDRIVE:
+ case PR_SANCTUARY: case PR_STRECOVERY: case PR_MAGNIFICAT: case WZ_QUAGMIRE:
+ case ALL_RESURRECTION: case PR_LEXDIVINA: case PR_LEXAETERNA: case HW_GRAVITATION:
+ case PR_MAGNUS: case PR_TURNUNDEAD: case MG_SRECOVERY: case HW_MAGICPOWER:
+ case MG_SIGHT: case MG_NAPALMBEAT: case MG_SAFETYWALL: case HW_GANBANTEIN:
+ case MG_SOULSTRIKE: case MG_COLDBOLT: case MG_FROSTDIVER: case WL_DRAINLIFE:
+ case MG_STONECURSE: case MG_FIREBALL: case MG_FIREWALL: case WL_SOULEXPANSION:
+ case MG_FIREBOLT: case MG_LIGHTNINGBOLT: case MG_THUNDERSTORM: case MG_ENERGYCOAT:
+ case WL_WHITEIMPRISON: case WL_SUMMONFB: case WL_SUMMONBL: case WL_SUMMONWB:
+ case WL_SUMMONSTONE: case WL_SIENNAEXECRATE: case WL_RELEASE: case WL_EARTHSTRAIN:
+ case WL_RECOGNIZEDSPELL: case WL_READING_SB: case SA_MAGICROD: case SA_SPELLBREAKER:
+ case SA_DISPELL: case SA_FLAMELAUNCHER: case SA_FROSTWEAPON: case SA_LIGHTNINGLOADER:
+ case SA_SEISMICWEAPON: case SA_VOLCANO: case SA_DELUGE: case SA_VIOLENTGALE:
+ case SA_LANDPROTECTOR: case PF_HPCONVERSION: case PF_SOULCHANGE: case PF_SPIDERWEB:
+ case PF_FOGWALL: case TK_RUN: case TK_HIGHJUMP: case TK_SEVENWIND:
+ case SL_KAAHI: case SL_KAUPE: case SL_KAITE:
+
+ // Skills that need to be confirmed.
+ case SO_FIREWALK: case SO_ELECTRICWALK: case SO_SPELLFIST: case SO_EARTHGRAVE:
+ case SO_DIAMONDDUST: case SO_POISON_BUSTER: case SO_PSYCHIC_WAVE: case SO_CLOUD_KILL:
+ case SO_STRIKING: case SO_WARMER: case SO_VACUUM_EXTREME: case SO_VARETYR_SPEAR:
+ case SO_ARRULLO:
+ return 1; // Can't do it.
+ }
+ break;
+ case SC_KAGEHUMI:
+ switch(skill_id){
+ case TF_HIDING: case AS_CLOAKING: case GC_CLOAKINGEXCEED: case SC_SHADOWFORM:
+ case MI_HARMONIZE: case CG_MARIONETTE: case AL_TELEPORT: case TF_BACKSLIDING:
+ case RA_CAMOUFLAGE: case ST_CHASEWALK: case GD_EMERGENCYCALL:
+ return 1; // needs more info
+ }
+ break;
+ }
+
+ return 0;
+}
+
+int skill_get_elemental_type( uint16 skill_id , uint16 skill_lv ) {
+ int type = 0;
+
+ switch( skill_id ) {
+ case SO_SUMMON_AGNI: type = 2114; break;
+ case SO_SUMMON_AQUA: type = 2117; break;
+ case SO_SUMMON_VENTUS: type = 2120; break;
+ case SO_SUMMON_TERA: type = 2123; break;
+ }
+
+ type += skill_lv - 1;
+
+ return type;
+}
+
+/**
+ * reload stored skill cooldowns when a player logs in.
+ * @param sd the affected player structure
+ */
+void skill_cooldown_load(struct map_session_data * sd)
+{
+ int i;
+ struct skill_cd* cd = NULL;
+
+ // always check to make sure the session properly exists
+ nullpo_retv(sd);
+
+ if( !(cd = idb_get(skillcd_db, sd->status.char_id)) )
+ {// no skill cooldown is associated with this character
+ return;
+ }
+
+ // process each individual cooldown associated with the character
+ for( i = 0; i < cd->cursor; i++ )
+ {
+ // block the skill from usage but ensure it is not recorded (load = true)
+ skill_blockpc_start_( sd, cd->nameid[i], cd->duration[i], true );
+ }
+}
+
+/*==========================================
+ * sub-function of DB reading.
+ * skill_db.txt
+ *------------------------------------------*/
+
+static bool skill_parse_row_skilldb(char* split[], int columns, int current)
+{// id,range,hit,inf,element,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count,name,description
+ uint16 skill_id = atoi(split[0]);
+ uint16 idx;
+ if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX)
+ || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX)
+ || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX)
+ || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) ) {
+ ShowWarning("skill_parse_row_skilldb: Skill id %d is forbidden (interferes with guild/homun/mercenary skill mapping)!\n", skill_id);
+ return false;
+ }
+
+ idx = skill_get_index(skill_id);
+ if( !idx ) // invalid skill id
+ return false;
+
+ skill_split_atoi(split[1],skill_db[idx].range);
+ skill_db[idx].hit = atoi(split[2]);
+ skill_db[idx].inf = atoi(split[3]);
+ skill_split_atoi(split[4],skill_db[idx].element);
+ skill_db[idx].nk = (int)strtol(split[5], NULL, 0);
+ skill_split_atoi(split[6],skill_db[idx].splash);
+ skill_db[idx].max = atoi(split[7]);
+ skill_split_atoi(split[8],skill_db[idx].num);
+
+ if( strcmpi(split[9],"yes") == 0 )
+ skill_db[idx].castcancel = 1;
+ else
+ skill_db[idx].castcancel = 0;
+ skill_db[idx].cast_def_rate = atoi(split[10]);
+ skill_db[idx].inf2 = (int)strtol(split[11], NULL, 0);
+ skill_split_atoi(split[12],skill_db[idx].maxcount);
+ if( strcmpi(split[13],"weapon") == 0 )
+ skill_db[idx].skill_type = BF_WEAPON;
+ else if( strcmpi(split[13],"magic") == 0 )
+ skill_db[idx].skill_type = BF_MAGIC;
+ else if( strcmpi(split[13],"misc") == 0 )
+ skill_db[idx].skill_type = BF_MISC;
+ else
+ skill_db[idx].skill_type = 0;
+ skill_split_atoi(split[14],skill_db[idx].blewcount);
+ safestrncpy(skill_db[idx].name, trim(split[15]), sizeof(skill_db[idx].name));
+ safestrncpy(skill_db[idx].desc, trim(split[16]), sizeof(skill_db[idx].desc));
+ strdb_iput(skilldb_name2id, skill_db[idx].name, skill_id);
+
+ return true;
+}
+
+static bool skill_parse_row_requiredb(char* split[], int columns, int current)
+{// skill_id,HPCost,MaxHPTrigger,SPCost,HPRateCost,SPRateCost,ZenyCost,RequiredWeapons,RequiredAmmoTypes,RequiredAmmoAmount,RequiredState,SpiritSphereCost,RequiredItemID1,RequiredItemAmount1,RequiredItemID2,RequiredItemAmount2,RequiredItemID3,RequiredItemAmount3,RequiredItemID4,RequiredItemAmount4,RequiredItemID5,RequiredItemAmount5,RequiredItemID6,RequiredItemAmount6,RequiredItemID7,RequiredItemAmount7,RequiredItemID8,RequiredItemAmount8,RequiredItemID9,RequiredItemAmount9,RequiredItemID10,RequiredItemAmount10
+ char* p;
+ int j;
+
+ uint16 skill_id = atoi(split[0]);
+ uint16 idx = skill_get_index(skill_id);
+ if( !idx ) // invalid skill id
+ return false;
+
+ skill_split_atoi(split[1],skill_db[idx].hp);
+ skill_split_atoi(split[2],skill_db[idx].mhp);
+ skill_split_atoi(split[3],skill_db[idx].sp);
+ skill_split_atoi(split[4],skill_db[idx].hp_rate);
+ skill_split_atoi(split[5],skill_db[idx].sp_rate);
+ skill_split_atoi(split[6],skill_db[idx].zeny);
+
+ //Wich weapon type are required, see doc/item_db for types
+ p = split[7];
+ for( j = 0; j < 32; j++ )
+ {
+ int l = atoi(p);
+ if( l == 99 ) // Any weapon
+ {
+ skill_db[idx].weapon = 0;
+ break;
+ }
+ else
+ skill_db[idx].weapon |= 1<<l;
+ p = strchr(p,':');
+ if(!p)
+ break;
+ p++;
+ }
+
+ //FIXME: document this
+ p = split[8];
+ for( j = 0; j < 32; j++ )
+ {
+ int l = atoi(p);
+ if( l == 99 ) // Any ammo type
+ {
+ skill_db[idx].ammo = 0xFFFFFFFF;
+ break;
+ }
+ else if( l ) // 0 stands for no requirement
+ skill_db[idx].ammo |= 1<<l;
+ p = strchr(p,':');
+ if( !p )
+ break;
+ p++;
+ }
+ skill_split_atoi(split[9],skill_db[idx].ammo_qty);
+
+ if( strcmpi(split[10],"hiding")==0 ) skill_db[idx].state = ST_HIDING;
+ else if( strcmpi(split[10],"cloaking")==0 ) skill_db[idx].state = ST_CLOAKING;
+ else if( strcmpi(split[10],"hidden")==0 ) skill_db[idx].state = ST_HIDDEN;
+ else if( strcmpi(split[10],"riding")==0 ) skill_db[idx].state = ST_RIDING;
+ else if( strcmpi(split[10],"falcon")==0 ) skill_db[idx].state = ST_FALCON;
+ else if( strcmpi(split[10],"cart")==0 ) skill_db[idx].state = ST_CART;
+ else if( strcmpi(split[10],"shield")==0 ) skill_db[idx].state = ST_SHIELD;
+ else if( strcmpi(split[10],"sight")==0 ) skill_db[idx].state = ST_SIGHT;
+ else if( strcmpi(split[10],"explosionspirits")==0 ) skill_db[idx].state = ST_EXPLOSIONSPIRITS;
+ else if( strcmpi(split[10],"cartboost")==0 ) skill_db[idx].state = ST_CARTBOOST;
+ else if( strcmpi(split[10],"recover_weight_rate")==0 ) skill_db[idx].state = ST_RECOV_WEIGHT_RATE;
+ else if( strcmpi(split[10],"move_enable")==0 ) skill_db[idx].state = ST_MOVE_ENABLE;
+ else if( strcmpi(split[10],"water")==0 ) skill_db[idx].state = ST_WATER;
+ /**
+ * New States
+ **/
+ else if( strcmpi(split[10],"dragon")==0 ) skill_db[idx].state = ST_RIDINGDRAGON;
+ else if( strcmpi(split[10],"warg")==0 ) skill_db[idx].state = ST_WUG;
+ else if( strcmpi(split[10],"ridingwarg")==0 ) skill_db[idx].state = ST_RIDINGWUG;
+ else if( strcmpi(split[10],"mado")==0 ) skill_db[idx].state = ST_MADO;
+ else if( strcmpi(split[10],"elementalspirit")==0 ) skill_db[idx].state = ST_ELEMENTALSPIRIT;
+ else if (strcmpi(split[10], "poisonweapon") == 0) skill_db[idx].state = ST_POISONINGWEAPON;
+ else if (strcmpi(split[10], "rollingcutter") == 0) skill_db[idx].state = ST_ROLLINGCUTTER;
+ else if (strcmpi(split[10], "mh_fighting") == 0) skill_db[idx].state = ST_MH_FIGHTING;
+ else if (strcmpi(split[10], "mh_grappling") == 0) skill_db[idx].state = ST_MH_GRAPPLING;
+
+ /**
+ * Unknown or no state
+ **/
+ else skill_db[idx].state = ST_NONE;
+
+ skill_split_atoi(split[11],skill_db[idx].spiritball);
+ for( j = 0; j < MAX_SKILL_ITEM_REQUIRE; j++ ) {
+ skill_db[idx].itemid[j] = atoi(split[12+ 2*j]);
+ skill_db[idx].amount[j] = atoi(split[13+ 2*j]);
+ }
+
+ return true;
+}
+
+static bool skill_parse_row_castdb(char* split[], int columns, int current)
+{// skill_id,CastingTime,AfterCastActDelay,AfterCastWalkDelay,Duration1,Duration2
+ uint16 skill_id = atoi(split[0]);
+ uint16 idx = skill_get_index(skill_id);
+ if( !idx ) // invalid skill id
+ return false;
+
+ skill_split_atoi(split[1],skill_db[idx].cast);
+ skill_split_atoi(split[2],skill_db[idx].delay);
+ skill_split_atoi(split[3],skill_db[idx].walkdelay);
+ skill_split_atoi(split[4],skill_db[idx].upkeep_time);
+ skill_split_atoi(split[5],skill_db[idx].upkeep_time2);
+ skill_split_atoi(split[6],skill_db[idx].cooldown);
+#ifdef RENEWAL_CAST
+ skill_split_atoi(split[7],skill_db[idx].fixed_cast);
+#endif
+ return true;
+}
+
+static bool skill_parse_row_castnodexdb(char* split[], int columns, int current)
+{// Skill id,Cast,Delay (optional)
+ uint16 skill_id = atoi(split[0]);
+ uint16 idx = skill_get_index(skill_id);
+ if( !idx ) // invalid skill id
+ return false;
+
+ skill_split_atoi(split[1],skill_db[idx].castnodex);
+ if( split[2] ) // optional column
+ skill_split_atoi(split[2],skill_db[idx].delaynodex);
+
+ return true;
+}
+
+static bool skill_parse_row_nocastdb(char* split[], int columns, int current)
+{// skill_id,Flag
+ uint16 skill_id = atoi(split[0]);
+ uint16 idx = skill_get_index(skill_id);
+ if( !idx ) // invalid skill id
+ return false;
+
+ skill_db[idx].nocast |= atoi(split[1]);
+
+ return true;
+}
+
+static bool skill_parse_row_unitdb(char* split[], int columns, int current)
+{// ID,unit ID,unit ID 2,layout,range,interval,target,flag
+ uint16 skill_id = atoi(split[0]);
+ uint16 idx = skill_get_index(skill_id);
+ if( !idx ) // invalid skill id
+ return false;
+
+ skill_db[idx].unit_id[0] = strtol(split[1],NULL,16);
+ skill_db[idx].unit_id[1] = strtol(split[2],NULL,16);
+ skill_split_atoi(split[3],skill_db[idx].unit_layout_type);
+ skill_split_atoi(split[4],skill_db[idx].unit_range);
+ skill_db[idx].unit_interval = atoi(split[5]);
+
+ if( strcmpi(split[6],"noenemy")==0 ) skill_db[idx].unit_target = BCT_NOENEMY;
+ else if( strcmpi(split[6],"friend")==0 ) skill_db[idx].unit_target = BCT_NOENEMY;
+ else if( strcmpi(split[6],"party")==0 ) skill_db[idx].unit_target = BCT_PARTY;
+ else if( strcmpi(split[6],"ally")==0 ) skill_db[idx].unit_target = BCT_PARTY|BCT_GUILD;
+ else if( strcmpi(split[6],"guild")==0 ) skill_db[idx].unit_target = BCT_GUILD;
+ else if( strcmpi(split[6],"all")==0 ) skill_db[idx].unit_target = BCT_ALL;
+ else if( strcmpi(split[6],"enemy")==0 ) skill_db[idx].unit_target = BCT_ENEMY;
+ else if( strcmpi(split[6],"self")==0 ) skill_db[idx].unit_target = BCT_SELF;
+ else if( strcmpi(split[6],"noone")==0 ) skill_db[idx].unit_target = BCT_NOONE;
+ else skill_db[idx].unit_target = strtol(split[6],NULL,16);
+
+ skill_db[idx].unit_flag = strtol(split[7],NULL,16);
+
+ if (skill_db[idx].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy)
+ skill_db[idx].unit_target = BCT_NOENEMY;
+
+ //By default, target just characters.
+ skill_db[idx].unit_target |= BL_CHAR;
+ if (skill_db[idx].unit_flag&UF_NOPC)
+ skill_db[idx].unit_target &= ~BL_PC;
+ if (skill_db[idx].unit_flag&UF_NOMOB)
+ skill_db[idx].unit_target &= ~BL_MOB;
+ if (skill_db[idx].unit_flag&UF_SKILL)
+ skill_db[idx].unit_target |= BL_SKILL;
+
+ return true;
+}
+
+static bool skill_parse_row_producedb(char* split[], int columns, int current)
+{// ProduceItemID,ItemLV,RequireSkill,Requireskill_lv,MaterialID1,MaterialAmount1,......
+ int x,y;
+
+ int i = atoi(split[0]);
+ if( !i )
+ return false;
+
+ skill_produce_db[current].nameid = i;
+ skill_produce_db[current].itemlv = atoi(split[1]);
+ skill_produce_db[current].req_skill = atoi(split[2]);
+ skill_produce_db[current].req_skill_lv = atoi(split[3]);
+
+ for( x = 4, y = 0; x+1 < columns && split[x] && split[x+1] && y < MAX_PRODUCE_RESOURCE; x += 2, y++ )
+ {
+ skill_produce_db[current].mat_id[y] = atoi(split[x]);
+ skill_produce_db[current].mat_amount[y] = atoi(split[x+1]);
+ }
+
+ return true;
+}
+
+static bool skill_parse_row_createarrowdb(char* split[], int columns, int current)
+{// SourceID,MakeID1,MakeAmount1,...,MakeID5,MakeAmount5
+ int x,y;
+
+ int i = atoi(split[0]);
+ if( !i )
+ return false;
+
+ skill_arrow_db[current].nameid = i;
+
+ for( x = 1, y = 0; x+1 < columns && split[x] && split[x+1] && y < MAX_ARROW_RESOURCE; x += 2, y++ )
+ {
+ skill_arrow_db[current].cre_id[y] = atoi(split[x]);
+ skill_arrow_db[current].cre_amount[y] = atoi(split[x+1]);
+ }
+
+ return true;
+}
+static bool skill_parse_row_spellbookdb(char* split[], int columns, int current)
+{// skill_id,PreservePoints
+
+ uint16 skill_id = atoi(split[0]);
+ int points = atoi(split[1]);
+ int nameid = atoi(split[2]);
+
+ if( !skill_get_index(skill_id) || !skill_get_max(skill_id) )
+ ShowError("spellbook_db: Invalid skill ID %d\n", skill_id);
+ if ( !skill_get_inf(skill_id) )
+ ShowError("spellbook_db: Passive skills cannot be memorized (%d/%s)\n", skill_id, skill_get_name(skill_id));
+ if( points < 1 )
+ ShowError("spellbook_db: PreservePoints have to be 1 or above! (%d/%s)\n", skill_id, skill_get_name(skill_id));
+ else
+ {
+ skill_spellbook_db[current].skill_id = skill_id;
+ skill_spellbook_db[current].point = points;
+ skill_spellbook_db[current].nameid = nameid;
+
+ return true;
+ }
+
+ return false;
+}
+static bool skill_parse_row_improvisedb(char* split[], int columns, int current)
+{// SkillID,Rate
+ uint16 skill_id = atoi(split[0]);
+ short j = atoi(split[1]);
+
+ if( !skill_get_index(skill_id) || !skill_get_max(skill_id) ) {
+ ShowError("skill_improvise_db: Invalid skill ID %d\n", skill_id);
+ return false;
+ }
+ if ( !skill_get_inf(skill_id) ) {
+ ShowError("skill_improvise_db: Passive skills cannot be casted (%d/%s)\n", skill_id, skill_get_name(skill_id));
+ return false;
+ }
+ if( j < 1 ) {
+ ShowError("skill_improvise_db: Chances have to be 1 or above! (%d/%s)\n", skill_id, skill_get_name(skill_id));
+ return false;
+ }
+ if( current >= MAX_SKILL_IMPROVISE_DB ) {
+ ShowError("skill_improvise_db: Maximum amount of entries reached (%d), increase MAX_SKILL_IMPROVISE_DB\n",MAX_SKILL_IMPROVISE_DB);
+ }
+ skill_improvise_db[current].skill_id = skill_id;
+ skill_improvise_db[current].per = j; // Still need confirm it.
+
+ return true;
+}
+static bool skill_parse_row_magicmushroomdb(char* split[], int column, int current)
+{// SkillID
+ uint16 skill_id = atoi(split[0]);
+
+ if( !skill_get_index(skill_id) || !skill_get_max(skill_id) )
+ {
+ ShowError("magicmushroom_db: Invalid skill ID %d\n", skill_id);
+ return false;
+ }
+ if ( !skill_get_inf(skill_id) )
+ {
+ ShowError("magicmushroom_db: Passive skills cannot be casted (%d/%s)\n", skill_id, skill_get_name(skill_id));
+ return false;
+ }
+
+ skill_magicmushroom_db[current].skill_id = skill_id;
+
+ return true;
+}
+
+static bool skill_parse_row_reproducedb(char* split[], int column, int current) {
+ uint16 skill_id = atoi(split[0]);
+ uint16 idx = skill_get_index(skill_id);
+ if( !idx )
+ return false;
+
+ skill_reproduce_db[idx] = true;
+
+ return true;
+}
+
+
+static bool skill_parse_row_abradb(char* split[], int columns, int current)
+{// skill_id,DummyName,RequiredHocusPocusLevel,Rate
+ uint16 skill_id = atoi(split[0]);
+ if( !skill_get_index(skill_id) || !skill_get_max(skill_id) )
+ {
+ ShowError("abra_db: Invalid skill ID %d\n", skill_id);
+ return false;
+ }
+ if ( !skill_get_inf(skill_id) )
+ {
+ ShowError("abra_db: Passive skills cannot be casted (%d/%s)\n", skill_id, skill_get_name(skill_id));
+ return false;
+ }
+
+ skill_abra_db[current].skill_id = skill_id;
+ skill_abra_db[current].req_lv = atoi(split[2]);
+ skill_abra_db[current].per = atoi(split[3]);
+
+ return true;
+}
+
+static bool skill_parse_row_changematerialdb(char* split[], int columns, int current)
+{// ProductID,BaseRate,MakeAmount1,MakeAmountRate1...,MakeAmount5,MakeAmountRate5
+ uint16 skill_id = atoi(split[0]);
+ short j = atoi(split[1]);
+ int x,y;
+
+ for(x=0; x<MAX_SKILL_PRODUCE_DB; x++){
+ if( skill_produce_db[x].nameid == skill_id )
+ if( skill_produce_db[x].req_skill == GN_CHANGEMATERIAL )
+ break;
+ }
+
+ if( x >= MAX_SKILL_PRODUCE_DB ){
+ ShowError("changematerial_db: Not supported item ID(%d) for Change Material. \n", skill_id);
+ return false;
+ }
+
+ if( current >= MAX_SKILL_PRODUCE_DB ) {
+ ShowError("skill_changematerial_db: Maximum amount of entries reached (%d), increase MAX_SKILL_PRODUCE_DB\n",MAX_SKILL_PRODUCE_DB);
+ }
+
+ skill_changematerial_db[current].itemid = skill_id;
+ skill_changematerial_db[current].rate = j;
+
+ for( x = 2, y = 0; x+1 < columns && split[x] && split[x+1] && y < 5; x += 2, y++ )
+ {
+ skill_changematerial_db[current].qty[y] = atoi(split[x]);
+ skill_changematerial_db[current].qty_rate[y] = atoi(split[x+1]);
+ }
+
+ return true;
+}
+
+/*===============================
+ * DB reading.
+ * skill_db.txt
+ * skill_require_db.txt
+ * skill_cast_db.txt
+ * skill_castnodex_db.txt
+ * skill_nocast_db.txt
+ * skill_unit_db.txt
+ * produce_db.txt
+ * create_arrow_db.txt
+ * abra_db.txt
+ *------------------------------*/
+static void skill_readdb(void)
+{
+ // init skill db structures
+ db_clear(skilldb_name2id);
+ memset(skill_db,0,sizeof(skill_db));
+ memset(skill_produce_db,0,sizeof(skill_produce_db));
+ memset(skill_arrow_db,0,sizeof(skill_arrow_db));
+ memset(skill_abra_db,0,sizeof(skill_abra_db));
+ memset(skill_spellbook_db,0,sizeof(skill_spellbook_db));
+ memset(skill_magicmushroom_db,0,sizeof(skill_magicmushroom_db));
+ memset(skill_reproduce_db,0,sizeof(skill_reproduce_db));
+ memset(skill_changematerial_db,0,sizeof(skill_changematerial_db));
+
+ // load skill databases
+ safestrncpy(skill_db[0].name, "UNKNOWN_SKILL", sizeof(skill_db[0].name));
+ safestrncpy(skill_db[0].desc, "Unknown Skill", sizeof(skill_db[0].desc));
+
+ sv_readdb(db_path, DBPATH"skill_db.txt" , ',', 17, 17, MAX_SKILL_DB, skill_parse_row_skilldb);
+ sv_readdb(db_path, DBPATH"skill_require_db.txt" , ',', 32, 32, MAX_SKILL_DB, skill_parse_row_requiredb);
+#ifdef RENEWAL_CAST
+ sv_readdb(db_path, "re/skill_cast_db.txt" , ',', 8, 8, MAX_SKILL_DB, skill_parse_row_castdb);
+#else
+ sv_readdb(db_path, "pre-re/skill_cast_db.txt" , ',', 7, 7, MAX_SKILL_DB, skill_parse_row_castdb);
+#endif
+ sv_readdb(db_path, DBPATH"skill_castnodex_db.txt", ',', 2, 3, MAX_SKILL_DB, skill_parse_row_castnodexdb);
+ sv_readdb(db_path, DBPATH"skill_unit_db.txt" , ',', 8, 8, MAX_SKILL_DB, skill_parse_row_unitdb);
+
+ sv_readdb(db_path, DBPATH"skill_nocast_db.txt" , ',', 2, 2, MAX_SKILL_DB, skill_parse_row_nocastdb);
+
+ skill_init_unit_layout();
+ sv_readdb(db_path, "produce_db.txt" , ',', 4, 4+2*MAX_PRODUCE_RESOURCE, MAX_SKILL_PRODUCE_DB, skill_parse_row_producedb);
+ sv_readdb(db_path, "create_arrow_db.txt" , ',', 1+2, 1+2*MAX_ARROW_RESOURCE, MAX_SKILL_ARROW_DB, skill_parse_row_createarrowdb);
+ sv_readdb(db_path, "abra_db.txt" , ',', 4, 4, MAX_SKILL_ABRA_DB, skill_parse_row_abradb);
+ //Warlock
+ sv_readdb(db_path, "spellbook_db.txt" , ',', 3, 3, MAX_SKILL_SPELLBOOK_DB, skill_parse_row_spellbookdb);
+ //Guillotine Cross
+ sv_readdb(db_path, "magicmushroom_db.txt" , ',', 1, 1, MAX_SKILL_MAGICMUSHROOM_DB, skill_parse_row_magicmushroomdb);
+ sv_readdb(db_path, "skill_reproduce_db.txt", ',', 1, 1, MAX_SKILL_DB, skill_parse_row_reproducedb);
+ sv_readdb(db_path, "skill_improvise_db.txt" , ',', 2, 2, MAX_SKILL_IMPROVISE_DB, skill_parse_row_improvisedb);
+ sv_readdb(db_path, "skill_changematerial_db.txt" , ',', 4, 4+2*5, MAX_SKILL_PRODUCE_DB, skill_parse_row_changematerialdb);
+
+}
+
+void skill_reload (void) {
+ struct s_mapiterator *iter;
+ struct map_session_data *sd;
+ skill_readdb();
+ /* lets update all players skill tree : so that if any skill modes were changed they're properly updated */
+ iter = mapit_getallusers();
+ for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) )
+ clif_skillinfoblock(sd);
+ mapit_free(iter);
+
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+int do_init_skill (void)
+{
+ skilldb_name2id = strdb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, 0);
+ skill_readdb();
+
+ group_db = idb_alloc(DB_OPT_BASE);
+ skillunit_db = idb_alloc(DB_OPT_BASE);
+ skillcd_db = idb_alloc(DB_OPT_RELEASE_DATA);
+ skillusave_db = idb_alloc(DB_OPT_RELEASE_DATA);
+ skill_unit_ers = ers_new(sizeof(struct skill_unit_group),"skill.c::skill_unit_ers",ERS_OPT_NONE);
+ skill_timer_ers = ers_new(sizeof(struct skill_timerskill),"skill.c::skill_timer_ers",ERS_OPT_NONE);
+
+ add_timer_func_list(skill_unit_timer,"skill_unit_timer");
+ add_timer_func_list(skill_castend_id,"skill_castend_id");
+ add_timer_func_list(skill_castend_pos,"skill_castend_pos");
+ add_timer_func_list(skill_timerskill,"skill_timerskill");
+ add_timer_func_list(skill_blockpc_end, "skill_blockpc_end");
+
+ add_timer_interval(gettick()+SKILLUNITTIMER_INTERVAL,skill_unit_timer,0,0,SKILLUNITTIMER_INTERVAL);
+
+ return 0;
+}
+
+int do_final_skill(void)
+{
+ db_destroy(skilldb_name2id);
+ db_destroy(group_db);
+ db_destroy(skillunit_db);
+ db_destroy(skillcd_db);
+ db_destroy(skillusave_db);
+ ers_destroy(skill_unit_ers);
+ ers_destroy(skill_timer_ers);
+ return 0;
+}
diff --git a/src/map/status.c b/src/map/status.c
index 649cfa1ae..cc322b686 100644
--- a/src/map/status.c
+++ b/src/map/status.c
@@ -1,11294 +1,11290 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include "../common/cbasetypes.h"
-#include "../common/timer.h"
-#include "../common/nullpo.h"
-#include "../common/random.h"
-#include "../common/showmsg.h"
-#include "../common/malloc.h"
-#include "../common/utils.h"
-#include "../common/ers.h"
-#include "../common/strlib.h"
-
-#include "map.h"
-#include "path.h"
-#include "pc.h"
-#include "pet.h"
-#include "npc.h"
-#include "mob.h"
-#include "clif.h"
-#include "guild.h"
-#include "skill.h"
-#include "itemdb.h"
-#include "battle.h"
-#include "chrif.h"
-#include "skill.h"
-#include "status.h"
-#include "script.h"
-#include "unit.h"
-#include "homunculus.h"
-#include "mercenary.h"
-#include "elemental.h"
-#include "vending.h"
-
-#include <time.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <memory.h>
-#include <string.h>
-#include <math.h>
-
-//Regen related flags.
-enum e_regen
-{
- RGN_HP = 0x01,
- RGN_SP = 0x02,
- RGN_SHP = 0x04,
- RGN_SSP = 0x08,
-};
-
-static int max_weight_base[CLASS_COUNT];
-static int hp_coefficient[CLASS_COUNT];
-static int hp_coefficient2[CLASS_COUNT];
-static int hp_sigma_val[CLASS_COUNT][MAX_LEVEL+1];
-static int sp_coefficient[CLASS_COUNT];
-#ifdef RENEWAL_ASPD
-static int aspd_base[CLASS_COUNT][MAX_WEAPON_TYPE+1];
-#else
-static int aspd_base[CLASS_COUNT][MAX_WEAPON_TYPE]; //[blackhole89]
-#endif
-
-// bonus values and upgrade chances for refining equipment
-static struct {
- int chance[MAX_REFINE]; // success chance
- int bonus[MAX_REFINE]; // cumulative fixed bonus damage
- int randombonus_max[MAX_REFINE]; // cumulative maximum random bonus damage
-} refine_info[REFINE_TYPE_MAX];
-
-static int atkmods[3][MAX_WEAPON_TYPE]; //ATK weapon modification for size (size_fix.txt)
-static char job_bonus[CLASS_COUNT][MAX_LEVEL];
-
-static struct eri *sc_data_ers; //For sc_data entries
-static struct status_data dummy_status;
-
-int current_equip_item_index; //Contains inventory index of an equipped item. To pass it into the EQUP_SCRIPT [Lupus]
-int current_equip_card_id; //To prevent card-stacking (from jA) [Skotlex]
-//we need it for new cards 15 Feb 2005, to check if the combo cards are insrerted into the CURRENT weapon only
-//to avoid cards exploits
-
-static sc_type SkillStatusChangeTable[MAX_SKILL]; // skill -> status
-static int StatusIconChangeTable[SC_MAX]; // status -> "icon" (icon is a bit of a misnomer, since there exist values with no icon associated)
-static unsigned int StatusChangeFlagTable[SC_MAX]; // status -> flags
-static int StatusSkillChangeTable[SC_MAX]; // status -> skill
-static int StatusRelevantBLTypes[SI_MAX]; // "icon" -> enum bl_type (for clif_status_change to identify for which bl types to send packets)
-static unsigned int StatusChangeStateTable[SC_MAX]; // status -> flags
-
-
-/**
- * Returns the status change associated with a skill.
- * @param skill The skill to look up
- * @return The status registered for this skill
- **/
-sc_type status_skill2sc(int skill)
-{
- int idx = skill_get_index(skill);
- if( idx == 0 ) {
- ShowError("status_skill2sc: Unsupported skill id %d\n", skill);
- return SC_NONE;
- }
- return SkillStatusChangeTable[idx];
-}
-
-/**
- * Returns the FIRST skill (in order of definition in initChangeTables) to use a given status change.
- * Utilized for various duration lookups. Use with caution!
- * @param sc The status to look up
- * @return A skill associated with the status
- **/
-int status_sc2skill(sc_type sc)
-{
- if( sc < 0 || sc >= SC_MAX ) {
- ShowError("status_sc2skill: Unsupported status change id %d\n", sc);
- return 0;
- }
-
- return StatusSkillChangeTable[sc];
-}
-
-/**
- * Returns the status calculation flag associated with a given status change.
- * @param sc The status to look up
- * @return The scb_flag registered for this status (see enum scb_flag)
- **/
-unsigned int status_sc2scb_flag(sc_type sc)
-{
- if( sc < 0 || sc >= SC_MAX ) {
- ShowError("status_sc2scb_flag: Unsupported status change id %d\n", sc);
- return SCB_NONE;
- }
-
- return StatusChangeFlagTable[sc];
-}
-
-/**
- * Returns the bl types which require a status change packet to be sent for a given client status identifier.
- * @param type The client-side status identifier to look up (see enum si_type)
- * @return The bl types relevant to the type (see enum bl_type)
- **/
-int status_type2relevant_bl_types(int type)
-{
- if( type < 0 || type >= SI_MAX ) {
- ShowError("status_type2relevant_bl_types: Unsupported type %d\n", type);
- return SI_BLANK;
- }
-
- return StatusRelevantBLTypes[type];
-}
-
-#define add_sc(skill,sc) set_sc(skill,sc,SI_BLANK,SCB_NONE)
-// indicates that the status displays a visual effect for the affected unit, and should be sent to the client for all supported units
-#define set_sc_with_vfx(skill, sc, icon, flag) set_sc((skill), (sc), (icon), (flag)); if((icon) < SI_MAX) StatusRelevantBLTypes[(icon)] |= BL_SCEFFECT
-
-static void set_sc(uint16 skill_id, sc_type sc, int icon, unsigned int flag)
-{
- uint16 idx = skill_get_index(skill_id);
- if( idx == 0 ) {
- ShowError("set_sc: Unsupported skill id %d\n", skill_id);
- return;
- }
- if( sc < 0 || sc >= SC_MAX ) {
- ShowError("set_sc: Unsupported status change id %d\n", sc);
- return;
- }
-
- if( StatusSkillChangeTable[sc] == 0 )
- StatusSkillChangeTable[sc] = skill_id;
- if( StatusIconChangeTable[sc] == SI_BLANK )
- StatusIconChangeTable[sc] = icon;
- StatusChangeFlagTable[sc] |= flag;
-
- if( SkillStatusChangeTable[idx] == SC_NONE )
- SkillStatusChangeTable[idx] = sc;
-}
-
-void initChangeTables(void) {
- int i;
-
- for (i = 0; i < SC_MAX; i++)
- StatusIconChangeTable[i] = SI_BLANK;
-
- for (i = 0; i < MAX_SKILL; i++)
- SkillStatusChangeTable[i] = SC_NONE;
-
- for (i = 0; i < SI_MAX; i++)
- StatusRelevantBLTypes[i] = BL_PC;
-
- memset(StatusSkillChangeTable, 0, sizeof(StatusSkillChangeTable));
- memset(StatusChangeFlagTable, 0, sizeof(StatusChangeFlagTable));
- memset(StatusChangeStateTable, 0, sizeof(StatusChangeStateTable));
-
-
- //First we define the skill for common ailments. These are used in skill_additional_effect through sc cards. [Skotlex]
- set_sc( NPC_PETRIFYATTACK , SC_STONE , SI_BLANK , SCB_DEF_ELE|SCB_DEF|SCB_MDEF );
- set_sc( NPC_WIDEFREEZE , SC_FREEZE , SI_BLANK , SCB_DEF_ELE|SCB_DEF|SCB_MDEF );
- set_sc( NPC_STUNATTACK , SC_STUN , SI_BLANK , SCB_NONE );
- set_sc( NPC_SLEEPATTACK , SC_SLEEP , SI_BLANK , SCB_NONE );
- set_sc( NPC_POISON , SC_POISON , SI_BLANK , SCB_DEF2|SCB_REGEN );
- set_sc( NPC_CURSEATTACK , SC_CURSE , SI_BLANK , SCB_LUK|SCB_BATK|SCB_WATK|SCB_SPEED );
- set_sc( NPC_SILENCEATTACK , SC_SILENCE , SI_BLANK , SCB_NONE );
- set_sc( NPC_WIDECONFUSE , SC_CONFUSION , SI_BLANK , SCB_NONE );
- set_sc( NPC_BLINDATTACK , SC_BLIND , SI_BLANK , SCB_HIT|SCB_FLEE );
- set_sc( NPC_BLEEDING , SC_BLEEDING , SI_BLEEDING , SCB_REGEN );
- set_sc( NPC_POISON , SC_DPOISON , SI_BLANK , SCB_DEF2|SCB_REGEN );
-
- //The main status definitions
- add_sc( SM_BASH , SC_STUN );
- set_sc( SM_PROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK );
- add_sc( SM_MAGNUM , SC_WATK_ELEMENT );
- set_sc( SM_ENDURE , SC_ENDURE , SI_ENDURE , SCB_MDEF|SCB_DSPD );
- add_sc( MG_SIGHT , SC_SIGHT );
- add_sc( MG_SAFETYWALL , SC_SAFETYWALL );
- add_sc( MG_FROSTDIVER , SC_FREEZE );
- add_sc( MG_STONECURSE , SC_STONE );
- add_sc( AL_RUWACH , SC_RUWACH );
- add_sc( AL_PNEUMA , SC_PNEUMA );
- set_sc( AL_INCAGI , SC_INCREASEAGI , SI_INCREASEAGI , SCB_AGI|SCB_SPEED );
- set_sc( AL_DECAGI , SC_DECREASEAGI , SI_DECREASEAGI , SCB_AGI|SCB_SPEED );
- set_sc( AL_CRUCIS , SC_SIGNUMCRUCIS , SI_SIGNUMCRUCIS , SCB_DEF );
- set_sc( AL_ANGELUS , SC_ANGELUS , SI_ANGELUS , SCB_DEF2 );
- set_sc( AL_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX );
- set_sc( AC_CONCENTRATION , SC_CONCENTRATE , SI_CONCENTRATE , SCB_AGI|SCB_DEX );
- set_sc( TF_HIDING , SC_HIDING , SI_HIDING , SCB_SPEED );
- add_sc( TF_POISON , SC_POISON );
- set_sc( KN_TWOHANDQUICKEN , SC_TWOHANDQUICKEN , SI_TWOHANDQUICKEN , SCB_ASPD );
- add_sc( KN_AUTOCOUNTER , SC_AUTOCOUNTER );
- set_sc( PR_IMPOSITIO , SC_IMPOSITIO , SI_IMPOSITIO , SCB_WATK );
- set_sc( PR_SUFFRAGIUM , SC_SUFFRAGIUM , SI_SUFFRAGIUM , SCB_NONE );
- set_sc( PR_ASPERSIO , SC_ASPERSIO , SI_ASPERSIO , SCB_ATK_ELE );
- set_sc( PR_BENEDICTIO , SC_BENEDICTIO , SI_BENEDICTIO , SCB_DEF_ELE );
- set_sc( PR_SLOWPOISON , SC_SLOWPOISON , SI_SLOWPOISON , SCB_REGEN );
- set_sc( PR_KYRIE , SC_KYRIE , SI_KYRIE , SCB_NONE );
- set_sc( PR_MAGNIFICAT , SC_MAGNIFICAT , SI_MAGNIFICAT , SCB_REGEN );
- set_sc( PR_GLORIA , SC_GLORIA , SI_GLORIA , SCB_LUK );
- add_sc( PR_LEXDIVINA , SC_SILENCE );
- set_sc( PR_LEXAETERNA , SC_AETERNA , SI_AETERNA , SCB_NONE );
- add_sc( WZ_METEOR , SC_STUN );
- add_sc( WZ_VERMILION , SC_BLIND );
- add_sc( WZ_FROSTNOVA , SC_FREEZE );
- add_sc( WZ_STORMGUST , SC_FREEZE );
- set_sc( WZ_QUAGMIRE , SC_QUAGMIRE , SI_QUAGMIRE , SCB_AGI|SCB_DEX|SCB_ASPD|SCB_SPEED );
- set_sc( BS_ADRENALINE , SC_ADRENALINE , SI_ADRENALINE , SCB_ASPD );
- set_sc( BS_WEAPONPERFECT , SC_WEAPONPERFECTION, SI_WEAPONPERFECTION, SCB_NONE );
- set_sc( BS_OVERTHRUST , SC_OVERTHRUST , SI_OVERTHRUST , SCB_NONE );
- set_sc( BS_MAXIMIZE , SC_MAXIMIZEPOWER , SI_MAXIMIZEPOWER , SCB_REGEN );
- add_sc( HT_LANDMINE , SC_STUN );
- add_sc( HT_ANKLESNARE , SC_ANKLE );
- add_sc( HT_SANDMAN , SC_SLEEP );
- add_sc( HT_FLASHER , SC_BLIND );
- add_sc( HT_FREEZINGTRAP , SC_FREEZE );
- set_sc( AS_CLOAKING , SC_CLOAKING , SI_CLOAKING , SCB_CRI|SCB_SPEED );
- add_sc( AS_SONICBLOW , SC_STUN );
- set_sc( AS_ENCHANTPOISON , SC_ENCPOISON , SI_ENCPOISON , SCB_ATK_ELE );
- set_sc( AS_POISONREACT , SC_POISONREACT , SI_POISONREACT , SCB_NONE );
- add_sc( AS_VENOMDUST , SC_POISON );
- add_sc( AS_SPLASHER , SC_SPLASHER );
- set_sc( NV_TRICKDEAD , SC_TRICKDEAD , SI_TRICKDEAD , SCB_REGEN );
- set_sc( SM_AUTOBERSERK , SC_AUTOBERSERK , SI_AUTOBERSERK , SCB_NONE );
- add_sc( TF_SPRINKLESAND , SC_BLIND );
- add_sc( TF_THROWSTONE , SC_STUN );
- set_sc( MC_LOUD , SC_LOUD , SI_LOUD , SCB_STR );
- set_sc( MG_ENERGYCOAT , SC_ENERGYCOAT , SI_ENERGYCOAT , SCB_NONE );
- set_sc( NPC_EMOTION , SC_MODECHANGE , SI_BLANK , SCB_MODE );
- add_sc( NPC_EMOTION_ON , SC_MODECHANGE );
- set_sc( NPC_ATTRICHANGE , SC_ELEMENTALCHANGE , SI_ARMOR_PROPERTY , SCB_DEF_ELE );
- add_sc( NPC_CHANGEWATER , SC_ELEMENTALCHANGE );
- add_sc( NPC_CHANGEGROUND , SC_ELEMENTALCHANGE );
- add_sc( NPC_CHANGEFIRE , SC_ELEMENTALCHANGE );
- add_sc( NPC_CHANGEWIND , SC_ELEMENTALCHANGE );
- add_sc( NPC_CHANGEPOISON , SC_ELEMENTALCHANGE );
- add_sc( NPC_CHANGEHOLY , SC_ELEMENTALCHANGE );
- add_sc( NPC_CHANGEDARKNESS , SC_ELEMENTALCHANGE );
- add_sc( NPC_CHANGETELEKINESIS, SC_ELEMENTALCHANGE );
- add_sc( NPC_POISON , SC_POISON );
- add_sc( NPC_BLINDATTACK , SC_BLIND );
- add_sc( NPC_SILENCEATTACK , SC_SILENCE );
- add_sc( NPC_STUNATTACK , SC_STUN );
- add_sc( NPC_PETRIFYATTACK , SC_STONE );
- add_sc( NPC_CURSEATTACK , SC_CURSE );
- add_sc( NPC_SLEEPATTACK , SC_SLEEP );
- add_sc( NPC_MAGICALATTACK , SC_MAGICALATTACK );
- set_sc( NPC_KEEPING , SC_KEEPING , SI_BLANK , SCB_DEF );
- add_sc( NPC_DARKBLESSING , SC_COMA );
- set_sc( NPC_BARRIER , SC_BARRIER , SI_BLANK , SCB_MDEF|SCB_DEF );
- add_sc( NPC_DEFENDER , SC_ARMOR );
- add_sc( NPC_LICK , SC_STUN );
- set_sc( NPC_HALLUCINATION , SC_HALLUCINATION , SI_HALLUCINATION , SCB_NONE );
- add_sc( NPC_REBIRTH , SC_REBIRTH );
- add_sc( RG_RAID , SC_STUN );
-#ifdef RENEWAL
- add_sc( RG_RAID , SC_RAID );
- add_sc( RG_BACKSTAP , SC_STUN );
-#endif
- set_sc( RG_STRIPWEAPON , SC_STRIPWEAPON , SI_STRIPWEAPON , SCB_WATK );
- set_sc( RG_STRIPSHIELD , SC_STRIPSHIELD , SI_STRIPSHIELD , SCB_DEF );
- set_sc( RG_STRIPARMOR , SC_STRIPARMOR , SI_STRIPARMOR , SCB_VIT );
- set_sc( RG_STRIPHELM , SC_STRIPHELM , SI_STRIPHELM , SCB_INT );
- add_sc( AM_ACIDTERROR , SC_BLEEDING );
- set_sc( AM_CP_WEAPON , SC_CP_WEAPON , SI_CP_WEAPON , SCB_NONE );
- set_sc( AM_CP_SHIELD , SC_CP_SHIELD , SI_CP_SHIELD , SCB_NONE );
- set_sc( AM_CP_ARMOR , SC_CP_ARMOR , SI_CP_ARMOR , SCB_NONE );
- set_sc( AM_CP_HELM , SC_CP_HELM , SI_CP_HELM , SCB_NONE );
- set_sc( CR_AUTOGUARD , SC_AUTOGUARD , SI_AUTOGUARD , SCB_NONE );
- add_sc( CR_SHIELDCHARGE , SC_STUN );
- set_sc( CR_REFLECTSHIELD , SC_REFLECTSHIELD , SI_REFLECTSHIELD , SCB_NONE );
- add_sc( CR_HOLYCROSS , SC_BLIND );
- add_sc( CR_GRANDCROSS , SC_BLIND );
- add_sc( CR_DEVOTION , SC_DEVOTION );
- set_sc( CR_PROVIDENCE , SC_PROVIDENCE , SI_PROVIDENCE , SCB_ALL );
- set_sc( CR_DEFENDER , SC_DEFENDER , SI_DEFENDER , SCB_SPEED|SCB_ASPD );
- set_sc( CR_SPEARQUICKEN , SC_SPEARQUICKEN , SI_SPEARQUICKEN , SCB_ASPD|SCB_CRI|SCB_FLEE );
- set_sc( MO_STEELBODY , SC_STEELBODY , SI_STEELBODY , SCB_DEF|SCB_MDEF|SCB_ASPD|SCB_SPEED );
- add_sc( MO_BLADESTOP , SC_BLADESTOP_WAIT );
- add_sc( MO_BLADESTOP , SC_BLADESTOP );
- set_sc( MO_EXPLOSIONSPIRITS , SC_EXPLOSIONSPIRITS, SI_EXPLOSIONSPIRITS, SCB_CRI|SCB_REGEN );
- set_sc( MO_EXTREMITYFIST , SC_EXTREMITYFIST , SI_BLANK , SCB_REGEN );
-#ifdef RENEWAL
- set_sc( MO_EXTREMITYFIST , SC_EXTREMITYFIST2 , SI_EXTREMITYFIST , SCB_NONE );
-#endif
- add_sc( SA_MAGICROD , SC_MAGICROD );
- set_sc( SA_AUTOSPELL , SC_AUTOSPELL , SI_AUTOSPELL , SCB_NONE );
- set_sc( SA_FLAMELAUNCHER , SC_FIREWEAPON , SI_FIREWEAPON , SCB_ATK_ELE );
- set_sc( SA_FROSTWEAPON , SC_WATERWEAPON , SI_WATERWEAPON , SCB_ATK_ELE );
- set_sc( SA_LIGHTNINGLOADER , SC_WINDWEAPON , SI_WINDWEAPON , SCB_ATK_ELE );
- set_sc( SA_SEISMICWEAPON , SC_EARTHWEAPON , SI_EARTHWEAPON , SCB_ATK_ELE );
- set_sc( SA_VOLCANO , SC_VOLCANO , SI_LANDENDOW , SCB_WATK );
- set_sc( SA_DELUGE , SC_DELUGE , SI_LANDENDOW , SCB_MAXHP );
- set_sc( SA_VIOLENTGALE , SC_VIOLENTGALE , SI_LANDENDOW , SCB_FLEE );
- add_sc( SA_REVERSEORCISH , SC_ORCISH );
- add_sc( SA_COMA , SC_COMA );
- set_sc( BD_ENCORE , SC_DANCING , SI_BLANK , SCB_SPEED|SCB_REGEN );
- add_sc( BD_RICHMANKIM , SC_RICHMANKIM );
- set_sc( BD_ETERNALCHAOS , SC_ETERNALCHAOS , SI_BLANK , SCB_DEF2 );
- set_sc( BD_DRUMBATTLEFIELD , SC_DRUMBATTLE , SI_BLANK , SCB_WATK|SCB_DEF );
- set_sc( BD_RINGNIBELUNGEN , SC_NIBELUNGEN , SI_BLANK , SCB_WATK );
- add_sc( BD_ROKISWEIL , SC_ROKISWEIL );
- add_sc( BD_INTOABYSS , SC_INTOABYSS );
- set_sc( BD_SIEGFRIED , SC_SIEGFRIED , SI_BLANK , SCB_ALL );
- add_sc( BA_FROSTJOKER , SC_FREEZE );
- set_sc( BA_WHISTLE , SC_WHISTLE , SI_BLANK , SCB_FLEE|SCB_FLEE2 );
- set_sc( BA_ASSASSINCROSS , SC_ASSNCROS , SI_BLANK , SCB_ASPD );
- add_sc( BA_POEMBRAGI , SC_POEMBRAGI );
- set_sc( BA_APPLEIDUN , SC_APPLEIDUN , SI_BLANK , SCB_MAXHP );
- add_sc( DC_SCREAM , SC_STUN );
- set_sc( DC_HUMMING , SC_HUMMING , SI_BLANK , SCB_HIT );
- set_sc( DC_DONTFORGETME , SC_DONTFORGETME , SI_BLANK , SCB_SPEED|SCB_ASPD );
- set_sc( DC_FORTUNEKISS , SC_FORTUNE , SI_BLANK , SCB_CRI );
- set_sc( DC_SERVICEFORYOU , SC_SERVICE4U , SI_BLANK , SCB_ALL );
- add_sc( NPC_DARKCROSS , SC_BLIND );
- add_sc( NPC_GRANDDARKNESS , SC_BLIND );
- set_sc( NPC_STOP , SC_STOP , SI_STOP , SCB_NONE );
- set_sc( NPC_WEAPONBRAKER , SC_BROKENWEAPON , SI_BROKENWEAPON , SCB_NONE );
- set_sc( NPC_ARMORBRAKE , SC_BROKENARMOR , SI_BROKENARMOR , SCB_NONE );
- set_sc( NPC_CHANGEUNDEAD , SC_CHANGEUNDEAD , SI_UNDEAD , SCB_DEF_ELE );
- set_sc( NPC_POWERUP , SC_INCHITRATE , SI_BLANK , SCB_HIT );
- set_sc( NPC_AGIUP , SC_INCFLEERATE , SI_BLANK , SCB_FLEE );
- add_sc( NPC_INVISIBLE , SC_CLOAKING );
- set_sc( LK_AURABLADE , SC_AURABLADE , SI_AURABLADE , SCB_NONE );
- set_sc( LK_PARRYING , SC_PARRYING , SI_PARRYING , SCB_NONE );
- set_sc( LK_CONCENTRATION , SC_CONCENTRATION , SI_CONCENTRATION , SCB_BATK|SCB_WATK|SCB_HIT|SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_DSPD );
- set_sc( LK_TENSIONRELAX , SC_TENSIONRELAX , SI_TENSIONRELAX , SCB_REGEN );
- set_sc( LK_BERSERK , SC_BERSERK , SI_BERSERK , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP|SCB_REGEN );
- set_sc( HP_ASSUMPTIO , SC_ASSUMPTIO , SI_ASSUMPTIO , SCB_NONE );
- add_sc( HP_BASILICA , SC_BASILICA );
- set_sc( HW_MAGICPOWER , SC_MAGICPOWER , SI_MAGICPOWER , SCB_MATK );
- add_sc( PA_SACRIFICE , SC_SACRIFICE );
- set_sc( PA_GOSPEL , SC_GOSPEL , SI_BLANK , SCB_SPEED|SCB_ASPD );
- add_sc( PA_GOSPEL , SC_SCRESIST );
- add_sc( CH_TIGERFIST , SC_STOP );
- set_sc( ASC_EDP , SC_EDP , SI_EDP , SCB_NONE );
- set_sc( SN_SIGHT , SC_TRUESIGHT , SI_TRUESIGHT , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK|SCB_CRI|SCB_HIT );
- set_sc( SN_WINDWALK , SC_WINDWALK , SI_WINDWALK , SCB_FLEE|SCB_SPEED );
- set_sc( WS_MELTDOWN , SC_MELTDOWN , SI_MELTDOWN , SCB_NONE );
- set_sc( WS_CARTBOOST , SC_CARTBOOST , SI_CARTBOOST , SCB_SPEED );
- set_sc( ST_CHASEWALK , SC_CHASEWALK , SI_BLANK , SCB_SPEED );
- set_sc( ST_REJECTSWORD , SC_REJECTSWORD , SI_REJECTSWORD , SCB_NONE );
- add_sc( ST_REJECTSWORD , SC_AUTOCOUNTER );
- set_sc( CG_MARIONETTE , SC_MARIONETTE , SI_MARIONETTE , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK );
- set_sc( CG_MARIONETTE , SC_MARIONETTE2 , SI_MARIONETTE2 , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK );
- add_sc( LK_SPIRALPIERCE , SC_STOP );
- add_sc( LK_HEADCRUSH , SC_BLEEDING );
- set_sc( LK_JOINTBEAT , SC_JOINTBEAT , SI_JOINTBEAT , SCB_BATK|SCB_DEF2|SCB_SPEED|SCB_ASPD );
- add_sc( HW_NAPALMVULCAN , SC_CURSE );
- set_sc( PF_MINDBREAKER , SC_MINDBREAKER , SI_BLANK , SCB_MATK|SCB_MDEF2 );
- add_sc( PF_MEMORIZE , SC_MEMORIZE );
- add_sc( PF_FOGWALL , SC_FOGWALL );
- set_sc( PF_SPIDERWEB , SC_SPIDERWEB , SI_BLANK , SCB_FLEE );
- set_sc( WE_BABY , SC_BABY , SI_BABY , SCB_NONE );
- set_sc( TK_RUN , SC_RUN , SI_RUN , SCB_SPEED|SCB_DSPD );
- set_sc( TK_RUN , SC_SPURT , SI_SPURT , SCB_STR );
- set_sc( TK_READYSTORM , SC_READYSTORM , SI_READYSTORM , SCB_NONE );
- set_sc( TK_READYDOWN , SC_READYDOWN , SI_READYDOWN , SCB_NONE );
- add_sc( TK_DOWNKICK , SC_STUN );
- set_sc( TK_READYTURN , SC_READYTURN , SI_READYTURN , SCB_NONE );
- set_sc( TK_READYCOUNTER , SC_READYCOUNTER , SI_READYCOUNTER , SCB_NONE );
- set_sc( TK_DODGE , SC_DODGE , SI_DODGE , SCB_NONE );
- set_sc( TK_SPTIME , SC_EARTHSCROLL , SI_EARTHSCROLL , SCB_NONE );
- add_sc( TK_SEVENWIND , SC_SEVENWIND );
- set_sc( TK_SEVENWIND , SC_GHOSTWEAPON , SI_GHOSTWEAPON , SCB_ATK_ELE );
- set_sc( TK_SEVENWIND , SC_SHADOWWEAPON , SI_SHADOWWEAPON , SCB_ATK_ELE );
- set_sc( SG_SUN_WARM , SC_WARM , SI_WARM , SCB_NONE );
- add_sc( SG_MOON_WARM , SC_WARM );
- add_sc( SG_STAR_WARM , SC_WARM );
- set_sc( SG_SUN_COMFORT , SC_SUN_COMFORT , SI_SUN_COMFORT , SCB_DEF2 );
- set_sc( SG_MOON_COMFORT , SC_MOON_COMFORT , SI_MOON_COMFORT , SCB_FLEE );
- set_sc( SG_STAR_COMFORT , SC_STAR_COMFORT , SI_STAR_COMFORT , SCB_ASPD );
- add_sc( SG_FRIEND , SC_SKILLRATE_UP );
- set_sc( SG_KNOWLEDGE , SC_KNOWLEDGE , SI_BLANK , SCB_ALL );
- set_sc( SG_FUSION , SC_FUSION , SI_BLANK , SCB_SPEED );
- set_sc( BS_ADRENALINE2 , SC_ADRENALINE2 , SI_ADRENALINE2 , SCB_ASPD );
- set_sc( SL_KAIZEL , SC_KAIZEL , SI_KAIZEL , SCB_NONE );
- set_sc( SL_KAAHI , SC_KAAHI , SI_KAAHI , SCB_NONE );
- set_sc( SL_KAUPE , SC_KAUPE , SI_KAUPE , SCB_NONE );
- set_sc( SL_KAITE , SC_KAITE , SI_KAITE , SCB_NONE );
- add_sc( SL_STUN , SC_STUN );
- set_sc( SL_SWOO , SC_SWOO , SI_BLANK , SCB_SPEED );
- set_sc( SL_SKE , SC_SKE , SI_BLANK , SCB_BATK|SCB_WATK|SCB_DEF|SCB_DEF2 );
- set_sc( SL_SKA , SC_SKA , SI_BLANK , SCB_DEF|SCB_MDEF|SCB_ASPD );
- set_sc( SL_SMA , SC_SMA , SI_SMA , SCB_NONE );
- set_sc( SM_SELFPROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK );
- set_sc( ST_PRESERVE , SC_PRESERVE , SI_PRESERVE , SCB_NONE );
- set_sc( PF_DOUBLECASTING , SC_DOUBLECAST , SI_DOUBLECAST , SCB_NONE );
- set_sc( HW_GRAVITATION , SC_GRAVITATION , SI_BLANK , SCB_ASPD );
- add_sc( WS_CARTTERMINATION , SC_STUN );
- set_sc( WS_OVERTHRUSTMAX , SC_MAXOVERTHRUST , SI_MAXOVERTHRUST , SCB_NONE );
- set_sc( CG_LONGINGFREEDOM , SC_LONGING , SI_BLANK , SCB_SPEED|SCB_ASPD );
- add_sc( CG_HERMODE , SC_HERMODE );
- set_sc( ITEM_ENCHANTARMS , SC_ENCHANTARMS , SI_BLANK , SCB_ATK_ELE );
- set_sc( SL_HIGH , SC_SPIRIT , SI_SPIRIT , SCB_ALL );
- set_sc( KN_ONEHAND , SC_ONEHAND , SI_ONEHAND , SCB_ASPD );
- set_sc( GS_FLING , SC_FLING , SI_BLANK , SCB_DEF|SCB_DEF2 );
- add_sc( GS_CRACKER , SC_STUN );
- add_sc( GS_DISARM , SC_STRIPWEAPON );
- add_sc( GS_PIERCINGSHOT , SC_BLEEDING );
- set_sc( GS_MADNESSCANCEL , SC_MADNESSCANCEL , SI_MADNESSCANCEL , SCB_BATK|SCB_ASPD );
- set_sc( GS_ADJUSTMENT , SC_ADJUSTMENT , SI_ADJUSTMENT , SCB_HIT|SCB_FLEE );
- set_sc( GS_INCREASING , SC_INCREASING , SI_ACCURACY , SCB_AGI|SCB_DEX|SCB_HIT );
- set_sc( GS_GATLINGFEVER , SC_GATLINGFEVER , SI_GATLINGFEVER , SCB_BATK|SCB_FLEE|SCB_SPEED|SCB_ASPD );
- set_sc( NJ_TATAMIGAESHI , SC_TATAMIGAESHI , SI_BLANK , SCB_NONE );
- set_sc( NJ_SUITON , SC_SUITON , SI_BLANK , SCB_AGI|SCB_SPEED );
- add_sc( NJ_HYOUSYOURAKU , SC_FREEZE );
- set_sc( NJ_NEN , SC_NEN , SI_NEN , SCB_STR|SCB_INT );
- set_sc( NJ_UTSUSEMI , SC_UTSUSEMI , SI_UTSUSEMI , SCB_NONE );
- set_sc( NJ_BUNSINJYUTSU , SC_BUNSINJYUTSU , SI_BUNSINJYUTSU , SCB_DYE );
-
- add_sc( NPC_ICEBREATH , SC_FREEZE );
- add_sc( NPC_ACIDBREATH , SC_POISON );
- add_sc( NPC_HELLJUDGEMENT , SC_CURSE );
- add_sc( NPC_WIDESILENCE , SC_SILENCE );
- add_sc( NPC_WIDEFREEZE , SC_FREEZE );
- add_sc( NPC_WIDEBLEEDING , SC_BLEEDING );
- add_sc( NPC_WIDESTONE , SC_STONE );
- add_sc( NPC_WIDECONFUSE , SC_CONFUSION );
- add_sc( NPC_WIDESLEEP , SC_SLEEP );
- add_sc( NPC_WIDESIGHT , SC_SIGHT );
- add_sc( NPC_EVILLAND , SC_BLIND );
- add_sc( NPC_MAGICMIRROR , SC_MAGICMIRROR );
- set_sc( NPC_SLOWCAST , SC_SLOWCAST , SI_SLOWCAST , SCB_NONE );
- set_sc( NPC_CRITICALWOUND , SC_CRITICALWOUND , SI_CRITICALWOUND , SCB_NONE );
- set_sc( NPC_STONESKIN , SC_ARMORCHANGE , SI_BLANK , SCB_DEF|SCB_MDEF );
- add_sc( NPC_ANTIMAGIC , SC_ARMORCHANGE );
- add_sc( NPC_WIDECURSE , SC_CURSE );
- add_sc( NPC_WIDESTUN , SC_STUN );
-
- set_sc( NPC_HELLPOWER , SC_HELLPOWER , SI_HELLPOWER , SCB_NONE );
- set_sc( NPC_WIDEHELLDIGNITY , SC_HELLPOWER , SI_HELLPOWER , SCB_NONE );
- set_sc( NPC_INVINCIBLE , SC_INVINCIBLE , SI_INVINCIBLE , SCB_SPEED );
- set_sc( NPC_INVINCIBLEOFF , SC_INVINCIBLEOFF , SI_BLANK , SCB_SPEED );
-
- set_sc( CASH_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX );
- set_sc( CASH_INCAGI , SC_INCREASEAGI , SI_INCREASEAGI , SCB_AGI|SCB_SPEED );
- set_sc( CASH_ASSUMPTIO , SC_ASSUMPTIO , SI_ASSUMPTIO , SCB_NONE );
-
- set_sc( ALL_PARTYFLEE , SC_PARTYFLEE , SI_PARTYFLEE , SCB_NONE );
- set_sc( ALL_ODINS_POWER , SC_ODINS_POWER , SI_ODINS_POWER , SCB_MATK|SCB_BATK|SCB_MDEF|SCB_DEF );
-
- set_sc( CR_SHRINK , SC_SHRINK , SI_SHRINK , SCB_NONE );
- set_sc( RG_CLOSECONFINE , SC_CLOSECONFINE2 , SI_CLOSECONFINE2 , SCB_NONE );
- set_sc( RG_CLOSECONFINE , SC_CLOSECONFINE , SI_CLOSECONFINE , SCB_FLEE );
- set_sc( WZ_SIGHTBLASTER , SC_SIGHTBLASTER , SI_SIGHTBLASTER , SCB_NONE );
- set_sc( DC_WINKCHARM , SC_WINKCHARM , SI_WINKCHARM , SCB_NONE );
- add_sc( MO_BALKYOUNG , SC_STUN );
- add_sc( SA_ELEMENTWATER , SC_ELEMENTALCHANGE );
- add_sc( SA_ELEMENTFIRE , SC_ELEMENTALCHANGE );
- add_sc( SA_ELEMENTGROUND , SC_ELEMENTALCHANGE );
- add_sc( SA_ELEMENTWIND , SC_ELEMENTALCHANGE );
-
- set_sc( HLIF_AVOID , SC_AVOID , SI_BLANK , SCB_SPEED );
- set_sc( HLIF_CHANGE , SC_CHANGE , SI_BLANK , SCB_VIT|SCB_INT );
- set_sc( HFLI_FLEET , SC_FLEET , SI_BLANK , SCB_ASPD|SCB_BATK|SCB_WATK );
- set_sc( HFLI_SPEED , SC_SPEED , SI_BLANK , SCB_FLEE );
- set_sc( HAMI_DEFENCE , SC_DEFENCE , SI_BLANK , SCB_DEF );
- set_sc( HAMI_BLOODLUST , SC_BLOODLUST , SI_BLANK , SCB_BATK|SCB_WATK );
-
- // Homunculus S
- add_sc(MH_STAHL_HORN, SC_STUN);
- set_sc(MH_ANGRIFFS_MODUS, SC_ANGRIFFS_MODUS, SI_ANGRIFFS_MODUS, SCB_BATK | SCB_DEF | SCB_FLEE | SCB_MAXHP);
- set_sc(MH_GOLDENE_FERSE, SC_GOLDENE_FERSE, SI_GOLDENE_FERSE, SCB_ASPD|SCB_MAXHP);
- add_sc( MH_STEINWAND, SC_SAFETYWALL );
- add_sc(MH_ERASER_CUTTER, SC_ERASER_CUTTER);
- set_sc(MH_OVERED_BOOST, SC_OVERED_BOOST, SI_BLANK, SCB_FLEE|SCB_ASPD);
- add_sc(MH_LIGHT_OF_REGENE, SC_LIGHT_OF_REGENE);
- set_sc(MH_VOLCANIC_ASH, SC_ASH, SI_VOLCANIC_ASH, SCB_DEF|SCB_DEF2|SCB_HIT|SCB_BATK|SCB_FLEE);
- set_sc(MH_GRANITIC_ARMOR, SC_GRANITIC_ARMOR, SI_GRANITIC_ARMOR, SCB_NONE);
- set_sc(MH_MAGMA_FLOW, SC_MAGMA_FLOW, SI_MAGMA_FLOW, SCB_NONE);
- set_sc(MH_PYROCLASTIC, SC_PYROCLASTIC, SI_PYROCLASTIC, SCB_BATK|SCB_ATK_ELE);
- add_sc(MH_LAVA_SLIDE, SC_BURNING);
- set_sc(MH_NEEDLE_OF_PARALYZE, SC_PARALYSIS, SI_NEEDLE_OF_PARALYZE, SCB_DEF2);
- add_sc(MH_POISON_MIST, SC_BLIND);
- set_sc(MH_PAIN_KILLER, SC_PAIN_KILLER, SI_PAIN_KILLER, SCB_ASPD);
-
- add_sc(MH_STYLE_CHANGE, SC_STYLE_CHANGE);
- set_sc( MH_TINDER_BREAKER , SC_CLOSECONFINE2 , SI_CLOSECONFINE2 , SCB_NONE );
- set_sc( MH_TINDER_BREAKER , SC_CLOSECONFINE , SI_CLOSECONFINE , SCB_FLEE );
-
-
- add_sc( MER_CRASH , SC_STUN );
- set_sc( MER_PROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK );
- add_sc( MS_MAGNUM , SC_WATK_ELEMENT );
- add_sc( MER_SIGHT , SC_SIGHT );
- set_sc( MER_DECAGI , SC_DECREASEAGI , SI_DECREASEAGI , SCB_AGI|SCB_SPEED );
- set_sc( MER_MAGNIFICAT , SC_MAGNIFICAT , SI_MAGNIFICAT , SCB_REGEN );
- add_sc( MER_LEXDIVINA , SC_SILENCE );
- add_sc( MA_LANDMINE , SC_STUN );
- add_sc( MA_SANDMAN , SC_SLEEP );
- add_sc( MA_FREEZINGTRAP , SC_FREEZE );
- set_sc( MER_AUTOBERSERK , SC_AUTOBERSERK , SI_AUTOBERSERK , SCB_NONE );
- set_sc( ML_AUTOGUARD , SC_AUTOGUARD , SI_AUTOGUARD , SCB_NONE );
- set_sc( MS_REFLECTSHIELD , SC_REFLECTSHIELD , SI_REFLECTSHIELD , SCB_NONE );
- set_sc( ML_DEFENDER , SC_DEFENDER , SI_DEFENDER , SCB_SPEED|SCB_ASPD );
- set_sc( MS_PARRYING , SC_PARRYING , SI_PARRYING , SCB_NONE );
- set_sc( MS_BERSERK , SC_BERSERK , SI_BERSERK , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP|SCB_REGEN );
- add_sc( ML_SPIRALPIERCE , SC_STOP );
- set_sc( MER_QUICKEN , SC_MERC_QUICKEN , SI_BLANK , SCB_ASPD );
- add_sc( ML_DEVOTION , SC_DEVOTION );
- set_sc( MER_KYRIE , SC_KYRIE , SI_KYRIE , SCB_NONE );
- set_sc( MER_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX );
- set_sc( MER_INCAGI , SC_INCREASEAGI , SI_INCREASEAGI , SCB_AGI|SCB_SPEED );
-
- set_sc( GD_LEADERSHIP , SC_LEADERSHIP , SI_BLANK , SCB_STR );
- set_sc( GD_GLORYWOUNDS , SC_GLORYWOUNDS , SI_BLANK , SCB_VIT );
- set_sc( GD_SOULCOLD , SC_SOULCOLD , SI_BLANK , SCB_AGI );
- set_sc( GD_HAWKEYES , SC_HAWKEYES , SI_BLANK , SCB_DEX );
-
- set_sc( GD_BATTLEORDER , SC_BATTLEORDERS , SI_BLANK , SCB_STR|SCB_INT|SCB_DEX );
- set_sc( GD_REGENERATION , SC_REGENERATION , SI_BLANK , SCB_REGEN );
-
- /**
- * Rune Knight
- **/
- set_sc( RK_ENCHANTBLADE , SC_ENCHANTBLADE , SI_ENCHANTBLADE , SCB_NONE );
- set_sc( RK_DRAGONHOWLING , SC_FEAR , SI_BLANK , SCB_FLEE|SCB_HIT );
- set_sc( RK_DEATHBOUND , SC_DEATHBOUND , SI_DEATHBOUND , SCB_NONE );
- set_sc( RK_WINDCUTTER , SC_FEAR , SI_BLANK , SCB_FLEE|SCB_HIT );
- add_sc( RK_DRAGONBREATH , SC_BURNING );
- set_sc( RK_MILLENNIUMSHIELD , SC_MILLENNIUMSHIELD , SI_REUSE_MILLENNIUMSHIELD , SCB_NONE );
- set_sc( RK_REFRESH , SC_REFRESH , SI_REFRESH , SCB_NONE );
- set_sc( RK_GIANTGROWTH , SC_GIANTGROWTH , SI_GIANTGROWTH , SCB_STR );
- set_sc( RK_STONEHARDSKIN , SC_STONEHARDSKIN , SI_STONEHARDSKIN , SCB_NONE );
- set_sc( RK_VITALITYACTIVATION, SC_VITALITYACTIVATION, SI_VITALITYACTIVATION, SCB_REGEN );
- set_sc( RK_FIGHTINGSPIRIT , SC_FIGHTINGSPIRIT , SI_FIGHTINGSPIRIT , SCB_WATK|SCB_ASPD );
- set_sc( RK_ABUNDANCE , SC_ABUNDANCE , SI_ABUNDANCE , SCB_NONE );
- set_sc( RK_CRUSHSTRIKE , SC_CRUSHSTRIKE , SI_CRUSHSTRIKE , SCB_NONE );
- /**
- * GC Guillotine Cross
- **/
- set_sc_with_vfx( GC_VENOMIMPRESS , SC_VENOMIMPRESS , SI_VENOMIMPRESS , SCB_NONE );
- set_sc( GC_POISONINGWEAPON , SC_POISONINGWEAPON , SI_POISONINGWEAPON , SCB_NONE );
- set_sc( GC_WEAPONBLOCKING , SC_WEAPONBLOCKING , SI_WEAPONBLOCKING , SCB_NONE );
- set_sc( GC_CLOAKINGEXCEED , SC_CLOAKINGEXCEED , SI_CLOAKINGEXCEED , SCB_SPEED );
- set_sc( GC_HALLUCINATIONWALK , SC_HALLUCINATIONWALK, SI_HALLUCINATIONWALK, SCB_FLEE );
- set_sc( GC_ROLLINGCUTTER , SC_ROLLINGCUTTER , SI_ROLLINGCUTTER , SCB_NONE );
- /**
- * Arch Bishop
- **/
- set_sc( AB_ADORAMUS , SC_ADORAMUS , SI_ADORAMUS , SCB_AGI|SCB_SPEED );
- add_sc( AB_CLEMENTIA , SC_BLESSING );
- add_sc( AB_CANTO , SC_INCREASEAGI );
- set_sc( AB_EPICLESIS , SC_EPICLESIS , SI_EPICLESIS , SCB_MAXHP );
- add_sc( AB_PRAEFATIO , SC_KYRIE );
- set_sc_with_vfx( AB_ORATIO , SC_ORATIO , SI_ORATIO , SCB_NONE );
- set_sc( AB_LAUDAAGNUS , SC_LAUDAAGNUS , SI_LAUDAAGNUS , SCB_VIT );
- set_sc( AB_LAUDARAMUS , SC_LAUDARAMUS , SI_LAUDARAMUS , SCB_LUK );
- set_sc( AB_RENOVATIO , SC_RENOVATIO , SI_RENOVATIO , SCB_REGEN );
- set_sc( AB_EXPIATIO , SC_EXPIATIO , SI_EXPIATIO , SCB_ATK_ELE );
- set_sc( AB_DUPLELIGHT , SC_DUPLELIGHT , SI_DUPLELIGHT , SCB_NONE );
- set_sc( AB_SECRAMENT , SC_SECRAMENT , SI_SECRAMENT , SCB_NONE );
- /**
- * Warlock
- **/
- add_sc( WL_WHITEIMPRISON , SC_WHITEIMPRISON );
- set_sc_with_vfx( WL_FROSTMISTY , SC_FREEZING , SI_FROSTMISTY , SCB_ASPD|SCB_SPEED|SCB_DEF|SCB_DEF2 );
- set_sc( WL_MARSHOFABYSS , SC_MARSHOFABYSS , SI_MARSHOFABYSS , SCB_SPEED|SCB_FLEE|SCB_DEF|SCB_MDEF );
- set_sc(WL_RECOGNIZEDSPELL , SC_RECOGNIZEDSPELL , SI_RECOGNIZEDSPELL , SCB_MATK);
- set_sc( WL_STASIS , SC_STASIS , SI_STASIS , SCB_NONE );
- /**
- * Ranger
- **/
- set_sc( RA_FEARBREEZE , SC_FEARBREEZE , SI_FEARBREEZE , SCB_NONE );
- set_sc( RA_ELECTRICSHOCKER , SC_ELECTRICSHOCKER , SI_ELECTRICSHOCKER , SCB_NONE );
- set_sc( RA_WUGDASH , SC_WUGDASH , SI_WUGDASH , SCB_SPEED );
- set_sc( RA_CAMOUFLAGE , SC_CAMOUFLAGE , SI_CAMOUFLAGE , SCB_SPEED );
- add_sc( RA_MAGENTATRAP , SC_ELEMENTALCHANGE );
- add_sc( RA_COBALTTRAP , SC_ELEMENTALCHANGE );
- add_sc( RA_MAIZETRAP , SC_ELEMENTALCHANGE );
- add_sc( RA_VERDURETRAP , SC_ELEMENTALCHANGE );
- add_sc( RA_FIRINGTRAP , SC_BURNING );
- set_sc_with_vfx( RA_ICEBOUNDTRAP , SC_FREEZING , SI_FROSTMISTY , SCB_NONE );
- /**
- * Mechanic
- **/
- set_sc( NC_ACCELERATION , SC_ACCELERATION , SI_ACCELERATION , SCB_SPEED );
- set_sc( NC_HOVERING , SC_HOVERING , SI_HOVERING , SCB_SPEED );
- set_sc( NC_SHAPESHIFT , SC_SHAPESHIFT , SI_SHAPESHIFT , SCB_DEF_ELE );
- set_sc( NC_INFRAREDSCAN , SC_INFRAREDSCAN , SI_INFRAREDSCAN , SCB_FLEE );
- set_sc( NC_ANALYZE , SC_ANALYZE , SI_ANALYZE , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2 );
- set_sc( NC_MAGNETICFIELD , SC_MAGNETICFIELD , SI_MAGNETICFIELD , SCB_NONE );
- set_sc( NC_NEUTRALBARRIER , SC_NEUTRALBARRIER , SI_NEUTRALBARRIER , SCB_NONE );
- set_sc( NC_STEALTHFIELD , SC_STEALTHFIELD , SI_STEALTHFIELD , SCB_NONE );
- /**
- * Royal Guard
- **/
- set_sc( LG_REFLECTDAMAGE , SC_REFLECTDAMAGE , SI_LG_REFLECTDAMAGE, SCB_NONE );
- set_sc( LG_FORCEOFVANGUARD , SC_FORCEOFVANGUARD , SI_FORCEOFVANGUARD , SCB_MAXHP|SCB_DEF );
- set_sc( LG_EXEEDBREAK , SC_EXEEDBREAK , SI_EXEEDBREAK , SCB_NONE );
- set_sc( LG_PRESTIGE , SC_PRESTIGE , SI_PRESTIGE , SCB_DEF );
- set_sc( LG_BANDING , SC_BANDING , SI_BANDING , SCB_DEF2|SCB_WATK );// Renewal: atk2 & def2
- set_sc( LG_PIETY , SC_BENEDICTIO , SI_BENEDICTIO , SCB_DEF_ELE );
- set_sc( LG_EARTHDRIVE , SC_EARTHDRIVE , SI_EARTHDRIVE , SCB_DEF|SCB_ASPD );
- set_sc( LG_INSPIRATION , SC_INSPIRATION , SI_INSPIRATION , SCB_MAXHP|SCB_WATK|SCB_HIT|SCB_VIT|SCB_AGI|SCB_STR|SCB_DEX|SCB_INT|SCB_LUK);
- set_sc( LG_SHIELDSPELL , SC_SHIELDSPELL_DEF , SI_SHIELDSPELL_DEF , SCB_WATK );
- set_sc( LG_SHIELDSPELL , SC_SHIELDSPELL_REF , SI_SHIELDSPELL_REF , SCB_DEF );
- /**
- * Shadow Chaser
- **/
- set_sc( SC_REPRODUCE , SC__REPRODUCE , SI_REPRODUCE , SCB_NONE );
- set_sc( SC_AUTOSHADOWSPELL , SC__AUTOSHADOWSPELL, SI_AUTOSHADOWSPELL , SCB_NONE );
- set_sc( SC_SHADOWFORM , SC__SHADOWFORM , SI_SHADOWFORM , SCB_NONE );
- set_sc( SC_BODYPAINT , SC__BODYPAINT , SI_BODYPAINT , SCB_ASPD );
- set_sc( SC_INVISIBILITY , SC__INVISIBILITY , SI_INVISIBILITY , SCB_ASPD|SCB_CRI|SCB_ATK_ELE );
- set_sc( SC_DEADLYINFECT , SC__DEADLYINFECT , SI_DEADLYINFECT , SCB_NONE );
- set_sc( SC_ENERVATION , SC__ENERVATION , SI_ENERVATION , SCB_BATK );
- set_sc( SC_GROOMY , SC__GROOMY , SI_GROOMY , SCB_ASPD|SCB_HIT|SCB_SPEED );
- set_sc( SC_IGNORANCE , SC__IGNORANCE , SI_IGNORANCE , SCB_NONE );
- set_sc( SC_LAZINESS , SC__LAZINESS , SI_LAZINESS , SCB_FLEE );
- set_sc( SC_UNLUCKY , SC__UNLUCKY , SI_UNLUCKY , SCB_CRI|SCB_FLEE2 );
- set_sc( SC_WEAKNESS , SC__WEAKNESS , SI_WEAKNESS , SCB_FLEE2|SCB_MAXHP );
- set_sc( SC_STRIPACCESSARY , SC__STRIPACCESSORY , SI_STRIPACCESSARY , SCB_DEX|SCB_INT|SCB_LUK );
- set_sc_with_vfx( SC_MANHOLE , SC__MANHOLE , SI_MANHOLE , SCB_NONE );
- add_sc( SC_CHAOSPANIC , SC_CONFUSION );
- set_sc_with_vfx( SC_BLOODYLUST , SC__BLOODYLUST , SI_BLOODYLUST , SCB_DEF | SCB_DEF2 | SCB_MDEF | SCB_MDEF2 | SCB_FLEE | SCB_SPEED | SCB_ASPD | SCB_MAXHP | SCB_REGEN );
- /**
- * Sura
- **/
- add_sc( SR_DRAGONCOMBO , SC_STUN );
- add_sc( SR_EARTHSHAKER , SC_STUN );
- set_sc( SR_CRESCENTELBOW , SC_CRESCENTELBOW , SI_CRESCENTELBOW , SCB_NONE );
- set_sc_with_vfx( SR_CURSEDCIRCLE , SC_CURSEDCIRCLE_TARGET, SI_CURSEDCIRCLE_TARGET , SCB_NONE );
- set_sc( SR_LIGHTNINGWALK , SC_LIGHTNINGWALK , SI_LIGHTNINGWALK , SCB_NONE );
- set_sc( SR_RAISINGDRAGON , SC_RAISINGDRAGON , SI_RAISINGDRAGON , SCB_REGEN|SCB_MAXHP|SCB_MAXSP );
- set_sc( SR_GENTLETOUCH_ENERGYGAIN, SC_GT_ENERGYGAIN , SI_GENTLETOUCH_ENERGYGAIN, SCB_NONE );
- set_sc( SR_GENTLETOUCH_CHANGE , SC_GT_CHANGE , SI_GENTLETOUCH_CHANGE , SCB_ASPD|SCB_MDEF|SCB_MAXHP );
- set_sc( SR_GENTLETOUCH_REVITALIZE, SC_GT_REVITALIZE , SI_GENTLETOUCH_REVITALIZE, SCB_MAXHP|SCB_REGEN );
- /**
- * Wanderer / Minstrel
- **/
- set_sc( WA_SWING_DANCE , SC_SWINGDANCE , SI_SWINGDANCE , SCB_SPEED|SCB_ASPD );
- set_sc( WA_SYMPHONY_OF_LOVER , SC_SYMPHONYOFLOVER , SI_SYMPHONYOFLOVERS , SCB_MDEF );
- set_sc( WA_MOONLIT_SERENADE , SC_MOONLITSERENADE , SI_MOONLITSERENADE , SCB_MATK );
- set_sc( MI_RUSH_WINDMILL , SC_RUSHWINDMILL , SI_RUSHWINDMILL , SCB_BATK );
- set_sc( MI_ECHOSONG , SC_ECHOSONG , SI_ECHOSONG , SCB_DEF2 );
- set_sc( MI_HARMONIZE , SC_HARMONIZE , SI_HARMONIZE , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK );
- set_sc_with_vfx( WM_POEMOFNETHERWORLD , SC_NETHERWORLD , SI_NETHERWORLD , SCB_NONE );
- set_sc_with_vfx( WM_VOICEOFSIREN , SC_VOICEOFSIREN , SI_VOICEOFSIREN , SCB_NONE );
- set_sc_with_vfx( WM_LULLABY_DEEPSLEEP , SC_DEEPSLEEP , SI_DEEPSLEEP , SCB_NONE );
- set_sc( WM_SIRCLEOFNATURE , SC_SIRCLEOFNATURE , SI_SIRCLEOFNATURE , SCB_NONE );
- set_sc( WM_GLOOMYDAY , SC_GLOOMYDAY , SI_GLOOMYDAY , SCB_FLEE|SCB_ASPD );
- set_sc( WM_SONG_OF_MANA , SC_SONGOFMANA , SI_SONGOFMANA , SCB_NONE );
- set_sc( WM_DANCE_WITH_WUG , SC_DANCEWITHWUG , SI_DANCEWITHWUG , SCB_ASPD );
- set_sc( WM_SATURDAY_NIGHT_FEVER , SC_SATURDAYNIGHTFEVER , SI_SATURDAYNIGHTFEVER , SCB_BATK|SCB_DEF|SCB_FLEE|SCB_REGEN );
- set_sc( WM_LERADS_DEW , SC_LERADSDEW , SI_LERADSDEW , SCB_MAXHP );
- set_sc( WM_MELODYOFSINK , SC_MELODYOFSINK , SI_MELODYOFSINK , SCB_BATK|SCB_MATK );
- set_sc( WM_BEYOND_OF_WARCRY , SC_BEYONDOFWARCRY , SI_WARCRYOFBEYOND , SCB_BATK|SCB_MATK );
- set_sc( WM_UNLIMITED_HUMMING_VOICE, SC_UNLIMITEDHUMMINGVOICE, SI_UNLIMITEDHUMMINGVOICE, SCB_NONE );
- /**
- * Sorcerer
- **/
- set_sc( SO_FIREWALK , SC_PROPERTYWALK , SI_PROPERTYWALK , SCB_NONE );
- set_sc( SO_ELECTRICWALK , SC_PROPERTYWALK , SI_PROPERTYWALK , SCB_NONE );
- set_sc( SO_SPELLFIST , SC_SPELLFIST , SI_SPELLFIST , SCB_NONE );
- set_sc_with_vfx( SO_DIAMONDDUST , SC_CRYSTALIZE , SI_COLD , SCB_NONE ); // it does show the snow icon on mobs but doesn't affect it.
- add_sc( SO_CLOUD_KILL , SC_POISON );
- set_sc( SO_STRIKING , SC_STRIKING , SI_STRIKING , SCB_WATK|SCB_CRI );
- set_sc( SO_WARMER , SC_WARMER , SI_WARMER , SCB_NONE );
- set_sc( SO_VACUUM_EXTREME , SC_VACUUM_EXTREME , SI_VACUUM_EXTREME , SCB_NONE );
- set_sc( SO_ARRULLO , SC_DEEPSLEEP , SI_DEEPSLEEP , SCB_NONE );
- set_sc( SO_FIRE_INSIGNIA , SC_FIRE_INSIGNIA , SI_FIRE_INSIGNIA , SCB_MATK | SCB_BATK | SCB_WATK | SCB_ATK_ELE | SCB_REGEN );
- set_sc( SO_WATER_INSIGNIA , SC_WATER_INSIGNIA , SI_WATER_INSIGNIA , SCB_WATK | SCB_ATK_ELE | SCB_REGEN );
- set_sc( SO_WIND_INSIGNIA , SC_WIND_INSIGNIA , SI_WIND_INSIGNIA , SCB_WATK | SCB_ATK_ELE | SCB_REGEN );
- set_sc( SO_EARTH_INSIGNIA , SC_EARTH_INSIGNIA , SI_EARTH_INSIGNIA , SCB_MDEF|SCB_DEF|SCB_MAXHP|SCB_MAXSP|SCB_WATK | SCB_ATK_ELE | SCB_REGEN );
- /**
- * Genetic
- **/
- set_sc( GN_CARTBOOST , SC_GN_CARTBOOST, SI_CARTSBOOST , SCB_SPEED );
- set_sc( GN_THORNS_TRAP , SC_THORNSTRAP , SI_THORNTRAP , SCB_NONE );
- set_sc_with_vfx( GN_BLOOD_SUCKER , SC_BLOODSUCKER , SI_BLOODSUCKER , SCB_NONE );
- set_sc( GN_WALLOFTHORN , SC_STOP , SI_BLANK , SCB_NONE );
- set_sc( GN_FIRE_EXPANSION_SMOKE_POWDER, SC_SMOKEPOWDER , SI_FIRE_EXPANSION_SMOKE_POWDER, SCB_NONE );
- set_sc( GN_FIRE_EXPANSION_TEAR_GAS , SC_TEARGAS , SI_FIRE_EXPANSION_TEAR_GAS , SCB_NONE );
- set_sc( GN_MANDRAGORA , SC_MANDRAGORA , SI_MANDRAGORA , SCB_INT );
-
- // Elemental Spirit summoner's 'side' status changes.
- set_sc( EL_CIRCLE_OF_FIRE , SC_CIRCLE_OF_FIRE_OPTION, SI_CIRCLE_OF_FIRE_OPTION, SCB_NONE );
- set_sc( EL_FIRE_CLOAK , SC_FIRE_CLOAK_OPTION , SI_FIRE_CLOAK_OPTION , SCB_ALL );
- set_sc( EL_WATER_SCREEN , SC_WATER_SCREEN_OPTION , SI_WATER_SCREEN_OPTION , SCB_NONE );
- set_sc( EL_WATER_DROP , SC_WATER_DROP_OPTION , SI_WATER_DROP_OPTION , SCB_ALL );
- set_sc( EL_WATER_BARRIER , SC_WATER_BARRIER , SI_WATER_BARRIER , SCB_MDEF|SCB_WATK|SCB_MATK|SCB_FLEE );
- set_sc( EL_WIND_STEP , SC_WIND_STEP_OPTION , SI_WIND_STEP_OPTION , SCB_SPEED|SCB_FLEE );
- set_sc( EL_WIND_CURTAIN , SC_WIND_CURTAIN_OPTION , SI_WIND_CURTAIN_OPTION , SCB_ALL );
- set_sc( EL_ZEPHYR , SC_ZEPHYR , SI_ZEPHYR , SCB_FLEE );
- set_sc( EL_SOLID_SKIN , SC_SOLID_SKIN_OPTION , SI_SOLID_SKIN_OPTION , SCB_DEF|SCB_MAXHP );
- set_sc( EL_STONE_SHIELD , SC_STONE_SHIELD_OPTION , SI_STONE_SHIELD_OPTION , SCB_ALL );
- set_sc( EL_POWER_OF_GAIA , SC_POWER_OF_GAIA , SI_POWER_OF_GAIA , SCB_MAXHP|SCB_DEF|SCB_SPEED );
- set_sc( EL_PYROTECHNIC , SC_PYROTECHNIC_OPTION , SI_PYROTECHNIC_OPTION , SCB_WATK );
- set_sc( EL_HEATER , SC_HEATER_OPTION , SI_HEATER_OPTION , SCB_WATK );
- set_sc( EL_TROPIC , SC_TROPIC_OPTION , SI_TROPIC_OPTION , SCB_WATK );
- set_sc( EL_AQUAPLAY , SC_AQUAPLAY_OPTION , SI_AQUAPLAY_OPTION , SCB_MATK );
- set_sc( EL_COOLER , SC_COOLER_OPTION , SI_COOLER_OPTION , SCB_MATK );
- set_sc( EL_CHILLY_AIR , SC_CHILLY_AIR_OPTION , SI_CHILLY_AIR_OPTION , SCB_MATK );
- set_sc( EL_GUST , SC_GUST_OPTION , SI_GUST_OPTION , SCB_ASPD );
- set_sc( EL_BLAST , SC_BLAST_OPTION , SI_BLAST_OPTION , SCB_ASPD );
- set_sc( EL_WILD_STORM , SC_WILD_STORM_OPTION , SI_WILD_STORM_OPTION , SCB_ASPD );
- set_sc( EL_PETROLOGY , SC_PETROLOGY_OPTION , SI_PETROLOGY_OPTION , SCB_MAXHP );
- set_sc( EL_CURSED_SOIL , SC_CURSED_SOIL_OPTION , SI_CURSED_SOIL_OPTION , SCB_NONE );
- set_sc( EL_UPHEAVAL , SC_UPHEAVAL_OPTION , SI_UPHEAVAL_OPTION , SCB_NONE );
- set_sc( EL_TIDAL_WEAPON , SC_TIDAL_WEAPON_OPTION , SI_TIDAL_WEAPON_OPTION , SCB_ALL );
- set_sc( EL_ROCK_CRUSHER , SC_ROCK_CRUSHER , SI_ROCK_CRUSHER , SCB_DEF );
- set_sc( EL_ROCK_CRUSHER_ATK, SC_ROCK_CRUSHER_ATK , SI_ROCK_CRUSHER_ATK , SCB_SPEED );
-
- add_sc( KO_YAMIKUMO , SC_HIDING );
- set_sc_with_vfx( KO_JYUMONJIKIRI , SC_JYUMONJIKIRI , SI_KO_JYUMONJIKIRI , SCB_NONE );
- add_sc( KO_MAKIBISHI , SC_STUN );
- set_sc( KO_MEIKYOUSISUI , SC_MEIKYOUSISUI , SI_MEIKYOUSISUI , SCB_NONE );
- set_sc( KO_KYOUGAKU , SC_KYOUGAKU , SI_KYOUGAKU , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK );
- add_sc( KO_JYUSATSU , SC_CURSE );
- set_sc( KO_ZENKAI , SC_ZENKAI , SI_ZENKAI , SCB_NONE );
- set_sc( KO_IZAYOI , SC_IZAYOI , SI_IZAYOI , SCB_MATK );
- set_sc( KG_KYOMU , SC_KYOMU , SI_KYOMU , SCB_NONE );
- set_sc( KG_KAGEMUSYA , SC_KAGEMUSYA , SI_KAGEMUSYA , SCB_NONE );
- set_sc( KG_KAGEHUMI , SC_KAGEHUMI , SI_KG_KAGEHUMI , SCB_NONE );
- set_sc( OB_ZANGETSU , SC_ZANGETSU , SI_ZANGETSU , SCB_MATK|SCB_BATK );
- set_sc_with_vfx( OB_AKAITSUKI , SC_AKAITSUKI , SI_AKAITSUKI , SCB_NONE );
- set_sc( OB_OBOROGENSOU , SC_GENSOU , SI_GENSOU , SCB_NONE );
-
- // Storing the target job rather than simply SC_SPIRIT simplifies code later on.
- SkillStatusChangeTable[SL_ALCHEMIST] = (sc_type)MAPID_ALCHEMIST,
- SkillStatusChangeTable[SL_MONK] = (sc_type)MAPID_MONK,
- SkillStatusChangeTable[SL_STAR] = (sc_type)MAPID_STAR_GLADIATOR,
- SkillStatusChangeTable[SL_SAGE] = (sc_type)MAPID_SAGE,
- SkillStatusChangeTable[SL_CRUSADER] = (sc_type)MAPID_CRUSADER,
- SkillStatusChangeTable[SL_SUPERNOVICE] = (sc_type)MAPID_SUPER_NOVICE,
- SkillStatusChangeTable[SL_KNIGHT] = (sc_type)MAPID_KNIGHT,
- SkillStatusChangeTable[SL_WIZARD] = (sc_type)MAPID_WIZARD,
- SkillStatusChangeTable[SL_PRIEST] = (sc_type)MAPID_PRIEST,
- SkillStatusChangeTable[SL_BARDDANCER] = (sc_type)MAPID_BARDDANCER,
- SkillStatusChangeTable[SL_ROGUE] = (sc_type)MAPID_ROGUE,
- SkillStatusChangeTable[SL_ASSASIN] = (sc_type)MAPID_ASSASSIN,
- SkillStatusChangeTable[SL_BLACKSMITH] = (sc_type)MAPID_BLACKSMITH,
- SkillStatusChangeTable[SL_HUNTER] = (sc_type)MAPID_HUNTER,
- SkillStatusChangeTable[SL_SOULLINKER] = (sc_type)MAPID_SOUL_LINKER,
-
- //Status that don't have a skill associated.
- StatusIconChangeTable[SC_WEIGHT50] = SI_WEIGHT50;
- StatusIconChangeTable[SC_WEIGHT90] = SI_WEIGHT90;
- StatusIconChangeTable[SC_ASPDPOTION0] = SI_ASPDPOTION0;
- StatusIconChangeTable[SC_ASPDPOTION1] = SI_ASPDPOTION1;
- StatusIconChangeTable[SC_ASPDPOTION2] = SI_ASPDPOTION2;
- StatusIconChangeTable[SC_ASPDPOTION3] = SI_ASPDPOTIONINFINITY;
- StatusIconChangeTable[SC_SPEEDUP0] = SI_MOVHASTE_HORSE;
- StatusIconChangeTable[SC_SPEEDUP1] = SI_SPEEDPOTION1;
- StatusIconChangeTable[SC_INCSTR] = SI_INCSTR;
- StatusIconChangeTable[SC_MIRACLE] = SI_SPIRIT;
- StatusIconChangeTable[SC_INTRAVISION] = SI_INTRAVISION;
- StatusIconChangeTable[SC_STRFOOD] = SI_FOODSTR;
- StatusIconChangeTable[SC_AGIFOOD] = SI_FOODAGI;
- StatusIconChangeTable[SC_VITFOOD] = SI_FOODVIT;
- StatusIconChangeTable[SC_INTFOOD] = SI_FOODINT;
- StatusIconChangeTable[SC_DEXFOOD] = SI_FOODDEX;
- StatusIconChangeTable[SC_LUKFOOD] = SI_FOODLUK;
- StatusIconChangeTable[SC_FLEEFOOD]= SI_FOODFLEE;
- StatusIconChangeTable[SC_HITFOOD] = SI_FOODHIT;
- StatusIconChangeTable[SC_MANU_ATK] = SI_MANU_ATK;
- StatusIconChangeTable[SC_MANU_DEF] = SI_MANU_DEF;
- StatusIconChangeTable[SC_SPL_ATK] = SI_SPL_ATK;
- StatusIconChangeTable[SC_SPL_DEF] = SI_SPL_DEF;
- StatusIconChangeTable[SC_MANU_MATK] = SI_MANU_MATK;
- StatusIconChangeTable[SC_SPL_MATK] = SI_SPL_MATK;
- StatusIconChangeTable[SC_ATKPOTION] = SI_PLUSATTACKPOWER;
- StatusIconChangeTable[SC_MATKPOTION] = SI_PLUSMAGICPOWER;
- //Cash Items
- StatusIconChangeTable[SC_FOOD_STR_CASH] = SI_FOOD_STR_CASH;
- StatusIconChangeTable[SC_FOOD_AGI_CASH] = SI_FOOD_AGI_CASH;
- StatusIconChangeTable[SC_FOOD_VIT_CASH] = SI_FOOD_VIT_CASH;
- StatusIconChangeTable[SC_FOOD_DEX_CASH] = SI_FOOD_DEX_CASH;
- StatusIconChangeTable[SC_FOOD_INT_CASH] = SI_FOOD_INT_CASH;
- StatusIconChangeTable[SC_FOOD_LUK_CASH] = SI_FOOD_LUK_CASH;
- StatusIconChangeTable[SC_EXPBOOST] = SI_EXPBOOST;
- StatusIconChangeTable[SC_ITEMBOOST] = SI_ITEMBOOST;
- StatusIconChangeTable[SC_JEXPBOOST] = SI_CASH_PLUSONLYJOBEXP;
- StatusIconChangeTable[SC_LIFEINSURANCE] = SI_LIFEINSURANCE;
- StatusIconChangeTable[SC_BOSSMAPINFO] = SI_BOSSMAPINFO;
- StatusIconChangeTable[SC_DEF_RATE] = SI_DEF_RATE;
- StatusIconChangeTable[SC_MDEF_RATE] = SI_MDEF_RATE;
- StatusIconChangeTable[SC_INCCRI] = SI_INCCRI;
- StatusIconChangeTable[SC_INCFLEE2] = SI_PLUSAVOIDVALUE;
- StatusIconChangeTable[SC_INCHEALRATE] = SI_INCHEALRATE;
- StatusIconChangeTable[SC_S_LIFEPOTION] = SI_S_LIFEPOTION;
- StatusIconChangeTable[SC_L_LIFEPOTION] = SI_L_LIFEPOTION;
- StatusIconChangeTable[SC_SPCOST_RATE] = SI_ATKER_BLOOD;
- StatusIconChangeTable[SC_COMMONSC_RESIST] = SI_TARGET_BLOOD;
- // Mercenary Bonus Effects
- StatusIconChangeTable[SC_MERC_FLEEUP] = SI_MERC_FLEEUP;
- StatusIconChangeTable[SC_MERC_ATKUP] = SI_MERC_ATKUP;
- StatusIconChangeTable[SC_MERC_HPUP] = SI_MERC_HPUP;
- StatusIconChangeTable[SC_MERC_SPUP] = SI_MERC_SPUP;
- StatusIconChangeTable[SC_MERC_HITUP] = SI_MERC_HITUP;
- // Warlock Spheres
- StatusIconChangeTable[SC_SPHERE_1] = SI_SPHERE_1;
- StatusIconChangeTable[SC_SPHERE_2] = SI_SPHERE_2;
- StatusIconChangeTable[SC_SPHERE_3] = SI_SPHERE_3;
- StatusIconChangeTable[SC_SPHERE_4] = SI_SPHERE_4;
- StatusIconChangeTable[SC_SPHERE_5] = SI_SPHERE_5;
- // Warlock Preserved spells
- StatusIconChangeTable[SC_SPELLBOOK1] = SI_SPELLBOOK1;
- StatusIconChangeTable[SC_SPELLBOOK2] = SI_SPELLBOOK2;
- StatusIconChangeTable[SC_SPELLBOOK3] = SI_SPELLBOOK3;
- StatusIconChangeTable[SC_SPELLBOOK4] = SI_SPELLBOOK4;
- StatusIconChangeTable[SC_SPELLBOOK5] = SI_SPELLBOOK5;
- StatusIconChangeTable[SC_SPELLBOOK6] = SI_SPELLBOOK6;
- StatusIconChangeTable[SC_MAXSPELLBOOK] = SI_SPELLBOOK7;
-
- StatusIconChangeTable[SC_NEUTRALBARRIER_MASTER] = SI_NEUTRALBARRIER_MASTER;
- StatusIconChangeTable[SC_STEALTHFIELD_MASTER] = SI_STEALTHFIELD_MASTER;
- StatusIconChangeTable[SC_OVERHEAT] = SI_OVERHEAT;
- StatusIconChangeTable[SC_OVERHEAT_LIMITPOINT] = SI_OVERHEAT_LIMITPOINT;
-
- StatusIconChangeTable[SC_HALLUCINATIONWALK_POSTDELAY] = SI_HALLUCINATIONWALK_POSTDELAY;
- StatusIconChangeTable[SC_TOXIN] = SI_TOXIN;
- StatusIconChangeTable[SC_PARALYSE] = SI_PARALYSE;
- StatusIconChangeTable[SC_VENOMBLEED] = SI_VENOMBLEED;
- StatusIconChangeTable[SC_MAGICMUSHROOM] = SI_MAGICMUSHROOM;
- StatusIconChangeTable[SC_DEATHHURT] = SI_DEATHHURT;
- StatusIconChangeTable[SC_PYREXIA] = SI_PYREXIA;
- StatusIconChangeTable[SC_OBLIVIONCURSE] = SI_OBLIVIONCURSE;
- StatusIconChangeTable[SC_LEECHESEND] = SI_LEECHESEND;
-
- StatusIconChangeTable[SC_SHIELDSPELL_DEF] = SI_SHIELDSPELL_DEF;
- StatusIconChangeTable[SC_SHIELDSPELL_MDEF] = SI_SHIELDSPELL_MDEF;
- StatusIconChangeTable[SC_SHIELDSPELL_REF] = SI_SHIELDSPELL_REF;
- StatusIconChangeTable[SC_BANDING_DEFENCE] = SI_BANDING_DEFENCE;
-
- StatusIconChangeTable[SC_GLOOMYDAY_SK] = SI_GLOOMYDAY;
-
- StatusIconChangeTable[SC_CURSEDCIRCLE_ATKER] = SI_CURSEDCIRCLE_ATKER;
-
- StatusIconChangeTable[SC_STOMACHACHE] = SI_STOMACHACHE;
- StatusIconChangeTable[SC_MYSTERIOUS_POWDER] = SI_MYSTERIOUS_POWDER;
- StatusIconChangeTable[SC_MELON_BOMB] = SI_MELON_BOMB;
- StatusIconChangeTable[SC_BANANA_BOMB] = SI_BANANA_BOMB;
- StatusIconChangeTable[SC_BANANA_BOMB_SITDOWN] = SI_BANANA_BOMB_SITDOWN_POSTDELAY;
-
- //Genetics New Food Items Status Icons
- StatusIconChangeTable[SC_SAVAGE_STEAK] = SI_SAVAGE_STEAK;
- StatusIconChangeTable[SC_COCKTAIL_WARG_BLOOD] = SI_COCKTAIL_WARG_BLOOD;
- StatusIconChangeTable[SC_MINOR_BBQ] = SI_MINOR_BBQ;
- StatusIconChangeTable[SC_SIROMA_ICE_TEA] = SI_SIROMA_ICE_TEA;
- StatusIconChangeTable[SC_DROCERA_HERB_STEAMED] = SI_DROCERA_HERB_STEAMED;
- StatusIconChangeTable[SC_PUTTI_TAILS_NOODLES] = SI_PUTTI_TAILS_NOODLES;
-
- StatusIconChangeTable[SC_BOOST500] |= SI_BOOST500;
- StatusIconChangeTable[SC_FULL_SWING_K] |= SI_FULL_SWING_K;
- StatusIconChangeTable[SC_MANA_PLUS] |= SI_MANA_PLUS;
- StatusIconChangeTable[SC_MUSTLE_M] |= SI_MUSTLE_M;
- StatusIconChangeTable[SC_LIFE_FORCE_F] |= SI_LIFE_FORCE_F;
- StatusIconChangeTable[SC_EXTRACT_WHITE_POTION_Z] |= SI_EXTRACT_WHITE_POTION_Z;
- StatusIconChangeTable[SC_VITATA_500] |= SI_VITATA_500;
- StatusIconChangeTable[SC_EXTRACT_SALAMINE_JUICE] |= SI_EXTRACT_SALAMINE_JUICE;
-
- // Elemental Spirit's 'side' status change icons.
- StatusIconChangeTable[SC_CIRCLE_OF_FIRE] = SI_CIRCLE_OF_FIRE;
- StatusIconChangeTable[SC_FIRE_CLOAK] = SI_FIRE_CLOAK;
- StatusIconChangeTable[SC_WATER_SCREEN] = SI_WATER_SCREEN;
- StatusIconChangeTable[SC_WATER_DROP] = SI_WATER_DROP;
- StatusIconChangeTable[SC_WIND_STEP] = SI_WIND_STEP;
- StatusIconChangeTable[SC_WIND_CURTAIN] = SI_WIND_CURTAIN;
- StatusIconChangeTable[SC_SOLID_SKIN] = SI_SOLID_SKIN;
- StatusIconChangeTable[SC_STONE_SHIELD] = SI_STONE_SHIELD;
- StatusIconChangeTable[SC_PYROTECHNIC] = SI_PYROTECHNIC;
- StatusIconChangeTable[SC_HEATER] = SI_HEATER;
- StatusIconChangeTable[SC_TROPIC] = SI_TROPIC;
- StatusIconChangeTable[SC_AQUAPLAY] = SI_AQUAPLAY;
- StatusIconChangeTable[SC_COOLER] = SI_COOLER;
- StatusIconChangeTable[SC_CHILLY_AIR] = SI_CHILLY_AIR;
- StatusIconChangeTable[SC_GUST] = SI_GUST;
- StatusIconChangeTable[SC_BLAST] = SI_BLAST;
- StatusIconChangeTable[SC_WILD_STORM] = SI_WILD_STORM;
- StatusIconChangeTable[SC_PETROLOGY] = SI_PETROLOGY;
- StatusIconChangeTable[SC_CURSED_SOIL] = SI_CURSED_SOIL;
- StatusIconChangeTable[SC_UPHEAVAL] = SI_UPHEAVAL;
- StatusIconChangeTable[SC_PUSH_CART] = SI_ON_PUSH_CART;
-
- //Other SC which are not necessarily associated to skills.
- StatusChangeFlagTable[SC_ASPDPOTION0] = SCB_ASPD;
- StatusChangeFlagTable[SC_ASPDPOTION1] = SCB_ASPD;
- StatusChangeFlagTable[SC_ASPDPOTION2] = SCB_ASPD;
- StatusChangeFlagTable[SC_ASPDPOTION3] = SCB_ASPD;
- StatusChangeFlagTable[SC_SPEEDUP0] = SCB_SPEED;
- StatusChangeFlagTable[SC_SPEEDUP1] = SCB_SPEED;
- StatusChangeFlagTable[SC_ATKPOTION] = SCB_BATK;
- StatusChangeFlagTable[SC_MATKPOTION] = SCB_MATK;
- StatusChangeFlagTable[SC_INCALLSTATUS] |= SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK;
- StatusChangeFlagTable[SC_INCSTR] |= SCB_STR;
- StatusChangeFlagTable[SC_INCAGI] |= SCB_AGI;
- StatusChangeFlagTable[SC_INCVIT] |= SCB_VIT;
- StatusChangeFlagTable[SC_INCINT] |= SCB_INT;
- StatusChangeFlagTable[SC_INCDEX] |= SCB_DEX;
- StatusChangeFlagTable[SC_INCLUK] |= SCB_LUK;
- StatusChangeFlagTable[SC_INCHIT] |= SCB_HIT;
- StatusChangeFlagTable[SC_INCHITRATE] |= SCB_HIT;
- StatusChangeFlagTable[SC_INCFLEE] |= SCB_FLEE;
- StatusChangeFlagTable[SC_INCFLEERATE] |= SCB_FLEE;
- StatusChangeFlagTable[SC_INCCRI] |= SCB_CRI;
- StatusChangeFlagTable[SC_INCASPDRATE] |= SCB_ASPD;
- StatusChangeFlagTable[SC_INCFLEE2] |= SCB_FLEE2;
- StatusChangeFlagTable[SC_INCMHPRATE] |= SCB_MAXHP;
- StatusChangeFlagTable[SC_INCMSPRATE] |= SCB_MAXSP;
- StatusChangeFlagTable[SC_INCMHP] |= SCB_MAXHP;
- StatusChangeFlagTable[SC_INCMSP] |= SCB_MAXSP;
- StatusChangeFlagTable[SC_INCATKRATE] |= SCB_BATK|SCB_WATK;
- StatusChangeFlagTable[SC_INCMATKRATE] |= SCB_MATK;
- StatusChangeFlagTable[SC_INCDEFRATE] |= SCB_DEF;
- StatusChangeFlagTable[SC_STRFOOD] |= SCB_STR;
- StatusChangeFlagTable[SC_AGIFOOD] |= SCB_AGI;
- StatusChangeFlagTable[SC_VITFOOD] |= SCB_VIT;
- StatusChangeFlagTable[SC_INTFOOD] |= SCB_INT;
- StatusChangeFlagTable[SC_DEXFOOD] |= SCB_DEX;
- StatusChangeFlagTable[SC_LUKFOOD] |= SCB_LUK;
- StatusChangeFlagTable[SC_HITFOOD] |= SCB_HIT;
- StatusChangeFlagTable[SC_FLEEFOOD] |= SCB_FLEE;
- StatusChangeFlagTable[SC_BATKFOOD] |= SCB_BATK;
- StatusChangeFlagTable[SC_WATKFOOD] |= SCB_WATK;
- StatusChangeFlagTable[SC_MATKFOOD] |= SCB_MATK;
- StatusChangeFlagTable[SC_ARMOR_ELEMENT] |= SCB_ALL;
- StatusChangeFlagTable[SC_ARMOR_RESIST] |= SCB_ALL;
- StatusChangeFlagTable[SC_SPCOST_RATE] |= SCB_ALL;
- StatusChangeFlagTable[SC_WALKSPEED] |= SCB_SPEED;
- StatusChangeFlagTable[SC_ITEMSCRIPT] |= SCB_ALL;
- // Cash Items
- StatusChangeFlagTable[SC_FOOD_STR_CASH] = SCB_STR;
- StatusChangeFlagTable[SC_FOOD_AGI_CASH] = SCB_AGI;
- StatusChangeFlagTable[SC_FOOD_VIT_CASH] = SCB_VIT;
- StatusChangeFlagTable[SC_FOOD_DEX_CASH] = SCB_DEX;
- StatusChangeFlagTable[SC_FOOD_INT_CASH] = SCB_INT;
- StatusChangeFlagTable[SC_FOOD_LUK_CASH] = SCB_LUK;
- // Mercenary Bonus Effects
- StatusChangeFlagTable[SC_MERC_FLEEUP] |= SCB_FLEE;
- StatusChangeFlagTable[SC_MERC_ATKUP] |= SCB_WATK;
- StatusChangeFlagTable[SC_MERC_HPUP] |= SCB_MAXHP;
- StatusChangeFlagTable[SC_MERC_SPUP] |= SCB_MAXSP;
- StatusChangeFlagTable[SC_MERC_HITUP] |= SCB_HIT;
- // Guillotine Cross Poison Effects
- StatusChangeFlagTable[SC_PARALYSE] |= SCB_ASPD|SCB_FLEE|SCB_SPEED;
- StatusChangeFlagTable[SC_DEATHHURT] |= SCB_REGEN;
- StatusChangeFlagTable[SC_VENOMBLEED] |= SCB_MAXHP;
- StatusChangeFlagTable[SC_OBLIVIONCURSE] |= SCB_REGEN;
-
- StatusChangeFlagTable[SC_SAVAGE_STEAK] |= SCB_STR;
- StatusChangeFlagTable[SC_COCKTAIL_WARG_BLOOD] |= SCB_INT;
- StatusChangeFlagTable[SC_MINOR_BBQ] |= SCB_VIT;
- StatusChangeFlagTable[SC_SIROMA_ICE_TEA] |= SCB_DEX;
- StatusChangeFlagTable[SC_DROCERA_HERB_STEAMED] |= SCB_AGI;
- StatusChangeFlagTable[SC_PUTTI_TAILS_NOODLES] |= SCB_LUK;
- StatusChangeFlagTable[SC_BOOST500] |= SCB_ASPD;
- StatusChangeFlagTable[SC_FULL_SWING_K] |= SCB_BATK;
- StatusChangeFlagTable[SC_MANA_PLUS] |= SCB_MATK;
- StatusChangeFlagTable[SC_MUSTLE_M] |= SCB_MAXHP;
- StatusChangeFlagTable[SC_LIFE_FORCE_F] |= SCB_MAXSP;
- StatusChangeFlagTable[SC_EXTRACT_WHITE_POTION_Z] |= SCB_REGEN;
- StatusChangeFlagTable[SC_VITATA_500] |= SCB_REGEN;
- StatusChangeFlagTable[SC_EXTRACT_SALAMINE_JUICE] |= SCB_ASPD;
-
-#ifdef RENEWAL_EDP
- // renewal EDP increases your weapon atk
- StatusChangeFlagTable[SC_EDP] |= SCB_WATK;
-#endif
-
- if( !battle_config.display_hallucination ) //Disable Hallucination.
- StatusIconChangeTable[SC_HALLUCINATION] = SI_BLANK;
-
- /* StatusChangeState (SCS_) NOMOVE */
- StatusChangeStateTable[SC_ANKLE] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_AUTOCOUNTER] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_TRICKDEAD] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_BLADESTOP] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_BLADESTOP_WAIT] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_GOSPEL] |= SCS_NOMOVE|SCS_NOMOVECOND;
- StatusChangeStateTable[SC_BASILICA] |= SCS_NOMOVE|SCS_NOMOVECOND;
- StatusChangeStateTable[SC_STOP] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_CLOSECONFINE] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_CLOSECONFINE2] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_MADNESSCANCEL] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_GRAVITATION] |= SCS_NOMOVE|SCS_NOMOVECOND;
- StatusChangeStateTable[SC_WHITEIMPRISON] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_ELECTRICSHOCKER] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_BITE] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_THORNSTRAP] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_MAGNETICFIELD] |= SCS_NOMOVE;
- StatusChangeStateTable[SC__MANHOLE] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_CURSEDCIRCLE_ATKER] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_CURSEDCIRCLE_TARGET] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_CRYSTALIZE] |= SCS_NOMOVE|SCS_NOMOVECOND;
- StatusChangeStateTable[SC_NETHERWORLD] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_CAMOUFLAGE] |= SCS_NOMOVE|SCS_NOMOVECOND;
- StatusChangeStateTable[SC_MEIKYOUSISUI] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_KAGEHUMI] |= SCS_NOMOVE;
- StatusChangeStateTable[SC_KYOUGAKU] |= SCS_NOMOVE;
-
- /* StatusChangeState (SCS_) NOPICKUPITEMS */
- StatusChangeStateTable[SC_HIDING] |= SCS_NOPICKITEM;
- StatusChangeStateTable[SC_CLOAKING] |= SCS_NOPICKITEM;
- StatusChangeStateTable[SC_TRICKDEAD] |= SCS_NOPICKITEM;
- StatusChangeStateTable[SC_BLADESTOP] |= SCS_NOPICKITEM;
- StatusChangeStateTable[SC_CLOAKINGEXCEED] |= SCS_NOPICKITEM;
- StatusChangeStateTable[SC_NOCHAT] |= SCS_NOPICKITEM|SCS_NOPICKITEMCOND;
-
- /* StatusChangeState (SCS_) NODROPITEMS */
- StatusChangeStateTable[SC_AUTOCOUNTER] |= SCS_NODROPITEM;
- StatusChangeStateTable[SC_BLADESTOP] |= SCS_NODROPITEM;
- StatusChangeStateTable[SC_NOCHAT] |= SCS_NODROPITEM|SCS_NODROPITEMCOND;
-
- /* StatusChangeState (SCS_) NOCAST (skills) */
- StatusChangeStateTable[SC_SILENCE] |= SCS_NOCAST;
- StatusChangeStateTable[SC_STEELBODY] |= SCS_NOCAST;
- StatusChangeStateTable[SC_BERSERK] |= SCS_NOCAST;
- StatusChangeStateTable[SC__BLOODYLUST] |= SCS_NOCAST;
- StatusChangeStateTable[SC_OBLIVIONCURSE] |= SCS_NOCAST;
- StatusChangeStateTable[SC_WHITEIMPRISON] |= SCS_NOCAST;
- StatusChangeStateTable[SC__INVISIBILITY] |= SCS_NOCAST;
- StatusChangeStateTable[SC_CRYSTALIZE] |= SCS_NOCAST|SCS_NOCASTCOND;
- StatusChangeStateTable[SC__IGNORANCE] |= SCS_NOCAST;
- StatusChangeStateTable[SC_DEEPSLEEP] |= SCS_NOCAST;
- StatusChangeStateTable[SC_SATURDAYNIGHTFEVER] |= SCS_NOCAST;
- StatusChangeStateTable[SC_CURSEDCIRCLE_TARGET] |= SCS_NOCAST;
- StatusChangeStateTable[SC_SILENCE] |= SCS_NOCAST;
-
- //Homon S
- StatusChangeStateTable[SC_PARALYSIS] |= SCS_NOMOVE;
-
-}
-
-static void initDummyData(void)
-{
- memset(&dummy_status, 0, sizeof(dummy_status));
- dummy_status.hp =
- dummy_status.max_hp =
- dummy_status.max_sp =
- dummy_status.str =
- dummy_status.agi =
- dummy_status.vit =
- dummy_status.int_ =
- dummy_status.dex =
- dummy_status.luk =
- dummy_status.hit = 1;
- dummy_status.speed = 2000;
- dummy_status.adelay = 4000;
- dummy_status.amotion = 2000;
- dummy_status.dmotion = 2000;
- dummy_status.ele_lv = 1; //Min elemental level.
- dummy_status.mode = MD_CANMOVE;
-}
-
-
-//For copying a status_data structure from b to a, without overwriting current Hp and Sp
-static inline void status_cpy(struct status_data* a, const struct status_data* b)
-{
- memcpy((void*)&a->max_hp, (const void*)&b->max_hp, sizeof(struct status_data)-(sizeof(a->hp)+sizeof(a->sp)));
-}
-
-//Sets HP to given value. Flag is the flag passed to status_heal in case
-//final value is higher than current (use 2 to make a healing effect display
-//on players) It will always succeed (overrides Berserk block), but it can't kill.
-int status_set_hp(struct block_list *bl, unsigned int hp, int flag)
-{
- struct status_data *status;
- if (hp < 1) return 0;
- status = status_get_status_data(bl);
- if (status == &dummy_status)
- return 0;
-
- if (hp > status->max_hp) hp = status->max_hp;
- if (hp == status->hp) return 0;
- if (hp > status->hp)
- return status_heal(bl, hp - status->hp, 0, 1|flag);
- return status_zap(bl, status->hp - hp, 0);
-}
-
-//Sets SP to given value. Flag is the flag passed to status_heal in case
-//final value is higher than current (use 2 to make a healing effect display
-//on players)
-int status_set_sp(struct block_list *bl, unsigned int sp, int flag)
-{
- struct status_data *status;
-
- status = status_get_status_data(bl);
- if (status == &dummy_status)
- return 0;
-
- if (sp > status->max_sp) sp = status->max_sp;
- if (sp == status->sp) return 0;
- if (sp > status->sp)
- return status_heal(bl, 0, sp - status->sp, 1|flag);
- return status_zap(bl, 0, status->sp - sp);
-}
-
-int status_charge(struct block_list* bl, int hp, int sp)
-{
- if(!(bl->type&BL_CONSUME))
- return hp+sp; //Assume all was charged so there are no 'not enough' fails.
- return status_damage(NULL, bl, hp, sp, 0, 3);
-}
-
-//Inflicts damage on the target with the according walkdelay.
-//If flag&1, damage is passive and does not triggers cancelling status changes.
-//If flag&2, fail if target does not has enough to substract.
-//If flag&4, if killed, mob must not give exp/loot.
-//flag will be set to &8 when damaging sp of a dead character
-int status_damage(struct block_list *src,struct block_list *target,int hp, int sp, int walkdelay, int flag)
-{
- struct status_data *status;
- struct status_change *sc;
-
- if(sp && !(target->type&BL_CONSUME))
- sp = 0; //Not a valid SP target.
-
- if (hp < 0) { //Assume absorbed damage.
- status_heal(target, -hp, 0, 1);
- hp = 0;
- }
-
- if (sp < 0) {
- status_heal(target, 0, -sp, 1);
- sp = 0;
- }
-
- if (target->type == BL_SKILL)
- return skill_unit_ondamaged((struct skill_unit *)target, src, hp, gettick());
-
- status = status_get_status_data(target);
- if( status == &dummy_status )
- return 0;
-
- if ((unsigned int)hp >= status->hp) {
- if (flag&2) return 0;
- hp = status->hp;
- }
-
- if ((unsigned int)sp > status->sp) {
- if (flag&2) return 0;
- sp = status->sp;
- }
-
- if (!hp && !sp)
- return 0;
-
- if( !status->hp )
- flag |= 8;
-
-// Let through. battle.c/skill.c have the whole logic of when it's possible or
-// not to hurt someone (and this check breaks pet catching) [Skotlex]
-// if (!target->prev && !(flag&2))
-// return 0; //Cannot damage a bl not on a map, except when "charging" hp/sp
-
- sc = status_get_sc(target);
- if( hp && battle_config.invincible_nodamage && src && sc && sc->data[SC_INVINCIBLE] && !sc->data[SC_INVINCIBLEOFF] )
- hp = 1;
-
- if( hp && !(flag&1) ) {
- if( sc ) {
- struct status_change_entry *sce;
- if (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
- status_change_end(target, SC_STONE, INVALID_TIMER);
- status_change_end(target, SC_FREEZE, INVALID_TIMER);
- status_change_end(target, SC_SLEEP, INVALID_TIMER);
- status_change_end(target, SC_WINKCHARM, INVALID_TIMER);
- status_change_end(target, SC_CONFUSION, INVALID_TIMER);
- status_change_end(target, SC_TRICKDEAD, INVALID_TIMER);
- status_change_end(target, SC_HIDING, INVALID_TIMER);
- status_change_end(target, SC_CLOAKING, INVALID_TIMER);
- status_change_end(target, SC_CHASEWALK, INVALID_TIMER);
- status_change_end(target, SC_CAMOUFLAGE, INVALID_TIMER);
- status_change_end(target, SC__INVISIBILITY, INVALID_TIMER);
- status_change_end(target, SC_DEEPSLEEP, INVALID_TIMER);
- if ((sce=sc->data[SC_ENDURE]) && !sce->val4) {
- //Endure count is only reduced by non-players on non-gvg maps.
- //val4 signals infinite endure. [Skotlex]
- if (src && src->type != BL_PC && !map_flag_gvg(target->m) && !map[target->m].flag.battleground && --(sce->val2) < 0)
- status_change_end(target, SC_ENDURE, INVALID_TIMER);
- }
- if ((sce=sc->data[SC_GRAVITATION]) && sce->val3 == BCT_SELF) {
- struct skill_unit_group* sg = skill_id2group(sce->val4);
- if (sg) {
- skill_delunitgroup(sg);
- sce->val4 = 0;
- status_change_end(target, SC_GRAVITATION, INVALID_TIMER);
- }
- }
- if(sc->data[SC_DANCING] && (unsigned int)hp > status->max_hp>>2)
- status_change_end(target, SC_DANCING, INVALID_TIMER);
- if(sc->data[SC_CLOAKINGEXCEED] && --(sc->data[SC_CLOAKINGEXCEED]->val2) <= 0)
- status_change_end(target, SC_CLOAKINGEXCEED, INVALID_TIMER);
- if(sc->data[SC_KAGEMUSYA] && --(sc->data[SC_KAGEMUSYA]->val3) <= 0)
- status_change_end(target, SC_KAGEMUSYA, INVALID_TIMER);
- }
- unit_skillcastcancel(target, 2);
- }
-
- status->hp-= hp;
- status->sp-= sp;
-
- if (sc && hp && status->hp) {
- if (sc->data[SC_AUTOBERSERK] &&
- (!sc->data[SC_PROVOKE] || !sc->data[SC_PROVOKE]->val2) &&
- status->hp < status->max_hp>>2)
- sc_start4(target,SC_PROVOKE,100,10,1,0,0,0);
- if (sc->data[SC_BERSERK] && status->hp <= 100)
- status_change_end(target, SC_BERSERK, INVALID_TIMER);
- if( sc->data[SC_RAISINGDRAGON] && status->hp <= 1000 )
- status_change_end(target, SC_RAISINGDRAGON, INVALID_TIMER);
- if (sc->data[SC_SATURDAYNIGHTFEVER] && status->hp <= 100)
- status_change_end(target, SC_SATURDAYNIGHTFEVER, INVALID_TIMER);
- if (sc->data[SC__BLOODYLUST] && status->hp <= 100)
- status_change_end(target, SC__BLOODYLUST, INVALID_TIMER);
- }
-
- switch (target->type) {
- case BL_PC: pc_damage((TBL_PC*)target,src,hp,sp); break;
- case BL_MOB: mob_damage((TBL_MOB*)target, src, hp); break;
- case BL_HOM: merc_damage((TBL_HOM*)target); break;
- case BL_MER: mercenary_heal((TBL_MER*)target,hp,sp); break;
- case BL_ELEM: elemental_heal((TBL_ELEM*)target,hp,sp); break;
- }
-
- if( src && target->type == BL_PC && ((TBL_PC*)target)->disguise ) {// stop walking when attacked in disguise to prevent walk-delay bug
- unit_stop_walking( target, 1 );
- }
-
- if( status->hp || (flag&8) )
- { //Still lives or has been dead before this damage.
- if (walkdelay)
- unit_set_walkdelay(target, gettick(), walkdelay, 0);
- return hp+sp;
- }
-
- status->hp = 1; //To let the dead function cast skills and all that.
- //NOTE: These dead functions should return: [Skotlex]
- //0: Death cancelled, auto-revived.
- //Non-zero: Standard death. Clear status, cancel move/attack, etc
- //&2: Also remove object from map.
- //&4: Also delete object from memory.
- switch (target->type) {
- case BL_PC: flag = pc_dead((TBL_PC*)target,src); break;
- case BL_MOB: flag = mob_dead((TBL_MOB*)target, src, flag&4?3:0); break;
- case BL_HOM: flag = merc_hom_dead((TBL_HOM*)target); break;
- case BL_MER: flag = mercenary_dead((TBL_MER*)target); break;
- case BL_ELEM: flag = elemental_dead((TBL_ELEM*)target); break;
- default: //Unhandled case, do nothing to object.
- flag = 0;
- break;
- }
-
- if(!flag) //Death cancelled.
- return hp+sp;
-
- //Normal death
- status->hp = 0;
- if (battle_config.clear_unit_ondeath &&
- battle_config.clear_unit_ondeath&target->type)
- skill_clear_unitgroup(target);
-
- if(target->type&BL_REGEN)
- { //Reset regen ticks.
- struct regen_data *regen = status_get_regen_data(target);
- if (regen) {
- memset(&regen->tick, 0, sizeof(regen->tick));
- if (regen->sregen)
- memset(&regen->sregen->tick, 0, sizeof(regen->sregen->tick));
- if (regen->ssregen)
- memset(&regen->ssregen->tick, 0, sizeof(regen->ssregen->tick));
- }
- }
-
- if( sc && sc->data[SC_KAIZEL] && !map_flag_gvg(target->m) )
- { //flag&8 = disable Kaizel
- int time = skill_get_time2(SL_KAIZEL,sc->data[SC_KAIZEL]->val1);
- //Look for Osiris Card's bonus effect on the character and revive 100% or revive normally
- if ( target->type == BL_PC && BL_CAST(BL_PC,target)->special_state.restart_full_recover )
- status_revive(target, 100, 100);
- else
- status_revive(target, sc->data[SC_KAIZEL]->val2, 0);
- status_change_clear(target,0);
- clif_skill_nodamage(target,target,ALL_RESURRECTION,1,1);
- sc_start(target,status_skill2sc(PR_KYRIE),100,10,time);
-
- if( target->type == BL_MOB )
- ((TBL_MOB*)target)->state.rebirth = 1;
-
- return hp+sp;
- }
- if(target->type == BL_PC){
- TBL_PC *sd = BL_CAST(BL_PC,target);
- TBL_HOM *hd = sd->hd;
- if(hd && hd->sc.data[SC_LIGHT_OF_REGENE]){
- clif_skillcasting(&hd->bl, hd->bl.id, target->id, 0,0, MH_LIGHT_OF_REGENE, skill_get_ele(MH_LIGHT_OF_REGENE, 1), 10); //just to display usage
- clif_skill_nodamage(&sd->bl, target, ALL_RESURRECTION, 1, status_revive(&sd->bl,10*hd->sc.data[SC_LIGHT_OF_REGENE]->val1,0));
- status_change_end(&sd->hd->bl,SC_LIGHT_OF_REGENE,INVALID_TIMER);
- return hp + sp;
- }
- }
- if (target->type == BL_MOB && sc && sc->data[SC_REBIRTH] && !((TBL_MOB*) target)->state.rebirth) {// Ensure the monster has not already rebirthed before doing so.
- status_revive(target, sc->data[SC_REBIRTH]->val2, 0);
- status_change_clear(target,0);
- ((TBL_MOB*)target)->state.rebirth = 1;
-
- return hp+sp;
- }
-
- status_change_clear(target,0);
-
- if(flag&4) //Delete from memory. (also invokes map removal code)
- unit_free(target,CLR_DEAD);
- else
- if(flag&2) //remove from map
- unit_remove_map(target,CLR_DEAD);
- else
- { //Some death states that would normally be handled by unit_remove_map
- unit_stop_attack(target);
- unit_stop_walking(target,1);
- unit_skillcastcancel(target,0);
- clif_clearunit_area(target,CLR_DEAD);
- skill_unit_move(target,gettick(),4);
- skill_cleartimerskill(target);
- }
-
- return hp+sp;
-}
-
-//Heals a character. If flag&1, this is forced healing (otherwise stuff like Berserk can block it)
-//If flag&2, when the player is healed, show the HP/SP heal effect.
-int status_heal(struct block_list *bl,int hp,int sp, int flag)
-{
- struct status_data *status;
- struct status_change *sc;
-
- status = status_get_status_data(bl);
-
- if (status == &dummy_status || !status->hp)
- return 0;
-
- sc = status_get_sc(bl);
- if (sc && !sc->count)
- sc = NULL;
-
- if (hp < 0) {
- if (hp == INT_MIN) hp++; //-INT_MIN == INT_MIN in some architectures!
- status_damage(NULL, bl, -hp, 0, 0, 1);
- hp = 0;
- }
-
- if(hp) {
- if( sc && (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) ) {
- if( flag&1 )
- flag &= ~2;
- else
- hp = 0;
- }
-
- if((unsigned int)hp > status->max_hp - status->hp)
- hp = status->max_hp - status->hp;
- }
-
- if(sp < 0) {
- if (sp==INT_MIN) sp++;
- status_damage(NULL, bl, 0, -sp, 0, 1);
- sp = 0;
- }
-
- if(sp) {
- if((unsigned int)sp > status->max_sp - status->sp)
- sp = status->max_sp - status->sp;
- }
-
- if(!sp && !hp) return 0;
-
- status->hp+= hp;
- status->sp+= sp;
-
- if(hp && sc &&
- sc->data[SC_AUTOBERSERK] &&
- sc->data[SC_PROVOKE] &&
- sc->data[SC_PROVOKE]->val2==1 &&
- status->hp>=status->max_hp>>2
- ) //End auto berserk.
- status_change_end(bl, SC_PROVOKE, INVALID_TIMER);
-
- // send hp update to client
- switch(bl->type) {
- case BL_PC: pc_heal((TBL_PC*)bl,hp,sp,flag&2?1:0); break;
- case BL_MOB: mob_heal((TBL_MOB*)bl,hp); break;
- case BL_HOM: merc_hom_heal((TBL_HOM*)bl); break;
- case BL_MER: mercenary_heal((TBL_MER*)bl,hp,sp); break;
- case BL_ELEM: elemental_heal((TBL_ELEM*)bl,hp,sp); break;
- }
-
- return hp+sp;
-}
-
-//Does percentual non-flinching damage/heal. If mob is killed this way,
-//no exp/drops will be awarded if there is no src (or src is target)
-//If rates are > 0, percent is of current HP/SP
-//If rates are < 0, percent is of max HP/SP
-//If !flag, this is heal, otherwise it is damage.
-//Furthermore, if flag==2, then the target must not die from the substraction.
-int status_percent_change(struct block_list *src,struct block_list *target,signed char hp_rate, signed char sp_rate, int flag)
-{
- struct status_data *status;
- unsigned int hp =0, sp = 0;
-
- status = status_get_status_data(target);
-
-
- //It's safe now [MarkZD]
- if (hp_rate > 99)
- hp = status->hp;
- else if (hp_rate > 0)
- hp = status->hp>10000?
- hp_rate*(status->hp/100):
- ((int64)hp_rate*status->hp)/100;
- else if (hp_rate < -99)
- hp = status->max_hp;
- else if (hp_rate < 0)
- hp = status->max_hp>10000?
- (-hp_rate)*(status->max_hp/100):
- ((int64)-hp_rate*status->max_hp)/100;
- if (hp_rate && !hp)
- hp = 1;
-
- if (flag == 2 && hp >= status->hp)
- hp = status->hp-1; //Must not kill target.
-
- if (sp_rate > 99)
- sp = status->sp;
- else if (sp_rate > 0)
- sp = ((int64)sp_rate*status->sp)/100;
- else if (sp_rate < -99)
- sp = status->max_sp;
- else if (sp_rate < 0)
- sp = ((int64)-sp_rate)*status->max_sp/100;
- if (sp_rate && !sp)
- sp = 1;
-
- //Ugly check in case damage dealt is too much for the received args of
- //status_heal / status_damage. [Skotlex]
- if (hp > INT_MAX) {
- hp -= INT_MAX;
- if (flag)
- status_damage(src, target, INT_MAX, 0, 0, (!src||src==target?5:1));
- else
- status_heal(target, INT_MAX, 0, 0);
- }
- if (sp > INT_MAX) {
- sp -= INT_MAX;
- if (flag)
- status_damage(src, target, 0, INT_MAX, 0, (!src||src==target?5:1));
- else
- status_heal(target, 0, INT_MAX, 0);
- }
- if (flag)
- return status_damage(src, target, hp, sp, 0, (!src||src==target?5:1));
- return status_heal(target, hp, sp, 0);
-}
-
-int status_revive(struct block_list *bl, unsigned char per_hp, unsigned char per_sp)
-{
- struct status_data *status;
- unsigned int hp, sp;
- if (!status_isdead(bl)) return 0;
-
- status = status_get_status_data(bl);
- if (status == &dummy_status)
- return 0; //Invalid target.
-
- hp = (int64)status->max_hp * per_hp/100;
- sp = (int64)status->max_sp * per_sp/100;
-
- if(hp > status->max_hp - status->hp)
- hp = status->max_hp - status->hp;
- else if (per_hp && !hp)
- hp = 1;
-
- if(sp > status->max_sp - status->sp)
- sp = status->max_sp - status->sp;
- else if (per_sp && !sp)
- sp = 1;
-
- status->hp += hp;
- status->sp += sp;
-
- if (bl->prev) //Animation only if character is already on a map.
- clif_resurrection(bl, 1);
- switch (bl->type) {
- case BL_PC: pc_revive((TBL_PC*)bl, hp, sp); break;
- case BL_MOB: mob_revive((TBL_MOB*)bl, hp); break;
- case BL_HOM: merc_hom_revive((TBL_HOM*)bl, hp, sp); break;
- }
- return 1;
-}
-
-/*==========================================
- * Checks whether the src can use the skill on the target,
- * taking into account status/option of both source/target. [Skotlex]
- * flag:
- * 0 - Trying to use skill on target.
- * 1 - Cast bar is done.
- * 2 - Skill already pulled off, check is due to ground-based skills or splash-damage ones.
- * src MAY be null to indicate we shouldn't check it, this is a ground-based skill attack.
- * target MAY Be null, in which case the checks are only to see
- * whether the source can cast or not the skill on the ground.
- *------------------------------------------*/
-int status_check_skilluse(struct block_list *src, struct block_list *target, uint16 skill_id, int flag)
-{
- struct status_data *status;
- struct status_change *sc=NULL, *tsc;
- int hide_flag;
-
- status = src?status_get_status_data(src):&dummy_status;
-
- if (src && src->type != BL_PC && status_isdead(src))
- return 0;
-
- if (!skill_id) { //Normal attack checks.
- if (!(status->mode&MD_CANATTACK))
- return 0; //This mode is only needed for melee attacking.
- //Dead state is not checked for skills as some skills can be used
- //on dead characters, said checks are left to skill.c [Skotlex]
- if (target && status_isdead(target))
- return 0;
- if( src && (sc = status_get_sc(src)) && sc->data[SC_CRYSTALIZE] && src->type != BL_MOB)
- return 0;
- }
-
- switch( skill_id ) {
- case PA_PRESSURE:
- if( flag && target ) {
- //Gloria Avoids pretty much everything....
- tsc = status_get_sc(target);
- if(tsc && tsc->option&OPTION_HIDE)
- return 0;
- }
- break;
- case GN_WALLOFTHORN:
- if( target && status_isdead(target) )
- return 0;
- break;
- case AL_TELEPORT:
- //Should fail when used on top of Land Protector [Skotlex]
- if (src && map_getcell(src->m, src->x, src->y, CELL_CHKLANDPROTECTOR)
- && !(status->mode&MD_BOSS)
- && (src->type != BL_PC || ((TBL_PC*)src)->skillitem != skill_id))
- return 0;
- break;
- default:
- break;
- }
-
- if ( src ) sc = status_get_sc(src);
-
- if( sc && sc->count ) {
-
- if (skill_id != RK_REFRESH && sc->opt1 >0 && (sc->opt1 != OPT1_CRYSTALIZE && src->type != BL_MOB) && sc->opt1 != OPT1_BURNING && skill_id != SR_GENTLETOUCH_CURE) { //Stuned/Frozen/etc
- if (flag != 1) //Can't cast, casted stuff can't damage.
- return 0;
- if (!(skill_get_inf(skill_id)&INF_GROUND_SKILL))
- return 0; //Targetted spells can't come off.
- }
-
- if (
- (sc->data[SC_TRICKDEAD] && skill_id != NV_TRICKDEAD)
- || (sc->data[SC_AUTOCOUNTER] && !flag)
- || (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF && skill_id != PA_GOSPEL)
- || (sc->data[SC_GRAVITATION] && sc->data[SC_GRAVITATION]->val3 == BCT_SELF && flag != 2)
- )
- return 0;
-
- if (sc->data[SC_WINKCHARM] && target && !flag) { //Prevents skill usage
- if( unit_bl2ud(src) && (unit_bl2ud(src))->walktimer == INVALID_TIMER )
- unit_walktobl(src, map_id2bl(sc->data[SC_WINKCHARM]->val2), 3, 1);
- clif_emotion(src, E_LV);
- return 0;
- }
-
- if (sc->data[SC_BLADESTOP]) {
- switch (sc->data[SC_BLADESTOP]->val1)
- {
- case 5: if (skill_id == MO_EXTREMITYFIST) break;
- case 4: if (skill_id == MO_CHAINCOMBO) break;
- case 3: if (skill_id == MO_INVESTIGATE) break;
- case 2: if (skill_id == MO_FINGEROFFENSIVE) break;
- default: return 0;
- }
- }
-
- if (sc->data[SC_DANCING] && flag!=2) {
- if( src->type == BL_PC && skill_id >= WA_SWING_DANCE && skill_id <= WM_UNLIMITED_HUMMING_VOICE )
- { // Lvl 5 Lesson or higher allow you use 3rd job skills while dancing.v
- if( pc_checkskill((TBL_PC*)src,WM_LESSON) < 5 )
- return 0;
- } else if(sc->data[SC_LONGING]) { //Allow everything except dancing/re-dancing. [Skotlex]
- if (skill_id == BD_ENCORE ||
- skill_get_inf2(skill_id)&(INF2_SONG_DANCE|INF2_ENSEMBLE_SKILL)
- )
- return 0;
- } else {
- switch (skill_id) {
- case BD_ADAPTATION:
- case CG_LONGINGFREEDOM:
- case BA_MUSICALSTRIKE:
- case DC_THROWARROW:
- break;
- default:
- return 0;
- }
- }
- if ((sc->data[SC_DANCING]->val1&0xFFFF) == CG_HERMODE && skill_id == BD_ADAPTATION)
- return 0; //Can't amp out of Wand of Hermode :/ [Skotlex]
- }
-
- if (skill_id && //Do not block item-casted skills.
- (src->type != BL_PC || ((TBL_PC*)src)->skillitem != skill_id)
- ) { //Skills blocked through status changes...
- if (!flag && ( //Blocked only from using the skill (stuff like autospell may still go through
- sc->cant.cast ||
- (sc->data[SC_MARIONETTE] && skill_id != CG_MARIONETTE) || //Only skill you can use is marionette again to cancel it
- (sc->data[SC_MARIONETTE2] && skill_id == CG_MARIONETTE) || //Cannot use marionette if you are being buffed by another
- (sc->data[SC_STASIS] && skill_block_check(src, SC_STASIS, skill_id)) ||
- (sc->data[SC_KAGEHUMI] && skill_block_check(src, SC_KAGEHUMI, skill_id))
- ))
- return 0;
-
- //Skill blocking.
- if (
- (sc->data[SC_VOLCANO] && skill_id == WZ_ICEWALL) ||
- (sc->data[SC_ROKISWEIL] && skill_id != BD_ADAPTATION) ||
- (sc->data[SC_HERMODE] && skill_get_inf(skill_id) & INF_SUPPORT_SKILL) ||
- (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOSKILL)
- )
- return 0;
-
- if( sc->data[SC__MANHOLE] || ((tsc = status_get_sc(target)) && tsc->data[SC__MANHOLE]) ) {
- switch(skill_id) {//##TODO## make this a flag in skill_db?
- // Skills that can be used even under Man Hole effects.
- case SC_SHADOWFORM:
- case SC_STRIPACCESSARY:
- break;
- default:
- return 0;
- }
- }
-
- }
- }
-
- if (sc && sc->option)
- {
- if (sc->option&OPTION_HIDE)
- switch (skill_id) { //Usable skills while hiding.
- case TF_HIDING:
- case AS_GRIMTOOTH:
- case RG_BACKSTAP:
- case RG_RAID:
- case NJ_SHADOWJUMP:
- case NJ_KIRIKAGE:
- case KO_YAMIKUMO:
- break;
- default:
- //Non players can use all skills while hidden.
- if (!skill_id || src->type == BL_PC)
- return 0;
- }
- if (sc->option&OPTION_CHASEWALK && skill_id != ST_CHASEWALK)
- return 0;
- if(sc->option&OPTION_MOUNTING)
- return 0;//New mounts can't attack nor use skills in the client; this check makes it cheat-safe [Ind]
- }
-
- if (target == NULL || target == src) //No further checking needed.
- return 1;
-
- tsc = status_get_sc(target);
-
- if(tsc && tsc->count) {
- /* attacks in invincible are capped to 1 damage and handled in batte.c; allow spell break and eske for sealed shrine GDB when in INVINCIBLE state. */
- if( tsc->data[SC_INVINCIBLE] && !tsc->data[SC_INVINCIBLEOFF] && skill_id && !(skill_id&(SA_SPELLBREAKER|SL_SKE)) )
- return 0;
- if(!skill_id && tsc->data[SC_TRICKDEAD])
- return 0;
- if((skill_id == WZ_STORMGUST || skill_id == WZ_FROSTNOVA || skill_id == NJ_HYOUSYOURAKU)
- && tsc->data[SC_FREEZE])
- return 0;
- if(skill_id == PR_LEXAETERNA && (tsc->data[SC_FREEZE] || (tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE)))
- return 0;
- }
-
- //If targetting, cloak+hide protect you, otherwise only hiding does.
- hide_flag = flag?OPTION_HIDE:(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK);
-
- //You cannot hide from ground skills.
- if( skill_get_ele(skill_id,1) == ELE_EARTH ) //TODO: Need Skill Lv here :/
- hide_flag &= ~OPTION_HIDE;
-
- switch( target->type ) {
- case BL_PC: {
- struct map_session_data *sd = (TBL_PC*) target;
- bool is_boss = (status->mode&MD_BOSS);
- bool is_detect = ((status->mode&MD_DETECTOR)?true:false);//god-knows-why gcc doesn't shut up until this happens
- if (pc_isinvisible(sd))
- return 0;
- if (tsc->option&hide_flag && !is_boss &&
- ((sd->special_state.perfect_hiding || !is_detect) ||
- (tsc->data[SC_CLOAKINGEXCEED] && is_detect)))
- return 0;
- if( tsc->data[SC_CAMOUFLAGE] && !(is_boss || is_detect) && !skill_id )
- return 0;
- if( tsc->data[SC_STEALTHFIELD] && !is_boss )
- return 0;
- }
- break;
- case BL_ITEM: //Allow targetting of items to pick'em up (or in the case of mobs, to loot them).
- //TODO: Would be nice if this could be used to judge whether the player can or not pick up the item it targets. [Skotlex]
- if (status->mode&MD_LOOTER)
- return 1;
- return 0;
- case BL_HOM:
- case BL_MER:
- case BL_ELEM:
- if( target->type == BL_HOM && skill_id && battle_config.hom_setting&0x1 && skill_get_inf(skill_id)&INF_SUPPORT_SKILL && battle_get_master(target) != src )
- return 0; // Can't use support skills on Homunculus (only Master/Self)
- if( target->type == BL_MER && (skill_id == PR_ASPERSIO || (skill_id >= SA_FLAMELAUNCHER && skill_id <= SA_SEISMICWEAPON)) && battle_get_master(target) != src )
- return 0; // Can't use Weapon endow skills on Mercenary (only Master)
- if( skill_id == AM_POTIONPITCHER && ( target->type == BL_MER || target->type == BL_ELEM) )
- return 0; // Can't use Potion Pitcher on Mercenaries
- default:
- //Check for chase-walk/hiding/cloaking opponents.
- if( tsc ) {
- if( tsc->option&hide_flag && !(status->mode&(MD_BOSS|MD_DETECTOR)))
- return 0;
- if( tsc->data[SC_STEALTHFIELD] && !(status->mode&MD_BOSS) )
- return 0;
- }
- }
- return 1;
-}
-
-//Checks whether the source can see and chase target.
-int status_check_visibility(struct block_list *src, struct block_list *target)
-{
- int view_range;
- struct status_data* status = status_get_status_data(src);
- struct status_change* tsc = status_get_sc(target);
- switch (src->type) {
- case BL_MOB:
- view_range = ((TBL_MOB*)src)->min_chase;
- break;
- case BL_PET:
- view_range = ((TBL_PET*)src)->db->range2;
- break;
- default:
- view_range = AREA_SIZE;
- }
-
- if (src->m != target->m || !check_distance_bl(src, target, view_range))
- return 0;
-
- if( tsc && tsc->data[SC_STEALTHFIELD] )
- return 0;
-
- switch (target->type)
- { //Check for chase-walk/hiding/cloaking opponents.
- case BL_PC:
- if ( tsc->data[SC_CLOAKINGEXCEED] && !(status->mode&MD_BOSS) )
- return 0;
- if( (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC__INVISIBILITY] || tsc->data[SC_CAMOUFLAGE]) && !(status->mode&MD_BOSS) &&
- ( ((TBL_PC*)target)->special_state.perfect_hiding || !(status->mode&MD_DETECTOR) ) )
- return 0;
- break;
- default:
- if( tsc && (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC__INVISIBILITY] || tsc->data[SC_CAMOUFLAGE]) && !(status->mode&(MD_BOSS|MD_DETECTOR)) )
- return 0;
-
- }
-
- return 1;
-}
-
-// Basic ASPD value
-int status_base_amotion_pc(struct map_session_data* sd, struct status_data* status)
-{
- int amotion;
-#ifdef RENEWAL_ASPD
- short mod = -1;
-
- switch( sd->weapontype2 ){ // adjustment for dual weilding
- case W_DAGGER: mod = 0; break; // 0, 1, 1
- case W_1HSWORD:
- case W_1HAXE: mod = 1;
- if( (sd->class_&MAPID_THIRDMASK) == MAPID_GUILLOTINE_CROSS ) // 0, 2, 3
- mod = sd->weapontype2 / W_1HSWORD + W_1HSWORD / sd->weapontype2 ;
- }
-
- amotion = ( sd->status.weapon < MAX_WEAPON_TYPE && mod < 0 )
- ? (aspd_base[pc_class2idx(sd->status.class_)][sd->status.weapon]) // single weapon
- : ((aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2] // dual-wield
- + aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2]) * 6 / 10 + 10 * mod
- - aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2]
- + aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype1]);
-
- if ( sd->status.shield )
- amotion += ( 2000 - aspd_base[pc_class2idx(sd->status.class_)][W_FIST] ) +
- ( aspd_base[pc_class2idx(sd->status.class_)][MAX_WEAPON_TYPE] - 2000 );
-
-#else
- // base weapon delay
- amotion = (sd->status.weapon < MAX_WEAPON_TYPE)
- ? (aspd_base[pc_class2idx(sd->status.class_)][sd->status.weapon]) // single weapon
- : (aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype1] + aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2])*7/10; // dual-wield
-
- // percentual delay reduction from stats
- amotion -= amotion * (4*status->agi + status->dex)/1000;
-#endif
- // raw delay adjustment from bAspd bonus
- amotion += sd->bonus.aspd_add;
-
- return amotion;
-}
-
-static unsigned short status_base_atk(const struct block_list *bl, const struct status_data *status)
-{
- int flag = 0, str, dex,
-#ifdef RENEWAL
- rstr,
-#endif
- dstr;
-
-
- if(!(bl->type&battle_config.enable_baseatk))
- return 0;
-
- if (bl->type == BL_PC)
- switch(((TBL_PC*)bl)->status.weapon){
- case W_BOW:
- case W_MUSICAL:
- case W_WHIP:
- case W_REVOLVER:
- case W_RIFLE:
- case W_GATLING:
- case W_SHOTGUN:
- case W_GRENADE:
- flag = 1;
- }
- if (flag) {
-#ifdef RENEWAL
- rstr =
-#endif
- str = status->dex;
- dex = status->str;
- } else {
-#ifdef RENEWAL
- rstr =
-#endif
- str = status->str;
- dex = status->dex;
- }
- //Normally only players have base-atk, but homunc have a different batk
- // equation, hinting that perhaps non-players should use this for batk.
- // [Skotlex]
- dstr = str/10;
- str += dstr*dstr;
- if (bl->type == BL_PC)
-#ifdef RENEWAL
- str = (rstr*10 + dex*10/5 + status->luk*10/3 + ((TBL_PC*)bl)->status.base_level*10/4)/10;
-#else
- str+= dex/5 + status->luk/5;
-#endif
- return cap_value(str, 0, USHRT_MAX);
-}
-
-#ifndef RENEWAL
-static inline unsigned short status_base_matk_min(const struct status_data* status){ return status->int_+(status->int_/7)*(status->int_/7); }
-static inline unsigned short status_base_matk_max(const struct status_data* status){ return status->int_+(status->int_/5)*(status->int_/5); }
-#else
-unsigned short status_base_matk(const struct status_data* status, int level){ return status->int_+(status->int_/2)+(status->dex/5)+(status->luk/3)+(level/4); }
-#endif
-
-//Fills in the misc data that can be calculated from the other status info (except for level)
-void status_calc_misc(struct block_list *bl, struct status_data *status, int level)
-{
- //Non players get the value set, players need to stack with previous bonuses.
- if( bl->type != BL_PC )
- status->batk =
- status->hit = status->flee =
- status->def2 = status->mdef2 =
- status->cri = status->flee2 = 0;
-
-#ifdef RENEWAL // renewal formulas
- status->matk_min = status->matk_max = status_base_matk(status, level);
- status->hit += level + status->dex + status->luk/3 + 175; //base level + ( every 1 dex = +1 hit ) + (every 3 luk = +1 hit) + 175
- status->flee += level + status->agi + status->luk/5 + 100; //base level + ( every 1 agi = +1 flee ) + (every 5 luk = +1 flee) + 100
- status->def2 += (int)(((float)level + status->vit)/2 + ((float)status->agi/5)); //base level + (every 2 vit = +1 def) + (every 5 agi = +1 def)
- status->mdef2 += (int)(status->int_ + ((float)level/4) + ((float)status->dex/5) + ((float)status->vit/5)); //(every 4 base level = +1 mdef) + (every 1 int = +1 mdef) + (every 5 dex = +1 mdef) + (every 5 vit = +1 mdef)
-#else
- status->matk_min = status_base_matk_min(status);
- status->matk_max = status_base_matk_max(status);
- status->hit += level + status->dex;
- status->flee += level + status->agi;
- status->def2 += status->vit;
- status->mdef2 += status->int_ + (status->vit>>1);
-#endif
-
- if( bl->type&battle_config.enable_critical )
- status->cri += 10 + (status->luk*10/3); //(every 1 luk = +0.3 critical)
- else
- status->cri = 0;
-
- if (bl->type&battle_config.enable_perfect_flee)
- status->flee2 += status->luk + 10; //(every 10 luk = +1 perfect flee)
- else
- status->flee2 = 0;
-
- if (status->batk) {
- int temp = status->batk + status_base_atk(bl, status);
- status->batk = cap_value(temp, 0, USHRT_MAX);
- } else
- status->batk = status_base_atk(bl, status);
- if (status->cri)
- switch (bl->type) {
- case BL_MOB:
- if(battle_config.mob_critical_rate != 100)
- status->cri = status->cri*battle_config.mob_critical_rate/100;
- if(!status->cri && battle_config.mob_critical_rate)
- status->cri = 10;
- break;
- case BL_PC:
- //Players don't have a critical adjustment setting as of yet.
- break;
- default:
- if(battle_config.critical_rate != 100)
- status->cri = status->cri*battle_config.critical_rate/100;
- if (!status->cri && battle_config.critical_rate)
- status->cri = 10;
- }
- if(bl->type&BL_REGEN)
- status_calc_regen(bl, status, status_get_regen_data(bl));
-}
-
-//Skotlex: Calculates the initial status for the given mob
-//first will only be false when the mob leveled up or got a GuardUp level.
-int status_calc_mob_(struct mob_data* md, bool first)
-{
- struct status_data *status;
- struct block_list *mbl = NULL;
- int flag=0;
-
- if(first)
- { //Set basic level on respawn.
- if (md->level > 0 && md->level <= MAX_LEVEL && md->level != md->db->lv)
- ;
- else
- md->level = md->db->lv;
- }
-
- //Check if we need custom base-status
- if (battle_config.mobs_level_up && md->level > md->db->lv)
- flag|=1;
-
- if (md->special_state.size)
- flag|=2;
-
- if (md->guardian_data && md->guardian_data->guardup_lv)
- flag|=4;
- if (md->class_ == MOBID_EMPERIUM)
- flag|=4;
-
- if (battle_config.slaves_inherit_speed && md->master_id)
- flag|=8;
-
- if (md->master_id && md->special_state.ai>1)
- flag|=16;
-
- if (!flag)
- { //No special status required.
- if (md->base_status) {
- aFree(md->base_status);
- md->base_status = NULL;
- }
- if(first)
- memcpy(&md->status, &md->db->status, sizeof(struct status_data));
- return 0;
- }
- if (!md->base_status)
- md->base_status = (struct status_data*)aCalloc(1, sizeof(struct status_data));
-
- status = md->base_status;
- memcpy(status, &md->db->status, sizeof(struct status_data));
-
- if (flag&(8|16))
- mbl = map_id2bl(md->master_id);
-
- if (flag&8 && mbl) {
- struct status_data *mstatus = status_get_base_status(mbl);
- if (mstatus &&
- battle_config.slaves_inherit_speed&(mstatus->mode&MD_CANMOVE?1:2))
- status->speed = mstatus->speed;
- if( status->speed < 2 ) /* minimum for the unit to function properly */
- status->speed = 2;
- }
-
- if (flag&16 && mbl)
- { //Max HP setting from Summon Flora/marine Sphere
- struct unit_data *ud = unit_bl2ud(mbl);
- //Remove special AI when this is used by regular mobs.
- if (mbl->type == BL_MOB && !((TBL_MOB*)mbl)->special_state.ai)
- md->special_state.ai = 0;
- if (ud)
- { // different levels of HP according to skill level
- if (ud->skill_id == AM_SPHEREMINE) {
- status->max_hp = 2000 + 400*ud->skill_lv;
- } else if(ud->skill_id == KO_ZANZOU){
- status->max_hp = 3000 + 3000 * ud->skill_lv;
- } else { //AM_CANNIBALIZE
- status->max_hp = 1500 + 200*ud->skill_lv + 10*status_get_lv(mbl);
- status->mode|= MD_CANATTACK|MD_AGGRESSIVE;
- }
- status->hp = status->max_hp;
- }
- }
-
- if (flag&1)
- { // increase from mobs leveling up [Valaris]
- int diff = md->level - md->db->lv;
- status->str+= diff;
- status->agi+= diff;
- status->vit+= diff;
- status->int_+= diff;
- status->dex+= diff;
- status->luk+= diff;
- status->max_hp += diff*status->vit;
- status->max_sp += diff*status->int_;
- status->hp = status->max_hp;
- status->sp = status->max_sp;
- status->speed -= cap_value(diff, 0, status->speed - 10);
- }
-
-
- if (flag&2 && battle_config.mob_size_influence)
- { // change for sized monsters [Valaris]
- if (md->special_state.size==SZ_MEDIUM) {
- status->max_hp>>=1;
- status->max_sp>>=1;
- if (!status->max_hp) status->max_hp = 1;
- if (!status->max_sp) status->max_sp = 1;
- status->hp=status->max_hp;
- status->sp=status->max_sp;
- status->str>>=1;
- status->agi>>=1;
- status->vit>>=1;
- status->int_>>=1;
- status->dex>>=1;
- status->luk>>=1;
- if (!status->str) status->str = 1;
- if (!status->agi) status->agi = 1;
- if (!status->vit) status->vit = 1;
- if (!status->int_) status->int_ = 1;
- if (!status->dex) status->dex = 1;
- if (!status->luk) status->luk = 1;
- } else if (md->special_state.size==SZ_BIG) {
- status->max_hp<<=1;
- status->max_sp<<=1;
- status->hp=status->max_hp;
- status->sp=status->max_sp;
- status->str<<=1;
- status->agi<<=1;
- status->vit<<=1;
- status->int_<<=1;
- status->dex<<=1;
- status->luk<<=1;
- }
- }
-
- status_calc_misc(&md->bl, status, md->level);
-
- if(flag&4)
- { // Strengthen Guardians - custom value +10% / lv
- struct guild_castle *gc;
- gc=guild_mapname2gc(map[md->bl.m].name);
- if (!gc)
- ShowError("status_calc_mob: No castle set at map %s\n", map[md->bl.m].name);
- else
- if(gc->castle_id < 24 || md->class_ == MOBID_EMPERIUM) {
-#ifdef RENEWAL
- status->max_hp += 50 * gc->defense;
- status->max_sp += 70 * gc->defense;
-#else
- status->max_hp += 1000 * gc->defense;
- status->max_sp += 200 * gc->defense;
-#endif
- status->hp = status->max_hp;
- status->sp = status->max_sp;
- status->def += (gc->defense+2)/3;
- status->mdef += (gc->defense+2)/3;
- }
- if(md->class_ != MOBID_EMPERIUM) {
- status->batk += status->batk * 10*md->guardian_data->guardup_lv/100;
- status->rhw.atk += status->rhw.atk * 10*md->guardian_data->guardup_lv/100;
- status->rhw.atk2 += status->rhw.atk2 * 10*md->guardian_data->guardup_lv/100;
- status->aspd_rate -= 100*md->guardian_data->guardup_lv;
- }
- }
-
- if( first ) //Initial battle status
- memcpy(&md->status, status, sizeof(struct status_data));
-
- return 1;
-}
-
-//Skotlex: Calculates the stats of the given pet.
-int status_calc_pet_(struct pet_data *pd, bool first)
-{
- nullpo_ret(pd);
-
- if (first) {
- memcpy(&pd->status, &pd->db->status, sizeof(struct status_data));
- pd->status.mode = MD_CANMOVE; // pets discard all modes, except walking
- pd->status.speed = pd->petDB->speed;
-
- if(battle_config.pet_attack_support || battle_config.pet_damage_support)
- {// attack support requires the pet to be able to attack
- pd->status.mode|= MD_CANATTACK;
- }
- }
-
- if (battle_config.pet_lv_rate && pd->msd)
- {
- struct map_session_data *sd = pd->msd;
- int lv;
-
- lv =sd->status.base_level*battle_config.pet_lv_rate/100;
- if (lv < 0)
- lv = 1;
- if (lv != pd->pet.level || first)
- {
- struct status_data *bstat = &pd->db->status, *status = &pd->status;
- pd->pet.level = lv;
- if (!first) //Lv Up animation
- clif_misceffect(&pd->bl, 0);
- status->rhw.atk = (bstat->rhw.atk*lv)/pd->db->lv;
- status->rhw.atk2 = (bstat->rhw.atk2*lv)/pd->db->lv;
- status->str = (bstat->str*lv)/pd->db->lv;
- status->agi = (bstat->agi*lv)/pd->db->lv;
- status->vit = (bstat->vit*lv)/pd->db->lv;
- status->int_ = (bstat->int_*lv)/pd->db->lv;
- status->dex = (bstat->dex*lv)/pd->db->lv;
- status->luk = (bstat->luk*lv)/pd->db->lv;
-
- status->rhw.atk = cap_value(status->rhw.atk, 1, battle_config.pet_max_atk1);
- status->rhw.atk2 = cap_value(status->rhw.atk2, 2, battle_config.pet_max_atk2);
- status->str = cap_value(status->str,1,battle_config.pet_max_stats);
- status->agi = cap_value(status->agi,1,battle_config.pet_max_stats);
- status->vit = cap_value(status->vit,1,battle_config.pet_max_stats);
- status->int_= cap_value(status->int_,1,battle_config.pet_max_stats);
- status->dex = cap_value(status->dex,1,battle_config.pet_max_stats);
- status->luk = cap_value(status->luk,1,battle_config.pet_max_stats);
-
- status_calc_misc(&pd->bl, &pd->status, lv);
-
- if (!first) //Not done the first time because the pet is not visible yet
- clif_send_petstatus(sd);
- }
- } else if (first) {
- status_calc_misc(&pd->bl, &pd->status, pd->db->lv);
- if (!battle_config.pet_lv_rate && pd->pet.level != pd->db->lv)
- pd->pet.level = pd->db->lv;
- }
-
- //Support rate modifier (1000 = 100%)
- pd->rate_fix = 1000*(pd->pet.intimate - battle_config.pet_support_min_friendly)/(1000- battle_config.pet_support_min_friendly) +500;
- if(battle_config.pet_support_rate != 100)
- pd->rate_fix = pd->rate_fix*battle_config.pet_support_rate/100;
-
- return 1;
-}
-
-/// Helper function for status_base_pc_maxhp(), used to pre-calculate the hp_sigma_val[] array
-static void status_calc_sigma(void)
-{
- int i,j;
-
- for(i = 0; i < CLASS_COUNT; i++)
- {
- unsigned int k = 0;
- hp_sigma_val[i][0] = hp_sigma_val[i][1] = 0;
- for(j = 2; j <= MAX_LEVEL; j++)
- {
- k += (hp_coefficient[i]*j + 50) / 100;
- hp_sigma_val[i][j] = k;
- if (k >= INT_MAX)
- break; //Overflow protection. [Skotlex]
- }
- for(; j <= MAX_LEVEL; j++)
- hp_sigma_val[i][j] = INT_MAX;
- }
-}
-
-/// Calculates base MaxHP value according to class and base level
-/// The recursive equation used to calculate level bonus is (using integer operations)
-/// f(0) = 35 | f(x+1) = f(x) + A + (x + B)*C/D
-/// which reduces to something close to
-/// f(x) = 35 + x*(A + B*C/D) + sum(i=2..x){ i*C/D }
-static unsigned int status_base_pc_maxhp(struct map_session_data* sd, struct status_data* status)
-{
- uint64 val = pc_class2idx(sd->status.class_);
- val = 35 + sd->status.base_level*(int64)hp_coefficient2[val]/100 + hp_sigma_val[val][sd->status.base_level];
-
- if((sd->class_&MAPID_UPPERMASK) == MAPID_NINJA || (sd->class_&MAPID_UPPERMASK) == MAPID_GUNSLINGER)
- val += 100; //Since their HP can't be approximated well enough without this.
- if((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON))
- val *= 3; //Triple max HP for top ranking Taekwons over level 90.
- if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.base_level >= 99)
- val += 2000; //Supernovice lvl99 hp bonus.
-
- val += val * status->vit/100; // +1% per each point of VIT
-
- if (sd->class_&JOBL_UPPER)
- val += val * 25/100; //Trans classes get a 25% hp bonus
- else if (sd->class_&JOBL_BABY)
- val -= val * 30/100; //Baby classes get a 30% hp penalty
- return (unsigned int)val;
-}
-
-static unsigned int status_base_pc_maxsp(struct map_session_data* sd, struct status_data *status)
-{
- uint64 val;
-
- val = 10 + sd->status.base_level*(int64)sp_coefficient[pc_class2idx(sd->status.class_)]/100;
- val += val * status->int_/100;
-
- if (sd->class_&JOBL_UPPER)
- val += val * 25/100;
- else if (sd->class_&JOBL_BABY)
- val -= val * 30/100;
- if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON))
- val *= 3; //Triple max SP for top ranking Taekwons over level 90.
-
- return (unsigned int)val;
-}
-
-//Calculates player data from scratch without counting SC adjustments.
-//Should be invoked whenever players raise stats, learn passive skills or change equipment.
-int status_calc_pc_(struct map_session_data* sd, bool first)
-{
- static int calculating = 0; //Check for recursive call preemption. [Skotlex]
- struct status_data *status; // pointer to the player's base status
- const struct status_change *sc = &sd->sc;
- struct s_skill b_skill[MAX_SKILL]; // previous skill tree
- int b_weight, b_max_weight, b_cart_weight_max, // previous weight
- i, index, skill,refinedef=0;
- int64 i64;
-
- if (++calculating > 10) //Too many recursive calls!
- return -1;
-
- // remember player-specific values that are currently being shown to the client (for refresh purposes)
- memcpy(b_skill, &sd->status.skill, sizeof(b_skill));
- b_weight = sd->weight;
- b_max_weight = sd->max_weight;
- b_cart_weight_max = sd->cart_weight_max;
-
- pc_calc_skilltree(sd); // SkillTree calculation
-
- sd->max_weight = max_weight_base[pc_class2idx(sd->status.class_)]+sd->status.str*300;
-
- if(first) {
- //Load Hp/SP from char-received data.
- sd->battle_status.hp = sd->status.hp;
- sd->battle_status.sp = sd->status.sp;
- sd->regen.sregen = &sd->sregen;
- sd->regen.ssregen = &sd->ssregen;
- sd->weight=0;
- for(i=0;i<MAX_INVENTORY;i++){
- if(sd->status.inventory[i].nameid==0 || sd->inventory_data[i] == NULL)
- continue;
- sd->weight += sd->inventory_data[i]->weight*sd->status.inventory[i].amount;
- }
- sd->cart_weight=0;
- sd->cart_num=0;
- for(i=0;i<MAX_CART;i++){
- if(sd->status.cart[i].nameid==0)
- continue;
- sd->cart_weight+=itemdb_weight(sd->status.cart[i].nameid)*sd->status.cart[i].amount;
- sd->cart_num++;
- }
- }
-
- status = &sd->base_status;
- // these are not zeroed. [zzo]
- sd->hprate=100;
- sd->sprate=100;
- sd->castrate=100;
- sd->delayrate=100;
- sd->dsprate=100;
- sd->hprecov_rate = 100;
- sd->sprecov_rate = 100;
- sd->matk_rate = 100;
- sd->critical_rate = sd->hit_rate = sd->flee_rate = sd->flee2_rate = 100;
- sd->def_rate = sd->def2_rate = sd->mdef_rate = sd->mdef2_rate = 100;
- sd->regen.state.block = 0;
-
- // zeroed arrays, order follows the order in pc.h.
- // add new arrays to the end of zeroed area in pc.h (see comments) and size here. [zzo]
- memset (sd->param_bonus, 0, sizeof(sd->param_bonus)
- + sizeof(sd->param_equip)
- + sizeof(sd->subele)
- + sizeof(sd->subrace)
- + sizeof(sd->subrace2)
- + sizeof(sd->subsize)
- + sizeof(sd->reseff)
- + sizeof(sd->weapon_coma_ele)
- + sizeof(sd->weapon_coma_race)
- + sizeof(sd->weapon_atk)
- + sizeof(sd->weapon_atk_rate)
- + sizeof(sd->arrow_addele)
- + sizeof(sd->arrow_addrace)
- + sizeof(sd->arrow_addsize)
- + sizeof(sd->magic_addele)
- + sizeof(sd->magic_addrace)
- + sizeof(sd->magic_addsize)
- + sizeof(sd->magic_atk_ele)
- + sizeof(sd->critaddrace)
- + sizeof(sd->expaddrace)
- + sizeof(sd->ignore_mdef)
- + sizeof(sd->ignore_def)
- + sizeof(sd->itemgrouphealrate)
- + sizeof(sd->sp_gain_race)
- + sizeof(sd->sp_gain_race_attack)
- + sizeof(sd->hp_gain_race_attack)
- );
-
- memset (&sd->right_weapon.overrefine, 0, sizeof(sd->right_weapon) - sizeof(sd->right_weapon.atkmods));
- memset (&sd->left_weapon.overrefine, 0, sizeof(sd->left_weapon) - sizeof(sd->left_weapon.atkmods));
-
- if (sd->special_state.intravision && !sd->sc.data[SC_INTRAVISION]) //Clear intravision as long as nothing else is using it
- clif_status_load(&sd->bl, SI_INTRAVISION, 0);
-
- memset(&sd->special_state,0,sizeof(sd->special_state));
- memset(&status->max_hp, 0, sizeof(struct status_data)-(sizeof(status->hp)+sizeof(status->sp)));
-
- //FIXME: Most of these stuff should be calculated once, but how do I fix the memset above to do that? [Skotlex]
- status->speed = DEFAULT_WALK_SPEED;
- //Give them all modes except these (useful for clones)
- status->mode = MD_MASK&~(MD_BOSS|MD_PLANT|MD_DETECTOR|MD_ANGRY|MD_TARGETWEAK);
-
- status->size = (sd->class_&JOBL_BABY)?SZ_SMALL:SZ_MEDIUM;
- if (battle_config.character_size && pc_isriding(sd)) { //[Lupus]
- if (sd->class_&JOBL_BABY) {
- if (battle_config.character_size&SZ_BIG)
- status->size++;
- } else
- if(battle_config.character_size&SZ_MEDIUM)
- status->size++;
- }
- status->aspd_rate = 1000;
- status->ele_lv = 1;
- status->race = RC_DEMIHUMAN;
-
- //zero up structures...
- memset(&sd->autospell,0,sizeof(sd->autospell)
- + sizeof(sd->autospell2)
- + sizeof(sd->autospell3)
- + sizeof(sd->addeff)
- + sizeof(sd->addeff2)
- + sizeof(sd->addeff3)
- + sizeof(sd->skillatk)
- + sizeof(sd->skillusesprate)
- + sizeof(sd->skillusesp)
- + sizeof(sd->skillheal)
- + sizeof(sd->skillheal2)
- + sizeof(sd->hp_loss)
- + sizeof(sd->sp_loss)
- + sizeof(sd->hp_regen)
- + sizeof(sd->sp_regen)
- + sizeof(sd->skillblown)
- + sizeof(sd->skillcast)
- + sizeof(sd->add_def)
- + sizeof(sd->add_mdef)
- + sizeof(sd->add_mdmg)
- + sizeof(sd->add_drop)
- + sizeof(sd->itemhealrate)
- + sizeof(sd->subele2)
- + sizeof(sd->skillcooldown)
- + sizeof(sd->skillfixcast)
- + sizeof(sd->skillvarcast)
- );
-
- memset (&sd->bonus, 0,sizeof(sd->bonus));
-
- // Autobonus
- pc_delautobonus(sd,sd->autobonus,ARRAYLENGTH(sd->autobonus),true);
- pc_delautobonus(sd,sd->autobonus2,ARRAYLENGTH(sd->autobonus2),true);
- pc_delautobonus(sd,sd->autobonus3,ARRAYLENGTH(sd->autobonus3),true);
-
- // Parse equipment.
- for(i=0;i<EQI_MAX-1;i++) {
- current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus]
- if(index < 0)
- continue;
- if(i == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index)
- continue;
- if(i == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index)
- continue;
- if(i == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index))
- continue;
- if(i == EQI_COSTUME_MID && sd->equip_index[EQI_COSTUME_LOW] == index)
- continue;
- if(i == EQI_COSTUME_TOP && (sd->equip_index[EQI_COSTUME_MID] == index || sd->equip_index[EQI_COSTUME_LOW] == index))
- continue;
- if(!sd->inventory_data[index])
- continue;
-
- status->def += sd->inventory_data[index]->def;
-
- if(first && sd->inventory_data[index]->equip_script)
- { //Execute equip-script on login
- run_script(sd->inventory_data[index]->equip_script,0,sd->bl.id,0);
- if (!calculating)
- return 1;
- }
-
- // sanitize the refine level in case someone decreased the value inbetween
- if (sd->status.inventory[index].refine > MAX_REFINE)
- sd->status.inventory[index].refine = MAX_REFINE;
-
- if(sd->inventory_data[index]->type == IT_WEAPON) {
- int r,wlv = sd->inventory_data[index]->wlv;
- struct weapon_data *wd;
- struct weapon_atk *wa;
- if (wlv >= REFINE_TYPE_MAX)
- wlv = REFINE_TYPE_MAX - 1;
- if(i == EQI_HAND_L && sd->status.inventory[index].equip == EQP_HAND_L) {
- wd = &sd->left_weapon; // Left-hand weapon
- wa = &status->lhw;
- } else {
- wd = &sd->right_weapon;
- wa = &status->rhw;
- }
- wa->atk += sd->inventory_data[index]->atk;
- if ( (r = sd->status.inventory[index].refine) )
- wa->atk2 = refine_info[wlv].bonus[r-1] / 100;
-
-#ifdef RENEWAL
- wa->matk += sd->inventory_data[index]->matk;
- wa->wlv = wlv;
- if( r ) // renewal magic attack refine bonus
- wa->matk += refine_info[wlv].bonus[r-1] / 100;
-#endif
-
- //Overrefine bonus.
- if (r)
- wd->overrefine = refine_info[wlv].randombonus_max[r-1] / 100;
-
- wa->range += sd->inventory_data[index]->range;
- if(sd->inventory_data[index]->script) {
- if (wd == &sd->left_weapon) {
- sd->state.lr_flag = 1;
- run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
- sd->state.lr_flag = 0;
- } else
- run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
- if (!calculating) //Abort, run_script retriggered this. [Skotlex]
- return 1;
- }
-
- if(sd->status.inventory[index].card[0]==CARD0_FORGE)
- { // Forged weapon
- wd->star += (sd->status.inventory[index].card[1]>>8);
- if(wd->star >= 15) wd->star = 40; // 3 Star Crumbs now give +40 dmg
- if(pc_famerank(MakeDWord(sd->status.inventory[index].card[2],sd->status.inventory[index].card[3]) ,MAPID_BLACKSMITH))
- wd->star += 10;
-
- if (!wa->ele) //Do not overwrite element from previous bonuses.
- wa->ele = (sd->status.inventory[index].card[1]&0x0f);
- }
- }
- else if(sd->inventory_data[index]->type == IT_ARMOR) {
- int r;
- if ( (r = sd->status.inventory[index].refine) )
- refinedef += refine_info[REFINE_TYPE_ARMOR].bonus[r-1];
- if(sd->inventory_data[index]->script) {
- if( i == EQI_HAND_L ) //Shield
- sd->state.lr_flag = 3;
- run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
- if( i == EQI_HAND_L ) //Shield
- sd->state.lr_flag = 0;
- if (!calculating) //Abort, run_script retriggered this. [Skotlex]
- return 1;
- }
- }
- }
-
- if(sd->equip_index[EQI_AMMO] >= 0){
- index = sd->equip_index[EQI_AMMO];
- if(sd->inventory_data[index]){ // Arrows
- sd->bonus.arrow_atk += sd->inventory_data[index]->atk;
- sd->state.lr_flag = 2;
- if( !itemdb_is_GNthrowable(sd->inventory_data[index]->nameid) ) //don't run scripts on throwable items
- run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
- sd->state.lr_flag = 0;
- if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex]
- return 1;
- }
- }
-
- /* we've got combos to process */
- if( sd->combos.count ) {
- for( i = 0; i < sd->combos.count; i++ ) {
- run_script(sd->combos.bonus[i],0,sd->bl.id,0);
- if (!calculating) //Abort, run_script retriggered this.
- return 1;
- }
- }
-
- //Store equipment script bonuses
- memcpy(sd->param_equip,sd->param_bonus,sizeof(sd->param_equip));
- memset(sd->param_bonus, 0, sizeof(sd->param_bonus));
-
- status->def += (refinedef+50)/100;
-
- //Parse Cards
- for(i=0;i<EQI_MAX-1;i++) {
- current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus]
- if(index < 0)
- continue;
- if(i == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index)
- continue;
- if(i == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index)
- continue;
- if(i == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index))
- continue;
-
- if(sd->inventory_data[index]) {
- int j,c;
- struct item_data *data;
-
- //Card script execution.
- if(itemdb_isspecial(sd->status.inventory[index].card[0]))
- continue;
- for(j=0;j<MAX_SLOTS;j++){ // Uses MAX_SLOTS to support Soul Bound system [Inkfish]
- current_equip_card_id= c= sd->status.inventory[index].card[j];
- if(!c)
- continue;
- data = itemdb_exists(c);
- if(!data)
- continue;
- if(first && data->equip_script)
- { //Execute equip-script on login
- run_script(data->equip_script,0,sd->bl.id,0);
- if (!calculating)
- return 1;
- }
- if(!data->script)
- continue;
- if(data->flag.no_equip) { //Card restriction checks.
- if(map[sd->bl.m].flag.restricted && data->flag.no_equip&(8*map[sd->bl.m].zone))
- continue;
- if(!map_flag_vs(sd->bl.m) && data->flag.no_equip&1)
- continue;
- if(map[sd->bl.m].flag.pvp && data->flag.no_equip&2)
- continue;
- if(map_flag_gvg(sd->bl.m) && data->flag.no_equip&4)
- continue;
- if(map[sd->bl.m].flag.battleground && data->flag.no_equip&8)
- continue;
- }
- if(i == EQI_HAND_L && sd->status.inventory[index].equip == EQP_HAND_L)
- { //Left hand status.
- sd->state.lr_flag = 1;
- run_script(data->script,0,sd->bl.id,0);
- sd->state.lr_flag = 0;
- } else
- run_script(data->script,0,sd->bl.id,0);
- if (!calculating) //Abort, run_script his function. [Skotlex]
- return 1;
- }
- }
- }
-
- if( sc->count && sc->data[SC_ITEMSCRIPT] )
- {
- struct item_data *data = itemdb_exists(sc->data[SC_ITEMSCRIPT]->val1);
- if( data && data->script )
- run_script(data->script,0,sd->bl.id,0);
- }
-
- if( sd->pd )
- { // Pet Bonus
- struct pet_data *pd = sd->pd;
- if( pd && pd->petDB && pd->petDB->equip_script && pd->pet.intimate >= battle_config.pet_equip_min_friendly )
- run_script(pd->petDB->equip_script,0,sd->bl.id,0);
- if( pd && pd->pet.intimate > 0 && (!battle_config.pet_equip_required || pd->pet.equip > 0) && pd->state.skillbonus == 1 && pd->bonus )
- pc_bonus(sd,pd->bonus->type, pd->bonus->val);
- }
-
- //param_bonus now holds card bonuses.
- if(status->rhw.range < 1) status->rhw.range = 1;
- if(status->lhw.range < 1) status->lhw.range = 1;
- if(status->rhw.range < status->lhw.range)
- status->rhw.range = status->lhw.range;
-
- sd->bonus.double_rate += sd->bonus.double_add_rate;
- sd->bonus.perfect_hit += sd->bonus.perfect_hit_add;
- sd->bonus.splash_range += sd->bonus.splash_add_range;
-
- // Damage modifiers from weapon type
- sd->right_weapon.atkmods[0] = atkmods[0][sd->weapontype1];
- sd->right_weapon.atkmods[1] = atkmods[1][sd->weapontype1];
- sd->right_weapon.atkmods[2] = atkmods[2][sd->weapontype1];
- sd->left_weapon.atkmods[0] = atkmods[0][sd->weapontype2];
- sd->left_weapon.atkmods[1] = atkmods[1][sd->weapontype2];
- sd->left_weapon.atkmods[2] = atkmods[2][sd->weapontype2];
-
- if(pc_isriding(sd) &&
- (sd->status.weapon==W_1HSPEAR || sd->status.weapon==W_2HSPEAR))
- { //When Riding with spear, damage modifier to mid-class becomes
- //same as versus large size.
- sd->right_weapon.atkmods[1] = sd->right_weapon.atkmods[2];
- sd->left_weapon.atkmods[1] = sd->left_weapon.atkmods[2];
- }
-
-// ----- STATS CALCULATION -----
-
- // Job bonuses
- index = pc_class2idx(sd->status.class_);
- for(i=0;i<(int)sd->status.job_level && i<MAX_LEVEL;i++){
- if(!job_bonus[index][i])
- continue;
- switch(job_bonus[index][i]) {
- case 1: status->str++; break;
- case 2: status->agi++; break;
- case 3: status->vit++; break;
- case 4: status->int_++; break;
- case 5: status->dex++; break;
- case 6: status->luk++; break;
- }
- }
-
- // If a Super Novice has never died and is at least joblv 70, he gets all stats +10
- if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->die_counter == 0 && sd->status.job_level >= 70){
- status->str += 10;
- status->agi += 10;
- status->vit += 10;
- status->int_+= 10;
- status->dex += 10;
- status->luk += 10;
- }
-
- // Absolute modifiers from passive skills
- if(pc_checkskill(sd,BS_HILTBINDING)>0)
- status->str++;
- if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0)
- status->int_ += (skill+1)/2; // +1 INT / 2 lv
- if((skill=pc_checkskill(sd,AC_OWL))>0)
- status->dex += skill;
- if((skill = pc_checkskill(sd,RA_RESEARCHTRAP))>0)
- status->int_ += skill;
-
- // Bonuses from cards and equipment as well as base stat, remember to avoid overflows.
- i = status->str + sd->status.str + sd->param_bonus[0] + sd->param_equip[0];
- status->str = cap_value(i,0,USHRT_MAX);
- i = status->agi + sd->status.agi + sd->param_bonus[1] + sd->param_equip[1];
- status->agi = cap_value(i,0,USHRT_MAX);
- i = status->vit + sd->status.vit + sd->param_bonus[2] + sd->param_equip[2];
- status->vit = cap_value(i,0,USHRT_MAX);
- i = status->int_+ sd->status.int_+ sd->param_bonus[3] + sd->param_equip[3];
- status->int_ = cap_value(i,0,USHRT_MAX);
- i = status->dex + sd->status.dex + sd->param_bonus[4] + sd->param_equip[4];
- status->dex = cap_value(i,0,USHRT_MAX);
- i = status->luk + sd->status.luk + sd->param_bonus[5] + sd->param_equip[5];
- status->luk = cap_value(i,0,USHRT_MAX);
-
-// ------ BASE ATTACK CALCULATION ------
-
- // Base batk value is set on status_calc_misc
- // weapon-type bonus (FIXME: Why is the weapon_atk bonus applied to base attack?)
- if (sd->status.weapon < MAX_WEAPON_TYPE && sd->weapon_atk[sd->status.weapon])
- status->batk += sd->weapon_atk[sd->status.weapon];
- // Absolute modifiers from passive skills
- if((skill=pc_checkskill(sd,BS_HILTBINDING))>0)
- status->batk += 4;
-
-// ----- HP MAX CALCULATION -----
-
- // Basic MaxHP value
- //We hold the standard Max HP here to make it faster to recalculate on vit changes.
- sd->status.max_hp = status_base_pc_maxhp(sd,status);
- //This is done to handle underflows from negative Max HP bonuses
- i64 = sd->status.max_hp + (int)status->max_hp;
- status->max_hp = (unsigned int)cap_value(i64, 0, INT_MAX);
-
- // Absolute modifiers from passive skills
- if((skill=pc_checkskill(sd,CR_TRUST))>0)
- status->max_hp += skill*200;
-
- // Apply relative modifiers from equipment
- if(sd->hprate < 0)
- sd->hprate = 0;
- if(sd->hprate!=100)
- status->max_hp = (int64)status->max_hp * sd->hprate/100;
- if(battle_config.hp_rate != 100)
- status->max_hp = (int64)status->max_hp * battle_config.hp_rate/100;
-
- if(status->max_hp > (unsigned int)battle_config.max_hp)
- status->max_hp = battle_config.max_hp;
- else if(!status->max_hp)
- status->max_hp = 1;
-
-// ----- SP MAX CALCULATION -----
-
- // Basic MaxSP value
- sd->status.max_sp = status_base_pc_maxsp(sd,status);
- //This is done to handle underflows from negative Max SP bonuses
- i64 = sd->status.max_sp + (int)status->max_sp;
- status->max_sp = (unsigned int)cap_value(i64, 0, INT_MAX);
-
- // Absolute modifiers from passive skills
- if((skill=pc_checkskill(sd,SL_KAINA))>0)
- status->max_sp += 30*skill;
- if((skill=pc_checkskill(sd,HP_MEDITATIO))>0)
- status->max_sp += (int64)status->max_sp * skill/100;
- if((skill=pc_checkskill(sd,HW_SOULDRAIN))>0)
- status->max_sp += (int64)status->max_sp * 2*skill/100;
- if( (skill = pc_checkskill(sd,RA_RESEARCHTRAP)) > 0 )
- status->max_sp += 200 + 20 * skill;
- if( (skill = pc_checkskill(sd,WM_LESSON)) > 0 )
- status->max_sp += 30 * skill;
-
-
- // Apply relative modifiers from equipment
- if(sd->sprate < 0)
- sd->sprate = 0;
- if(sd->sprate!=100)
- status->max_sp = (int64)status->max_sp * sd->sprate/100;
- if(battle_config.sp_rate != 100)
- status->max_sp = (int64)status->max_sp * battle_config.sp_rate/100;
-
- if(status->max_sp > (unsigned int)battle_config.max_sp)
- status->max_sp = battle_config.max_sp;
- else if(!status->max_sp)
- status->max_sp = 1;
-
-// ----- RESPAWN HP/SP -----
-//
- //Calc respawn hp and store it on base_status
- if (sd->special_state.restart_full_recover)
- {
- status->hp = status->max_hp;
- status->sp = status->max_sp;
- } else {
- if((sd->class_&MAPID_BASEMASK) == MAPID_NOVICE && !(sd->class_&JOBL_2)
- && battle_config.restart_hp_rate < 50)
- status->hp = status->max_hp>>1;
- else
- status->hp = (int64)status->max_hp * battle_config.restart_hp_rate/100;
- if(!status->hp)
- status->hp = 1;
-
- status->sp = (int64)status->max_sp * battle_config.restart_sp_rate /100;
-
- if( !status->sp ) /* the minimum for the respawn setting is SP:1 */
- status->sp = 1;
- }
-
-// ----- MISC CALCULATION -----
- status_calc_misc(&sd->bl, status, sd->status.base_level);
-
- //Equipment modifiers for misc settings
- if(sd->matk_rate < 0)
- sd->matk_rate = 0;
-
- if(sd->matk_rate != 100){
- status->matk_max = status->matk_max * sd->matk_rate/100;
- status->matk_min = status->matk_min * sd->matk_rate/100;
- }
-
- if(sd->hit_rate < 0)
- sd->hit_rate = 0;
- if(sd->hit_rate != 100)
- status->hit = status->hit * sd->hit_rate/100;
-
- if(sd->flee_rate < 0)
- sd->flee_rate = 0;
- if(sd->flee_rate != 100)
- status->flee = status->flee * sd->flee_rate/100;
-
- if(sd->def2_rate < 0)
- sd->def2_rate = 0;
- if(sd->def2_rate != 100)
- status->def2 = status->def2 * sd->def2_rate/100;
-
- if(sd->mdef2_rate < 0)
- sd->mdef2_rate = 0;
- if(sd->mdef2_rate != 100)
- status->mdef2 = status->mdef2 * sd->mdef2_rate/100;
-
- if(sd->critical_rate < 0)
- sd->critical_rate = 0;
- if(sd->critical_rate != 100)
- status->cri = status->cri * sd->critical_rate/100;
-
- if(sd->flee2_rate < 0)
- sd->flee2_rate = 0;
- if(sd->flee2_rate != 100)
- status->flee2 = status->flee2 * sd->flee2_rate/100;
-
-// ----- HIT CALCULATION -----
-
- // Absolute modifiers from passive skills
- if((skill=pc_checkskill(sd,BS_WEAPONRESEARCH))>0)
- status->hit += skill*2;
- if((skill=pc_checkskill(sd,AC_VULTURE))>0){
-#ifndef RENEWAL
- status->hit += skill;
-#endif
- if(sd->status.weapon == W_BOW)
- status->rhw.range += skill;
- }
- if(sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)
- {
- if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0)
- status->hit += 2*skill;
- if((skill=pc_checkskill(sd,GS_SNAKEEYE))>0) {
- status->hit += skill;
- status->rhw.range += skill;
- }
- }
-
-// ----- FLEE CALCULATION -----
-
- // Absolute modifiers from passive skills
- if((skill=pc_checkskill(sd,TF_MISS))>0)
- status->flee += skill*(sd->class_&JOBL_2 && (sd->class_&MAPID_BASEMASK) == MAPID_THIEF? 4 : 3);
- if((skill=pc_checkskill(sd,MO_DODGE))>0)
- status->flee += (skill*3)>>1;
-// ----- EQUIPMENT-DEF CALCULATION -----
-
- // Apply relative modifiers from equipment
- if(sd->def_rate < 0)
- sd->def_rate = 0;
- if(sd->def_rate != 100) {
- i = status->def * sd->def_rate/100;
- status->def = cap_value(i, DEFTYPE_MIN, DEFTYPE_MAX);
- }
-
-#ifndef RENEWAL
- if (!battle_config.weapon_defense_type && status->def > battle_config.max_def)
- {
- status->def2 += battle_config.over_def_bonus*(status->def -battle_config.max_def);
- status->def = (unsigned char)battle_config.max_def;
- }
-#endif
-
-// ----- EQUIPMENT-MDEF CALCULATION -----
-
- // Apply relative modifiers from equipment
- if(sd->mdef_rate < 0)
- sd->mdef_rate = 0;
- if(sd->mdef_rate != 100) {
- i = status->mdef * sd->mdef_rate/100;
- status->mdef = cap_value(i, DEFTYPE_MIN, DEFTYPE_MAX);
- }
-
-#ifndef RENEWAL
- if (!battle_config.magic_defense_type && status->mdef > battle_config.max_def)
- {
- status->mdef2 += battle_config.over_def_bonus*(status->mdef -battle_config.max_def);
- status->mdef = (signed char)battle_config.max_def;
- }
-#endif
-
-// ----- ASPD CALCULATION -----
-// Unlike other stats, ASPD rate modifiers from skills/SCs/items/etc are first all added together, then the final modifier is applied
-
- // Basic ASPD value
- i = status_base_amotion_pc(sd,status);
- status->amotion = cap_value(i,((sd->class_&JOBL_THIRD) ? battle_config.max_third_aspd : battle_config.max_aspd),2000);
-
- // Relative modifiers from passive skills
-#ifndef RENEWAL_ASPD
- if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0 && sd->status.weapon == W_BOOK)
- status->aspd_rate -= 5*skill;
- if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && !pc_nextjobexp(sd))
- status->aspd_rate -= 30*skill;
- if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0 &&
- (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE))
- status->aspd_rate -= ((skill+1)/2) * 10;
- if(pc_isriding(sd))
- status->aspd_rate += 500-100*pc_checkskill(sd,KN_CAVALIERMASTERY);
- else if(pc_isridingdragon(sd))
- status->aspd_rate += 250-50*pc_checkskill(sd,RK_DRAGONTRAINING);
-#else // needs more info
- if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0 && sd->status.weapon == W_BOOK)
- status->aspd_rate += 5*skill;
- if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && !pc_nextjobexp(sd))
- status->aspd_rate += 30*skill;
- if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0 &&
- (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE))
- status->aspd_rate += ((skill+1)/2) * 10;
- if(pc_isriding(sd))
- status->aspd_rate -= 500-100*pc_checkskill(sd,KN_CAVALIERMASTERY);
- else if(pc_isridingdragon(sd))
- status->aspd_rate -= 250-50*pc_checkskill(sd,RK_DRAGONTRAINING);
-#endif
- status->adelay = 2*status->amotion;
-
-
-// ----- DMOTION -----
-//
- i = 800-status->agi*4;
- status->dmotion = cap_value(i, 400, 800);
- if(battle_config.pc_damage_delay_rate != 100)
- status->dmotion = status->dmotion*battle_config.pc_damage_delay_rate/100;
-
-// ----- MISC CALCULATIONS -----
-
- // Weight
- if((skill=pc_checkskill(sd,MC_INCCARRY))>0)
- sd->max_weight += 2000*skill;
- if(pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0)
- sd->max_weight += 10000;
- else if(pc_isridingdragon(sd))
- sd->max_weight += 5000+2000*pc_checkskill(sd,RK_DRAGONTRAINING);
- if(sc->data[SC_KNOWLEDGE])
- sd->max_weight += sd->max_weight*sc->data[SC_KNOWLEDGE]->val1/10;
- if((skill=pc_checkskill(sd,ALL_INCCARRY))>0)
- sd->max_weight += 2000*skill;
-
- sd->cart_weight_max = battle_config.max_cart_weight + (pc_checkskill(sd, GN_REMODELING_CART)*5000);
-
- if (pc_checkskill(sd,SM_MOVINGRECOVERY)>0)
- sd->regen.state.walk = 1;
- else
- sd->regen.state.walk = 0;
-
- // Skill SP cost
- if((skill=pc_checkskill(sd,HP_MANARECHARGE))>0 )
- sd->dsprate -= 4*skill;
-
- if(sc->data[SC_SERVICE4U])
- sd->dsprate -= sc->data[SC_SERVICE4U]->val3;
-
- if(sc->data[SC_SPCOST_RATE])
- sd->dsprate -= sc->data[SC_SPCOST_RATE]->val1;
-
- //Underflow protections.
- if(sd->dsprate < 0)
- sd->dsprate = 0;
- if(sd->castrate < 0)
- sd->castrate = 0;
- if(sd->delayrate < 0)
- sd->delayrate = 0;
- if(sd->hprecov_rate < 0)
- sd->hprecov_rate = 0;
- if(sd->sprecov_rate < 0)
- sd->sprecov_rate = 0;
-
- // Anti-element and anti-race
- if((skill=pc_checkskill(sd,CR_TRUST))>0)
- sd->subele[ELE_HOLY] += skill*5;
- if((skill=pc_checkskill(sd,BS_SKINTEMPER))>0) {
- sd->subele[ELE_NEUTRAL] += skill;
- sd->subele[ELE_FIRE] += skill*4;
- }
- if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0 ){
- skill = skill*4;
- sd->right_weapon.addrace[RC_DRAGON]+=skill;
- sd->left_weapon.addrace[RC_DRAGON]+=skill;
- sd->magic_addrace[RC_DRAGON]+=skill;
- sd->subrace[RC_DRAGON]+=skill;
- }
-
- if(sc->count){
- if(sc->data[SC_CONCENTRATE]) { //Update the card-bonus data
- sc->data[SC_CONCENTRATE]->val3 = sd->param_bonus[1]; //Agi
- sc->data[SC_CONCENTRATE]->val4 = sd->param_bonus[4]; //Dex
- }
- if(sc->data[SC_SIEGFRIED]){
- i = sc->data[SC_SIEGFRIED]->val2;
- sd->subele[ELE_WATER] += i;
- sd->subele[ELE_EARTH] += i;
- sd->subele[ELE_FIRE] += i;
- sd->subele[ELE_WIND] += i;
- sd->subele[ELE_POISON] += i;
- sd->subele[ELE_HOLY] += i;
- sd->subele[ELE_DARK] += i;
- sd->subele[ELE_GHOST] += i;
- sd->subele[ELE_UNDEAD] += i;
- }
- if(sc->data[SC_PROVIDENCE]){
- sd->subele[ELE_HOLY] += sc->data[SC_PROVIDENCE]->val2;
- sd->subrace[RC_DEMON] += sc->data[SC_PROVIDENCE]->val2;
- }
- if(sc->data[SC_ARMOR_ELEMENT]) { //This status change should grant card-type elemental resist.
- sd->subele[ELE_WATER] += sc->data[SC_ARMOR_ELEMENT]->val1;
- sd->subele[ELE_EARTH] += sc->data[SC_ARMOR_ELEMENT]->val2;
- sd->subele[ELE_FIRE] += sc->data[SC_ARMOR_ELEMENT]->val3;
- sd->subele[ELE_WIND] += sc->data[SC_ARMOR_ELEMENT]->val4;
- }
- if(sc->data[SC_ARMOR_RESIST]) { // Undead Scroll
- sd->subele[ELE_WATER] += sc->data[SC_ARMOR_RESIST]->val1;
- sd->subele[ELE_EARTH] += sc->data[SC_ARMOR_RESIST]->val2;
- sd->subele[ELE_FIRE] += sc->data[SC_ARMOR_RESIST]->val3;
- sd->subele[ELE_WIND] += sc->data[SC_ARMOR_RESIST]->val4;
- }
- if( sc->data[SC_FIRE_CLOAK_OPTION] ) {
- i = sc->data[SC_FIRE_CLOAK_OPTION]->val2;
- sd->subele[ELE_FIRE] += i;
- sd->subele[ELE_WATER] -= i;
- }
- if( sc->data[SC_WATER_DROP_OPTION] ) {
- i = sc->data[SC_WATER_DROP_OPTION]->val2;
- sd->subele[ELE_WATER] += i;
- sd->subele[ELE_WIND] -= i;
- }
- if( sc->data[SC_WIND_CURTAIN_OPTION] ) {
- i = sc->data[SC_WIND_CURTAIN_OPTION]->val2;
- sd->subele[ELE_WIND] += i;
- sd->subele[ELE_EARTH] -= i;
- }
- if( sc->data[SC_STONE_SHIELD_OPTION] ) {
- i = sc->data[SC_STONE_SHIELD_OPTION]->val2;
- sd->subele[ELE_EARTH] += i;
- sd->subele[ELE_FIRE] -= i;
- }
- if( sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3 )
- sd->magic_addele[ELE_FIRE] += 25;
- if( sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 3 )
- sd->magic_addele[ELE_WATER] += 25;
- if( sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 3 )
- sd->magic_addele[ELE_WIND] += 25;
- if( sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3 )
- sd->magic_addele[ELE_EARTH] += 25;
- }
- status_cpy(&sd->battle_status, status);
-
-// ----- CLIENT-SIDE REFRESH -----
- if(!sd->bl.prev) {
- //Will update on LoadEndAck
- calculating = 0;
- return 0;
- }
- if(memcmp(b_skill,sd->status.skill,sizeof(sd->status.skill)))
- clif_skillinfoblock(sd);
- if(b_weight != sd->weight)
- clif_updatestatus(sd,SP_WEIGHT);
- if(b_max_weight != sd->max_weight) {
- clif_updatestatus(sd,SP_MAXWEIGHT);
- pc_updateweightstatus(sd);
- }
- if( b_cart_weight_max != sd->cart_weight_max ) {
- clif_updatestatus(sd,SP_CARTINFO);
- }
-
- calculating = 0;
-
- return 0;
-}
-
-int status_calc_mercenary_(struct mercenary_data *md, bool first)
-{
- struct status_data *status = &md->base_status;
- struct s_mercenary *merc = &md->mercenary;
-
- if( first )
- {
- memcpy(status, &md->db->status, sizeof(struct status_data));
- status->mode = MD_CANMOVE|MD_CANATTACK;
- status->hp = status->max_hp;
- status->sp = status->max_sp;
- md->battle_status.hp = merc->hp;
- md->battle_status.sp = merc->sp;
- }
-
- status_calc_misc(&md->bl, status, md->db->lv);
- status_cpy(&md->battle_status, status);
-
- return 0;
-}
-
-int status_calc_homunculus_(struct homun_data *hd, bool first)
-{
- struct status_data *status = &hd->base_status;
- struct s_homunculus *hom = &hd->homunculus;
- int skill;
- int amotion;
-
- status->str = hom->str / 10;
- status->agi = hom->agi / 10;
- status->vit = hom->vit / 10;
- status->dex = hom->dex / 10;
- status->int_ = hom->int_ / 10;
- status->luk = hom->luk / 10;
-
- if (first) { //[orn]
- const struct s_homunculus_db *db = hd->homunculusDB;
- status->def_ele = db->element;
- status->ele_lv = 1;
- status->race = db->race;
- status->size = (hom->class_ == db->evo_class)?db->evo_size:db->base_size;
- status->rhw.range = 1 + status->size;
- status->mode = MD_CANMOVE|MD_CANATTACK;
- status->speed = DEFAULT_WALK_SPEED;
- if (battle_config.hom_setting&0x8 && hd->master)
- status->speed = status_get_speed(&hd->master->bl);
-
- status->hp = 1;
- status->sp = 1;
- }
- skill = hom->level/10 + status->vit/5;
- status->def = cap_value(skill, 0, 99);
-
- skill = hom->level/10 + status->int_/5;
- status->mdef = cap_value(skill, 0, 99);
-
- status->max_hp = hom->max_hp ;
- status->max_sp = hom->max_sp ;
-
- merc_hom_calc_skilltree(hd, 0);
-
- if((skill=merc_hom_checkskill(hd,HAMI_SKIN)) > 0)
- status->def += skill * 4;
-
- if((skill = merc_hom_checkskill(hd,HVAN_INSTRUCT)) > 0)
- {
- status->int_ += 1 +skill/2 +skill/4 +skill/5;
- status->str += 1 +skill/3 +skill/3 +skill/4;
- }
-
- if((skill=merc_hom_checkskill(hd,HAMI_SKIN)) > 0)
- status->max_hp += skill * 2 * status->max_hp / 100;
-
- if((skill = merc_hom_checkskill(hd,HLIF_BRAIN)) > 0)
- status->max_sp += (1 +skill/2 -skill/4 +skill/5) * status->max_sp / 100 ;
-
- if (first) {
- hd->battle_status.hp = hom->hp ;
- hd->battle_status.sp = hom->sp ;
- }
-
- status->rhw.atk = status->dex;
- status->rhw.atk2 = status->str + hom->level;
-
- status->aspd_rate = 1000;
-
- amotion = (1000 -4*status->agi -status->dex) * hd->homunculusDB->baseASPD/1000;
- status->amotion = cap_value(amotion,battle_config.max_aspd,2000);
- status->adelay = status->amotion; //It seems adelay = amotion for Homunculus.
-
- status_calc_misc(&hd->bl, status, hom->level);
-
-#ifdef RENEWAL
- status->matk_max = status->matk_min;
-#endif
-
- status_cpy(&hd->battle_status, status);
- return 1;
-}
-
-int status_calc_elemental_(struct elemental_data *ed, bool first) {
- struct status_data *status = &ed->base_status;
- struct s_elemental *ele = &ed->elemental;
- struct map_session_data *sd = ed->master;
-
- if( !sd )
- return 0;
-
- if( first ) {
- memcpy(status, &ed->db->status, sizeof(struct status_data));
- if( !ele->mode )
- status->mode = EL_MODE_PASSIVE;
- else
- status->mode = ele->mode;
-
- status_calc_misc(&ed->bl, status, 0);
-
- status->max_hp = ele->max_hp;
- status->max_sp = ele->max_sp;
- status->hp = ele->hp;
- status->sp = ele->sp;
- status->rhw.atk = ele->atk;
- status->rhw.atk2 = ele->atk2;
-
- status->matk_min += ele->matk;
- status->def += ele->def;
- status->mdef += ele->mdef;
- status->flee = ele->flee;
- status->hit = ele->hit;
-
- memcpy(&ed->battle_status,status,sizeof(struct status_data));
- } else {
- status_calc_misc(&ed->bl, status, 0);
- status_cpy(&ed->battle_status, status);
- }
-
- return 0;
-}
-
-int status_calc_npc_(struct npc_data *nd, bool first) {
- struct status_data *status = &nd->status;
-
- if (!nd)
- return 0;
-
- if (first) {
- status->hp = 1;
- status->sp = 1;
- status->max_hp = 1;
- status->max_sp = 1;
-
- status->def_ele = ELE_NEUTRAL;
- status->ele_lv = 1;
- status->race = RC_DEMIHUMAN;
- status->size = nd->size;
- status->rhw.range = 1 + status->size;
- status->mode = MD_CANMOVE|MD_CANATTACK;
- status->speed = nd->speed;
- }
-
- status->str = nd->stat_point;
- status->agi = nd->stat_point;
- status->vit = nd->stat_point;
- status->int_= nd->stat_point;
- status->dex = nd->stat_point;
- status->luk = nd->stat_point;
-
- status_calc_misc(&nd->bl, status, nd->level);
- status_cpy(&nd->status, status);
-
- return 0;
-}
-
-static unsigned short status_calc_str(struct block_list *,struct status_change *,int);
-static unsigned short status_calc_agi(struct block_list *,struct status_change *,int);
-static unsigned short status_calc_vit(struct block_list *,struct status_change *,int);
-static unsigned short status_calc_int(struct block_list *,struct status_change *,int);
-static unsigned short status_calc_dex(struct block_list *,struct status_change *,int);
-static unsigned short status_calc_luk(struct block_list *,struct status_change *,int);
-static unsigned short status_calc_batk(struct block_list *,struct status_change *,int);
-static unsigned short status_calc_watk(struct block_list *,struct status_change *,int);
-static unsigned short status_calc_matk(struct block_list *,struct status_change *,int);
-static signed short status_calc_hit(struct block_list *,struct status_change *,int);
-static signed short status_calc_critical(struct block_list *,struct status_change *,int);
-static signed short status_calc_flee(struct block_list *,struct status_change *,int);
-static signed short status_calc_flee2(struct block_list *,struct status_change *,int);
-static defType status_calc_def(struct block_list *bl, struct status_change *sc, int);
-static signed short status_calc_def2(struct block_list *,struct status_change *,int);
-static defType status_calc_mdef(struct block_list *bl, struct status_change *sc, int);
-static signed short status_calc_mdef2(struct block_list *,struct status_change *,int);
-static unsigned short status_calc_speed(struct block_list *,struct status_change *,int);
-static short status_calc_aspd_rate(struct block_list *,struct status_change *,int);
-static unsigned short status_calc_dmotion(struct block_list *bl, struct status_change *sc, int dmotion);
-#ifdef RENEWAL_ASPD
-static short status_calc_aspd(struct block_list *bl, struct status_change *sc, short flag);
-#endif
-static short status_calc_fix_aspd(struct block_list *bl, struct status_change *sc, int);
-static unsigned int status_calc_maxhp(struct block_list *,struct status_change *, uint64);
-static unsigned int status_calc_maxsp(struct block_list *,struct status_change *,unsigned int);
-static unsigned char status_calc_element(struct block_list *bl, struct status_change *sc, int element);
-static unsigned char status_calc_element_lv(struct block_list *bl, struct status_change *sc, int lv);
-static unsigned short status_calc_mode(struct block_list *bl, struct status_change *sc, int mode);
-#ifdef RENEWAL
-static unsigned short status_calc_ematk(struct block_list *,struct status_change *,int);
-#endif
-
-//Calculates base regen values.
-void status_calc_regen(struct block_list *bl, struct status_data *status, struct regen_data *regen)
-{
- struct map_session_data *sd;
- int val, skill, reg_flag;
-
- if( !(bl->type&BL_REGEN) || !regen )
- return;
-
- sd = BL_CAST(BL_PC,bl);
- val = 1 + (status->vit/5) + (status->max_hp/200);
-
- if( sd && sd->hprecov_rate != 100 )
- val = val*sd->hprecov_rate/100;
-
- reg_flag = bl->type == BL_PC ? 0 : 1;
-
- regen->hp = cap_value(val, reg_flag, SHRT_MAX);
-
- val = 1 + (status->int_/6) + (status->max_sp/100);
- if( status->int_ >= 120 )
- val += ((status->int_-120)>>1) + 4;
-
- if( sd && sd->sprecov_rate != 100 )
- val = val*sd->sprecov_rate/100;
-
- regen->sp = cap_value(val, reg_flag, SHRT_MAX);
-
- if( sd )
- {
- struct regen_data_sub *sregen;
- if( (skill=pc_checkskill(sd,HP_MEDITATIO)) > 0 )
- {
- val = regen->sp*(100+3*skill)/100;
- regen->sp = cap_value(val, 1, SHRT_MAX);
- }
- //Only players have skill/sitting skill regen for now.
- sregen = regen->sregen;
-
- val = 0;
- if( (skill=pc_checkskill(sd,SM_RECOVERY)) > 0 )
- val += skill*5 + skill*status->max_hp/500;
- sregen->hp = cap_value(val, 0, SHRT_MAX);
-
- val = 0;
- if( (skill=pc_checkskill(sd,MG_SRECOVERY)) > 0 )
- val += skill*3 + skill*status->max_sp/500;
- if( (skill=pc_checkskill(sd,NJ_NINPOU)) > 0 )
- val += skill*3 + skill*status->max_sp/500;
- if( (skill=pc_checkskill(sd,WM_LESSON)) > 0 )
- val += 3 + 3 * skill;
-
- sregen->sp = cap_value(val, 0, SHRT_MAX);
-
- // Skill-related recovery (only when sit)
- sregen = regen->ssregen;
-
- val = 0;
- if( (skill=pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0 )
- val += skill*4 + skill*status->max_hp/500;
-
- if( (skill=pc_checkskill(sd,TK_HPTIME)) > 0 && sd->state.rest )
- val += skill*30 + skill*status->max_hp/500;
- sregen->hp = cap_value(val, 0, SHRT_MAX);
-
- val = 0;
- if( (skill=pc_checkskill(sd,TK_SPTIME)) > 0 && sd->state.rest )
- {
- val += skill*3 + skill*status->max_sp/500;
- if ((skill=pc_checkskill(sd,SL_KAINA)) > 0) //Power up Enjoyable Rest
- val += (30+10*skill)*val/100;
- }
- if( (skill=pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0 )
- val += skill*2 + skill*status->max_sp/500;
- sregen->sp = cap_value(val, 0, SHRT_MAX);
- }
-
- if( bl->type == BL_HOM ) {
- struct homun_data *hd = (TBL_HOM*)bl;
- if( (skill = merc_hom_checkskill(hd,HAMI_SKIN)) > 0 ) {
- val = regen->hp*(100+5*skill)/100;
- regen->hp = cap_value(val, 1, SHRT_MAX);
- }
- if( (skill = merc_hom_checkskill(hd,HLIF_BRAIN)) > 0 ) {
- val = regen->sp*(100+3*skill)/100;
- regen->sp = cap_value(val, 1, SHRT_MAX);
- }
- } else if( bl->type == BL_MER ) {
- val = (status->max_hp * status->vit / 10000 + 1) * 6;
- regen->hp = cap_value(val, 1, SHRT_MAX);
-
- val = (status->max_sp * (status->int_ + 10) / 750) + 1;
- regen->sp = cap_value(val, 1, SHRT_MAX);
- } else if( bl->type == BL_ELEM ) {
- val = (status->max_hp * status->vit / 10000 + 1) * 6;
- regen->hp = cap_value(val, 1, SHRT_MAX);
-
- val = (status->max_sp * (status->int_ + 10) / 750) + 1;
- regen->sp = cap_value(val, 1, SHRT_MAX);
- }
-}
-
-//Calculates SC related regen rates.
-void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, struct status_change *sc)
-{
- if (!(bl->type&BL_REGEN) || !regen)
- return;
-
- regen->flag = RGN_HP|RGN_SP;
- if(regen->sregen)
- {
- if (regen->sregen->hp)
- regen->flag|=RGN_SHP;
-
- if (regen->sregen->sp)
- regen->flag|=RGN_SSP;
- regen->sregen->rate.hp = regen->sregen->rate.sp = 1;
- }
- if (regen->ssregen)
- {
- if (regen->ssregen->hp)
- regen->flag|=RGN_SHP;
-
- if (regen->ssregen->sp)
- regen->flag|=RGN_SSP;
- regen->ssregen->rate.hp = regen->ssregen->rate.sp = 1;
- }
- regen->rate.hp = regen->rate.sp = 1;
-
- if (!sc || !sc->count)
- return;
-
- if (
- (sc->data[SC_POISON] && !sc->data[SC_SLOWPOISON])
- || (sc->data[SC_DPOISON] && !sc->data[SC_SLOWPOISON])
- || sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]
- || sc->data[SC_TRICKDEAD]
- || sc->data[SC_BLEEDING]
- || sc->data[SC_MAGICMUSHROOM]
- || sc->data[SC_RAISINGDRAGON]
- || sc->data[SC_SATURDAYNIGHTFEVER]
- ) //No regen
- regen->flag = 0;
-
- if (
- sc->data[SC_DANCING] || sc->data[SC_OBLIVIONCURSE] || sc->data[SC_MAXIMIZEPOWER]
- || (
- (bl->type == BL_PC && ((TBL_PC*)bl)->class_&MAPID_UPPERMASK) == MAPID_MONK &&
- (sc->data[SC_EXTREMITYFIST] || (sc->data[SC_EXPLOSIONSPIRITS] && (!sc->data[SC_SPIRIT] || sc->data[SC_SPIRIT]->val2 != SL_MONK)))
- )
- ) //No natural SP regen
- regen->flag &=~RGN_SP;
-
- if(
- sc->data[SC_TENSIONRELAX]
- ) {
- regen->rate.hp += 2;
- if (regen->sregen)
- regen->sregen->rate.hp += 3;
- }
- if (sc->data[SC_MAGNIFICAT])
- {
- regen->rate.hp += 1;
- regen->rate.sp += 1;
- }
- if (sc->data[SC_REGENERATION])
- {
- const struct status_change_entry *sce = sc->data[SC_REGENERATION];
- if (!sce->val4)
- {
- regen->rate.hp += sce->val2;
- regen->rate.sp += sce->val3;
- } else
- regen->flag&=~sce->val4; //Remove regen as specified by val4
- }
- if(sc->data[SC_GT_REVITALIZE]){
- regen->hp = cap_value(regen->hp*sc->data[SC_GT_REVITALIZE]->val3/100, 1, SHRT_MAX);
- regen->state.walk= 1;
- }
- if ((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 1) //if insignia lvl 1
- || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 1)
- || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 1)
- || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 1))
- regen->rate.hp *= 2;
-
-}
-void status_calc_state( struct block_list *bl, struct status_change *sc, enum scs_flag flag, bool start ) {
-
- /* no sc at all, we can zero without any extra weight over our conciousness */
- if( !sc->count ) {
- memset(&sc->cant, 0, sizeof (sc->cant));
- return;
- }
-
- /* can move? */
- if( flag&SCS_NOMOVE ) {
- if( !(flag&SCS_NOMOVECOND) ) {
- sc->cant.move += ( start ? 1 : -1 );
- } else if(
- (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF) // cannot move while gospel is in effect
- || (sc->data[SC_BASILICA] && sc->data[SC_BASILICA]->val4 == bl->id) // Basilica caster cannot move
- || (sc->data[SC_GRAVITATION] && sc->data[SC_GRAVITATION]->val3 == BCT_SELF)
- || (sc->data[SC_CRYSTALIZE] && bl->type != BL_MOB)
- || (sc->data[SC_CAMOUFLAGE] && sc->data[SC_CAMOUFLAGE]->val1 < 3
- && !(sc->data[SC_CAMOUFLAGE]->val3&1))
- ) {
- sc->cant.move += ( start ? 1 : -1 );
- }
- }
-
- /* can't use skills */
- if( flag&SCS_NOCAST ) {
- if( !(flag&SCS_NOCASTCOND) ) {
- sc->cant.cast += ( start ? 1 : -1 );
- } else if( (sc->data[SC_CRYSTALIZE] && bl->type != BL_MOB) ){
- sc->cant.cast += ( start ? 1 : -1 );
- }
- }
-
- /* player-only states */
- if( bl->type == BL_PC ) {
-
- /* can pick items? */
- if( flag&SCS_NOPICKITEM ) {
- if( !(flag&SCS_NOPICKITEMCOND) ) {
- sc->cant.pickup += ( start ? 1 : -1 );
- } else if( (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOITEM) ) {
- sc->cant.pickup += ( start ? 1 : -1 );
- }
- }
-
- /* can drop items? */
- if( flag&SCS_NODROPITEM ) {
- if( !(flag&SCS_NODROPITEMCOND) ) {
- sc->cant.drop += ( start ? 1 : -1 );
- } else if( (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOITEM) ) {
- sc->cant.drop += ( start ? 1 : -1 );
- }
- }
- }
-
- return;
-}
-/// Recalculates parts of an object's battle status according to the specified flags.
-/// @param flag bitfield of values from enum scb_flag
-void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
-{
- const struct status_data *b_status = status_get_base_status(bl);
- struct status_data *status = status_get_status_data(bl);
- struct status_change *sc = status_get_sc(bl);
- TBL_PC *sd = BL_CAST(BL_PC,bl);
- int temp;
-
- if (!b_status || !status)
- return;
-
- if((!(bl->type&BL_REGEN)) && (!sc || !sc->count)) { //No difference.
- status_cpy(status, b_status);
- return;
- }
-
- if(flag&SCB_STR) {
- status->str = status_calc_str(bl, sc, b_status->str);
- flag|=SCB_BATK;
- if( bl->type&BL_HOM )
- flag |= SCB_WATK;
- }
-
- if(flag&SCB_AGI) {
- status->agi = status_calc_agi(bl, sc, b_status->agi);
- flag|=SCB_FLEE
-#ifdef RENEWAL
- |SCB_DEF2
-#endif
- ;
- if( bl->type&(BL_PC|BL_HOM) )
- flag |= SCB_ASPD|SCB_DSPD;
- }
-
- if(flag&SCB_VIT) {
- status->vit = status_calc_vit(bl, sc, b_status->vit);
- flag|=SCB_DEF2|SCB_MDEF2;
- if( bl->type&(BL_PC|BL_HOM|BL_MER|BL_ELEM) )
- flag |= SCB_MAXHP;
- if( bl->type&BL_HOM )
- flag |= SCB_DEF;
- }
-
- if(flag&SCB_INT) {
- status->int_ = status_calc_int(bl, sc, b_status->int_);
- flag|=SCB_MATK|SCB_MDEF2;
- if( bl->type&(BL_PC|BL_HOM|BL_MER|BL_ELEM) )
- flag |= SCB_MAXSP;
- if( bl->type&BL_HOM )
- flag |= SCB_MDEF;
- }
-
- if(flag&SCB_DEX) {
- status->dex = status_calc_dex(bl, sc, b_status->dex);
- flag|=SCB_BATK|SCB_HIT
-#ifdef RENEWAL
- |SCB_MATK|SCB_MDEF2
-#endif
- ;
- if( bl->type&(BL_PC|BL_HOM) )
- flag |= SCB_ASPD;
- if( bl->type&BL_HOM )
- flag |= SCB_WATK;
- }
-
- if(flag&SCB_LUK) {
- status->luk = status_calc_luk(bl, sc, b_status->luk);
- flag|=SCB_BATK|SCB_CRI|SCB_FLEE2
-#ifdef RENEWAL
- |SCB_MATK|SCB_HIT|SCB_FLEE
-#endif
- ;
- }
-
- if(flag&SCB_BATK && b_status->batk) {
- status->batk = status_base_atk(bl,status);
- temp = b_status->batk - status_base_atk(bl,b_status);
- if (temp)
- {
- temp += status->batk;
- status->batk = cap_value(temp, 0, USHRT_MAX);
- }
- status->batk = status_calc_batk(bl, sc, status->batk);
- }
-
- if(flag&SCB_WATK) {
-
- status->rhw.atk = status_calc_watk(bl, sc, b_status->rhw.atk);
- if (!sd) //Should not affect weapon refine bonus
- status->rhw.atk2 = status_calc_watk(bl, sc, b_status->rhw.atk2);
-
- if(b_status->lhw.atk) {
- if (sd) {
- sd->state.lr_flag = 1;
- status->lhw.atk = status_calc_watk(bl, sc, b_status->lhw.atk);
- sd->state.lr_flag = 0;
- } else {
- status->lhw.atk = status_calc_watk(bl, sc, b_status->lhw.atk);
- status->lhw.atk2= status_calc_watk(bl, sc, b_status->lhw.atk2);
- }
- }
-
- if( bl->type&BL_HOM )
- {
- status->rhw.atk += (status->dex - b_status->dex);
- status->rhw.atk2 += (status->str - b_status->str);
- if( status->rhw.atk2 < status->rhw.atk )
- status->rhw.atk2 = status->rhw.atk;
- }
- }
-
- if(flag&SCB_HIT) {
- if (status->dex == b_status->dex
-#ifdef RENEWAL
- && status->luk == b_status->luk
-#endif
- )
- status->hit = status_calc_hit(bl, sc, b_status->hit);
- else
- status->hit = status_calc_hit(bl, sc, b_status->hit + (status->dex - b_status->dex)
-#ifdef RENEWAL
- + (status->luk/3 - b_status->luk/3)
-#endif
- );
- }
-
- if(flag&SCB_FLEE) {
- if (status->agi == b_status->agi
-#ifdef RENEWAL
- && status->luk == b_status->luk
-#endif
- )
- status->flee = status_calc_flee(bl, sc, b_status->flee);
- else
- status->flee = status_calc_flee(bl, sc, b_status->flee +(status->agi - b_status->agi)
-#ifdef RENEWAL
- + (status->luk/5 - b_status->luk/5)
-#endif
- );
- }
-
- if(flag&SCB_DEF)
- {
- status->def = status_calc_def(bl, sc, b_status->def);
-
- if( bl->type&BL_HOM )
- status->def += (status->vit/5 - b_status->vit/5);
- }
-
- if(flag&SCB_DEF2) {
- if (status->vit == b_status->vit
-#ifdef RENEWAL
- && status->agi == b_status->agi
-#endif
- )
- status->def2 = status_calc_def2(bl, sc, b_status->def2);
- else
- status->def2 = status_calc_def2(bl, sc, b_status->def2
-#ifdef RENEWAL
- + (int)( ((float)status->vit/2 + (float)b_status->vit/2) + ((float)status->agi/5 + (float)b_status->agi/5) )
-#else
- + (status->vit - b_status->vit)
-#endif
- );
- }
-
- if(flag&SCB_MDEF)
- {
- status->mdef = status_calc_mdef(bl, sc, b_status->mdef);
-
- if( bl->type&BL_HOM )
- status->mdef += (status->int_/5 - b_status->int_/5);
- }
-
- if(flag&SCB_MDEF2) {
- if (status->int_ == b_status->int_ && status->vit == b_status->vit
-#ifdef RENEWAL
- && status->dex == b_status->dex
-#endif
- )
- status->mdef2 = status_calc_mdef2(bl, sc, b_status->mdef2);
- else
- status->mdef2 = status_calc_mdef2(bl, sc, b_status->mdef2 +(status->int_ - b_status->int_)
-#ifdef RENEWAL
- + (int)( ((float)status->dex/5 - (float)b_status->dex/5) + ((float)status->vit/5 + (float)b_status->vit/5) )
-#else
- + ((status->vit - b_status->vit)>>1)
-#endif
- );
- }
-
- if(flag&SCB_SPEED) {
- struct unit_data *ud = unit_bl2ud(bl);
- status->speed = status_calc_speed(bl, sc, b_status->speed);
-
- //Re-walk to adjust speed (we do not check if walktimer != INVALID_TIMER
- //because if you step on something while walking, the moment this
- //piece of code triggers the walk-timer is set on INVALID_TIMER) [Skotlex]
- if (ud)
- ud->state.change_walk_target = ud->state.speed_changed = 1;
-
- if( bl->type&BL_PC && status->speed < battle_config.max_walk_speed )
- status->speed = battle_config.max_walk_speed;
-
- if( bl->type&BL_HOM && battle_config.hom_setting&0x8 && ((TBL_HOM*)bl)->master)
- status->speed = status_get_speed(&((TBL_HOM*)bl)->master->bl);
-
-
- }
-
- if(flag&SCB_CRI && b_status->cri) {
- if (status->luk == b_status->luk)
- status->cri = status_calc_critical(bl, sc, b_status->cri);
- else
- status->cri = status_calc_critical(bl, sc, b_status->cri + 3*(status->luk - b_status->luk));
- /**
- * after status_calc_critical so the bonus is applied despite if you have or not a sc bugreport:5240
- **/
- if( bl->type == BL_PC && ((TBL_PC*)bl)->status.weapon == W_KATAR )
- status->cri <<= 1;
-
- }
-
- if(flag&SCB_FLEE2 && b_status->flee2) {
- if (status->luk == b_status->luk)
- status->flee2 = status_calc_flee2(bl, sc, b_status->flee2);
- else
- status->flee2 = status_calc_flee2(bl, sc, b_status->flee2 +(status->luk - b_status->luk));
- }
-
- if(flag&SCB_ATK_ELE) {
- status->rhw.ele = status_calc_attack_element(bl, sc, b_status->rhw.ele);
- if (sd) sd->state.lr_flag = 1;
- status->lhw.ele = status_calc_attack_element(bl, sc, b_status->lhw.ele);
- if (sd) sd->state.lr_flag = 0;
- }
-
- if(flag&SCB_DEF_ELE) {
- status->def_ele = status_calc_element(bl, sc, b_status->def_ele);
- status->ele_lv = status_calc_element_lv(bl, sc, b_status->ele_lv);
- }
-
- if(flag&SCB_MODE)
- {
- status->mode = status_calc_mode(bl, sc, b_status->mode);
- //Since mode changed, reset their state.
- if (!(status->mode&MD_CANATTACK))
- unit_stop_attack(bl);
- if (!(status->mode&MD_CANMOVE))
- unit_stop_walking(bl,1);
- }
-
-// No status changes alter these yet.
-// if(flag&SCB_SIZE)
-// if(flag&SCB_RACE)
-// if(flag&SCB_RANGE)
-
- if(flag&SCB_MAXHP) {
- if( bl->type&BL_PC )
- {
- status->max_hp = status_base_pc_maxhp(sd,status);
- status->max_hp += b_status->max_hp - sd->status.max_hp;
-
- status->max_hp = status_calc_maxhp(bl, sc, status->max_hp);
-
- if( status->max_hp > (unsigned int)battle_config.max_hp )
- status->max_hp = (unsigned int)battle_config.max_hp;
- }
- else
- {
- status->max_hp = status_calc_maxhp(bl, sc, b_status->max_hp);
- }
-
- if( status->hp > status->max_hp ) //FIXME: Should perhaps a status_zap should be issued?
- {
- status->hp = status->max_hp;
- if( sd ) clif_updatestatus(sd,SP_HP);
- }
- }
-
- if(flag&SCB_MAXSP) {
- if( bl->type&BL_PC )
- {
- status->max_sp = status_base_pc_maxsp(sd,status);
- status->max_sp += b_status->max_sp - sd->status.max_sp;
-
- status->max_sp = status_calc_maxsp(&sd->bl, &sd->sc, status->max_sp);
-
- if( status->max_sp > (unsigned int)battle_config.max_sp )
- status->max_sp = (unsigned int)battle_config.max_sp;
- }
- else
- {
- status->max_sp = status_calc_maxsp(bl, sc, b_status->max_sp);
- }
-
- if( status->sp > status->max_sp )
- {
- status->sp = status->max_sp;
- if( sd ) clif_updatestatus(sd,SP_SP);
- }
- }
-
- if(flag&SCB_MATK) {
-#ifndef RENEWAL
- status->matk_min = status_base_matk_min(status) + (sd?sd->bonus.ematk:0);
- status->matk_max = status_base_matk_max(status) + (sd?sd->bonus.ematk:0);
-#else
- /**
- * RE MATK Formula (from irowiki:http://irowiki.org/wiki/MATK)
- * MATK = (sMATK + wMATK + eMATK) * Multiplicative Modifiers
- **/
- status->matk_min = status->matk_max = status_base_matk(status, status_get_lv(bl));
- if( bl->type&BL_PC ){
- // Any +MATK you get from skills and cards, including cards in weapon, is added here.
- if( sd->bonus.ematk > 0 ){
- status->matk_max += sd->bonus.ematk;
- status->matk_min += sd->bonus.ematk;
- }
- status->matk_min = status_calc_ematk(bl, sc, status->matk_min);
- status->matk_max = status_calc_ematk(bl, sc, status->matk_max);
- //This is the only portion in MATK that varies depending on the weapon level and refinement rate.
- if( status->rhw.matk > 0 ){
- int wMatk = status->rhw.matk;
- int variance = wMatk * status->rhw.wlv / 10;
- status->matk_min += wMatk - variance;
- status->matk_max += wMatk + variance;
- }
- }
-#endif
- if (bl->type&BL_PC && sd->matk_rate != 100) {
- status->matk_max = status->matk_max * sd->matk_rate/100;
- status->matk_min = status->matk_min * sd->matk_rate/100;
- }
-
- status->matk_min = status_calc_matk(bl, sc, status->matk_min);
- status->matk_max = status_calc_matk(bl, sc, status->matk_max);
-
- if ((bl->type&BL_HOM && battle_config.hom_setting&0x20) //Hom Min Matk is always the same as Max Matk
- || sc->data[SC_RECOGNIZEDSPELL])
- status->matk_min = status->matk_max;
-
-#ifdef RENEWAL
- if( sd && sd->right_weapon.overrefine > 0){
- status->matk_min++;
- status->matk_max += sd->right_weapon.overrefine - 1;
- }
-#endif
-
- }
-
- if(flag&SCB_ASPD) {
- int amotion;
- if( bl->type&BL_PC )
- {
- amotion = status_base_amotion_pc(sd,status);
-#ifndef RENEWAL_ASPD
- status->aspd_rate = status_calc_aspd_rate(bl, sc, b_status->aspd_rate);
-
- if(status->aspd_rate != 1000)
- amotion = amotion*status->aspd_rate/1000;
-#else
- // aspd = baseaspd + floor(sqrt((agi^2/2) + (dex^2/5))/4 + (potskillbonus*agi/200))
- amotion -= (int)(sqrt( (pow(status->agi, 2) / 2) + (pow(status->dex, 2) / 5) ) / 4 + (status_calc_aspd(bl, sc, 1) * status->agi / 200)) * 10;
-
- if( (status_calc_aspd(bl, sc, 2) + status->aspd_rate2) != 0 ) // RE ASPD percertage modifier
- amotion -= ( amotion - ((sd->class_&JOBL_THIRD) ? battle_config.max_third_aspd : battle_config.max_aspd) )
- * (status_calc_aspd(bl, sc, 2) + status->aspd_rate2) / 100;
-
- if(status->aspd_rate != 1000) // absolute percentage modifier
- amotion = ( 200 - (200-amotion/10) * status->aspd_rate / 1000 ) * 10;
-#endif
- amotion = status_calc_fix_aspd(bl, sc, amotion);
- status->amotion = cap_value(amotion,((sd->class_&JOBL_THIRD) ? battle_config.max_third_aspd : battle_config.max_aspd),2000);
-
- status->adelay = 2*status->amotion;
- }
- else
- if( bl->type&BL_HOM )
- {
- amotion = (1000 -4*status->agi -status->dex) * ((TBL_HOM*)bl)->homunculusDB->baseASPD/1000;
- status->aspd_rate = status_calc_aspd_rate(bl, sc, b_status->aspd_rate);
-
- if(status->aspd_rate != 1000)
- amotion = amotion*status->aspd_rate/1000;
-
- amotion = status_calc_fix_aspd(bl, sc, amotion);
- status->amotion = cap_value(amotion,battle_config.max_aspd,2000);
-
- status->adelay = status->amotion;
- }
- else // mercenary and mobs
- {
- amotion = b_status->amotion;
- status->aspd_rate = status_calc_aspd_rate(bl, sc, b_status->aspd_rate);
-
- if(status->aspd_rate != 1000)
- amotion = amotion*status->aspd_rate/1000;
-
- amotion = status_calc_fix_aspd(bl, sc, amotion);
- status->amotion = cap_value(amotion, battle_config.monster_max_aspd, 2000);
-
- temp = b_status->adelay*status->aspd_rate/1000;
- status->adelay = cap_value(temp, battle_config.monster_max_aspd*2, 4000);
- }
- }
-
- if(flag&SCB_DSPD) {
- int dmotion;
- if( bl->type&BL_PC )
- {
- if (b_status->agi == status->agi)
- status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion);
- else {
- dmotion = 800-status->agi*4;
- status->dmotion = cap_value(dmotion, 400, 800);
- if(battle_config.pc_damage_delay_rate != 100)
- status->dmotion = status->dmotion*battle_config.pc_damage_delay_rate/100;
- //It's safe to ignore b_status->dmotion since no bonus affects it.
- status->dmotion = status_calc_dmotion(bl, sc, status->dmotion);
- }
- }
- else
- if( bl->type&BL_HOM )
- {
- dmotion = 800-status->agi*4;
- status->dmotion = cap_value(dmotion, 400, 800);
- status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion);
- }
- else // mercenary and mobs
- {
- status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion);
- }
- }
-
- if(flag&(SCB_VIT|SCB_MAXHP|SCB_INT|SCB_MAXSP) && bl->type&BL_REGEN)
- status_calc_regen(bl, status, status_get_regen_data(bl));
-
- if(flag&SCB_REGEN && bl->type&BL_REGEN)
- status_calc_regen_rate(bl, status_get_regen_data(bl), sc);
-}
-/// Recalculates parts of an object's base status and battle status according to the specified flags.
-/// Also sends updates to the client wherever applicable.
-/// @param flag bitfield of values from enum scb_flag
-/// @param first if true, will cause status_calc_* functions to run their base status initialization code
-void status_calc_bl_(struct block_list* bl, enum scb_flag flag, bool first)
-{
- struct status_data b_status; // previous battle status
- struct status_data* status; // pointer to current battle status
-
- // remember previous values
- status = status_get_status_data(bl);
- memcpy(&b_status, status, sizeof(struct status_data));
-
- if( flag&SCB_BASE ) {// calculate the object's base status too
- switch( bl->type ) {
- case BL_PC: status_calc_pc_(BL_CAST(BL_PC,bl), first); break;
- case BL_MOB: status_calc_mob_(BL_CAST(BL_MOB,bl), first); break;
- case BL_PET: status_calc_pet_(BL_CAST(BL_PET,bl), first); break;
- case BL_HOM: status_calc_homunculus_(BL_CAST(BL_HOM,bl), first); break;
- case BL_MER: status_calc_mercenary_(BL_CAST(BL_MER,bl), first); break;
- case BL_ELEM: status_calc_elemental_(BL_CAST(BL_ELEM,bl), first); break;
- case BL_NPC: status_calc_npc_(BL_CAST(BL_NPC,bl), first); break;
- }
- }
-
- if( bl->type == BL_PET )
- return; // pets are not affected by statuses
-
- if( first && bl->type == BL_MOB )
- return; // assume there will be no statuses active
-
- status_calc_bl_main(bl, flag);
-
- if( first && bl->type == BL_HOM )
- return; // client update handled by caller
-
- // compare against new values and send client updates
- if( bl->type == BL_PC )
- {
- TBL_PC* sd = BL_CAST(BL_PC, bl);
- if(b_status.str != status->str)
- clif_updatestatus(sd,SP_STR);
- if(b_status.agi != status->agi)
- clif_updatestatus(sd,SP_AGI);
- if(b_status.vit != status->vit)
- clif_updatestatus(sd,SP_VIT);
- if(b_status.int_ != status->int_)
- clif_updatestatus(sd,SP_INT);
- if(b_status.dex != status->dex)
- clif_updatestatus(sd,SP_DEX);
- if(b_status.luk != status->luk)
- clif_updatestatus(sd,SP_LUK);
- if(b_status.hit != status->hit)
- clif_updatestatus(sd,SP_HIT);
- if(b_status.flee != status->flee)
- clif_updatestatus(sd,SP_FLEE1);
- if(b_status.amotion != status->amotion)
- clif_updatestatus(sd,SP_ASPD);
- if(b_status.speed != status->speed)
- clif_updatestatus(sd,SP_SPEED);
-
- if(b_status.batk != status->batk
-#ifndef RENEWAL
- || b_status.rhw.atk != status->rhw.atk || b_status.lhw.atk != status->lhw.atk
-#endif
- )
- clif_updatestatus(sd,SP_ATK1);
-
- if(b_status.def != status->def){
- clif_updatestatus(sd,SP_DEF1);
-#ifdef RENEWAL
- clif_updatestatus(sd,SP_DEF2);
-#endif
- }
-
- if(b_status.rhw.atk2 != status->rhw.atk2 || b_status.lhw.atk2 != status->lhw.atk2
-#ifdef RENEWAL
- || b_status.rhw.atk != status->rhw.atk || b_status.lhw.atk != status->lhw.atk
-#endif
- )
- clif_updatestatus(sd,SP_ATK2);
-
- if(b_status.def2 != status->def2){
- clif_updatestatus(sd,SP_DEF2);
-#ifdef RENEWAL
- clif_updatestatus(sd,SP_DEF1);
-#endif
- }
- if(b_status.flee2 != status->flee2)
- clif_updatestatus(sd,SP_FLEE2);
- if(b_status.cri != status->cri)
- clif_updatestatus(sd,SP_CRITICAL);
-#ifndef RENEWAL
- if(b_status.matk_max != status->matk_max)
- clif_updatestatus(sd,SP_MATK1);
- if(b_status.matk_min != status->matk_min)
- clif_updatestatus(sd,SP_MATK2);
-#else
- if(b_status.matk_max != status->matk_max || b_status.matk_min != status->matk_min){
- clif_updatestatus(sd,SP_MATK2);
- clif_updatestatus(sd,SP_MATK1);
- }
-#endif
- if(b_status.mdef != status->mdef){
- clif_updatestatus(sd,SP_MDEF1);
-#ifdef RENEWAL
- clif_updatestatus(sd,SP_MDEF2);
-#endif
- }
- if(b_status.mdef2 != status->mdef2){
- clif_updatestatus(sd,SP_MDEF2);
-#ifdef RENEWAL
- clif_updatestatus(sd,SP_MDEF1);
-#endif
- }
- if(b_status.rhw.range != status->rhw.range)
- clif_updatestatus(sd,SP_ATTACKRANGE);
- if(b_status.max_hp != status->max_hp)
- clif_updatestatus(sd,SP_MAXHP);
- if(b_status.max_sp != status->max_sp)
- clif_updatestatus(sd,SP_MAXSP);
- if(b_status.hp != status->hp)
- clif_updatestatus(sd,SP_HP);
- if(b_status.sp != status->sp)
- clif_updatestatus(sd,SP_SP);
- } else if( bl->type == BL_HOM ) {
- TBL_HOM* hd = BL_CAST(BL_HOM, bl);
- if( hd->master && memcmp(&b_status, status, sizeof(struct status_data)) != 0 )
- clif_hominfo(hd->master,hd,0);
- } else if( bl->type == BL_MER ) {
- TBL_MER* md = BL_CAST(BL_MER, bl);
- if( b_status.rhw.atk != status->rhw.atk || b_status.rhw.atk2 != status->rhw.atk2 )
- clif_mercenary_updatestatus(md->master, SP_ATK1);
- if( b_status.matk_max != status->matk_max )
- clif_mercenary_updatestatus(md->master, SP_MATK1);
- if( b_status.hit != status->hit )
- clif_mercenary_updatestatus(md->master, SP_HIT);
- if( b_status.cri != status->cri )
- clif_mercenary_updatestatus(md->master, SP_CRITICAL);
- if( b_status.def != status->def )
- clif_mercenary_updatestatus(md->master, SP_DEF1);
- if( b_status.mdef != status->mdef )
- clif_mercenary_updatestatus(md->master, SP_MDEF1);
- if( b_status.flee != status->flee )
- clif_mercenary_updatestatus(md->master, SP_MERCFLEE);
- if( b_status.amotion != status->amotion )
- clif_mercenary_updatestatus(md->master, SP_ASPD);
- if( b_status.max_hp != status->max_hp )
- clif_mercenary_updatestatus(md->master, SP_MAXHP);
- if( b_status.max_sp != status->max_sp )
- clif_mercenary_updatestatus(md->master, SP_MAXSP);
- if( b_status.hp != status->hp )
- clif_mercenary_updatestatus(md->master, SP_HP);
- if( b_status.sp != status->sp )
- clif_mercenary_updatestatus(md->master, SP_SP);
- } else if( bl->type == BL_ELEM ) {
- TBL_ELEM* ed = BL_CAST(BL_ELEM, bl);
- if( b_status.max_hp != status->max_hp )
- clif_elemental_updatestatus(ed->master, SP_MAXHP);
- if( b_status.max_sp != status->max_sp )
- clif_elemental_updatestatus(ed->master, SP_MAXSP);
- if( b_status.hp != status->hp )
- clif_elemental_updatestatus(ed->master, SP_HP);
- if( b_status.sp != status->sp )
- clif_mercenary_updatestatus(ed->master, SP_SP);
- }
-}
-
-/*==========================================
- * Apply shared stat mods from status changes [DracoRPG]
- *------------------------------------------*/
-static unsigned short status_calc_str(struct block_list *bl, struct status_change *sc, int str)
-{
- if(!sc || !sc->count)
- return cap_value(str,0,USHRT_MAX);
-
- if(sc->data[SC_HARMONIZE]) {
- str -= sc->data[SC_HARMONIZE]->val2;
- return (unsigned short)cap_value(str,0,USHRT_MAX);
- }
- if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && str < 50)
- return 50;
- if(sc->data[SC_INCALLSTATUS])
- str += sc->data[SC_INCALLSTATUS]->val1;
- if(sc->data[SC_INCSTR])
- str += sc->data[SC_INCSTR]->val1;
- if(sc->data[SC_STRFOOD])
- str += sc->data[SC_STRFOOD]->val1;
- if(sc->data[SC_FOOD_STR_CASH])
- str += sc->data[SC_FOOD_STR_CASH]->val1;
- if(sc->data[SC_BATTLEORDERS])
- str += 5;
- if(sc->data[SC_LEADERSHIP])
- str += sc->data[SC_LEADERSHIP]->val1;
- if(sc->data[SC_LOUD])
- str += 4;
- if(sc->data[SC_TRUESIGHT])
- str += 5;
- if(sc->data[SC_SPURT])
- str += 10;
- if(sc->data[SC_NEN])
- str += sc->data[SC_NEN]->val1;
- if(sc->data[SC_BLESSING]){
- if(sc->data[SC_BLESSING]->val2)
- str += sc->data[SC_BLESSING]->val2;
- else
- str >>= 1;
- }
- if(sc->data[SC_MARIONETTE])
- str -= ((sc->data[SC_MARIONETTE]->val3)>>16)&0xFF;
- if(sc->data[SC_MARIONETTE2])
- str += ((sc->data[SC_MARIONETTE2]->val3)>>16)&0xFF;
- if(sc->data[SC_GIANTGROWTH])
- str += 30;
- if(sc->data[SC_SAVAGE_STEAK])
- str += sc->data[SC_SAVAGE_STEAK]->val1;
- if(sc->data[SC_INSPIRATION])
- str += sc->data[SC_INSPIRATION]->val3;
- if(sc->data[SC_STOMACHACHE])
- str -= sc->data[SC_STOMACHACHE]->val1;
- if(sc->data[SC_KYOUGAKU])
- str -= sc->data[SC_KYOUGAKU]->val2;
-
- return (unsigned short)cap_value(str,0,USHRT_MAX);
-}
-
-static unsigned short status_calc_agi(struct block_list *bl, struct status_change *sc, int agi)
-{
- if(!sc || !sc->count)
- return cap_value(agi,0,USHRT_MAX);
-
- if(sc->data[SC_HARMONIZE]) {
- agi -= sc->data[SC_HARMONIZE]->val2;
- return (unsigned short)cap_value(agi,0,USHRT_MAX);
- }
- if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && agi < 50)
- return 50;
- if(sc->data[SC_CONCENTRATE] && !sc->data[SC_QUAGMIRE])
- agi += (agi-sc->data[SC_CONCENTRATE]->val3)*sc->data[SC_CONCENTRATE]->val2/100;
- if(sc->data[SC_INCALLSTATUS])
- agi += sc->data[SC_INCALLSTATUS]->val1;
- if(sc->data[SC_INCAGI])
- agi += sc->data[SC_INCAGI]->val1;
- if(sc->data[SC_AGIFOOD])
- agi += sc->data[SC_AGIFOOD]->val1;
- if(sc->data[SC_FOOD_AGI_CASH])
- agi += sc->data[SC_FOOD_AGI_CASH]->val1;
- if(sc->data[SC_SOULCOLD])
- agi += sc->data[SC_SOULCOLD]->val1;
- if(sc->data[SC_TRUESIGHT])
- agi += 5;
- if(sc->data[SC_INCREASEAGI])
- agi += sc->data[SC_INCREASEAGI]->val2;
- if(sc->data[SC_INCREASING])
- agi += 4; // added based on skill updates [Reddozen]
- if(sc->data[SC_DECREASEAGI])
- agi -= sc->data[SC_DECREASEAGI]->val2;
- if(sc->data[SC_QUAGMIRE])
- agi -= sc->data[SC_QUAGMIRE]->val2;
- if(sc->data[SC_SUITON] && sc->data[SC_SUITON]->val3)
- agi -= sc->data[SC_SUITON]->val2;
- if(sc->data[SC_MARIONETTE])
- agi -= ((sc->data[SC_MARIONETTE]->val3)>>8)&0xFF;
- if(sc->data[SC_MARIONETTE2])
- agi += ((sc->data[SC_MARIONETTE2]->val3)>>8)&0xFF;
- if(sc->data[SC_ADORAMUS])
- agi -= sc->data[SC_ADORAMUS]->val2;
- if(sc->data[SC_DROCERA_HERB_STEAMED])
- agi += sc->data[SC_DROCERA_HERB_STEAMED]->val1;
- if(sc->data[SC_INSPIRATION])
- agi += sc->data[SC_INSPIRATION]->val3;
- if(sc->data[SC_STOMACHACHE])
- agi -= sc->data[SC_STOMACHACHE]->val1;
- if(sc->data[SC_KYOUGAKU])
- agi -= sc->data[SC_KYOUGAKU]->val2;
-
- return (unsigned short)cap_value(agi,0,USHRT_MAX);
-}
-
-static unsigned short status_calc_vit(struct block_list *bl, struct status_change *sc, int vit)
-{
- if(!sc || !sc->count)
- return cap_value(vit,0,USHRT_MAX);
-
- if(sc->data[SC_HARMONIZE]) {
- vit -= sc->data[SC_HARMONIZE]->val2;
- return (unsigned short)cap_value(vit,0,USHRT_MAX);
- }
- if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && vit < 50)
- return 50;
- if(sc->data[SC_INCALLSTATUS])
- vit += sc->data[SC_INCALLSTATUS]->val1;
- if(sc->data[SC_INCVIT])
- vit += sc->data[SC_INCVIT]->val1;
- if(sc->data[SC_VITFOOD])
- vit += sc->data[SC_VITFOOD]->val1;
- if(sc->data[SC_FOOD_VIT_CASH])
- vit += sc->data[SC_FOOD_VIT_CASH]->val1;
- if(sc->data[SC_CHANGE])
- vit += sc->data[SC_CHANGE]->val2;
- if(sc->data[SC_GLORYWOUNDS])
- vit += sc->data[SC_GLORYWOUNDS]->val1;
- if(sc->data[SC_TRUESIGHT])
- vit += 5;
- if(sc->data[SC_MARIONETTE])
- vit -= sc->data[SC_MARIONETTE]->val3&0xFF;
- if(sc->data[SC_MARIONETTE2])
- vit += sc->data[SC_MARIONETTE2]->val3&0xFF;
- if(sc->data[SC_LAUDAAGNUS])
- vit += 4 + sc->data[SC_LAUDAAGNUS]->val1;
- if(sc->data[SC_MINOR_BBQ])
- vit += sc->data[SC_MINOR_BBQ]->val1;
- if(sc->data[SC_INSPIRATION])
- vit += sc->data[SC_INSPIRATION]->val3;
- if(sc->data[SC_STOMACHACHE])
- vit -= sc->data[SC_STOMACHACHE]->val1;
- if(sc->data[SC_KYOUGAKU])
- vit -= sc->data[SC_KYOUGAKU]->val2;
-
- if(sc->data[SC_STRIPARMOR])
- vit -= vit * sc->data[SC_STRIPARMOR]->val2/100;
-
- return (unsigned short)cap_value(vit,0,USHRT_MAX);
-}
-
-static unsigned short status_calc_int(struct block_list *bl, struct status_change *sc, int int_)
-{
- if(!sc || !sc->count)
- return cap_value(int_,0,USHRT_MAX);
-
- if(sc->data[SC_HARMONIZE]) {
- int_ -= sc->data[SC_HARMONIZE]->val2;
- return (unsigned short)cap_value(int_,0,USHRT_MAX);
- }
- if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && int_ < 50)
- return 50;
- if(sc->data[SC_INCALLSTATUS])
- int_ += sc->data[SC_INCALLSTATUS]->val1;
- if(sc->data[SC_INCINT])
- int_ += sc->data[SC_INCINT]->val1;
- if(sc->data[SC_INTFOOD])
- int_ += sc->data[SC_INTFOOD]->val1;
- if(sc->data[SC_FOOD_INT_CASH])
- int_ += sc->data[SC_FOOD_INT_CASH]->val1;
- if(sc->data[SC_CHANGE])
- int_ += sc->data[SC_CHANGE]->val3;
- if(sc->data[SC_BATTLEORDERS])
- int_ += 5;
- if(sc->data[SC_TRUESIGHT])
- int_ += 5;
- if(sc->data[SC_BLESSING]){
- if (sc->data[SC_BLESSING]->val2)
- int_ += sc->data[SC_BLESSING]->val2;
- else
- int_ >>= 1;
- }
- if(sc->data[SC_NEN])
- int_ += sc->data[SC_NEN]->val1;
- if(sc->data[SC_MARIONETTE])
- int_ -= ((sc->data[SC_MARIONETTE]->val4)>>16)&0xFF;
- if(sc->data[SC_MARIONETTE2])
- int_ += ((sc->data[SC_MARIONETTE2]->val4)>>16)&0xFF;
- if(sc->data[SC_MANDRAGORA])
- int_ -= 5 + 5 * sc->data[SC_MANDRAGORA]->val1;
- if(sc->data[SC_COCKTAIL_WARG_BLOOD])
- int_ += sc->data[SC_COCKTAIL_WARG_BLOOD]->val1;
- if(sc->data[SC_INSPIRATION])
- int_ += sc->data[SC_INSPIRATION]->val3;
- if(sc->data[SC_STOMACHACHE])
- int_ -= sc->data[SC_STOMACHACHE]->val1;
- if(sc->data[SC_KYOUGAKU])
- int_ -= sc->data[SC_KYOUGAKU]->val2;
-
- if(sc->data[SC_STRIPHELM])
- int_ -= int_ * sc->data[SC_STRIPHELM]->val2/100;
- if(sc->data[SC__STRIPACCESSORY])
- int_ -= int_ * sc->data[SC__STRIPACCESSORY]->val2 / 100;
-
- return (unsigned short)cap_value(int_,0,USHRT_MAX);
-}
-
-static unsigned short status_calc_dex(struct block_list *bl, struct status_change *sc, int dex)
-{
- if(!sc || !sc->count)
- return cap_value(dex,0,USHRT_MAX);
-
- if(sc->data[SC_HARMONIZE]) {
- dex -= sc->data[SC_HARMONIZE]->val2;
- return (unsigned short)cap_value(dex,0,USHRT_MAX);
- }
- if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && dex < 50)
- return 50;
- if(sc->data[SC_CONCENTRATE] && !sc->data[SC_QUAGMIRE])
- dex += (dex-sc->data[SC_CONCENTRATE]->val4)*sc->data[SC_CONCENTRATE]->val2/100;
- if(sc->data[SC_INCALLSTATUS])
- dex += sc->data[SC_INCALLSTATUS]->val1;
- if(sc->data[SC_INCDEX])
- dex += sc->data[SC_INCDEX]->val1;
- if(sc->data[SC_DEXFOOD])
- dex += sc->data[SC_DEXFOOD]->val1;
- if(sc->data[SC_FOOD_DEX_CASH])
- dex += sc->data[SC_FOOD_DEX_CASH]->val1;
- if(sc->data[SC_BATTLEORDERS])
- dex += 5;
- if(sc->data[SC_HAWKEYES])
- dex += sc->data[SC_HAWKEYES]->val1;
- if(sc->data[SC_TRUESIGHT])
- dex += 5;
- if(sc->data[SC_QUAGMIRE])
- dex -= sc->data[SC_QUAGMIRE]->val2;
- if(sc->data[SC_BLESSING]){
- if (sc->data[SC_BLESSING]->val2)
- dex += sc->data[SC_BLESSING]->val2;
- else
- dex >>= 1;
- }
- if(sc->data[SC_INCREASING])
- dex += 4; // added based on skill updates [Reddozen]
- if(sc->data[SC_MARIONETTE])
- dex -= ((sc->data[SC_MARIONETTE]->val4)>>8)&0xFF;
- if(sc->data[SC_MARIONETTE2])
- dex += ((sc->data[SC_MARIONETTE2]->val4)>>8)&0xFF;
- if(sc->data[SC_SIROMA_ICE_TEA])
- dex += sc->data[SC_SIROMA_ICE_TEA]->val1;
- if(sc->data[SC_INSPIRATION])
- dex += sc->data[SC_INSPIRATION]->val3;
- if(sc->data[SC_STOMACHACHE])
- dex -= sc->data[SC_STOMACHACHE]->val1;
- if(sc->data[SC_KYOUGAKU])
- dex -= sc->data[SC_KYOUGAKU]->val2;
-
- if(sc->data[SC__STRIPACCESSORY])
- dex -= dex * sc->data[SC__STRIPACCESSORY]->val2 / 100;
-
- return (unsigned short)cap_value(dex,0,USHRT_MAX);
-}
-
-static unsigned short status_calc_luk(struct block_list *bl, struct status_change *sc, int luk)
-{
- if(!sc || !sc->count)
- return cap_value(luk,0,USHRT_MAX);
-
- if(sc->data[SC_HARMONIZE]) {
- luk -= sc->data[SC_HARMONIZE]->val2;
- return (unsigned short)cap_value(luk,0,USHRT_MAX);
- }
- if(sc->data[SC_CURSE])
- return 0;
- if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && luk < 50)
- return 50;
- if(sc->data[SC_INCALLSTATUS])
- luk += sc->data[SC_INCALLSTATUS]->val1;
- if(sc->data[SC_INCLUK])
- luk += sc->data[SC_INCLUK]->val1;
- if(sc->data[SC_LUKFOOD])
- luk += sc->data[SC_LUKFOOD]->val1;
- if(sc->data[SC_FOOD_LUK_CASH])
- luk += sc->data[SC_FOOD_LUK_CASH]->val1;
- if(sc->data[SC_TRUESIGHT])
- luk += 5;
- if(sc->data[SC_GLORIA])
- luk += 30;
- if(sc->data[SC_MARIONETTE])
- luk -= sc->data[SC_MARIONETTE]->val4&0xFF;
- if(sc->data[SC_MARIONETTE2])
- luk += sc->data[SC_MARIONETTE2]->val4&0xFF;
- if(sc->data[SC_PUTTI_TAILS_NOODLES])
- luk += sc->data[SC_PUTTI_TAILS_NOODLES]->val1;
- if(sc->data[SC_INSPIRATION])
- luk += sc->data[SC_INSPIRATION]->val3;
- if(sc->data[SC_STOMACHACHE])
- luk -= sc->data[SC_STOMACHACHE]->val1;
- if(sc->data[SC_KYOUGAKU])
- luk -= sc->data[SC_KYOUGAKU]->val2;
- if(sc->data[SC_LAUDARAMUS])
- luk += 4 + sc->data[SC_LAUDARAMUS]->val1;
-
- if(sc->data[SC__STRIPACCESSORY])
- luk -= luk * sc->data[SC__STRIPACCESSORY]->val2 / 100;
- if(sc->data[SC_BANANA_BOMB])
- luk -= luk * sc->data[SC_BANANA_BOMB]->val1 / 100;
-
- return (unsigned short)cap_value(luk,0,USHRT_MAX);
-}
-
-static unsigned short status_calc_batk(struct block_list *bl, struct status_change *sc, int batk)
-{
- if(!sc || !sc->count)
- return cap_value(batk,0,USHRT_MAX);
-
- if(sc->data[SC_ATKPOTION])
- batk += sc->data[SC_ATKPOTION]->val1;
- if(sc->data[SC_BATKFOOD])
- batk += sc->data[SC_BATKFOOD]->val1;
- if(sc->data[SC_GATLINGFEVER])
- batk += sc->data[SC_GATLINGFEVER]->val3;
- if(sc->data[SC_MADNESSCANCEL])
- batk += 100;
- if(sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2)
- batk += 50;
- if(bl->type == BL_ELEM
- && ((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 1)
- || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 1)
- || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 1)
- || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 1))
- )
- batk += batk / 5;
- if(sc->data[SC_FULL_SWING_K])
- batk += sc->data[SC_FULL_SWING_K]->val1;
- if(sc->data[SC_ODINS_POWER])
- batk += 70;
- if(sc->data[SC_ASH] && (bl->type==BL_MOB)){
- if(status_get_element(bl) == ELE_WATER) //water type
- batk /= 2;
- }
- if(sc->data[SC_PYROCLASTIC])
- batk += sc->data[SC_PYROCLASTIC]->val2;
- if (sc->data[SC_ANGRIFFS_MODUS])
- batk += sc->data[SC_ANGRIFFS_MODUS]->val2;
-
- if(sc->data[SC_INCATKRATE])
- batk += batk * sc->data[SC_INCATKRATE]->val1/100;
- if(sc->data[SC_PROVOKE])
- batk += batk * sc->data[SC_PROVOKE]->val3/100;
- if(sc->data[SC_CONCENTRATION])
- batk += batk * sc->data[SC_CONCENTRATION]->val2/100;
- if(sc->data[SC_SKE])
- batk += batk * 3;
- if(sc->data[SC_BLOODLUST])
- batk += batk * sc->data[SC_BLOODLUST]->val2/100;
- if(sc->data[SC_JOINTBEAT] && sc->data[SC_JOINTBEAT]->val2&BREAK_WAIST)
- batk -= batk * 25/100;
- if(sc->data[SC_CURSE])
- batk -= batk * 25/100;
-//Curse shouldn't effect on this? <- Curse OR Bleeding??
-// if(sc->data[SC_BLEEDING])
-// batk -= batk * 25/100;
- if(sc->data[SC_FLEET])
- batk += batk * sc->data[SC_FLEET]->val3/100;
- if(sc->data[SC__ENERVATION])
- batk -= batk * sc->data[SC__ENERVATION]->val2 / 100;
- if(sc->data[SC_RUSHWINDMILL])
- batk += batk * sc->data[SC_RUSHWINDMILL]->val2/100;
- if(sc->data[SC_SATURDAYNIGHTFEVER])
- batk += 100 * sc->data[SC_SATURDAYNIGHTFEVER]->val1;
- if(sc->data[SC_MELODYOFSINK])
- batk -= batk * sc->data[SC_MELODYOFSINK]->val3/100;
- if(sc->data[SC_BEYONDOFWARCRY])
- batk += batk * sc->data[SC_BEYONDOFWARCRY]->val3/100;
- if( sc->data[SC_ZANGETSU] )
- batk += batk * sc->data[SC_ZANGETSU]->val2 / 100;
-
- return (unsigned short)cap_value(batk,0,USHRT_MAX);
-}
-
-static unsigned short status_calc_watk(struct block_list *bl, struct status_change *sc, int watk)
-{
- if(!sc || !sc->count)
- return cap_value(watk,0,USHRT_MAX);
-
- if(sc->data[SC_IMPOSITIO])
- watk += sc->data[SC_IMPOSITIO]->val2;
- if(sc->data[SC_WATKFOOD])
- watk += sc->data[SC_WATKFOOD]->val1;
- if(sc->data[SC_DRUMBATTLE])
- watk += sc->data[SC_DRUMBATTLE]->val2;
- if(sc->data[SC_VOLCANO])
- watk += sc->data[SC_VOLCANO]->val2;
- if(sc->data[SC_MERC_ATKUP])
- watk += sc->data[SC_MERC_ATKUP]->val2;
- if(sc->data[SC_FIGHTINGSPIRIT])
- watk += sc->data[SC_FIGHTINGSPIRIT]->val1;
- if(sc->data[SC_STRIKING])
- watk += sc->data[SC_STRIKING]->val2;
- if(sc->data[SC_SHIELDSPELL_DEF] && sc->data[SC_SHIELDSPELL_DEF]->val1 == 3)
- watk += sc->data[SC_SHIELDSPELL_DEF]->val2;
- if(sc->data[SC_INSPIRATION])
- watk += sc->data[SC_INSPIRATION]->val2;
- if( sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 0 )
- watk += (10 + 10 * sc->data[SC_BANDING]->val1) * (sc->data[SC_BANDING]->val2);
- if( sc->data[SC_TROPIC_OPTION] )
- watk += sc->data[SC_TROPIC_OPTION]->val2;
- if( sc->data[SC_HEATER_OPTION] )
- watk += sc->data[SC_HEATER_OPTION]->val2;
- if( sc->data[SC_WATER_BARRIER] )
- watk -= sc->data[SC_WATER_BARRIER]->val3;
- if( sc->data[SC_PYROTECHNIC_OPTION] )
- watk += sc->data[SC_PYROTECHNIC_OPTION]->val2;
- if(sc->data[SC_NIBELUNGEN]) {
- if (bl->type != BL_PC)
- watk += sc->data[SC_NIBELUNGEN]->val2;
- else {
- #ifndef RENEWAL
- TBL_PC *sd = (TBL_PC*)bl;
- int index = sd->equip_index[sd->state.lr_flag?EQI_HAND_L:EQI_HAND_R];
- if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 4)
- #endif
- watk += sc->data[SC_NIBELUNGEN]->val2;
- }
- }
-
- if(sc->data[SC_INCATKRATE])
- watk += watk * sc->data[SC_INCATKRATE]->val1/100;
- if(sc->data[SC_PROVOKE])
- watk += watk * sc->data[SC_PROVOKE]->val3/100;
- if(sc->data[SC_CONCENTRATION])
- watk += watk * sc->data[SC_CONCENTRATION]->val2/100;
- if(sc->data[SC_SKE])
- watk += watk * 3;
- if(sc->data[SC__ENERVATION])
- watk -= watk * sc->data[SC__ENERVATION]->val2 / 100;
- if(sc->data[SC_FLEET])
- watk += watk * sc->data[SC_FLEET]->val3/100;
- if(sc->data[SC_CURSE])
- watk -= watk * 25/100;
- if(sc->data[SC_STRIPWEAPON])
- watk -= watk * sc->data[SC_STRIPWEAPON]->val2/100;
- if(sc->data[SC__ENERVATION])
- watk -= watk * sc->data[SC__ENERVATION]->val2 / 100;
- if((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2)
- || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2)
- || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 2)
- || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2)
- )
- watk += watk / 10;
- if( sc && sc->data[SC_TIDAL_WEAPON] )
- watk += watk * sc->data[SC_TIDAL_WEAPON]->val2 / 100;
- if(sc->data[SC_ANGRIFFS_MODUS])
- watk += watk * sc->data[SC_ANGRIFFS_MODUS]->val2/100;
-#ifdef RENEWAL_EDP
- if( sc->data[SC_EDP] )
- watk = watk * (100 + sc->data[SC_EDP]->val1 * 80) / 100;
-#endif
-
- return (unsigned short)cap_value(watk,0,USHRT_MAX);
-}
-#ifdef RENEWAL
-static unsigned short status_calc_ematk(struct block_list *bl, struct status_change *sc, int matk)
-{
-
- if (!sc || !sc->count)
- return cap_value(matk,0,USHRT_MAX);
- if (sc->data[SC_MATKPOTION])
- matk += sc->data[SC_MATKPOTION]->val1;
- if (sc->data[SC_MATKFOOD])
- matk += sc->data[SC_MATKFOOD]->val1;
- if(sc->data[SC_MANA_PLUS])
- matk += sc->data[SC_MANA_PLUS]->val1;
- if(sc->data[SC_AQUAPLAY_OPTION])
- matk += sc->data[SC_AQUAPLAY_OPTION]->val2;
- if(sc->data[SC_CHILLY_AIR_OPTION])
- matk += sc->data[SC_CHILLY_AIR_OPTION]->val2;
- if(sc->data[SC_WATER_BARRIER])
- matk -= sc->data[SC_WATER_BARRIER]->val3;
- if(sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3)
- matk += 50;
- if(sc->data[SC_ODINS_POWER])
- matk += 40 + 30 * sc->data[SC_ODINS_POWER]->val1; //70 lvl1, 100lvl2
- if(sc->data[SC_IZAYOI])
- matk += 50 * sc->data[SC_IZAYOI]->val1;
- return (unsigned short)cap_value(matk,0,USHRT_MAX);
-}
-#endif
-static unsigned short status_calc_matk(struct block_list *bl, struct status_change *sc, int matk)
-{
- if(!sc || !sc->count)
- return cap_value(matk,0,USHRT_MAX);
-#ifndef RENEWAL
- // take note fixed value first before % modifiers
- if (sc->data[SC_MATKPOTION])
- matk += sc->data[SC_MATKPOTION]->val1;
- if (sc->data[SC_MATKFOOD])
- matk += sc->data[SC_MATKFOOD]->val1;
- if (sc->data[SC_MANA_PLUS])
- matk += sc->data[SC_MANA_PLUS]->val1;
- if (sc->data[SC_AQUAPLAY_OPTION])
- matk += sc->data[SC_AQUAPLAY_OPTION]->val2;
- if (sc->data[SC_CHILLY_AIR_OPTION])
- matk += sc->data[SC_CHILLY_AIR_OPTION]->val2;
- if (sc->data[SC_WATER_BARRIER])
- matk -= sc->data[SC_WATER_BARRIER]->val3;
- if (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3)
- matk += 50;
- if (sc->data[SC_ODINS_POWER])
- matk += 40 + 30 * sc->data[SC_ODINS_POWER]->val1; //70 lvl1, 100lvl2
- if (sc->data[SC_IZAYOI])
- matk += 50 * sc->data[SC_IZAYOI]->val1;
-#endif
- if (sc->data[SC_MAGICPOWER])
- matk += matk * sc->data[SC_MAGICPOWER]->val3/100;
- if (sc->data[SC_MINDBREAKER])
- matk += matk * sc->data[SC_MINDBREAKER]->val2/100;
- if (sc->data[SC_INCMATKRATE])
- matk += matk * sc->data[SC_INCMATKRATE]->val1/100;
- if (sc->data[SC_MOONLITSERENADE])
- matk += matk * sc->data[SC_MOONLITSERENADE]->val2/100;
- if (sc->data[SC_MELODYOFSINK])
- matk += matk * sc->data[SC_MELODYOFSINK]->val3/100;
- if (sc->data[SC_BEYONDOFWARCRY])
- matk -= matk * sc->data[SC_BEYONDOFWARCRY]->val3/100;
- if( sc->data[SC_ZANGETSU] )
- matk += matk * sc->data[SC_ZANGETSU]->val2 / 100;
-
- return (unsigned short)cap_value(matk,0,USHRT_MAX);
-}
-
-static signed short status_calc_critical(struct block_list *bl, struct status_change *sc, int critical) {
-
- if(!sc || !sc->count)
- return cap_value(critical,10,SHRT_MAX);
-
- if (sc->data[SC_INCCRI])
- critical += sc->data[SC_INCCRI]->val2;
- if (sc->data[SC_EXPLOSIONSPIRITS])
- critical += sc->data[SC_EXPLOSIONSPIRITS]->val2;
- if (sc->data[SC_FORTUNE])
- critical += sc->data[SC_FORTUNE]->val2;
- if (sc->data[SC_TRUESIGHT])
- critical += sc->data[SC_TRUESIGHT]->val2;
- if(sc->data[SC_CLOAKING])
- critical += critical;
- if(sc->data[SC_STRIKING])
- critical += sc->data[SC_STRIKING]->val1;
-#ifdef RENEWAL
- if (sc->data[SC_SPEARQUICKEN])
- critical += 3*sc->data[SC_SPEARQUICKEN]->val1*10;
-#endif
-
- if(sc->data[SC__INVISIBILITY])
- critical += critical * sc->data[SC__INVISIBILITY]->val3 / 100;
- if(sc->data[SC__UNLUCKY])
- critical -= critical * sc->data[SC__UNLUCKY]->val2 / 100;
-
- return (short)cap_value(critical,10,SHRT_MAX);
-}
-
-static signed short status_calc_hit(struct block_list *bl, struct status_change *sc, int hit)
-{
-
- if(!sc || !sc->count)
- return cap_value(hit,1,SHRT_MAX);
-
- if(sc->data[SC_INCHIT])
- hit += sc->data[SC_INCHIT]->val1;
- if(sc->data[SC_HITFOOD])
- hit += sc->data[SC_HITFOOD]->val1;
- if(sc->data[SC_TRUESIGHT])
- hit += sc->data[SC_TRUESIGHT]->val3;
- if(sc->data[SC_HUMMING])
- hit += sc->data[SC_HUMMING]->val2;
- if(sc->data[SC_CONCENTRATION])
- hit += sc->data[SC_CONCENTRATION]->val3;
- if(sc->data[SC_INSPIRATION])
- hit += 5 * sc->data[SC_INSPIRATION]->val1;
- if(sc->data[SC_ADJUSTMENT])
- hit -= 30;
- if(sc->data[SC_INCREASING])
- hit += 20; // RockmanEXE; changed based on updated [Reddozen]
- if(sc->data[SC_MERC_HITUP])
- hit += sc->data[SC_MERC_HITUP]->val2;
-
- if(sc->data[SC_INCHITRATE])
- hit += hit * sc->data[SC_INCHITRATE]->val1/100;
- if(sc->data[SC_BLIND])
- hit -= hit * 25/100;
- if(sc->data[SC__GROOMY])
- hit -= hit * sc->data[SC__GROOMY]->val3 / 100;
- if(sc->data[SC_FEAR])
- hit -= hit * 20 / 100;
- if (sc->data[SC_ASH])
- hit /= 2;
-
- return (short)cap_value(hit,1,SHRT_MAX);
-}
-
-static signed short status_calc_flee(struct block_list *bl, struct status_change *sc, int flee)
-{
- if( bl->type == BL_PC )
- {
- if( map_flag_gvg(bl->m) )
- flee -= flee * battle_config.gvg_flee_penalty/100;
- else if( map[bl->m].flag.battleground )
- flee -= flee * battle_config.bg_flee_penalty/100;
- }
-
- if(!sc || !sc->count)
- return cap_value(flee,1,SHRT_MAX);
-
- if(sc->data[SC_INCFLEE])
- flee += sc->data[SC_INCFLEE]->val1;
- if(sc->data[SC_FLEEFOOD])
- flee += sc->data[SC_FLEEFOOD]->val1;
- if(sc->data[SC_WHISTLE])
- flee += sc->data[SC_WHISTLE]->val2;
- if(sc->data[SC_WINDWALK])
- flee += sc->data[SC_WINDWALK]->val2;
- if(sc->data[SC_VIOLENTGALE])
- flee += sc->data[SC_VIOLENTGALE]->val2;
- if(sc->data[SC_MOON_COMFORT]) //SG skill [Komurka]
- flee += sc->data[SC_MOON_COMFORT]->val2;
- if(sc->data[SC_CLOSECONFINE])
- flee += 10;
- if (sc->data[SC_ANGRIFFS_MODUS])
- flee -= sc->data[SC_ANGRIFFS_MODUS]->val3;
- if (sc->data[SC_OVERED_BOOST])
- flee = max(flee,sc->data[SC_OVERED_BOOST]->val2);
- if(sc->data[SC_ADJUSTMENT])
- flee += 30;
- if(sc->data[SC_SPEED])
- flee += 10 + sc->data[SC_SPEED]->val1 * 10;
- if(sc->data[SC_GATLINGFEVER])
- flee -= sc->data[SC_GATLINGFEVER]->val4;
- if(sc->data[SC_PARTYFLEE])
- flee += sc->data[SC_PARTYFLEE]->val1 * 10;
- if(sc->data[SC_MERC_FLEEUP])
- flee += sc->data[SC_MERC_FLEEUP]->val2;
- if( sc->data[SC_HALLUCINATIONWALK] )
- flee += sc->data[SC_HALLUCINATIONWALK]->val2;
- if( sc->data[SC_WATER_BARRIER] )
- flee -= sc->data[SC_WATER_BARRIER]->val3;
- if( sc->data[SC_MARSHOFABYSS] )
- flee -= (9 * sc->data[SC_MARSHOFABYSS]->val3 / 10 + sc->data[SC_MARSHOFABYSS]->val2 / 10) * (bl->type == BL_MOB ? 2 : 1);
-#ifdef RENEWAL
- if( sc->data[SC_SPEARQUICKEN] )
- flee += 2 * sc->data[SC_SPEARQUICKEN]->val1;
-#endif
-
- if(sc->data[SC_INCFLEERATE])
- flee += flee * sc->data[SC_INCFLEERATE]->val1/100;
- if(sc->data[SC_SPIDERWEB] && sc->data[SC_SPIDERWEB]->val1)
- flee -= flee * 50/100;
- if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
- flee -= flee * 50/100;
- if(sc->data[SC_BLIND])
- flee -= flee * 25/100;
- if(sc->data[SC_FEAR])
- flee -= flee * 20 / 100;
- if(sc->data[SC_PARALYSE])
- flee -= flee * 10 / 100; // 10% Flee reduction
- if(sc->data[SC_INFRAREDSCAN])
- flee -= flee * 30 / 100;
- if( sc->data[SC__LAZINESS] )
- flee -= flee * sc->data[SC__LAZINESS]->val3 / 100;
- if( sc->data[SC_GLOOMYDAY] )
- flee -= flee * sc->data[SC_GLOOMYDAY]->val2 / 100;
- if( sc->data[SC_SATURDAYNIGHTFEVER] )
- flee -= flee * (40 + 10 * sc->data[SC_SATURDAYNIGHTFEVER]->val1) / 100;
- if( sc->data[SC_WIND_STEP_OPTION] )
- flee += flee * sc->data[SC_WIND_STEP_OPTION]->val2 / 100;
- if( sc->data[SC_ZEPHYR] )
- flee += flee * sc->data[SC_ZEPHYR]->val2 / 100;
- if(sc->data[SC_ASH] && (bl->type==BL_MOB)){ //mob
- if(status_get_element(bl) == ELE_WATER) //water type
- flee /= 2;
- }
-
- return (short)cap_value(flee,1,SHRT_MAX);
-}
-
-static signed short status_calc_flee2(struct block_list *bl, struct status_change *sc, int flee2)
-{
- if(!sc || !sc->count)
- return cap_value(flee2,10,SHRT_MAX);
-
- if(sc->data[SC_INCFLEE2])
- flee2 += sc->data[SC_INCFLEE2]->val2;
- if(sc->data[SC_WHISTLE])
- flee2 += sc->data[SC_WHISTLE]->val3*10;
- if(sc->data[SC__UNLUCKY])
- flee2 -= flee2 * sc->data[SC__UNLUCKY]->val2 / 100;
-
- return (short)cap_value(flee2,10,SHRT_MAX);
-}
-static defType status_calc_def(struct block_list *bl, struct status_change *sc, int def) {
-
- if(!sc || !sc->count)
- return (defType)cap_value(def,DEFTYPE_MIN,DEFTYPE_MAX);
-
- if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
- return 0;
- if(sc->data[SC_SKA])
- return sc->data[SC_SKA]->val3;
- if(sc->data[SC_BARRIER])
- return 100;
- if(sc->data[SC_KEEPING])
- return 90;
-#ifndef RENEWAL // does not provide 90 DEF in renewal mode
- if(sc->data[SC_STEELBODY])
- return 90;
-#endif
-
- if(sc->data[SC_ARMORCHANGE])
- def += sc->data[SC_ARMORCHANGE]->val2;
- if(sc->data[SC_DRUMBATTLE])
- def += sc->data[SC_DRUMBATTLE]->val3;
- if(sc->data[SC_DEFENCE]) //[orn]
- def += sc->data[SC_DEFENCE]->val2 ;
- if(sc->data[SC_INCDEFRATE])
- def += def * sc->data[SC_INCDEFRATE]->val1/100;
- if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2)
- def += 50;
- if(sc->data[SC_ODINS_POWER])
- def -= 20;
- if( sc->data[SC_ANGRIFFS_MODUS] )
- def -= 30 + 20 * sc->data[SC_ANGRIFFS_MODUS]->val1;
- if(sc->data[SC_STONEHARDSKIN])// Final DEF increase divided by 10 since were using classic (pre-renewal) mechanics. [Rytech]
- def += sc->data[SC_STONEHARDSKIN]->val1;
- if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
- def >>=1;
- if(sc->data[SC_FREEZE])
- def >>=1;
- if(sc->data[SC_SIGNUMCRUCIS])
- def -= def * sc->data[SC_SIGNUMCRUCIS]->val2/100;
- if(sc->data[SC_CONCENTRATION])
- def -= def * sc->data[SC_CONCENTRATION]->val4/100;
- if(sc->data[SC_SKE])
- def >>=1;
- if(sc->data[SC_PROVOKE] && bl->type != BL_PC) // Provoke doesn't alter player defense->
- def -= def * sc->data[SC_PROVOKE]->val4/100;
- if(sc->data[SC_STRIPSHIELD])
- def -= def * sc->data[SC_STRIPSHIELD]->val2/100;
- if (sc->data[SC_FLING])
- def -= def * (sc->data[SC_FLING]->val2)/100;
- if( sc->data[SC_FREEZING] )
- def -= def * 10 / 100;
- if( sc->data[SC_MARSHOFABYSS] )
- def -= def * ( 6 + 6 * sc->data[SC_MARSHOFABYSS]->val3/10 + (bl->type == BL_MOB ? 5 : 3) * sc->data[SC_MARSHOFABYSS]->val2/36 ) / 100;
- if( sc->data[SC_ANALYZE] )
- def -= def * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100;
- if( sc->data[SC_FORCEOFVANGUARD] )
- def += def * 2 * sc->data[SC_FORCEOFVANGUARD]->val1 / 100;
- if(sc->data[SC_SATURDAYNIGHTFEVER])
- def -= def * (10 + 10 * sc->data[SC_SATURDAYNIGHTFEVER]->val1) / 100;
- if(sc->data[SC_EARTHDRIVE])
- def -= def * 25 / 100;
- if( sc->data[SC_ROCK_CRUSHER] )
- def -= def * sc->data[SC_ROCK_CRUSHER]->val2 / 100;
- if( sc->data[SC_POWER_OF_GAIA] )
- def += def * sc->data[SC_POWER_OF_GAIA]->val2 / 100;
- if( sc->data[SC_PRESTIGE] )
- def += def * sc->data[SC_PRESTIGE]->val1 / 100;
- if(sc->data[SC_ASH] && (bl->type==BL_MOB)){
- if(status_get_race(bl)==RC_PLANT)
- def /= 2;
- }
-
- return (defType)cap_value(def,DEFTYPE_MIN,DEFTYPE_MAX);;
-}
-
-static signed short status_calc_def2(struct block_list *bl, struct status_change *sc, int def2)
-{
- if(!sc || !sc->count)
-#ifdef RENEWAL
- return (short)cap_value(def2,SHRT_MIN,SHRT_MAX);
-#else
- return (short)cap_value(def2,1,SHRT_MAX);
-#endif
-
- if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
- return 0;
- if(sc->data[SC_ETERNALCHAOS])
- return 0;
- if(sc->data[SC_SUN_COMFORT])
- def2 += sc->data[SC_SUN_COMFORT]->val2;
- if( sc->data[SC_SHIELDSPELL_REF] && sc->data[SC_SHIELDSPELL_REF]->val1 == 1 )
- def2 += sc->data[SC_SHIELDSPELL_REF]->val2;
- if( sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 0 )
- def2 += (5 + sc->data[SC_BANDING]->val1) * (sc->data[SC_BANDING]->val2);
-
- if(sc->data[SC_ANGELUS])
-#ifdef RENEWAL //in renewal only the VIT stat bonus is boosted by angelus
- def2 += status_get_vit(bl) / 2 * sc->data[SC_ANGELUS]->val2/100;
-#else
- def2 += def2 * sc->data[SC_ANGELUS]->val2/100;
-#endif
- if(sc->data[SC_CONCENTRATION])
- def2 -= def2 * sc->data[SC_CONCENTRATION]->val4/100;
- if(sc->data[SC_POISON])
- def2 -= def2 * 25/100;
- if(sc->data[SC_DPOISON])
- def2 -= def2 * 25/100;
- if(sc->data[SC_SKE])
- def2 -= def2 * 50/100;
- if(sc->data[SC_PROVOKE])
- def2 -= def2 * sc->data[SC_PROVOKE]->val4/100;
- if(sc->data[SC_JOINTBEAT])
- def2 -= def2 * ( sc->data[SC_JOINTBEAT]->val2&BREAK_SHOULDER ? 50 : 0 ) / 100
- + def2 * ( sc->data[SC_JOINTBEAT]->val2&BREAK_WAIST ? 25 : 0 ) / 100;
- if(sc->data[SC_FLING])
- def2 -= def2 * (sc->data[SC_FLING]->val3)/100;
- if( sc->data[SC_FREEZING] )
- def2 -= def2 * 3 / 10;
- if(sc->data[SC_ANALYZE])
- def2 -= def2 * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100;
- if( sc->data[SC_ECHOSONG] )
- def2 += def2 * sc->data[SC_ECHOSONG]->val2/100;
- if( sc->data[SC_GT_REVITALIZE] && sc->data[SC_GT_REVITALIZE]->val4)
- def2 += def2 * sc->data[SC_GT_REVITALIZE]->val4 / 100;
- if(sc->data[SC_ASH] && (bl->type==BL_MOB)){
- if(status_get_race(bl)==RC_PLANT)
- def2 /= 2;
- }
- if (sc->data[SC_PARALYSIS])
- def2 -= def2 * sc->data[SC_PARALYSIS]->val2 / 100;
-
-#ifdef RENEWAL
- return (short)cap_value(def2,SHRT_MIN,SHRT_MAX);
-#else
- return (short)cap_value(def2,1,SHRT_MAX);
-#endif
-}
-
-
-static defType status_calc_mdef(struct block_list *bl, struct status_change *sc, int mdef) {
-
- if(!sc || !sc->count)
- return (defType)cap_value(mdef,DEFTYPE_MIN,DEFTYPE_MAX);
-
- if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
- return 0;
- if(sc->data[SC_BARRIER])
- return 100;
-
-#ifndef RENEWAL // no longer provides 90 MDEF in renewal mode
- if(sc->data[SC_STEELBODY])
- return 90;
-#endif
-
- if(sc->data[SC_ARMORCHANGE])
- mdef += sc->data[SC_ARMORCHANGE]->val3;
- if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3)
- mdef += 50;
- if(sc->data[SC_ENDURE])// It has been confirmed that eddga card grants 1 MDEF, not 0, not 10, but 1.
- mdef += (sc->data[SC_ENDURE]->val4 == 0) ? sc->data[SC_ENDURE]->val1 : 1;
- if(sc->data[SC_CONCENTRATION])
- mdef += 1; //Skill info says it adds a fixed 1 Mdef point.
- if(sc->data[SC_STONEHARDSKIN])// Final MDEF increase divided by 10 since were using classic (pre-renewal) mechanics. [Rytech]
- mdef += sc->data[SC_STONEHARDSKIN]->val1;
- if(sc->data[SC_WATER_BARRIER])
- mdef += sc->data[SC_WATER_BARRIER]->val2;
- if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
- mdef += 25*mdef/100;
- if(sc->data[SC_FREEZE])
- mdef += 25*mdef/100;
- if( sc->data[SC_MARSHOFABYSS] )
- mdef -= mdef * ( 6 + 6 * sc->data[SC_MARSHOFABYSS]->val3/10 + (bl->type == BL_MOB ? 5 : 3) * sc->data[SC_MARSHOFABYSS]->val2/36 ) / 100;
- if(sc->data[SC_ANALYZE])
- mdef -= mdef * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100;
- if(sc->data[SC_SYMPHONYOFLOVER])
- mdef += mdef * sc->data[SC_SYMPHONYOFLOVER]->val2 / 100;
- if(sc->data[SC_GT_CHANGE] && sc->data[SC_GT_CHANGE]->val4)
- mdef -= mdef * sc->data[SC_GT_CHANGE]->val4 / 100;
- if (sc->data[SC_ODINS_POWER])
- mdef -= 20 * sc->data[SC_ODINS_POWER]->val1;
-
- return (defType)cap_value(mdef,DEFTYPE_MIN,DEFTYPE_MAX);
-}
-
-static signed short status_calc_mdef2(struct block_list *bl, struct status_change *sc, int mdef2)
-{
- if(!sc || !sc->count)
-#ifdef RENEWAL
- return (short)cap_value(mdef2,SHRT_MIN,SHRT_MAX);
-#else
- return (short)cap_value(mdef2,1,SHRT_MAX);
-#endif
-
-
- if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
- return 0;
- if(sc->data[SC_SKA])
- return 90;
- if(sc->data[SC_MINDBREAKER])
- mdef2 -= mdef2 * sc->data[SC_MINDBREAKER]->val3/100;
- if(sc->data[SC_ANALYZE])
- mdef2 -= mdef2 * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100;
-
-#ifdef RENEWAL
- return (short)cap_value(mdef2,SHRT_MIN,SHRT_MAX);
-#else
- return (short)cap_value(mdef2,1,SHRT_MAX);
-#endif
-}
-
-static unsigned short status_calc_speed(struct block_list *bl, struct status_change *sc, int speed)
-{
- TBL_PC* sd = BL_CAST(BL_PC, bl);
- int speed_rate;
-
- if( sc == NULL )
- return cap_value(speed,10,USHRT_MAX);
-
- if( sd && sd->ud.skilltimer != INVALID_TIMER && (pc_checkskill(sd,SA_FREECAST) > 0 || sd->ud.skill_id == LG_EXEEDBREAK) )
- {
- if( sd->ud.skill_id == LG_EXEEDBREAK )
- speed_rate = 100 + 60 - (sd->ud.skill_lv * 10);
- else
- speed_rate = 175 - 5 * pc_checkskill(sd,SA_FREECAST);
- }
- else
- {
- speed_rate = 100;
-
- //GetMoveHasteValue2()
- {
- int val = 0;
-
- if( sc->data[SC_FUSION] )
- val = 25;
- else if( sd ) {
- if( pc_isriding(sd) || sd->sc.option&(OPTION_DRAGON|OPTION_MOUNTING) )
- val = 25;//Same bonus
- else if( pc_isridingwug(sd) )
- val = 15 + 5 * pc_checkskill(sd, RA_WUGRIDER);
- else if( pc_ismadogear(sd) ) {
- val = (- 10 * (5 - pc_checkskill(sd,NC_MADOLICENCE)));
- if( sc->data[SC_ACCELERATION] )
- val += 25;
- }
- }
-
- speed_rate -= val;
- }
-
- //GetMoveSlowValue()
- {
- int val = 0;
-
- if( sd && sc->data[SC_HIDING] && pc_checkskill(sd,RG_TUNNELDRIVE) > 0 )
- val = 120 - 6 * pc_checkskill(sd,RG_TUNNELDRIVE);
- else
- if( sd && sc->data[SC_CHASEWALK] && sc->data[SC_CHASEWALK]->val3 < 0 )
- val = sc->data[SC_CHASEWALK]->val3;
- else
- {
- // Longing for Freedom cancels song/dance penalty
- if( sc->data[SC_LONGING] )
- val = max( val, 50 - 10 * sc->data[SC_LONGING]->val1 );
- else
- if( sd && sc->data[SC_DANCING] )
- val = max( val, 500 - (40 + 10 * (sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_BARDDANCER)) * pc_checkskill(sd,(sd->status.sex?BA_MUSICALLESSON:DC_DANCINGLESSON)) );
-
- if( sc->data[SC_DECREASEAGI] )
- val = max( val, 25 );
- if( sc->data[SC_QUAGMIRE] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY] || (sc->data[SC_GLOOMYDAY] && sc->data[SC_GLOOMYDAY]->val4) )
- val = max( val, 50 );
- if( sc->data[SC_DONTFORGETME] )
- val = max( val, sc->data[SC_DONTFORGETME]->val3 );
- if( sc->data[SC_CURSE] )
- val = max( val, 300 );
- if( sc->data[SC_CHASEWALK] )
- val = max( val, sc->data[SC_CHASEWALK]->val3 );
- if( sc->data[SC_WEDDING] )
- val = max( val, 100 );
- if( sc->data[SC_JOINTBEAT] && sc->data[SC_JOINTBEAT]->val2&(BREAK_ANKLE|BREAK_KNEE) )
- val = max( val, (sc->data[SC_JOINTBEAT]->val2&BREAK_ANKLE ? 50 : 0) + (sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE ? 30 : 0) );
- if( sc->data[SC_CLOAKING] && (sc->data[SC_CLOAKING]->val4&1) == 0 )
- val = max( val, sc->data[SC_CLOAKING]->val1 < 3 ? 300 : 30 - 3 * sc->data[SC_CLOAKING]->val1 );
- if( sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY )
- val = max( val, 75 );
- if( sc->data[SC_SLOWDOWN] ) // Slow Potion
- val = max( val, 100 );
- if( sc->data[SC_GATLINGFEVER] )
- val = max( val, 100 );
- if( sc->data[SC_SUITON] )
- val = max( val, sc->data[SC_SUITON]->val3 );
- if( sc->data[SC_SWOO] )
- val = max( val, 300 );
- if( sc->data[SC_FREEZING] )
- val = max( val, 70 );
- if( sc->data[SC_MARSHOFABYSS] )
- val = max( val, 40 + 10 * sc->data[SC_MARSHOFABYSS]->val1 );
- if( sc->data[SC_CAMOUFLAGE] && (sc->data[SC_CAMOUFLAGE]->val3&1) == 0 )
- val = max( val, sc->data[SC_CAMOUFLAGE]->val1 < 3 ? 0 : 25 * (5 - sc->data[SC_CAMOUFLAGE]->val1) );
- if( sc->data[SC__GROOMY] )
- val = max( val, sc->data[SC__GROOMY]->val2);
- if( sc->data[SC_STEALTHFIELD_MASTER] )
- val = max( val, 30 );
- if( sc->data[SC_BANDING_DEFENCE] )
- val = max( val, sc->data[SC_BANDING_DEFENCE]->val1 );//+90% walking speed.
- if( sc->data[SC_ROCK_CRUSHER_ATK] )
- val = max( val, sc->data[SC_ROCK_CRUSHER_ATK]->val2 );
- if( sc->data[SC_POWER_OF_GAIA] )
- val = max( val, sc->data[SC_POWER_OF_GAIA]->val2 );
- if( sc->data[SC_MELON_BOMB] )
- val = max( val, sc->data[SC_MELON_BOMB]->val1 );
-
- if( sd && sd->bonus.speed_rate + sd->bonus.speed_add_rate > 0 ) // permanent item-based speedup
- val = max( val, sd->bonus.speed_rate + sd->bonus.speed_add_rate );
- }
-
- speed_rate += val;
- }
-
- //GetMoveHasteValue1()
- {
- int val = 0;
-
- if( sc->data[SC_SPEEDUP1] ) //FIXME: used both by NPC_AGIUP and Speed Potion script
- val = max( val, 50 );
- if( sc->data[SC_INCREASEAGI] )
- val = max( val, 25 );
- if( sc->data[SC_WINDWALK] )
- val = max( val, 2 * sc->data[SC_WINDWALK]->val1 );
- if( sc->data[SC_CARTBOOST] )
- val = max( val, 20 );
- if( sd && (sd->class_&MAPID_UPPERMASK) == MAPID_ASSASSIN && pc_checkskill(sd,TF_MISS) > 0 )
- val = max( val, 1 * pc_checkskill(sd,TF_MISS) );
- if( sc->data[SC_CLOAKING] && (sc->data[SC_CLOAKING]->val4&1) == 1 )
- val = max( val, sc->data[SC_CLOAKING]->val1 >= 10 ? 25 : 3 * sc->data[SC_CLOAKING]->val1 - 3 );
- if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
- val = max( val, 25 );
- if( sc->data[SC_RUN] )
- val = max( val, 55 );
- if( sc->data[SC_AVOID] )
- val = max( val, 10 * sc->data[SC_AVOID]->val1 );
- if( sc->data[SC_INVINCIBLE] && !sc->data[SC_INVINCIBLEOFF] )
- val = max( val, 75 );
- if( sc->data[SC_CLOAKINGEXCEED] )
- val = max( val, sc->data[SC_CLOAKINGEXCEED]->val3);
- if( sc->data[SC_HOVERING] )
- val = max( val, 10 );
- if( sc->data[SC_GN_CARTBOOST] )
- val = max( val, sc->data[SC_GN_CARTBOOST]->val2 );
- if( sc->data[SC_SWINGDANCE] )
- val = max( val, sc->data[SC_SWINGDANCE]->val2 );
- if( sc->data[SC_WIND_STEP_OPTION] )
- val = max( val, sc->data[SC_WIND_STEP_OPTION]->val2 );
-
- //FIXME: official items use a single bonus for this [ultramage]
- if( sc->data[SC_SPEEDUP0] ) // temporary item-based speedup
- val = max( val, 25 );
- if( sd && sd->bonus.speed_rate + sd->bonus.speed_add_rate < 0 ) // permanent item-based speedup
- val = max( val, -(sd->bonus.speed_rate + sd->bonus.speed_add_rate) );
-
- speed_rate -= val;
- }
-
- if( speed_rate < 40 )
- speed_rate = 40;
- }
-
- //GetSpeed()
- {
- if( sd && pc_iscarton(sd) )
- speed += speed * (50 - 5 * pc_checkskill(sd,MC_PUSHCART)) / 100;
- if( sc->data[SC_PARALYSE] )
- speed += speed * 50 / 100;
- if( speed_rate != 100 )
- speed = speed * speed_rate / 100;
- if( sc->data[SC_STEELBODY] )
- speed = 200;
- if( sc->data[SC_DEFENDER] )
- speed = max(speed, 200);
- if( sc->data[SC_WALKSPEED] && sc->data[SC_WALKSPEED]->val1 > 0 ) // ChangeSpeed
- speed = speed * 100 / sc->data[SC_WALKSPEED]->val1;
- }
-
- return (short)cap_value(speed,10,USHRT_MAX);
-}
-
-#ifdef RENEWAL_ASPD
-// flag&1 - fixed value [malufett]
-// flag&2 - percentage value
-static short status_calc_aspd(struct block_list *bl, struct status_change *sc, short flag)
-{
- int i, pots = 0, skills1 = 0, skills2 = 0;
-
- if(!sc || !sc->count)
- return 0;
-
- if(sc->data[i=SC_ASPDPOTION3] ||
- sc->data[i=SC_ASPDPOTION2] ||
- sc->data[i=SC_ASPDPOTION1] ||
- sc->data[i=SC_ASPDPOTION0])
- pots += sc->data[i]->val1;
-
- if( !sc->data[SC_QUAGMIRE] ){
- if(sc->data[SC_STAR_COMFORT])
- skills1 = 5; // needs more info
-
- if(sc->data[SC_TWOHANDQUICKEN] && skills1 < 7)
- skills1 = 7;
-
- if(sc->data[SC_ONEHAND] && skills1 < 7) skills1 = 7;
-
- if(sc->data[SC_MERC_QUICKEN] && skills1 < 7) // needs more info
- skills1 = 7;
-
- if(sc->data[SC_ADRENALINE2] && skills1 < 6)
- skills1 = 6;
-
- if(sc->data[SC_ADRENALINE] && skills1 < 7)
- skills1 = 7;
-
- if(sc->data[SC_SPEARQUICKEN] && skills1 < 7)
- skills1 = 7;
-
- if(sc->data[SC_GATLINGFEVER] && skills1 < 9) // needs more info
- skills1 = 9;
-
- if(sc->data[SC_FLEET] && skills1 < 5)
- skills1 = 5;
-
- if(sc->data[SC_ASSNCROS] &&
- skills1 < 5+1*sc->data[SC_ASSNCROS]->val1) // needs more info
- {
- if (bl->type!=BL_PC)
- skills1 = 4+1*sc->data[SC_ASSNCROS]->val1;
- else
- switch(((TBL_PC*)bl)->status.weapon)
- {
- case W_BOW:
- case W_REVOLVER:
- case W_RIFLE:
- case W_GATLING:
- case W_SHOTGUN:
- case W_GRENADE:
- break;
- default:
- skills1 = 5+1*sc->data[SC_ASSNCROS]->val1;
- }
- }
- }
-
- if((sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) && skills1 < 15)
- skills1 = 15;
- else if(sc->data[SC_MADNESSCANCEL] && skills1 < 15) // needs more info
- skills1 = 15;
-
- if(sc->data[SC_DONTFORGETME])
- skills2 -= sc->data[SC_DONTFORGETME]->val2; // needs more info
- if(sc->data[SC_LONGING])
- skills2 -= sc->data[SC_LONGING]->val2; // needs more info
- if(sc->data[SC_STEELBODY])
- skills2 -= 25;
- if(sc->data[SC_SKA])
- skills2 -= 25;
- if(sc->data[SC_DEFENDER])
- skills2 -= sc->data[SC_DEFENDER]->val4; // needs more info
- if(sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY) // needs more info
- skills2 -= 25;
- if(sc->data[SC_GRAVITATION])
- skills2 -= sc->data[SC_GRAVITATION]->val2; // needs more info
- if(sc->data[SC_JOINTBEAT]) { // needs more info
- if( sc->data[SC_JOINTBEAT]->val2&BREAK_WRIST )
- skills2 -= 25;
- if( sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE )
- skills2 -= 10;
- }
- if( sc->data[SC_FREEZING] )
- skills2 -= 30;
- if( sc->data[SC_HALLUCINATIONWALK_POSTDELAY] )
- skills2 -= 50;
- if( sc->data[SC_PARALYSE] )
- skills2 -= 10;
- if( sc->data[SC__BODYPAINT] )
- skills2 -= 2 + 5 * sc->data[SC__BODYPAINT]->val1;
- if( sc->data[SC__INVISIBILITY] )
- skills2 -= sc->data[SC__INVISIBILITY]->val2 ;
- if( sc->data[SC__GROOMY] )
- skills2 -= sc->data[SC__GROOMY]->val2;
- if( sc->data[SC_SWINGDANCE] )
- skills2 += sc->data[SC_SWINGDANCE]->val2;
- if( sc->data[SC_DANCEWITHWUG] )
- skills2 += sc->data[SC_DANCEWITHWUG]->val3;
- if( sc->data[SC_GLOOMYDAY] )
- skills2 -= sc->data[SC_GLOOMYDAY]->val3;
- if( sc->data[SC_EARTHDRIVE] )
- skills2 -= 25;
- if( sc->data[SC_GT_CHANGE] )
- skills2 += sc->data[SC_GT_CHANGE]->val3;
- if( sc->data[SC_MELON_BOMB] )
- skills2 -= sc->data[SC_MELON_BOMB]->val1;
- if( sc->data[SC_BOOST500] )
- skills2 += sc->data[SC_BOOST500]->val1;
- if( sc->data[SC_EXTRACT_SALAMINE_JUICE] )
- skills2 += sc->data[SC_EXTRACT_SALAMINE_JUICE]->val1;
- if( sc->data[SC_INCASPDRATE] )
- skills2 += sc->data[SC_INCASPDRATE]->val1;
-
- return ( flag&1? (skills1 + pots) : skills2 );
-}
-#endif
-
-static short status_calc_fix_aspd(struct block_list *bl, struct status_change *sc, int aspd) {
- if (!sc || !sc->count)
- return cap_value(aspd, 0, 2000);
-
- if (!sc->data[SC_QUAGMIRE]) {
- if (sc->data[SC_OVERED_BOOST])
- aspd = 2000 - sc->data[SC_OVERED_BOOST]->val3*10;
- }
-
- if ((sc->data[SC_GUST_OPTION] || sc->data[SC_BLAST_OPTION]
- || sc->data[SC_WILD_STORM_OPTION]))
- aspd -= 50; // +5 ASPD
- if( sc && sc->data[SC_FIGHTINGSPIRIT] && sc->data[SC_FIGHTINGSPIRIT]->val2 )
- aspd -= (bl->type==BL_PC?pc_checkskill((TBL_PC *)bl, RK_RUNEMASTERY):10) / 10 * 40;
-
- return cap_value(aspd, 0, 2000); // will be recap for proper bl anyway
-}
-
-/// Calculates an object's ASPD modifier (alters the base amotion value).
-/// Note that the scale of aspd_rate is 1000 = 100%.
-static short status_calc_aspd_rate(struct block_list *bl, struct status_change *sc, int aspd_rate)
-{
- int i;
-
- if(!sc || !sc->count)
- return cap_value(aspd_rate,0,SHRT_MAX);
-
- if( !sc->data[SC_QUAGMIRE] ){
- int max = 0;
- if(sc->data[SC_STAR_COMFORT])
- max = sc->data[SC_STAR_COMFORT]->val2;
-
- if(sc->data[SC_TWOHANDQUICKEN] &&
- max < sc->data[SC_TWOHANDQUICKEN]->val2)
- max = sc->data[SC_TWOHANDQUICKEN]->val2;
-
- if(sc->data[SC_ONEHAND] &&
- max < sc->data[SC_ONEHAND]->val2)
- max = sc->data[SC_ONEHAND]->val2;
-
- if(sc->data[SC_MERC_QUICKEN] &&
- max < sc->data[SC_MERC_QUICKEN]->val2)
- max = sc->data[SC_MERC_QUICKEN]->val2;
-
- if(sc->data[SC_ADRENALINE2] &&
- max < sc->data[SC_ADRENALINE2]->val3)
- max = sc->data[SC_ADRENALINE2]->val3;
-
- if(sc->data[SC_ADRENALINE] &&
- max < sc->data[SC_ADRENALINE]->val3)
- max = sc->data[SC_ADRENALINE]->val3;
-
- if(sc->data[SC_SPEARQUICKEN] &&
- max < sc->data[SC_SPEARQUICKEN]->val2)
- max = sc->data[SC_SPEARQUICKEN]->val2;
-
- if(sc->data[SC_GATLINGFEVER] &&
- max < sc->data[SC_GATLINGFEVER]->val2)
- max = sc->data[SC_GATLINGFEVER]->val2;
-
- if(sc->data[SC_FLEET] &&
- max < sc->data[SC_FLEET]->val2)
- max = sc->data[SC_FLEET]->val2;
-
- if(sc->data[SC_ASSNCROS] &&
- max < sc->data[SC_ASSNCROS]->val2)
- {
- if (bl->type!=BL_PC)
- max = sc->data[SC_ASSNCROS]->val2;
- else
- switch(((TBL_PC*)bl)->status.weapon)
- {
- case W_BOW:
- case W_REVOLVER:
- case W_RIFLE:
- case W_GATLING:
- case W_SHOTGUN:
- case W_GRENADE:
- break;
- default:
- max = sc->data[SC_ASSNCROS]->val2;
- }
- }
- aspd_rate -= max;
-
- if((sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]))
- aspd_rate -= 300;
- else if(sc->data[SC_MADNESSCANCEL])
- aspd_rate -= 200;
- }
-
- if( sc->data[i=SC_ASPDPOTION3] ||
- sc->data[i=SC_ASPDPOTION2] ||
- sc->data[i=SC_ASPDPOTION1] ||
- sc->data[i=SC_ASPDPOTION0] )
- aspd_rate -= sc->data[i]->val2;
-
- if(sc->data[SC_DONTFORGETME])
- aspd_rate += 10 * sc->data[SC_DONTFORGETME]->val2;
- if(sc->data[SC_LONGING])
- aspd_rate += sc->data[SC_LONGING]->val2;
- if(sc->data[SC_STEELBODY])
- aspd_rate += 250;
- if(sc->data[SC_SKA])
- aspd_rate += 250;
- if(sc->data[SC_DEFENDER])
- aspd_rate += sc->data[SC_DEFENDER]->val4;
- if(sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY)
- aspd_rate += 250;
- if(sc->data[SC_GRAVITATION])
- aspd_rate += sc->data[SC_GRAVITATION]->val2;
- if(sc->data[SC_JOINTBEAT]) {
- if( sc->data[SC_JOINTBEAT]->val2&BREAK_WRIST )
- aspd_rate += 250;
- if( sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE )
- aspd_rate += 100;
- }
- if( sc->data[SC_FREEZING] )
- aspd_rate += 300;
- if( sc->data[SC_HALLUCINATIONWALK_POSTDELAY] )
- aspd_rate += 500;
- if( sc->data[SC_FIGHTINGSPIRIT] && sc->data[SC_FIGHTINGSPIRIT]->val2 )
- aspd_rate -= sc->data[SC_FIGHTINGSPIRIT]->val2;
- if( sc->data[SC_PARALYSE] )
- aspd_rate += 100;
- if( sc->data[SC__BODYPAINT] )
- aspd_rate += 200 + 50 * sc->data[SC__BODYPAINT]->val1;
- if( sc->data[SC__INVISIBILITY] )
- aspd_rate += sc->data[SC__INVISIBILITY]->val2 * 10 ;
- if( sc->data[SC__GROOMY] )
- aspd_rate += sc->data[SC__GROOMY]->val2 * 10;
- if( sc->data[SC_SWINGDANCE] )
- aspd_rate -= sc->data[SC_SWINGDANCE]->val2 * 10;
- if( sc->data[SC_DANCEWITHWUG] )
- aspd_rate -= sc->data[SC_DANCEWITHWUG]->val3 * 10;
- if( sc->data[SC_GLOOMYDAY] )
- aspd_rate += sc->data[SC_GLOOMYDAY]->val3 * 10;
- if( sc->data[SC_EARTHDRIVE] )
- aspd_rate += 250;
- if( sc->data[SC_GT_CHANGE] )
- aspd_rate -= sc->data[SC_GT_CHANGE]->val3 * 10;
- if( sc->data[SC_MELON_BOMB] )
- aspd_rate += sc->data[SC_MELON_BOMB]->val1 * 10;
- if( sc->data[SC_BOOST500] )
- aspd_rate -= sc->data[SC_BOOST500]->val1 *10;
- if( sc->data[SC_EXTRACT_SALAMINE_JUICE] )
- aspd_rate -= sc->data[SC_EXTRACT_SALAMINE_JUICE]->val1 * 10;
- if( sc->data[SC_INCASPDRATE] )
- aspd_rate -= sc->data[SC_INCASPDRATE]->val1 * 10;
- if( sc->data[SC_PAIN_KILLER])
- aspd_rate += sc->data[SC_PAIN_KILLER]->val2 * 10;
- if( sc->data[SC_GOLDENE_FERSE])
- aspd_rate -= sc->data[SC_GOLDENE_FERSE]->val3 * 10;
-
- return (short)cap_value(aspd_rate,0,SHRT_MAX);
-}
-
-static unsigned short status_calc_dmotion(struct block_list *bl, struct status_change *sc, int dmotion)
-{
- if( !sc || !sc->count || map_flag_gvg(bl->m) || map[bl->m].flag.battleground )
- return cap_value(dmotion,0,USHRT_MAX);
- /**
- * It has been confirmed on official servers that MvP mobs have no dmotion even without endure
- **/
- if( sc->data[SC_ENDURE] || ( bl->type == BL_MOB && (((TBL_MOB*)bl)->status.mode&MD_BOSS) ) )
- return 0;
- if( sc->data[SC_CONCENTRATION] )
- return 0;
- if( sc->data[SC_RUN] || sc->data[SC_WUGDASH] )
- return 0;
-
- return (unsigned short)cap_value(dmotion,0,USHRT_MAX);
-}
-
-static unsigned int status_calc_maxhp(struct block_list *bl, struct status_change *sc, uint64 maxhp)
-{
- if(!sc || !sc->count)
- return (unsigned int)cap_value(maxhp,1,UINT_MAX);
-
- if(sc->data[SC_INCMHPRATE])
- maxhp += maxhp * sc->data[SC_INCMHPRATE]->val1/100;
- if(sc->data[SC_INCMHP])
- maxhp += (sc->data[SC_INCMHP]->val1);
- if(sc->data[SC_APPLEIDUN])
- maxhp += maxhp * sc->data[SC_APPLEIDUN]->val2/100;
- if(sc->data[SC_DELUGE])
- maxhp += maxhp * sc->data[SC_DELUGE]->val2/100;
- if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
- maxhp += maxhp * 2;
- if(sc->data[SC_MARIONETTE])
- maxhp -= 1000;
- if(sc->data[SC_SOLID_SKIN_OPTION])
- maxhp += 2000;// Fix amount.
- if(sc->data[SC_POWER_OF_GAIA])
- maxhp += 3000;
- if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2)
- maxhp += 500;
-
- if(sc->data[SC_MERC_HPUP])
- maxhp += maxhp * sc->data[SC_MERC_HPUP]->val2/100;
-
- if(sc->data[SC_EPICLESIS])
- maxhp += maxhp * 5 * sc->data[SC_EPICLESIS]->val1 / 100;
- if(sc->data[SC_VENOMBLEED])
- maxhp -= maxhp * 15 / 100;
- if(sc->data[SC__WEAKNESS])
- maxhp -= maxhp * sc->data[SC__WEAKNESS]->val2 / 100;
- if(sc->data[SC_LERADSDEW])
- maxhp += maxhp * sc->data[SC_LERADSDEW]->val3 / 100;
- if(sc->data[SC_FORCEOFVANGUARD])
- maxhp += maxhp * 3 * sc->data[SC_FORCEOFVANGUARD]->val1 / 100;
- if(sc->data[SC_INSPIRATION]) //Custom value.
- maxhp += maxhp * 3 * sc->data[SC_INSPIRATION]->val1 / 100;
- if(sc->data[SC_RAISINGDRAGON])
- maxhp += maxhp * (2 + sc->data[SC_RAISINGDRAGON]->val1) / 100;
- if(sc->data[SC_GT_CHANGE]) // Max HP decrease: [Skill Level x 4] %
- maxhp -= maxhp * (4 * sc->data[SC_GT_CHANGE]->val1) / 100;
- if(sc->data[SC_GT_REVITALIZE])// Max HP increase: [Skill Level x 2] %
- maxhp += maxhp * (2 * sc->data[SC_GT_REVITALIZE]->val1) / 100;
- if(sc->data[SC_MUSTLE_M])
- maxhp += maxhp * sc->data[SC_MUSTLE_M]->val1/100;
- if(sc->data[SC_MYSTERIOUS_POWDER])
- maxhp -= sc->data[SC_MYSTERIOUS_POWDER]->val1 / 100;
- if(sc->data[SC_PETROLOGY_OPTION])
- maxhp += maxhp * sc->data[SC_PETROLOGY_OPTION]->val2 / 100;
- if (sc->data[SC_ANGRIFFS_MODUS])
- maxhp += maxhp * 5 * sc->data[SC_ANGRIFFS_MODUS]->val1 /100;
- if (sc->data[SC_GOLDENE_FERSE])
- maxhp += maxhp * sc->data[SC_GOLDENE_FERSE]->val2 / 100;
-
- return (unsigned int)cap_value(maxhp,1,UINT_MAX);
-}
-
-static unsigned int status_calc_maxsp(struct block_list *bl, struct status_change *sc, unsigned int maxsp)
-{
- if(!sc || !sc->count)
- return cap_value(maxsp,1,UINT_MAX);
-
- if(sc->data[SC_INCMSPRATE])
- maxsp += maxsp * sc->data[SC_INCMSPRATE]->val1/100;
- if(sc->data[SC_INCMSP])
- maxsp += (sc->data[SC_INCMSP]->val1);
- if(sc->data[SC_SERVICE4U])
- maxsp += maxsp * sc->data[SC_SERVICE4U]->val2/100;
- if(sc->data[SC_MERC_SPUP])
- maxsp += maxsp * sc->data[SC_MERC_SPUP]->val2/100;
- if(sc->data[SC_RAISINGDRAGON])
- maxsp += maxsp * (2 + sc->data[SC_RAISINGDRAGON]->val1) / 100;
- if(sc->data[SC_LIFE_FORCE_F])
- maxsp += maxsp * sc->data[SC_LIFE_FORCE_F]->val1/100;
- if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3)
- maxsp += 50;
-
- return cap_value(maxsp,1,UINT_MAX);
-}
-
-static unsigned char status_calc_element(struct block_list *bl, struct status_change *sc, int element)
-{
- if(!sc || !sc->count)
- return element;
-
- if(sc->data[SC_FREEZE])
- return ELE_WATER;
- if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
- return ELE_EARTH;
- if(sc->data[SC_BENEDICTIO])
- return ELE_HOLY;
- if(sc->data[SC_CHANGEUNDEAD])
- return ELE_UNDEAD;
- if(sc->data[SC_ELEMENTALCHANGE])
- return sc->data[SC_ELEMENTALCHANGE]->val2;
- if(sc->data[SC_SHAPESHIFT])
- return sc->data[SC_SHAPESHIFT]->val2;
-
- return (unsigned char)cap_value(element,0,UCHAR_MAX);
-}
-
-static unsigned char status_calc_element_lv(struct block_list *bl, struct status_change *sc, int lv)
-{
- if(!sc || !sc->count)
- return lv;
-
- if(sc->data[SC_FREEZE])
- return 1;
- if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
- return 1;
- if(sc->data[SC_BENEDICTIO])
- return 1;
- if(sc->data[SC_CHANGEUNDEAD])
- return 1;
- if(sc->data[SC_ELEMENTALCHANGE])
- return sc->data[SC_ELEMENTALCHANGE]->val1;
- if(sc->data[SC_SHAPESHIFT])
- return 1;
- if(sc->data[SC__INVISIBILITY])
- return 1;
-
- return (unsigned char)cap_value(lv,1,4);
-}
-
-
-unsigned char status_calc_attack_element(struct block_list *bl, struct status_change *sc, int element)
-{
- if(!sc || !sc->count)
- return element;
- if(sc->data[SC_ENCHANTARMS])
- return sc->data[SC_ENCHANTARMS]->val2;
- if(sc->data[SC_WATERWEAPON]
- || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2) )
- return ELE_WATER;
- if(sc->data[SC_EARTHWEAPON]
- || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2) )
- return ELE_EARTH;
- if(sc->data[SC_FIREWEAPON]
- || (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2) )
- return ELE_FIRE;
- if(sc->data[SC_WINDWEAPON]
- || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 2) )
- return ELE_WIND;
- if(sc->data[SC_ENCPOISON])
- return ELE_POISON;
- if(sc->data[SC_ASPERSIO])
- return ELE_HOLY;
- if(sc->data[SC_SHADOWWEAPON])
- return ELE_DARK;
- if(sc->data[SC_GHOSTWEAPON] || sc->data[SC__INVISIBILITY])
- return ELE_GHOST;
- if(sc->data[SC_TIDAL_WEAPON_OPTION] || sc->data[SC_TIDAL_WEAPON] )
- return ELE_WATER;
- if(sc->data[SC_PYROCLASTIC])
- return ELE_FIRE;
- return (unsigned char)cap_value(element,0,UCHAR_MAX);
-}
-
-static unsigned short status_calc_mode(struct block_list *bl, struct status_change *sc, int mode)
-{
- if(!sc || !sc->count)
- return mode;
- if(sc->data[SC_MODECHANGE]) {
- if (sc->data[SC_MODECHANGE]->val2)
- mode = sc->data[SC_MODECHANGE]->val2; //Set mode
- if (sc->data[SC_MODECHANGE]->val3)
- mode|= sc->data[SC_MODECHANGE]->val3; //Add mode
- if (sc->data[SC_MODECHANGE]->val4)
- mode&=~sc->data[SC_MODECHANGE]->val4; //Del mode
- }
- return cap_value(mode,0,USHRT_MAX);
-}
-
-const char* status_get_name(struct block_list *bl) {
- nullpo_ret(bl);
- switch (bl->type) {
- case BL_PC: return ((TBL_PC *)bl)->fakename[0] != '\0' ? ((TBL_PC*)bl)->fakename : ((TBL_PC*)bl)->status.name;
- case BL_MOB: return ((TBL_MOB*)bl)->name;
- case BL_PET: return ((TBL_PET*)bl)->pet.name;
- case BL_HOM: return ((TBL_HOM*)bl)->homunculus.name;
- case BL_NPC: return ((TBL_NPC*)bl)->name;
- }
- return "Unknown";
-}
-
-/*==========================================
- * Get the class of the current bl
- * return
- * 0 = fail
- * class_id = success
- *------------------------------------------*/
-int status_get_class(struct block_list *bl) {
- nullpo_ret(bl);
- switch( bl->type ) {
- case BL_PC: return ((TBL_PC*)bl)->status.class_;
- case BL_MOB: return ((TBL_MOB*)bl)->vd->class_; //Class used on all code should be the view class of the mob.
- case BL_PET: return ((TBL_PET*)bl)->pet.class_;
- case BL_HOM: return ((TBL_HOM*)bl)->homunculus.class_;
- case BL_MER: return ((TBL_MER*)bl)->mercenary.class_;
- case BL_NPC: return ((TBL_NPC*)bl)->class_;
- case BL_ELEM: return ((TBL_ELEM*)bl)->elemental.class_;
- }
- return 0;
-}
-/*==========================================
- * Get the base level of the current bl
- * return
- * 1 = fail
- * level = success
- *------------------------------------------*/
-int status_get_lv(struct block_list *bl) {
- nullpo_ret(bl);
- switch (bl->type) {
- case BL_PC: return ((TBL_PC*)bl)->status.base_level;
- case BL_MOB: return ((TBL_MOB*)bl)->level;
- case BL_PET: return ((TBL_PET*)bl)->pet.level;
- case BL_HOM: return ((TBL_HOM*)bl)->homunculus.level;
- case BL_MER: return ((TBL_MER*)bl)->db->lv;
- case BL_ELEM: return ((TBL_ELEM*)bl)->db->lv;
- case BL_NPC: return ((TBL_NPC*)bl)->level;
- }
- return 1;
-}
-
-struct regen_data *status_get_regen_data(struct block_list *bl)
-{
- nullpo_retr(NULL, bl);
- switch (bl->type) {
- case BL_PC: return &((TBL_PC*)bl)->regen;
- case BL_HOM: return &((TBL_HOM*)bl)->regen;
- case BL_MER: return &((TBL_MER*)bl)->regen;
- case BL_ELEM: return &((TBL_ELEM*)bl)->regen;
- default:
- return NULL;
- }
-}
-
-struct status_data *status_get_status_data(struct block_list *bl)
-{
- nullpo_retr(&dummy_status, bl);
-
- switch (bl->type) {
- case BL_PC: return &((TBL_PC*)bl)->battle_status;
- case BL_MOB: return &((TBL_MOB*)bl)->status;
- case BL_PET: return &((TBL_PET*)bl)->status;
- case BL_HOM: return &((TBL_HOM*)bl)->battle_status;
- case BL_MER: return &((TBL_MER*)bl)->battle_status;
- case BL_ELEM: return &((TBL_ELEM*)bl)->battle_status;
- case BL_NPC: return ((mobdb_checkid(((TBL_NPC*)bl)->class_) == 0) ? &((TBL_NPC*)bl)->status : &dummy_status);
- default:
- return &dummy_status;
- }
-}
-
-struct status_data *status_get_base_status(struct block_list *bl)
-{
- nullpo_retr(NULL, bl);
- switch (bl->type) {
- case BL_PC: return &((TBL_PC*)bl)->base_status;
- case BL_MOB: return ((TBL_MOB*)bl)->base_status ? ((TBL_MOB*)bl)->base_status : &((TBL_MOB*)bl)->db->status;
- case BL_PET: return &((TBL_PET*)bl)->db->status;
- case BL_HOM: return &((TBL_HOM*)bl)->base_status;
- case BL_MER: return &((TBL_MER*)bl)->base_status;
- case BL_ELEM: return &((TBL_ELEM*)bl)->base_status;
- case BL_NPC: return ((mobdb_checkid(((TBL_NPC*)bl)->class_) == 0) ? &((TBL_NPC*)bl)->status : NULL);
- default:
- return NULL;
- }
-}
-defType status_get_def(struct block_list *bl) {
- struct unit_data *ud;
- struct status_data *status = status_get_status_data(bl);
- int def = status?status->def:0;
- ud = unit_bl2ud(bl);
- if (ud && ud->skilltimer != INVALID_TIMER)
- def -= def * skill_get_castdef(ud->skill_id)/100;
-
- return cap_value(def, DEFTYPE_MIN, DEFTYPE_MAX);
-}
-
-unsigned short status_get_speed(struct block_list *bl)
-{
- if(bl->type==BL_NPC)//Only BL with speed data but no status_data [Skotlex]
- return ((struct npc_data *)bl)->speed;
- return status_get_status_data(bl)->speed;
-}
-
-int status_get_party_id(struct block_list *bl) {
- nullpo_ret(bl);
- switch (bl->type) {
- case BL_PC:
- return ((TBL_PC*)bl)->status.party_id;
- case BL_PET:
- if (((TBL_PET*)bl)->msd)
- return ((TBL_PET*)bl)->msd->status.party_id;
- break;
- case BL_MOB: {
- struct mob_data *md=(TBL_MOB*)bl;
- if( md->master_id > 0 ) {
- struct map_session_data *msd;
- if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
- return msd->status.party_id;
- return -md->master_id;
- }
- }
- break;
- case BL_HOM:
- if (((TBL_HOM*)bl)->master)
- return ((TBL_HOM*)bl)->master->status.party_id;
- break;
- case BL_MER:
- if (((TBL_MER*)bl)->master)
- return ((TBL_MER*)bl)->master->status.party_id;
- break;
- case BL_SKILL:
- return ((TBL_SKILL*)bl)->group->party_id;
- case BL_ELEM:
- if (((TBL_ELEM*)bl)->master)
- return ((TBL_ELEM*)bl)->master->status.party_id;
- break;
- }
- return 0;
-}
-
-int status_get_guild_id(struct block_list *bl) {
- nullpo_ret(bl);
- switch (bl->type) {
- case BL_PC:
- return ((TBL_PC*)bl)->status.guild_id;
- case BL_PET:
- if (((TBL_PET*)bl)->msd)
- return ((TBL_PET*)bl)->msd->status.guild_id;
- break;
- case BL_MOB: {
- struct map_session_data *msd;
- struct mob_data *md = (struct mob_data *)bl;
- if (md->guardian_data) //Guardian's guild [Skotlex]
- return md->guardian_data->guild_id;
- if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
- return msd->status.guild_id; //Alchemist's mobs [Skotlex]
- }
- break;
- case BL_HOM:
- if (((TBL_HOM*)bl)->master)
- return ((TBL_HOM*)bl)->master->status.guild_id;
- break;
- case BL_MER:
- if (((TBL_MER*)bl)->master)
- return ((TBL_MER*)bl)->master->status.guild_id;
- break;
- case BL_NPC:
- if (((TBL_NPC*)bl)->subtype == SCRIPT)
- return ((TBL_NPC*)bl)->u.scr.guild_id;
- break;
- case BL_SKILL:
- return ((TBL_SKILL*)bl)->group->guild_id;
- case BL_ELEM:
- if (((TBL_ELEM*)bl)->master)
- return ((TBL_ELEM*)bl)->master->status.guild_id;
- break;
- }
- return 0;
-}
-
-int status_get_emblem_id(struct block_list *bl) {
- nullpo_ret(bl);
- switch (bl->type) {
- case BL_PC:
- return ((TBL_PC*)bl)->guild_emblem_id;
- case BL_PET:
- if (((TBL_PET*)bl)->msd)
- return ((TBL_PET*)bl)->msd->guild_emblem_id;
- break;
- case BL_MOB: {
- struct map_session_data *msd;
- struct mob_data *md = (struct mob_data *)bl;
- if (md->guardian_data) //Guardian's guild [Skotlex]
- return md->guardian_data->emblem_id;
- if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
- return msd->guild_emblem_id; //Alchemist's mobs [Skotlex]
- }
- break;
- case BL_HOM:
- if (((TBL_HOM*)bl)->master)
- return ((TBL_HOM*)bl)->master->guild_emblem_id;
- break;
- case BL_MER:
- if (((TBL_MER*)bl)->master)
- return ((TBL_MER*)bl)->master->guild_emblem_id;
- break;
- case BL_NPC:
- if (((TBL_NPC*)bl)->subtype == SCRIPT && ((TBL_NPC*)bl)->u.scr.guild_id > 0) {
- struct guild *g = guild_search(((TBL_NPC*)bl)->u.scr.guild_id);
- if (g)
- return g->emblem_id;
- }
- break;
- case BL_ELEM:
- if (((TBL_ELEM*)bl)->master)
- return ((TBL_ELEM*)bl)->master->guild_emblem_id;
- break;
- }
- return 0;
-}
-
-int status_get_mexp(struct block_list *bl)
-{
- nullpo_ret(bl);
- if(bl->type==BL_MOB)
- return ((struct mob_data *)bl)->db->mexp;
- if(bl->type==BL_PET)
- return ((struct pet_data *)bl)->db->mexp;
- return 0;
-}
-int status_get_race2(struct block_list *bl)
-{
- nullpo_ret(bl);
- if(bl->type == BL_MOB)
- return ((struct mob_data *)bl)->db->race2;
- if(bl->type==BL_PET)
- return ((struct pet_data *)bl)->db->race2;
- return 0;
-}
-
-int status_isdead(struct block_list *bl)
-{
- nullpo_ret(bl);
- return status_get_status_data(bl)->hp == 0;
-}
-
-int status_isimmune(struct block_list *bl)
-{
- struct status_change *sc =status_get_sc(bl);
- if (sc && sc->data[SC_HERMODE])
- return 100;
-
- if (bl->type == BL_PC &&
- ((TBL_PC*)bl)->special_state.no_magic_damage >= battle_config.gtb_sc_immunity)
- return ((TBL_PC*)bl)->special_state.no_magic_damage;
- return 0;
-}
-
-struct view_data* status_get_viewdata(struct block_list *bl)
-{
- nullpo_retr(NULL, bl);
- switch (bl->type) {
- case BL_PC: return &((TBL_PC*)bl)->vd;
- case BL_MOB: return ((TBL_MOB*)bl)->vd;
- case BL_PET: return &((TBL_PET*)bl)->vd;
- case BL_NPC: return ((TBL_NPC*)bl)->vd;
- case BL_HOM: return ((TBL_HOM*)bl)->vd;
- case BL_MER: return ((TBL_MER*)bl)->vd;
- case BL_ELEM: return ((TBL_ELEM*)bl)->vd;
- }
- return NULL;
-}
-
-void status_set_viewdata(struct block_list *bl, int class_)
-{
- struct view_data* vd;
- nullpo_retv(bl);
- if (mobdb_checkid(class_) || mob_is_clone(class_))
- vd = mob_get_viewdata(class_);
- else if (npcdb_checkid(class_) || (bl->type == BL_NPC && class_ == WARP_CLASS))
- vd = npc_get_viewdata(class_);
- else if (homdb_checkid(class_))
- vd = merc_get_hom_viewdata(class_);
- else if (merc_class(class_))
- vd = merc_get_viewdata(class_);
- else if (elemental_class(class_))
- vd = elemental_get_viewdata(class_);
- else
- vd = NULL;
-
- switch (bl->type) {
- case BL_PC:
- {
- TBL_PC* sd = (TBL_PC*)bl;
- if (pcdb_checkid(class_)) {
- if (sd->sc.option&OPTION_WEDDING)
- class_ = JOB_WEDDING;
- else if (sd->sc.option&OPTION_SUMMER)
- class_ = JOB_SUMMER;
- else if (sd->sc.option&OPTION_XMAS)
- class_ = JOB_XMAS;
- else if (sd->sc.option&OPTION_RIDING) {
- switch (class_) { //Adapt class to a Mounted one.
- case JOB_KNIGHT:
- class_ = JOB_KNIGHT2;
- break;
- case JOB_CRUSADER:
- class_ = JOB_CRUSADER2;
- break;
- case JOB_LORD_KNIGHT:
- class_ = JOB_LORD_KNIGHT2;
- break;
- case JOB_PALADIN:
- class_ = JOB_PALADIN2;
- break;
- case JOB_BABY_KNIGHT:
- class_ = JOB_BABY_KNIGHT2;
- break;
- case JOB_BABY_CRUSADER:
- class_ = JOB_BABY_CRUSADER2;
- break;
- }
- }
- sd->vd.class_ = class_;
- clif_get_weapon_view(sd, &sd->vd.weapon, &sd->vd.shield);
- sd->vd.head_top = sd->status.head_top;
- sd->vd.head_mid = sd->status.head_mid;
- sd->vd.head_bottom = sd->status.head_bottom;
- sd->vd.hair_style = cap_value(sd->status.hair,0,battle_config.max_hair_style);
- sd->vd.hair_color = cap_value(sd->status.hair_color,0,battle_config.max_hair_color);
- sd->vd.cloth_color = cap_value(sd->status.clothes_color,0,battle_config.max_cloth_color);
- sd->vd.sex = sd->status.sex;
- } else if (vd)
- memcpy(&sd->vd, vd, sizeof(struct view_data));
- else
- ShowError("status_set_viewdata (PC): No view data for class %d\n", class_);
- }
- break;
- case BL_MOB:
- {
- TBL_MOB* md = (TBL_MOB*)bl;
- if (vd)
- md->vd = vd;
- else
- ShowError("status_set_viewdata (MOB): No view data for class %d\n", class_);
- }
- break;
- case BL_PET:
- {
- TBL_PET* pd = (TBL_PET*)bl;
- if (vd) {
- memcpy(&pd->vd, vd, sizeof(struct view_data));
- if (!pcdb_checkid(vd->class_)) {
- pd->vd.hair_style = battle_config.pet_hair_style;
- if(pd->pet.equip) {
- pd->vd.head_bottom = itemdb_viewid(pd->pet.equip);
- if (!pd->vd.head_bottom)
- pd->vd.head_bottom = pd->pet.equip;
- }
- }
- } else
- ShowError("status_set_viewdata (PET): No view data for class %d\n", class_);
- }
- break;
- case BL_NPC:
- {
- TBL_NPC* nd = (TBL_NPC*)bl;
- if (vd)
- nd->vd = vd;
- else
- ShowError("status_set_viewdata (NPC): No view data for class %d\n", class_);
- }
- break;
- case BL_HOM: //[blackhole89]
- {
- struct homun_data *hd = (struct homun_data*)bl;
- if (vd)
- hd->vd = vd;
- else
- ShowError("status_set_viewdata (HOMUNCULUS): No view data for class %d\n", class_);
- }
- break;
- case BL_MER:
- {
- struct mercenary_data *md = (struct mercenary_data*)bl;
- if (vd)
- md->vd = vd;
- else
- ShowError("status_set_viewdata (MERCENARY): No view data for class %d\n", class_);
- }
- break;
- case BL_ELEM:
- {
- struct elemental_data *ed = (struct elemental_data*)bl;
- if (vd)
- ed->vd = vd;
- else
- ShowError("status_set_viewdata (ELEMENTAL): No view data for class %d\n", class_);
- }
- break;
- }
- vd = status_get_viewdata(bl);
- if (vd && vd->cloth_color && (
- (vd->class_==JOB_WEDDING && battle_config.wedding_ignorepalette)
- || (vd->class_==JOB_XMAS && battle_config.xmas_ignorepalette)
- || (vd->class_==JOB_SUMMER && battle_config.summer_ignorepalette)
- ))
- vd->cloth_color = 0;
-}
-
-/// Returns the status_change data of bl or NULL if it doesn't exist.
-struct status_change *status_get_sc(struct block_list *bl) {
- if( bl )
- switch (bl->type) {
- case BL_PC: return &((TBL_PC*)bl)->sc;
- case BL_MOB: return &((TBL_MOB*)bl)->sc;
- case BL_NPC: return &((TBL_NPC*)bl)->sc;
- case BL_HOM: return &((TBL_HOM*)bl)->sc;
- case BL_MER: return &((TBL_MER*)bl)->sc;
- case BL_ELEM: return &((TBL_ELEM*)bl)->sc;
- }
- return NULL;
-}
-
-void status_change_init(struct block_list *bl)
-{
- struct status_change *sc = status_get_sc(bl);
- nullpo_retv(sc);
- memset(sc, 0, sizeof (struct status_change));
-}
-
-//Applies SC defense to a given status change.
-//Returns the adjusted duration based on flag values.
-//the flag values are the same as in status_change_start.
-int status_get_sc_def(struct block_list *bl, enum sc_type type, int rate, int tick, int flag)
-{
- int sc_def = 0, tick_def = 0;
- struct status_data* status;
- struct status_change* sc;
- struct map_session_data *sd;
-
- nullpo_ret(bl);
-
- //Status that are blocked by Golden Thief Bug card or Wand of Hermod
- if (status_isimmune(bl))
- switch (type) {
- case SC_DECREASEAGI:
- case SC_SILENCE:
- case SC_COMA:
- case SC_INCREASEAGI:
- case SC_BLESSING:
- case SC_SLOWPOISON:
- case SC_IMPOSITIO:
- case SC_AETERNA:
- case SC_SUFFRAGIUM:
- case SC_BENEDICTIO:
- case SC_PROVIDENCE:
- case SC_KYRIE:
- case SC_ASSUMPTIO:
- case SC_ANGELUS:
- case SC_MAGNIFICAT:
- case SC_GLORIA:
- case SC_WINDWALK:
- case SC_MAGICROD:
- case SC_HALLUCINATION:
- case SC_STONE:
- case SC_QUAGMIRE:
- case SC_SUITON:
- case SC_SWINGDANCE:
- case SC__ENERVATION:
- case SC__GROOMY:
- case SC__IGNORANCE:
- case SC__LAZINESS:
- case SC__UNLUCKY:
- case SC__WEAKNESS:
- case SC__BLOODYLUST:
- return 0;
- }
-
- sd = BL_CAST(BL_PC,bl);
- status = status_get_status_data(bl);
- sc = status_get_sc(bl);
- if( sc && !sc->count )
- sc = NULL;
- switch (type) {
- case SC_STUN:
- case SC_POISON:
- if( sc && sc->data[SC__UNLUCKY] )
- return tick;
- case SC_DPOISON:
- case SC_SILENCE:
- case SC_BLEEDING:
- sc_def = 3 +status->vit;
- break;
- case SC_SLEEP:
- sc_def = 3 +status->int_;
- break;
- case SC_DEEPSLEEP:
- tick_def = status->int_ / 10 + status_get_lv(bl) * 65 / 1000; // Seems to be -1 sec every 10 int and -5% chance every 10 int.
- sc_def = 5 * status->int_ /10;
- break;
- case SC_DECREASEAGI:
- case SC_ADORAMUS://Arch Bishop
- if (sd) tick>>=1; //Half duration for players.
- case SC_STONE:
- case SC_FREEZE:
- sc_def = 3 +status->mdef;
- break;
- case SC_CURSE:
- //Special property: inmunity when luk is greater than level or zero
- if (status->luk > status_get_lv(bl) || status->luk == 0)
- return 0;
- else
- sc_def = 3 +status->luk;
- tick_def = status->vit;
- break;
- case SC_BLIND:
- if( sc && sc->data[SC__UNLUCKY] )
- return tick;
- sc_def = 3 +(status->vit + status->int_)/2;
- break;
- case SC_CONFUSION:
- sc_def = 3 +(status->str + status->int_)/2;
- break;
- case SC_ANKLE:
- if(status->mode&MD_BOSS) // Lasts 5 times less on bosses
- tick /= 5;
- sc_def = status->agi / 2;
- break;
- case SC_MAGICMIRROR:
- case SC_ARMORCHANGE:
- if (sd) //Duration greatly reduced for players.
- tick /= 15;
- //No defense against it (buff).
- rate -= (status_get_lv(bl) / 5 + status->vit / 4 + status->agi / 10)*100; // Lineal Reduction of Rate
- break;
- case SC_MARSHOFABYSS:
- //5 second (Fixed) + 25 second - {( INT + LUK ) / 20 second }
- tick -= (status->int_ + status->luk) / 20 * 1000;
- break;
- case SC_STASIS:
- //5 second (fixed) + { Stasis Skill level * 5 - (Target�s VIT + DEX) / 20 }
- tick -= (status->vit + status->dex) / 20 * 1000;
- break;
- case SC_WHITEIMPRISON:
- if( tick == 5000 ) // 100% on caster
- break;
- if( bl->type == BL_PC )
- tick -= (status_get_lv(bl) / 5 + status->vit / 4 + status->agi / 10)*100;
- else
- tick -= (status->vit + status->luk) / 20 * 1000;
- break;
- case SC_BURNING:
- // From iROwiki : http://forums.irowiki.org/showpost.php?p=577240&postcount=583
- tick -= 50*status->luk + 60*status->int_ + 170*status->vit;
- tick = max(tick,10000); // Minimum Duration 10s.
- break;
- case SC_FREEZING:
- tick -= 1000 * ((status->vit + status->dex) / 20);
- tick = max(tick,10000); // Minimum Duration 10s.
- break;
- case SC_OBLIVIONCURSE: // 100% - (100 - 0.8 x INT)
- sc_def = 100 - ( 100 - status->int_* 8 / 10 );
- sc_def = max(sc_def, 5); // minimum of 5%
- break;
- case SC_BITE: // {(Base Success chance) - (Target's AGI / 4)}
- rate -= status->agi*1000/4;
- rate = max(rate,50000); // minimum of 50%
- break;
- case SC_ELECTRICSHOCKER:
- if( bl->type == BL_MOB )
- tick -= 1000 * (status->agi/10);
- break;
- case SC_CRYSTALIZE:
- tick -= (1000*(status->vit/10))+(status_get_lv(bl)/50);
- break;
- case SC_MANDRAGORA:
- sc_def = (status->vit+status->luk)/5;
- break;
- case SC_KYOUGAKU:
- tick -= 30*status->int_;
- break;
- case SC_PARALYSIS:
- tick -= 50 * (status->vit + status->luk); //(1000/20);
- break;
- default:
- //Effect that cannot be reduced? Likely a buff.
- if (!(rnd()%10000 < rate))
- return 0;
- return tick?tick:1;
- }
-
- if (sd) {
-
- if (battle_config.pc_sc_def_rate != 100)
- sc_def = sc_def*battle_config.pc_sc_def_rate/100;
-
- if (sc_def < battle_config.pc_max_sc_def)
- sc_def += (battle_config.pc_max_sc_def - sc_def)*
- status->luk/battle_config.pc_luk_sc_def;
- else
- sc_def = battle_config.pc_max_sc_def;
-
- if (tick_def) {
- if (battle_config.pc_sc_def_rate != 100)
- tick_def = tick_def*battle_config.pc_sc_def_rate/100;
- }
-
- } else {
-
- if (battle_config.mob_sc_def_rate != 100)
- sc_def = sc_def*battle_config.mob_sc_def_rate/100;
-
- if (sc_def < battle_config.mob_max_sc_def)
- sc_def += (battle_config.mob_max_sc_def - sc_def)*
- status->luk/battle_config.mob_luk_sc_def;
- else
- sc_def = battle_config.mob_max_sc_def;
-
- if (tick_def) {
- if (battle_config.mob_sc_def_rate != 100)
- tick_def = tick_def*battle_config.mob_sc_def_rate/100;
- }
- }
-
- if (sc) {
- if (sc->data[SC_SCRESIST])
- sc_def += sc->data[SC_SCRESIST]->val1; //Status resist
- else if (sc->data[SC_SIEGFRIED])
- sc_def += sc->data[SC_SIEGFRIED]->val3; //Status resistance.
- }
-
- //When no tick def, reduction is the same for both.
- if( !tick_def && type != SC_STONE ) //Recent tests show duration of petrify isn't reduced by MDEF. [Inkfish]
- tick_def = sc_def;
-
- //Natural resistance
- if (!(flag&8)) {
- rate -= rate*sc_def/100;
-
- //Item resistance (only applies to rate%)
- if(sd && SC_COMMON_MIN <= type && type <= SC_COMMON_MAX)
- {
- if( sd->reseff[type-SC_COMMON_MIN] > 0 )
- rate -= rate*sd->reseff[type-SC_COMMON_MIN]/10000;
- if( sd->sc.data[SC_COMMONSC_RESIST] )
- rate -= rate*sd->sc.data[SC_COMMONSC_RESIST]->val1/100;
- }
- }
- if (!(rnd()%10000 < rate))
- return 0;
-
- //Why would a status start with no duration? Presume it has
- //duration defined elsewhere.
- if (!tick) return 1;
-
- //Rate reduction
- if (flag&2)
- return tick;
-
- tick -= tick*tick_def/100;
- // Changed to 5 seconds according to recent tests [Playtester]
- if (type == SC_ANKLE && tick < 5000)
- tick = 5000;
- return tick<=0?0:tick;
-}
-
-/*==========================================
- * Starts a status change.
- * 'type' = type, 'val1~4' depend on the type.
- * 'rate' = base success rate. 10000 = 100%
- * 'tick' is base duration
- * 'flag':
- * &1: Cannot be avoided (it has to start)
- * &2: Tick should not be reduced (by vit, luk, lv, etc)
- * &4: sc_data loaded, no value has to be altered.
- * &8: rate should not be reduced
- *------------------------------------------*/
-int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val1,int val2,int val3,int val4,int tick,int flag)
-{
- struct map_session_data *sd = NULL;
- struct status_change* sc;
- struct status_change_entry* sce;
- struct status_data *status;
- struct view_data *vd;
- int opt_flag, calc_flag, undead_flag, val_flag = 0, tick_time = 0;
- bool sc_isnew = true;
-
- nullpo_ret(bl);
- sc = status_get_sc(bl);
- status = status_get_status_data(bl);
-
- if( type <= SC_NONE || type >= SC_MAX )
- {
- ShowError("status_change_start: invalid status change (%d)!\n", type);
- return 0;
- }
-
- if( !sc )
- return 0; //Unable to receive status changes
-
- if( status_isdead(bl) && type != SC_NOCHAT ) // SC_NOCHAT should work even on dead characters
- return 0;
-
- if( bl->type == BL_MOB)
- {
- struct mob_data *md = BL_CAST(BL_MOB,bl);
- if(md && (md->class_ == MOBID_EMPERIUM || mob_is_battleground(md)) && type != SC_SAFETYWALL && type != SC_PNEUMA)
- return 0; //Emperium/BG Monsters can't be afflicted by status changes
- // if(md && mob_is_gvg(md) && status_sc2scb_flag(type)&SCB_MAXHP)
- // return 0; //prevent status addinh hp to gvg mob (like bloodylust=hp*3 etc...
- }
-
- if( sc->data[SC_REFRESH] ) {
- if( type >= SC_COMMON_MIN && type <= SC_COMMON_MAX) // Confirmed.
- return 0; // Immune to status ailements
- switch( type ) {
- case SC_QUAGMIRE://Tester said it protects against this and decrease agi.
- case SC_DECREASEAGI:
- case SC_BURNING:
- case SC_FREEZING:
- //case SC_WHITEIMPRISON://Need confirm. Protected against this in the past. [Rytech]
- case SC_MARSHOFABYSS:
- case SC_TOXIN:
- case SC_PARALYSE:
- case SC_VENOMBLEED:
- case SC_MAGICMUSHROOM:
- case SC_DEATHHURT:
- case SC_PYREXIA:
- case SC_OBLIVIONCURSE:
- case SC_LEECHESEND:
- case SC_CRYSTALIZE: ////08/31/2011 - Class Balance Changes
- case SC_DEEPSLEEP:
- case SC_MANDRAGORA:
- return 0;
- }
- }
- else if( sc->data[SC_INSPIRATION] ) {
- if( type >= SC_COMMON_MIN && type <= SC_COMMON_MAX )
- return 0; // Immune to status ailements
- switch( type ) {
- case SC_DEEPSLEEP:
- case SC_SATURDAYNIGHTFEVER:
- case SC_PYREXIA:
- case SC_DEATHHURT:
- case SC_MAGICMUSHROOM:
- case SC_VENOMBLEED:
- case SC_TOXIN:
- case SC_OBLIVIONCURSE:
- case SC_LEECHESEND:
- case SC__ENERVATION:
- case SC__GROOMY:
- case SC__LAZINESS:
- case SC__UNLUCKY:
- case SC__WEAKNESS:
- case SC__BODYPAINT:
- case SC__IGNORANCE:
- return 0;
- }
- }
-
- sd = BL_CAST(BL_PC, bl);
-
- //Adjust tick according to status resistances
- if( !(flag&(1|4)) )
- {
- tick = status_get_sc_def(bl, type, rate, tick, flag);
- if( !tick ) return 0;
- }
-
- undead_flag = battle_check_undead(status->race,status->def_ele);
- //Check for inmunities / sc fails
- switch (type) {
- case SC_ANGRIFFS_MODUS:
- case SC_GOLDENE_FERSE:
- if ((type==SC_GOLDENE_FERSE && sc->data[SC_ANGRIFFS_MODUS])
- || (type==SC_ANGRIFFS_MODUS && sc->data[SC_GOLDENE_FERSE])
- )
- return 0;
- case SC_STONE:
- if(sc->data[SC_POWER_OF_GAIA])
- return 0;
- case SC_FREEZE:
- //Undead are immune to Freeze/Stone
- if (undead_flag && !(flag&1))
- return 0;
- case SC_DEEPSLEEP:
- case SC_SLEEP:
- case SC_STUN:
- case SC_FREEZING:
- case SC_CRYSTALIZE:
- if (sc->opt1)
- return 0; //Cannot override other opt1 status changes. [Skotlex]
- if((type == SC_FREEZE || type == SC_FREEZING || type == SC_CRYSTALIZE) && sc->data[SC_WARMER])
- return 0; //Immune to Frozen and Freezing status if under Warmer status. [Jobbie]
- break;
-
- //There all like berserk, do not everlap each other
- case SC__BLOODYLUST:
- if(!sd) return 0; //should only affect player
- case SC_BERSERK:
- if (((type == SC_BERSERK) && (sc->data[SC_SATURDAYNIGHTFEVER] || sc->data[SC__BLOODYLUST]))
- || ((type == SC__BLOODYLUST) && (sc->data[SC_SATURDAYNIGHTFEVER] || sc->data[SC_BERSERK]))
- )
- return 0;
- break;
-
- case SC_BURNING:
- if(sc->opt1 || sc->data[SC_FREEZING])
- return 0;
- break;
-
- case SC_SIGNUMCRUCIS:
- //Only affects demons and undead element (but not players)
- if((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC)
- return 0;
- break;
- case SC_AETERNA:
- if( (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) || sc->data[SC_FREEZE] )
- return 0;
- break;
- case SC_KYRIE:
- if (bl->type == BL_MOB)
- return 0;
- break;
- case SC_OVERTHRUST:
- if (sc->data[SC_MAXOVERTHRUST])
- return 0; //Overthrust can't take effect if under Max Overthrust. [Skotlex]
- case SC_MAXOVERTHRUST:
- if( sc->option&OPTION_MADOGEAR )
- return 0;//Overthrust and Overthrust Max cannot be used on Mado Gear [Ind]
- break;
- case SC_ADRENALINE:
- if(sd && !pc_check_weapontype(sd,skill_get_weapontype(BS_ADRENALINE)))
- return 0;
- if (sc->data[SC_QUAGMIRE] ||
- sc->data[SC_DECREASEAGI] ||
- sc->option&OPTION_MADOGEAR //Adrenaline doesn't affect Mado Gear [Ind]
- )
- return 0;
- break;
- case SC_ADRENALINE2:
- if(sd && !pc_check_weapontype(sd,skill_get_weapontype(BS_ADRENALINE2)))
- return 0;
- if (sc->data[SC_QUAGMIRE] ||
- sc->data[SC_DECREASEAGI]
- )
- return 0;
- break;
- case SC_MAGNIFICAT:
- if( sc->option&OPTION_MADOGEAR ) //Mado is immune to magnificat
- return 0;
- break;
- case SC_ONEHAND:
- case SC_MERC_QUICKEN:
- case SC_TWOHANDQUICKEN:
- if(sc->data[SC_DECREASEAGI])
- return 0;
-
- case SC_INCREASEAGI:
- if(sd && pc_issit(sd)){
- pc_setstand(sd);
- }
-
- case SC_CONCENTRATE:
- case SC_SPEARQUICKEN:
- case SC_TRUESIGHT:
- case SC_WINDWALK:
- case SC_CARTBOOST:
- case SC_ASSNCROS:
- if (sc->data[SC_QUAGMIRE])
- return 0;
- if(sc->option&OPTION_MADOGEAR)
- return 0;//Mado is immune to increase agi, wind walk, cart boost, etc (others above) [Ind]
- break;
- case SC_CLOAKING:
- //Avoid cloaking with no wall and low skill level. [Skotlex]
- //Due to the cloaking card, we have to check the wall versus to known
- //skill level rather than the used one. [Skotlex]
- //if (sd && val1 < 3 && skill_check_cloaking(bl,NULL))
- if( sd && pc_checkskill(sd, AS_CLOAKING) < 3 && !skill_check_cloaking(bl,NULL) )
- return 0;
- break;
- case SC_MODECHANGE:
- {
- int mode;
- struct status_data *bstatus = status_get_base_status(bl);
- if (!bstatus) return 0;
- if (sc->data[type])
- { //Pile up with previous values.
- if(!val2) val2 = sc->data[type]->val2;
- val3 |= sc->data[type]->val3;
- val4 |= sc->data[type]->val4;
- }
- mode = val2?val2:bstatus->mode; //Base mode
- if (val4) mode&=~val4; //Del mode
- if (val3) mode|= val3; //Add mode
- if (mode == bstatus->mode) { //No change.
- if (sc->data[type]) //Abort previous status
- return status_change_end(bl, type, INVALID_TIMER);
- return 0;
- }
- }
- break;
- //Strip skills, need to divest something or it fails.
- case SC_STRIPWEAPON:
- if (sd && !(flag&4)) { //apply sc anyway if loading saved sc_data
- int i;
- opt_flag = 0; //Reuse to check success condition.
- if(sd->bonus.unstripable_equip&EQP_WEAPON)
- return 0;
- i = sd->equip_index[EQI_HAND_L];
- if (i>=0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_WEAPON) {
- opt_flag|=1;
- pc_unequipitem(sd,i,3); //L-hand weapon
- }
-
- i = sd->equip_index[EQI_HAND_R];
- if (i>=0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_WEAPON) {
- opt_flag|=2;
- pc_unequipitem(sd,i,3);
- }
- if (!opt_flag) return 0;
- }
- if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
- break;
- case SC_STRIPSHIELD:
- if( val2 == 1 ) val2 = 0; //GX effect. Do not take shield off..
- else
- if (sd && !(flag&4)) {
- int i;
- if(sd->bonus.unstripable_equip&EQP_SHIELD)
- return 0;
- i = sd->equip_index[EQI_HAND_L];
- if ( i < 0 || !sd->inventory_data[i] || sd->inventory_data[i]->type != IT_ARMOR )
- return 0;
- pc_unequipitem(sd,i,3);
- }
- if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
- break;
- case SC_STRIPARMOR:
- if (sd && !(flag&4)) {
- int i;
- if(sd->bonus.unstripable_equip&EQP_ARMOR)
- return 0;
- i = sd->equip_index[EQI_ARMOR];
- if ( i < 0 || !sd->inventory_data[i] )
- return 0;
- pc_unequipitem(sd,i,3);
- }
- if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
- break;
- case SC_STRIPHELM:
- if (sd && !(flag&4)) {
- int i;
- if(sd->bonus.unstripable_equip&EQP_HELM)
- return 0;
- i = sd->equip_index[EQI_HEAD_TOP];
- if ( i < 0 || !sd->inventory_data[i] )
- return 0;
- pc_unequipitem(sd,i,3);
- }
- if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
- break;
- case SC_MERC_FLEEUP:
- case SC_MERC_ATKUP:
- case SC_MERC_HPUP:
- case SC_MERC_SPUP:
- case SC_MERC_HITUP:
- if( bl->type != BL_MER )
- return 0; // Stats only for Mercenaries
- break;
- case SC_STRFOOD:
- if (sc->data[SC_FOOD_STR_CASH] && sc->data[SC_FOOD_STR_CASH]->val1 > val1)
- return 0;
- break;
- case SC_AGIFOOD:
- if (sc->data[SC_FOOD_AGI_CASH] && sc->data[SC_FOOD_AGI_CASH]->val1 > val1)
- return 0;
- break;
- case SC_VITFOOD:
- if (sc->data[SC_FOOD_VIT_CASH] && sc->data[SC_FOOD_VIT_CASH]->val1 > val1)
- return 0;
- break;
- case SC_INTFOOD:
- if (sc->data[SC_FOOD_INT_CASH] && sc->data[SC_FOOD_INT_CASH]->val1 > val1)
- return 0;
- break;
- case SC_DEXFOOD:
- if (sc->data[SC_FOOD_DEX_CASH] && sc->data[SC_FOOD_DEX_CASH]->val1 > val1)
- return 0;
- break;
- case SC_LUKFOOD:
- if (sc->data[SC_FOOD_LUK_CASH] && sc->data[SC_FOOD_LUK_CASH]->val1 > val1)
- return 0;
- break;
- case SC_FOOD_STR_CASH:
- if (sc->data[SC_STRFOOD] && sc->data[SC_STRFOOD]->val1 > val1)
- return 0;
- break;
- case SC_FOOD_AGI_CASH:
- if (sc->data[SC_AGIFOOD] && sc->data[SC_AGIFOOD]->val1 > val1)
- return 0;
- break;
- case SC_FOOD_VIT_CASH:
- if (sc->data[SC_VITFOOD] && sc->data[SC_VITFOOD]->val1 > val1)
- return 0;
- break;
- case SC_FOOD_INT_CASH:
- if (sc->data[SC_INTFOOD] && sc->data[SC_INTFOOD]->val1 > val1)
- return 0;
- break;
- case SC_FOOD_DEX_CASH:
- if (sc->data[SC_DEXFOOD] && sc->data[SC_DEXFOOD]->val1 > val1)
- return 0;
- break;
- case SC_FOOD_LUK_CASH:
- if (sc->data[SC_LUKFOOD] && sc->data[SC_LUKFOOD]->val1 > val1)
- return 0;
- break;
- case SC_CAMOUFLAGE:
- if( sd && pc_checkskill(sd, RA_CAMOUFLAGE) < 3 && !skill_check_camouflage(bl,NULL) )
- return 0;
- break;
- case SC__STRIPACCESSORY:
- if( sd ) {
- int i = -1;
- if( !(sd->bonus.unstripable_equip&EQI_ACC_L) ) {
- i = sd->equip_index[EQI_ACC_L];
- if( i >= 0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_ARMOR )
- pc_unequipitem(sd,i,3); //L-Accessory
- } if( !(sd->bonus.unstripable_equip&EQI_ACC_R) ) {
- i = sd->equip_index[EQI_ACC_R];
- if( i >= 0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_ARMOR )
- pc_unequipitem(sd,i,3); //R-Accessory
- }
- if( i < 0 )
- return 0;
- }
- if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
- break;
- case SC_TOXIN:
- case SC_PARALYSE:
- case SC_VENOMBLEED:
- case SC_MAGICMUSHROOM:
- case SC_DEATHHURT:
- case SC_PYREXIA:
- case SC_OBLIVIONCURSE:
- case SC_LEECHESEND:
- { // it doesn't stack or even renewed
- int i = SC_TOXIN;
- for(; i<= SC_LEECHESEND; i++)
- if(sc->data[i]) return 0;
- }
- break;
- case SC_SATURDAYNIGHTFEVER:
- if (sc->data[SC_BERSERK] || sc->data[SC_INSPIRATION] || sc->data[SC__BLOODYLUST])
- return 0;
- break;
- }
-
- //Check for BOSS resistances
- if(status->mode&MD_BOSS && !(flag&1)) {
- if (type>=SC_COMMON_MIN && type <= SC_COMMON_MAX)
- return 0;
- switch (type) {
- case SC_BLESSING:
- case SC_DECREASEAGI:
- case SC_PROVOKE:
- case SC_COMA:
- case SC_GRAVITATION:
- case SC_SUITON:
- case SC_RICHMANKIM:
- case SC_ROKISWEIL:
- case SC_FOGWALL:
- case SC_FREEZING:
- case SC_BURNING:
- case SC_MARSHOFABYSS:
- case SC_ADORAMUS:
- case SC_PARALYSIS:
- case SC_DEEPSLEEP:
- case SC_CRYSTALIZE:
-
- // Exploit prevention - kRO Fix
- case SC_PYREXIA:
- case SC_DEATHHURT:
- case SC_TOXIN:
- case SC_PARALYSE:
- case SC_VENOMBLEED:
- case SC_MAGICMUSHROOM:
- case SC_OBLIVIONCURSE:
- case SC_LEECHESEND:
-
- // Ranger Effects
- case SC_BITE:
- case SC_ELECTRICSHOCKER:
- case SC_MAGNETICFIELD:
-
- return 0;
- }
- }
-
- //Before overlapping fail, one must check for status cured.
- switch (type) {
- case SC_BLESSING:
- //TO-DO Blessing and Agi up should do 1 damage against players on Undead Status, even on PvM
- //but cannot be plagiarized (this requires aegis investigation on packets and official behavior) [Brainstorm]
- if ((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC) {
- status_change_end(bl, SC_CURSE, INVALID_TIMER);
- if (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
- status_change_end(bl, SC_STONE, INVALID_TIMER);
- }
- break;
- case SC_INCREASEAGI:
- status_change_end(bl, SC_DECREASEAGI, INVALID_TIMER);
- break;
- case SC_QUAGMIRE:
- status_change_end(bl, SC_CONCENTRATE, INVALID_TIMER);
- status_change_end(bl, SC_TRUESIGHT, INVALID_TIMER);
- status_change_end(bl, SC_WINDWALK, INVALID_TIMER);
- //Also blocks the ones below...
- case SC_DECREASEAGI:
- status_change_end(bl, SC_CARTBOOST, INVALID_TIMER);
- //Also blocks the ones below...
- case SC_DONTFORGETME:
- status_change_end(bl, SC_INCREASEAGI, INVALID_TIMER);
- status_change_end(bl, SC_ADRENALINE, INVALID_TIMER);
- status_change_end(bl, SC_ADRENALINE2, INVALID_TIMER);
- status_change_end(bl, SC_SPEARQUICKEN, INVALID_TIMER);
- status_change_end(bl, SC_TWOHANDQUICKEN, INVALID_TIMER);
- status_change_end(bl, SC_ONEHAND, INVALID_TIMER);
- status_change_end(bl, SC_MERC_QUICKEN, INVALID_TIMER);
- status_change_end(bl, SC_ACCELERATION, INVALID_TIMER);
- break;
- case SC_ONEHAND:
- //Removes the Aspd potion effect, as reported by Vicious. [Skotlex]
- status_change_end(bl, SC_ASPDPOTION0, INVALID_TIMER);
- status_change_end(bl, SC_ASPDPOTION1, INVALID_TIMER);
- status_change_end(bl, SC_ASPDPOTION2, INVALID_TIMER);
- status_change_end(bl, SC_ASPDPOTION3, INVALID_TIMER);
- break;
- case SC_MAXOVERTHRUST:
- //Cancels Normal Overthrust. [Skotlex]
- status_change_end(bl, SC_OVERTHRUST, INVALID_TIMER);
- break;
- case SC_KYRIE:
- //Cancels Assumptio
- status_change_end(bl, SC_ASSUMPTIO, INVALID_TIMER);
- break;
- case SC_DELUGE:
- if (sc->data[SC_FOGWALL] && sc->data[SC_BLIND])
- status_change_end(bl, SC_BLIND, INVALID_TIMER);
- break;
- case SC_SILENCE:
- if (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF)
- status_change_end(bl, SC_GOSPEL, INVALID_TIMER);
- break;
- case SC_HIDING:
- status_change_end(bl, SC_CLOSECONFINE, INVALID_TIMER);
- status_change_end(bl, SC_CLOSECONFINE2, INVALID_TIMER);
- break;
- case SC__BLOODYLUST:
- case SC_BERSERK:
- if(battle_config.berserk_cancels_buffs) {
- status_change_end(bl, SC_ONEHAND, INVALID_TIMER);
- status_change_end(bl, SC_TWOHANDQUICKEN, INVALID_TIMER);
- status_change_end(bl, SC_CONCENTRATION, INVALID_TIMER);
- status_change_end(bl, SC_PARRYING, INVALID_TIMER);
- status_change_end(bl, SC_AURABLADE, INVALID_TIMER);
- status_change_end(bl, SC_MERC_QUICKEN, INVALID_TIMER);
- }
-#ifdef RENEWAL
- else {
- status_change_end(bl, SC_TWOHANDQUICKEN, INVALID_TIMER);
- }
-#endif
- break;
- case SC_ASSUMPTIO:
- status_change_end(bl, SC_KYRIE, INVALID_TIMER);
- status_change_end(bl, SC_KAITE, INVALID_TIMER);
- break;
- case SC_KAITE:
- status_change_end(bl, SC_ASSUMPTIO, INVALID_TIMER);
- break;
- case SC_CARTBOOST:
- if(sc->data[SC_DECREASEAGI])
- { //Cancel Decrease Agi, but take no further effect [Skotlex]
- status_change_end(bl, SC_DECREASEAGI, INVALID_TIMER);
- return 0;
- }
- break;
- case SC_FUSION:
- status_change_end(bl, SC_SPIRIT, INVALID_TIMER);
- break;
- case SC_ADJUSTMENT:
- status_change_end(bl, SC_MADNESSCANCEL, INVALID_TIMER);
- break;
- case SC_MADNESSCANCEL:
- status_change_end(bl, SC_ADJUSTMENT, INVALID_TIMER);
- break;
- //NPC_CHANGEUNDEAD will debuff Blessing and Agi Up
- case SC_CHANGEUNDEAD:
- status_change_end(bl, SC_BLESSING, INVALID_TIMER);
- status_change_end(bl, SC_INCREASEAGI, INVALID_TIMER);
- break;
- case SC_STRFOOD:
- status_change_end(bl, SC_FOOD_STR_CASH, INVALID_TIMER);
- break;
- case SC_AGIFOOD:
- status_change_end(bl, SC_FOOD_AGI_CASH, INVALID_TIMER);
- break;
- case SC_VITFOOD:
- status_change_end(bl, SC_FOOD_VIT_CASH, INVALID_TIMER);
- break;
- case SC_INTFOOD:
- status_change_end(bl, SC_FOOD_INT_CASH, INVALID_TIMER);
- break;
- case SC_DEXFOOD:
- status_change_end(bl, SC_FOOD_DEX_CASH, INVALID_TIMER);
- break;
- case SC_LUKFOOD:
- status_change_end(bl, SC_FOOD_LUK_CASH, INVALID_TIMER);
- break;
- case SC_FOOD_STR_CASH:
- status_change_end(bl, SC_STRFOOD, INVALID_TIMER);
- break;
- case SC_FOOD_AGI_CASH:
- status_change_end(bl, SC_AGIFOOD, INVALID_TIMER);
- break;
- case SC_FOOD_VIT_CASH:
- status_change_end(bl, SC_VITFOOD, INVALID_TIMER);
- break;
- case SC_FOOD_INT_CASH:
- status_change_end(bl, SC_INTFOOD, INVALID_TIMER);
- break;
- case SC_FOOD_DEX_CASH:
- status_change_end(bl, SC_DEXFOOD, INVALID_TIMER);
- break;
- case SC_FOOD_LUK_CASH:
- status_change_end(bl, SC_LUKFOOD, INVALID_TIMER);
- break;
- case SC_FIGHTINGSPIRIT:
- status_change_end(bl, type, INVALID_TIMER); // Remove previous one.
- break;
- case SC_MARSHOFABYSS:
- status_change_end(bl, SC_INCAGI, INVALID_TIMER);
- status_change_end(bl, SC_WINDWALK, INVALID_TIMER);
- status_change_end(bl, SC_ASPDPOTION0, INVALID_TIMER);
- status_change_end(bl, SC_ASPDPOTION1, INVALID_TIMER);
- status_change_end(bl, SC_ASPDPOTION2, INVALID_TIMER);
- status_change_end(bl, SC_ASPDPOTION3, INVALID_TIMER);
- break;
- case SC_SWINGDANCE:
- case SC_SYMPHONYOFLOVER:
- case SC_MOONLITSERENADE:
- case SC_RUSHWINDMILL:
- case SC_ECHOSONG:
- case SC_HARMONIZE: //group A doesn't overlap
- if (type != SC_SWINGDANCE) status_change_end(bl, SC_SWINGDANCE, INVALID_TIMER);
- if (type != SC_SYMPHONYOFLOVER) status_change_end(bl, SC_SYMPHONYOFLOVER, INVALID_TIMER);
- if (type != SC_MOONLITSERENADE) status_change_end(bl, SC_MOONLITSERENADE, INVALID_TIMER);
- if (type != SC_RUSHWINDMILL) status_change_end(bl, SC_RUSHWINDMILL, INVALID_TIMER);
- if (type != SC_ECHOSONG) status_change_end(bl, SC_ECHOSONG, INVALID_TIMER);
- if (type != SC_HARMONIZE) status_change_end(bl, SC_HARMONIZE, INVALID_TIMER);
- break;
- case SC_VOICEOFSIREN:
- case SC_DEEPSLEEP:
- case SC_GLOOMYDAY:
- case SC_SONGOFMANA:
- case SC_DANCEWITHWUG:
- case SC_SATURDAYNIGHTFEVER:
- case SC_LERADSDEW:
- case SC_MELODYOFSINK:
- case SC_BEYONDOFWARCRY:
- case SC_UNLIMITEDHUMMINGVOICE: //group B
- if (type != SC_VOICEOFSIREN) status_change_end(bl, SC_VOICEOFSIREN, INVALID_TIMER);
- if (type != SC_DEEPSLEEP) status_change_end(bl, SC_DEEPSLEEP, INVALID_TIMER);
- if (type != SC_LERADSDEW) status_change_end(bl, SC_LERADSDEW, INVALID_TIMER);
- if (type != SC_MELODYOFSINK) status_change_end(bl, SC_MELODYOFSINK, INVALID_TIMER);
- if (type != SC_BEYONDOFWARCRY) status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER);
- if (type != SC_UNLIMITEDHUMMINGVOICE) status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER);
- if (type != SC_GLOOMYDAY) {
- status_change_end(bl, SC_GLOOMYDAY, INVALID_TIMER);
- status_change_end(bl, SC_GLOOMYDAY_SK, INVALID_TIMER);
- }
- if (type != SC_SONGOFMANA) status_change_end(bl, SC_SONGOFMANA, INVALID_TIMER);
- if (type != SC_DANCEWITHWUG) status_change_end(bl, SC_DANCEWITHWUG, INVALID_TIMER);
- if (type != SC_SATURDAYNIGHTFEVER) {
- if (sc->data[SC_SATURDAYNIGHTFEVER]) {
- sc->data[SC_SATURDAYNIGHTFEVER]->val2 = 0; //mark to not lose hp
- status_change_end(bl, SC_SATURDAYNIGHTFEVER, INVALID_TIMER);
- }
- }
- break;
- case SC_REFLECTSHIELD:
- status_change_end(bl, SC_REFLECTDAMAGE, INVALID_TIMER);
- break;
- case SC_REFLECTDAMAGE:
- status_change_end(bl, SC_REFLECTSHIELD, INVALID_TIMER);
- break;
- case SC_SHIELDSPELL_DEF:
- case SC_SHIELDSPELL_MDEF:
- case SC_SHIELDSPELL_REF:
- status_change_end(bl, SC_MAGNIFICAT, INVALID_TIMER);
- if( type != SC_SHIELDSPELL_DEF )
- status_change_end(bl, SC_SHIELDSPELL_DEF, INVALID_TIMER);
- if( type != SC_SHIELDSPELL_MDEF )
- status_change_end(bl, SC_SHIELDSPELL_MDEF, INVALID_TIMER);
- if( type != SC_SHIELDSPELL_REF )
- status_change_end(bl, SC_SHIELDSPELL_REF, INVALID_TIMER);
- break;
- case SC_GT_ENERGYGAIN:
- case SC_GT_CHANGE:
- case SC_GT_REVITALIZE:
- if( type != SC_GT_REVITALIZE )
- status_change_end(bl, SC_GT_REVITALIZE, INVALID_TIMER);
- if( type != SC_GT_ENERGYGAIN )
- status_change_end(bl, SC_GT_ENERGYGAIN, INVALID_TIMER);
- if( type != SC_GT_CHANGE )
- status_change_end(bl, SC_GT_CHANGE, INVALID_TIMER);
- break;
- case SC_INVINCIBLE:
- status_change_end(bl, SC_INVINCIBLEOFF, INVALID_TIMER);
- break;
- case SC_INVINCIBLEOFF:
- status_change_end(bl, SC_INVINCIBLE, INVALID_TIMER);
- break;
- case SC_MAGICPOWER:
- status_change_end(bl, type, INVALID_TIMER);
- break;
- }
-
- //Check for overlapping fails
- if( (sce = sc->data[type]) ) {
- switch( type ) {
- case SC_MERC_FLEEUP:
- case SC_MERC_ATKUP:
- case SC_MERC_HPUP:
- case SC_MERC_SPUP:
- case SC_MERC_HITUP:
- if( sce->val1 > val1 )
- val1 = sce->val1;
- break;
- case SC_ADRENALINE:
- case SC_ADRENALINE2:
- case SC_WEAPONPERFECTION:
- case SC_OVERTHRUST:
- if (sce->val2 > val2)
- return 0;
- break;
- case SC_S_LIFEPOTION:
- case SC_L_LIFEPOTION:
- case SC_BOSSMAPINFO:
- case SC_STUN:
- case SC_SLEEP:
- case SC_POISON:
- case SC_CURSE:
- case SC_SILENCE:
- case SC_CONFUSION:
- case SC_BLIND:
- case SC_BLEEDING:
- case SC_DPOISON:
- case SC_CLOSECONFINE2: //Can't be re-closed in.
- case SC_MARIONETTE:
- case SC_MARIONETTE2:
- case SC_NOCHAT:
- case SC_CHANGE: //Otherwise your Hp/Sp would get refilled while still within effect of the last invocation.
- case SC__INVISIBILITY:
- case SC__ENERVATION:
- case SC__GROOMY:
- case SC__IGNORANCE:
- case SC__LAZINESS:
- case SC__WEAKNESS:
- case SC__UNLUCKY:
- return 0;
- case SC_COMBO:
- case SC_DANCING:
- case SC_DEVOTION:
- case SC_ASPDPOTION0:
- case SC_ASPDPOTION1:
- case SC_ASPDPOTION2:
- case SC_ASPDPOTION3:
- case SC_ATKPOTION:
- case SC_MATKPOTION:
- case SC_ENCHANTARMS:
- case SC_ARMOR_ELEMENT:
- case SC_ARMOR_RESIST:
- break;
- case SC_GOSPEL:
- //Must not override a casting gospel char.
- if(sce->val4 == BCT_SELF)
- return 0;
- if(sce->val1 > val1)
- return 1;
- break;
- case SC_ENDURE:
- if(sce->val4 && !val4)
- return 1; //Don't let you override infinite endure.
- if(sce->val1 > val1)
- return 1;
- break;
- case SC_KAAHI:
- //Kaahi overwrites previous level regardless of existing level.
- //Delete timer if it exists.
- if (sce->val4 != INVALID_TIMER) {
- delete_timer(sce->val4,kaahi_heal_timer);
- sce->val4 = INVALID_TIMER;
- }
- break;
- case SC_JAILED:
- //When a player is already jailed, do not edit the jail data.
- val2 = sce->val2;
- val3 = sce->val3;
- val4 = sce->val4;
- break;
- case SC_LERADSDEW:
- if (sc && (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]))
- return 0;
- case SC_SHAPESHIFT:
- case SC_PROPERTYWALK:
- break;
- case SC_LEADERSHIP:
- case SC_GLORYWOUNDS:
- case SC_SOULCOLD:
- case SC_HAWKEYES:
- if( sce->val4 && !val4 )//you cannot override master guild aura
- return 0;
- break;
- case SC_JOINTBEAT:
- val2 |= sce->val2; // stackable ailments
- default:
- if(sce->val1 > val1)
- return 1; //Return true to not mess up skill animations. [Skotlex]
- }
- }
-
- vd = status_get_viewdata(bl);
- calc_flag = StatusChangeFlagTable[type];
- if(!(flag&4)) //&4 - Do not parse val settings when loading SCs
- switch(type)
- {
- case SC_DECREASEAGI:
- case SC_INCREASEAGI:
- val2 = 2 + val1; //Agi change
- break;
- case SC_ENDURE:
- val2 = 7; // Hit-count [Celest]
- if( !(flag&1) && (bl->type&(BL_PC|BL_MER)) && !map_flag_gvg(bl->m) && !map[bl->m].flag.battleground && !val4 )
- {
- struct map_session_data *tsd;
- if( sd )
- {
- int i;
- for( i = 0; i < 5; i++ )
- {
- if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
- status_change_start(&tsd->bl, type, 10000, val1, val2, val3, val4, tick, 1);
- }
- }
- else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
- status_change_start(&tsd->bl, type, 10000, val1, val2, val3, val4, tick, 1);
- }
- //val4 signals infinite endure (if val4 == 2 it is infinite endure from Berserk)
- if( val4 )
- tick = -1;
- break;
- case SC_AUTOBERSERK:
- if (status->hp < status->max_hp>>2 &&
- (!sc->data[SC_PROVOKE] || sc->data[SC_PROVOKE]->val2==0))
- sc_start4(bl,SC_PROVOKE,100,10,1,0,0,60000);
- tick = -1;
- break;
- case SC_SIGNUMCRUCIS:
- val2 = 10 + 4*val1; //Def reduction
- tick = -1;
- clif_emotion(bl,E_SWT);
- break;
- case SC_MAXIMIZEPOWER:
- tick_time = val2 = tick>0?tick:60000;
- tick = -1; // duration sent to the client should be infinite
- break;
- case SC_EDP: // [Celest]
- val2 = val1 + 2; //Chance to Poison enemies.
-#ifndef RENEWAL_EDP
- val3 = 50*(val1+1); //Damage increase (+50 +50*lv%)
-#endif
- if( sd )//[Ind] - iROwiki says each level increases its duration by 3 seconds
- tick += pc_checkskill(sd,GC_RESEARCHNEWPOISON)*3000;
- break;
- case SC_POISONREACT:
- val2=(val1+1)/2 + val1/10; // Number of counters [Skotlex]
- val3=50; // + 5*val1; //Chance to counter. [Skotlex]
- break;
- case SC_MAGICROD:
- val2 = val1*20; //SP gained
- break;
- case SC_KYRIE:
- val2 = (int64)status->max_hp * (val1 * 2 + 10) / 100; //%Max HP to absorb
- val3 = (val1 / 2 + 5); //Hits
- break;
- case SC_MAGICPOWER:
- //val1: Skill lv
- val2 = 1; //Lasts 1 invocation
- val3 = 5*val1; //Matk% increase
- val4 = 0; // 0 = ready to be used, 1 = activated and running
- break;
- case SC_SACRIFICE:
- val2 = 5; //Lasts 5 hits
- tick = -1;
- break;
- case SC_ENCPOISON:
- val2= 250+50*val1; //Poisoning Chance (2.5+0.5%) in 1/10000 rate
- case SC_ASPERSIO:
- case SC_FIREWEAPON:
- case SC_WATERWEAPON:
- case SC_WINDWEAPON:
- case SC_EARTHWEAPON:
- case SC_SHADOWWEAPON:
- case SC_GHOSTWEAPON:
- skill_enchant_elemental_end(bl,type);
- break;
- case SC_ELEMENTALCHANGE:
- // val1 : Element Lvl (if called by skill lvl 1, takes random value between 1 and 4)
- // val2 : Element (When no element, random one is picked)
- // val3 : 0 = called by skill 1 = called by script (fixed level)
- if( !val2 ) val2 = rnd()%ELE_MAX;
-
- if( val1 == 1 && val3 == 0 )
- val1 = 1 + rnd()%4;
- else if( val1 > 4 )
- val1 = 4; // Max Level
- val3 = 0; // Not need to keep this info.
- break;
- case SC_PROVIDENCE:
- val2=val1*5; //Race/Ele resist
- break;
- case SC_REFLECTSHIELD:
- val2=10+val1*3; // %Dmg reflected
- if( !(flag&1) && (bl->type&(BL_PC|BL_MER)) )
- {
- struct map_session_data *tsd;
- if( sd )
- {
- int i;
- for( i = 0; i < 5; i++ )
- {
- if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
- status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
- }
- }
- else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
- status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
- }
- break;
- case SC_STRIPWEAPON:
- if (!sd) //Watk reduction
- val2 = 25;
- break;
- case SC_STRIPSHIELD:
- if (!sd) //Def reduction
- val2 = 15;
- break;
- case SC_STRIPARMOR:
- if (!sd) //Vit reduction
- val2 = 40;
- break;
- case SC_STRIPHELM:
- if (!sd) //Int reduction
- val2 = 40;
- break;
- case SC_AUTOSPELL:
- //Val1 Skill LV of Autospell
- //Val2 Skill ID to cast
- //Val3 Max Lv to cast
- val4 = 5 + val1*2; //Chance of casting
- break;
- case SC_VOLCANO:
- val2 = val1*10; //Watk increase
-#ifndef RENEWAL
- if (status->def_ele != ELE_FIRE)
- val2 = 0;
-#endif
- break;
- case SC_VIOLENTGALE:
- val2 = val1*3; //Flee increase
- #ifndef RENEWAL
- if (status->def_ele != ELE_WIND)
- val2 = 0;
- #endif
- break;
- case SC_DELUGE:
- val2 = deluge_eff[val1-1]; //HP increase
-#ifndef RENEWAL
- if(status->def_ele != ELE_WATER)
- val2 = 0;
-#endif
- break;
- case SC_SUITON:
- if (!val2 || (sd && (sd->class_&MAPID_UPPERMASK) == MAPID_NINJA)) {
- //No penalties.
- val2 = 0; //Agi penalty
- val3 = 0; //Walk speed penalty
- break;
- }
- val3 = 50;
- val2 = 3*((val1+1)/3);
- if (val1 > 4) val2--;
- break;
- case SC_ONEHAND:
- case SC_TWOHANDQUICKEN:
- val2 = 300;
- if (val1 > 10) //For boss casted skills [Skotlex]
- val2 += 20*(val1-10);
- break;
- case SC_MERC_QUICKEN:
- val2 = 300;
- break;
-#ifndef RENEWAL
- case SC_SPEARQUICKEN:
- val2 = 200+10*val1;
- break;
-#endif
- case SC_DANCING:
- //val1 : Skill ID + LV
- //val2 : Skill Group of the Dance.
- //val3 : Brings the skill_lv (merged into val1 here)
- //val4 : Partner
- if (val1 == CG_MOONLIT)
- clif_status_change(bl,SI_MOONLIT,1,tick,0, 0, 0);
- val1|= (val3<<16);
- val3 = tick/1000; //Tick duration
- tick_time = 1000; // [GodLesZ] tick time
- break;
- case SC_LONGING:
- val2 = 500-100*val1; //Aspd penalty.
- break;
- case SC_EXPLOSIONSPIRITS:
- val2 = 75 + 25*val1; //Cri bonus
- break;
-
- case SC_ASPDPOTION0:
- case SC_ASPDPOTION1:
- case SC_ASPDPOTION2:
- case SC_ASPDPOTION3:
- val2 = 50*(2+type-SC_ASPDPOTION0);
- break;
-
- case SC_WEDDING:
- case SC_XMAS:
- case SC_SUMMER:
- if (!vd) return 0;
- //Store previous values as they could be removed.
- val1 = vd->class_;
- val2 = vd->weapon;
- val3 = vd->shield;
- val4 = vd->cloth_color;
- unit_stop_attack(bl);
- clif_changelook(bl,LOOK_WEAPON,0);
- clif_changelook(bl,LOOK_SHIELD,0);
- clif_changelook(bl,LOOK_BASE,type==SC_WEDDING?JOB_WEDDING:type==SC_XMAS?JOB_XMAS:JOB_SUMMER);
- clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color);
- break;
- case SC_NOCHAT:
- // [GodLesZ] FIXME: is this correct? a hardcoded interval of 60sec? what about configuration ?_?
- tick = 60000;
- val1 = battle_config.manner_system; //Mute filters.
- if (sd)
- {
- clif_changestatus(sd,SP_MANNER,sd->status.manner);
- clif_updatestatus(sd,SP_MANNER);
- }
- break;
-
- case SC_STONE:
- val3 = tick/1000; //Petrified HP-damage iterations.
- if(val3 < 1) val3 = 1;
- tick = val4; //Petrifying time.
- tick = max(tick, 1000); //Min time
- calc_flag = 0; //Actual status changes take effect on petrified state.
- break;
-
- case SC_DPOISON:
- //Lose 10/15% of your life as long as it doesn't brings life below 25%
- if (status->hp > status->max_hp>>2) {
- int diff = status->max_hp*(bl->type==BL_PC?10:15)/100;
- if (status->hp - diff < status->max_hp>>2)
- diff = status->hp - (status->max_hp>>2);
- if( val2 && bl->type == BL_MOB ) {
- struct block_list* src = map_id2bl(val2);
- if( src )
- mob_log_damage((TBL_MOB*)bl,src,diff);
- }
- status_zap(bl, diff, 0);
- }
- // fall through
- case SC_POISON:
- val3 = tick/1000; //Damage iterations
- if(val3 < 1) val3 = 1;
- tick_time = 1000; // [GodLesZ] tick time
- //val4: HP damage
- if (bl->type == BL_PC)
- val4 = (type == SC_DPOISON) ? 3 + status->max_hp/50 : 3 + status->max_hp*3/200;
- else
- val4 = (type == SC_DPOISON) ? 3 + status->max_hp/100 : 3 + status->max_hp/200;
-
- break;
- case SC_CONFUSION:
- clif_emotion(bl,E_WHAT);
- break;
- case SC_BLEEDING:
- val4 = tick/10000;
- if (!val4) val4 = 1;
- tick_time = 10000; // [GodLesZ] tick time
- break;
- case SC_S_LIFEPOTION:
- case SC_L_LIFEPOTION:
- if( val1 == 0 ) return 0;
- // val1 = heal percent/amout
- // val2 = seconds between heals
- // val4 = total of heals
- if( val2 < 1 ) val2 = 1;
- if( (val4 = tick/(val2 * 1000)) < 1 )
- val4 = 1;
- tick_time = val2 * 1000; // [GodLesZ] tick time
- break;
- case SC_BOSSMAPINFO:
- if( sd != NULL )
- {
- struct mob_data *boss_md = map_getmob_boss(bl->m); // Search for Boss on this Map
- if( boss_md == NULL || boss_md->bl.prev == NULL )
- { // No MVP on this map - MVP is dead
- clif_bossmapinfo(sd->fd, boss_md, 1);
- return 0; // No need to start SC
- }
- val1 = boss_md->bl.id;
- if( (val4 = tick/1000) < 1 )
- val4 = 1;
- tick_time = 1000; // [GodLesZ] tick time
- }
- break;
- case SC_HIDING:
- val2 = tick/1000;
- tick_time = 1000; // [GodLesZ] tick time
- val3 = 0; // unused, previously speed adjustment
- val4 = val1+3; //Seconds before SP substraction happen.
- break;
- case SC_CHASEWALK:
- val2 = tick>0?tick:10000; //Interval at which SP is drained.
- val3 = 35 - 5 * val1; //Speed adjustment.
- if (sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_ROGUE)
- val3 -= 40;
- val4 = 10+val1*2; //SP cost.
- if (map_flag_gvg(bl->m) || map[bl->m].flag.battleground) val4 *= 5;
- break;
- case SC_CLOAKING:
- if (!sd) //Monsters should be able to walk with no penalties. [Skotlex]
- val1 = 10;
- tick_time = val2 = tick>0?tick:60000; //SP consumption rate.
- tick = -1; // duration sent to the client should be infinite
- val3 = 0; // unused, previously walk speed adjustment
- //val4&1 signals the presence of a wall.
- //val4&2 makes cloak not end on normal attacks [Skotlex]
- //val4&4 makes cloak not end on using skills
- if (bl->type == BL_PC || (bl->type == BL_MOB && ((TBL_MOB*)bl)->special_state.clone) ) //Standard cloaking.
- val4 |= battle_config.pc_cloak_check_type&7;
- else
- val4 |= battle_config.monster_cloak_check_type&7;
- break;
- case SC_SIGHT: /* splash status */
- case SC_RUWACH:
- case SC_SIGHTBLASTER:
- val3 = skill_get_splash(val2, val1); //Val2 should bring the skill-id.
- val2 = tick/250;
- tick_time = 10; // [GodLesZ] tick time
- break;
-
- //Permanent effects.
- case SC_AETERNA:
- case SC_MODECHANGE:
- case SC_WEIGHT50:
- case SC_WEIGHT90:
- case SC_BROKENWEAPON:
- case SC_BROKENARMOR:
- case SC_READYSTORM:
- case SC_READYDOWN:
- case SC_READYCOUNTER:
- case SC_READYTURN:
- case SC_DODGE:
- case SC_PUSH_CART:
- tick = -1;
- break;
-
- case SC_AUTOGUARD:
- if( !(flag&1) )
- {
- struct map_session_data *tsd;
- int i,t;
- for( i = val2 = 0; i < val1; i++)
- {
- t = 5-(i>>1);
- val2 += (t < 0)? 1:t;
- }
-
- if( bl->type&(BL_PC|BL_MER) )
- {
- if( sd )
- {
- for( i = 0; i < 5; i++ )
- {
- if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
- status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
- }
- }
- else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
- status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
- }
- }
- break;
-
- case SC_DEFENDER:
- if (!(flag&1))
- {
- val2 = 5 + 15*val1; //Damage reduction
- val3 = 0; // unused, previously speed adjustment
- val4 = 250 - 50*val1; //Aspd adjustment
-
- if (sd)
- {
- struct map_session_data *tsd;
- int i;
- for (i = 0; i < 5; i++)
- { //See if there are devoted characters, and pass the status to them. [Skotlex]
- if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])))
- status_change_start(&tsd->bl,type,10000,val1,5+val1*5,val3,val4,tick,1);
- }
- }
- }
- break;
-
- case SC_TENSIONRELAX:
- if (sd) {
- pc_setsit(sd);
- clif_sitting(&sd->bl);
- }
- val2 = 12; //SP cost
- val4 = 10000; //Decrease at 10secs intervals.
- val3 = tick/val4;
- tick = -1; // duration sent to the client should be infinite
- tick_time = val4; // [GodLesZ] tick time
- break;
- case SC_PARRYING:
- val2 = 20 + val1*3; //Block Chance
- break;
-
- case SC_WINDWALK:
- val2 = (val1+1)/2; // Flee bonus is 1/1/2/2/3/3/4/4/5/5
- break;
-
- case SC_JOINTBEAT:
- if( val2&BREAK_NECK )
- sc_start(bl,SC_BLEEDING,100,val1,skill_get_time2(status_sc2skill(type),val1));
- break;
-
- case SC_BERSERK:
- if (!sc->data[SC_ENDURE] || !sc->data[SC_ENDURE]->val4)
- sc_start4(bl, SC_ENDURE, 100,10,0,0,2, tick);
- case SC__BLOODYLUST:
- //HP healing is performing after the calc_status call.
- //Val2 holds HP penalty
- if (!val4) val4 = skill_get_time2(status_sc2skill(type),val1);
- if (!val4) val4 = 10000; //Val4 holds damage interval
- val3 = tick/val4; //val3 holds skill duration
- tick_time = val4; // [GodLesZ] tick time
- break;
-
- case SC_GOSPEL:
- if(val4 == BCT_SELF) { // self effect
- val2 = tick/10000;
- tick_time = 10000; // [GodLesZ] tick time
- status_change_clear_buffs(bl,3); //Remove buffs/debuffs
- }
- break;
-
- case SC_MARIONETTE:
- {
- int stat;
-
- val3 = 0;
- val4 = 0;
- stat = ( sd ? sd->status.str : status_get_base_status(bl)->str ) / 2; val3 |= cap_value(stat,0,0xFF)<<16;
- stat = ( sd ? sd->status.agi : status_get_base_status(bl)->agi ) / 2; val3 |= cap_value(stat,0,0xFF)<<8;
- stat = ( sd ? sd->status.vit : status_get_base_status(bl)->vit ) / 2; val3 |= cap_value(stat,0,0xFF);
- stat = ( sd ? sd->status.int_: status_get_base_status(bl)->int_) / 2; val4 |= cap_value(stat,0,0xFF)<<16;
- stat = ( sd ? sd->status.dex : status_get_base_status(bl)->dex ) / 2; val4 |= cap_value(stat,0,0xFF)<<8;
- stat = ( sd ? sd->status.luk : status_get_base_status(bl)->luk ) / 2; val4 |= cap_value(stat,0,0xFF);
- break;
- }
- case SC_MARIONETTE2:
- {
- int stat,max_stat;
- // fetch caster information
- struct block_list *pbl = map_id2bl(val1);
- struct status_change *psc = pbl?status_get_sc(pbl):NULL;
- struct status_change_entry *psce = psc?psc->data[SC_MARIONETTE]:NULL;
- // fetch target's stats
- struct status_data* status = status_get_status_data(bl); // battle status
-
- if (!psce)
- return 0;
-
- val3 = 0;
- val4 = 0;
- max_stat = battle_config.max_parameter; //Cap to 99 (default)
- stat = (psce->val3 >>16)&0xFF; stat = min(stat, max_stat - status->str ); val3 |= cap_value(stat,0,0xFF)<<16;
- stat = (psce->val3 >> 8)&0xFF; stat = min(stat, max_stat - status->agi ); val3 |= cap_value(stat,0,0xFF)<<8;
- stat = (psce->val3 >> 0)&0xFF; stat = min(stat, max_stat - status->vit ); val3 |= cap_value(stat,0,0xFF);
- stat = (psce->val4 >>16)&0xFF; stat = min(stat, max_stat - status->int_); val4 |= cap_value(stat,0,0xFF)<<16;
- stat = (psce->val4 >> 8)&0xFF; stat = min(stat, max_stat - status->dex ); val4 |= cap_value(stat,0,0xFF)<<8;
- stat = (psce->val4 >> 0)&0xFF; stat = min(stat, max_stat - status->luk ); val4 |= cap_value(stat,0,0xFF);
- break;
- }
- case SC_REJECTSWORD:
- val2 = 15*val1; //Reflect chance
- val3 = 3; //Reflections
- tick = -1;
- break;
-
- case SC_MEMORIZE:
- val2 = 5; //Memorized casts.
- tick = -1;
- break;
-
- case SC_GRAVITATION:
- val2 = 50*val1; //aspd reduction
- break;
-
- case SC_REGENERATION:
- if (val1 == 1)
- val2 = 2;
- else
- val2 = val1; //HP Regerenation rate: 200% 200% 300%
- val3 = val1; //SP Regeneration Rate: 100% 200% 300%
- //if val4 comes set, this blocks regen rather than increase it.
- break;
-
- case SC_DEVOTION:
- {
- struct block_list *d_bl;
- struct status_change *d_sc;
-
- if( (d_bl = map_id2bl(val1)) && (d_sc = status_get_sc(d_bl)) && d_sc->count )
- { // Inherits Status From Source
- const enum sc_type types[] = { SC_AUTOGUARD, SC_DEFENDER, SC_REFLECTSHIELD, SC_ENDURE };
- enum sc_type type2;
- int i = (map_flag_gvg(bl->m) || map[bl->m].flag.battleground)?2:3;
- while( i >= 0 )
- {
- type2 = types[i];
- if( d_sc->data[type2] )
- sc_start(bl, type2, 100, d_sc->data[type2]->val1, skill_get_time(status_sc2skill(type2),d_sc->data[type2]->val1));
- i--;
- }
- }
- break;
- }
-
- case SC_COMA: //Coma. Sends a char to 1HP. If val2, do not zap sp
- if( val3 && bl->type == BL_MOB ) {
- struct block_list* src = map_id2bl(val3);
- if( src )
- mob_log_damage((TBL_MOB*)bl,src,status->hp - 1);
- }
- status_zap(bl, status->hp-1, val2?0:status->sp);
- return 1;
- break;
- case SC_CLOSECONFINE2:
- {
- struct block_list *src = val2?map_id2bl(val2):NULL;
- struct status_change *sc2 = src?status_get_sc(src):NULL;
- struct status_change_entry *sce2 = sc2?sc2->data[SC_CLOSECONFINE]:NULL;
- if (src && sc2) {
- if (!sce2) //Start lock on caster.
- sc_start4(src,SC_CLOSECONFINE,100,val1,1,0,0,tick+1000);
- else { //Increase count of locked enemies and refresh time.
- (sce2->val2)++;
- delete_timer(sce2->timer, status_change_timer);
- sce2->timer = add_timer(gettick()+tick+1000, status_change_timer, src->id, SC_CLOSECONFINE);
- }
- } else //Status failed.
- return 0;
- }
- break;
- case SC_KAITE:
- val2 = 1+val1/5; //Number of bounces: 1 + skill_lv/5
- break;
- case SC_KAUPE:
- switch (val1) {
- case 3: //33*3 + 1 -> 100%
- val2++;
- case 1:
- case 2: //33, 66%
- val2 += 33*val1;
- val3 = 1; //Dodge 1 attack total.
- break;
- default: //Custom. For high level mob usage, higher level means more blocks. [Skotlex]
- val2 = 100;
- val3 = val1-2;
- break;
- }
- break;
-
- case SC_COMBO: {
- //val1: Skill ID
- //val2: When given, target (for autotargetting skills)
- //val3: When set, this combo time should NOT delay attack/movement
- //val3: TK: Last used kick
- //val4: TK: Combo time
- struct unit_data *ud = unit_bl2ud(bl);
- if (ud && !val3) {
- tick += 300 * battle_config.combo_delay_rate/100;
- ud->attackabletime = gettick()+tick;
- unit_set_walkdelay(bl, gettick(), tick, 1);
- }
- val3 = 0;
- val4 = tick;
- }
- break;
- case SC_EARTHSCROLL:
- val2 = 11-val1; //Chance to consume: 11-skill_lv%
- break;
- case SC_RUN:
- val4 = gettick(); //Store time at which you started running.
- tick = -1;
- break;
- case SC_KAAHI:
- val2 = 200*val1; //HP heal
- val3 = 5*val1; //SP cost
- val4 = INVALID_TIMER; //Kaahi Timer.
- break;
- case SC_BLESSING:
- if ((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC)
- val2 = val1;
- else
- val2 = 0; //0 -> Half stat.
- break;
- case SC_TRICKDEAD:
- if (vd) vd->dead_sit = 1;
- tick = -1;
- break;
- case SC_CONCENTRATE:
- val2 = 2 + val1;
- if (sd) { //Store the card-bonus data that should not count in the %
- val3 = sd->param_bonus[1]; //Agi
- val4 = sd->param_bonus[4]; //Dex
- } else {
- val3 = val4 = 0;
- }
- break;
- case SC_MAXOVERTHRUST:
- val2 = 20*val1; //Power increase
- break;
- case SC_OVERTHRUST:
- //val2 holds if it was casted on self, or is bonus received from others
- val3 = 5*val1; //Power increase
- if(sd && pc_checkskill(sd,BS_HILTBINDING)>0)
- tick += tick / 10;
- break;
- case SC_ADRENALINE2:
- case SC_ADRENALINE:
- val3 = (val2) ? 300 : 200; // aspd increase
- case SC_WEAPONPERFECTION:
- if(sd && pc_checkskill(sd,BS_HILTBINDING)>0)
- tick += tick / 10;
- break;
- case SC_CONCENTRATION:
- val2 = 5*val1; //Batk/Watk Increase
- val3 = 10*val1; //Hit Increase
- val4 = 5*val1; //Def reduction
- break;
- case SC_ANGELUS:
- val2 = 5*val1; //def increase
- break;
- case SC_IMPOSITIO:
- val2 = 5*val1; //watk increase
- break;
- case SC_MELTDOWN:
- val2 = 100*val1; //Chance to break weapon
- val3 = 70*val1; //Change to break armor
- break;
- case SC_TRUESIGHT:
- val2 = 10*val1; //Critical increase
- val3 = 3*val1; //Hit increase
- break;
- case SC_SUN_COMFORT:
- val2 = (status_get_lv(bl) + status->dex + status->luk)/2; //def increase
- break;
- case SC_MOON_COMFORT:
- val2 = (status_get_lv(bl) + status->dex + status->luk)/10; //flee increase
- break;
- case SC_STAR_COMFORT:
- val2 = (status_get_lv(bl) + status->dex + status->luk); //Aspd increase
- break;
- case SC_QUAGMIRE:
- val2 = (sd?5:10)*val1; //Agi/Dex decrease.
- break;
-
- // gs_something1 [Vicious]
- case SC_GATLINGFEVER:
- val2 = 20*val1; //Aspd increase
- val3 = 20+10*val1; //Batk increase
- val4 = 5*val1; //Flee decrease
- break;
-
- case SC_FLING:
- if (bl->type == BL_PC)
- val2 = 0; //No armor reduction to players.
- else
- val2 = 5*val1; //Def reduction
- val3 = 5*val1; //Def2 reduction
- break;
- case SC_PROVOKE:
- //val2 signals autoprovoke.
- val3 = 2+3*val1; //Atk increase
- val4 = 5+5*val1; //Def reduction.
- break;
- case SC_AVOID:
- //val2 = 10*val1; //Speed change rate.
- break;
- case SC_DEFENCE:
- val2 = 2*val1; //Def bonus
- break;
- case SC_BLOODLUST:
- val2 = 20+10*val1; //Atk rate change.
- val3 = 3*val1; //Leech chance
- val4 = 20; //Leech percent
- break;
- case SC_FLEET:
- val2 = 30*val1; //Aspd change
- val3 = 5+5*val1; //bAtk/wAtk rate change
- break;
- case SC_MINDBREAKER:
- val2 = 20*val1; //matk increase.
- val3 = 12*val1; //mdef2 reduction.
- break;
- case SC_SKA:
- val2 = tick/1000;
- val3 = rnd()%100; //Def changes randomly every second...
- tick_time = 1000; // [GodLesZ] tick time
- break;
- case SC_JAILED:
- //Val1 is duration in minutes. Use INT_MAX to specify 'unlimited' time.
- tick = val1>0?1000:250;
- if (sd)
- {
- if (sd->mapindex != val2)
- {
- int pos = (bl->x&0xFFFF)|(bl->y<<16), //Current Coordinates
- map = sd->mapindex; //Current Map
- //1. Place in Jail (val2 -> Jail Map, val3 -> x, val4 -> y
- pc_setpos(sd,(unsigned short)val2,val3,val4, CLR_TELEPORT);
- //2. Set restore point (val3 -> return map, val4 return coords
- val3 = map;
- val4 = pos;
- } else if (!val3 || val3 == sd->mapindex) { //Use save point.
- val3 = sd->status.save_point.map;
- val4 = (sd->status.save_point.x&0xFFFF)
- |(sd->status.save_point.y<<16);
- }
- }
- break;
- case SC_UTSUSEMI:
- val2=(val1+1)/2; // number of hits blocked
- val3=skill_get_blewcount(NJ_UTSUSEMI, val1); //knockback value.
- break;
- case SC_BUNSINJYUTSU:
- val2=(val1+1)/2; // number of hits blocked
- break;
- case SC_CHANGE:
- val2= 30*val1; //Vit increase
- val3= 20*val1; //Int increase
- break;
- case SC_SWOO:
- if(status->mode&MD_BOSS)
- tick /= 5; //TODO: Reduce skill's duration. But for how long?
- break;
- case SC_SPIDERWEB:
- if( bl->type == BL_PC )
- tick /= 2;
- break;
- case SC_ARMOR:
- //NPC_DEFENDER:
- val2 = 80; //Damage reduction
- //Attack requirements to be blocked:
- val3 = BF_LONG; //Range
- val4 = BF_WEAPON|BF_MISC; //Type
- break;
- case SC_ENCHANTARMS:
- //end previous enchants
- skill_enchant_elemental_end(bl,type);
- //Make sure the received element is valid.
- if (val2 >= ELE_MAX)
- val2 = val2%ELE_MAX;
- else if (val2 < 0)
- val2 = rnd()%ELE_MAX;
- break;
- case SC_CRITICALWOUND:
- val2 = 20*val1; //Heal effectiveness decrease
- break;
- case SC_MAGICMIRROR:
- case SC_SLOWCAST:
- val2 = 20*val1; //Magic reflection/cast rate
- break;
-
- case SC_ARMORCHANGE:
- if (val2 == NPC_ANTIMAGIC)
- { //Boost mdef
- val2 =-20;
- val3 = 20;
- } else { //Boost def
- val2 = 20;
- val3 =-20;
- }
- val2*=val1; //20% per level
- val3*=val1;
- break;
- case SC_EXPBOOST:
- case SC_JEXPBOOST:
- if (val1 < 0)
- val1 = 0;
- break;
- case SC_INCFLEE2:
- case SC_INCCRI:
- val2 = val1*10; //Actual boost (since 100% = 1000)
- break;
- case SC_SUFFRAGIUM:
- val2 = 15 * val1; //Speed cast decrease
- break;
- case SC_INCHEALRATE:
- if (val1 < 1)
- val1 = 1;
- break;
- case SC_HALLUCINATION:
- val2 = 5+val1; //Factor by which displayed damage is increased by
- break;
- case SC_DOUBLECAST:
- val2 = 30+10*val1; //Trigger rate
- break;
- case SC_KAIZEL:
- val2 = 10*val1; //% of life to be revived with
- break;
- // case SC_ARMOR_ELEMENT:
- // case SC_ARMOR_RESIST:
- // Mod your resistance against elements:
- // val1 = water | val2 = earth | val3 = fire | val4 = wind
- // break;
- //case ????:
- //Place here SCs that have no SCB_* data, no skill associated, no ICON
- //associated, and yet are not wrong/unknown. [Skotlex]
- //break;
-
- case SC_MERC_FLEEUP:
- case SC_MERC_ATKUP:
- case SC_MERC_HITUP:
- val2 = 15 * val1;
- break;
- case SC_MERC_HPUP:
- case SC_MERC_SPUP:
- val2 = 5 * val1;
- break;
- case SC_REBIRTH:
- val2 = 20*val1; //% of life to be revived with
- break;
-
- case SC_MANU_DEF:
- case SC_MANU_ATK:
- case SC_MANU_MATK:
- val2 = 1; // Manuk group
- break;
- case SC_SPL_DEF:
- case SC_SPL_ATK:
- case SC_SPL_MATK:
- val2 = 2; // Splendide group
- break;
- /**
- * General
- **/
- case SC_FEAR:
- val2 = 2;
- val4 = tick / 1000;
- tick_time = 1000; // [GodLesZ] tick time
- break;
- case SC_BURNING:
- val4 = tick / 2000; // Total Ticks to Burn!!
- tick_time = 2000; // [GodLesZ] tick time
- break;
- /**
- * Rune Knight
- **/
- case SC_DEATHBOUND:
- val2 = 500 + 100 * val1;
- break;
- case SC_FIGHTINGSPIRIT:
- val_flag |= 1|2;
- break;
- case SC_ABUNDANCE:
- val4 = tick / 10000;
- tick_time = 10000; // [GodLesZ] tick time
- break;
- case SC_GIANTGROWTH:
- val2 = 10; // Triple damage success rate.
- break;
- /**
- * Arch Bishop
- **/
- case SC_RENOVATIO:
- val4 = tick / 5000;
- tick_time = 5000;
- break;
- case SC_SECRAMENT:
- val2 = 10 * val1;
- break;
- case SC_VENOMIMPRESS:
- val2 = 10 * val1;
- val_flag |= 1|2;
- break;
- case SC_POISONINGWEAPON:
- val_flag |= 1|2|4;
- break;
- case SC_WEAPONBLOCKING:
- val2 = 10 + 2 * val1; // Chance
- val4 = tick / 3000;
- tick_time = 3000; // [GodLesZ] tick time
- val_flag |= 1|2;
- break;
- case SC_TOXIN:
- val4 = tick / 10000;
- tick_time = 10000; // [GodLesZ] tick time
- break;
- case SC_MAGICMUSHROOM:
- val4 = tick / 4000;
- tick_time = 4000; // [GodLesZ] tick time
- break;
- case SC_PYREXIA:
- status_change_start(bl,SC_BLIND,10000,val1,0,0,0,30000,11); // Blind status that last for 30 seconds
- val4 = tick / 3000;
- tick_time = 3000; // [GodLesZ] tick time
- break;
- case SC_LEECHESEND:
- val4 = tick / 1000;
- tick_time = 1000; // [GodLesZ] tick time
- break;
- case SC_OBLIVIONCURSE:
- val4 = tick / 3000;
- tick_time = 3000; // [GodLesZ] tick time
- break;
- case SC_ROLLINGCUTTER:
- val_flag |= 1;
- break;
- case SC_CLOAKINGEXCEED:
- val2 = ( val1 + 1 ) / 2; // Hits
- val3 = 90 + val1 * 10; // Walk speed
- val_flag |= 1|2|4;
- if (bl->type == BL_PC)
- val4 |= battle_config.pc_cloak_check_type&7;
- else
- val4 |= battle_config.monster_cloak_check_type&7;
- tick_time = 1000; // [GodLesZ] tick time
- break;
- case SC_HALLUCINATIONWALK:
- val2 = 50 * val1; // Evasion rate of physical attacks. Flee
- val3 = 10 * val1; // Evasion rate of magical attacks.
- val_flag |= 1|2|4;
- break;
- case SC_WHITEIMPRISON:
- status_change_end(bl, SC_BURNING, INVALID_TIMER);
- status_change_end(bl, SC_FREEZING, INVALID_TIMER);
- status_change_end(bl, SC_FREEZE, INVALID_TIMER);
- status_change_end(bl, SC_STONE, INVALID_TIMER);
- break;
- case SC_FREEZING:
- status_change_end(bl, SC_BURNING, INVALID_TIMER);
- break;
- case SC_READING_SB:
- // val2 = sp reduction per second
- tick_time = 5000; // [GodLesZ] tick time
- break;
- case SC_SPHERE_1:
- case SC_SPHERE_2:
- case SC_SPHERE_3:
- case SC_SPHERE_4:
- case SC_SPHERE_5:
- if( !sd )
- return 0; // Should only work on players.
- val4 = tick / 1000;
- if( val4 < 1 )
- val4 = 1;
- tick_time = 1000; // [GodLesZ] tick time
- val_flag |= 1;
- break;
- case SC_SHAPESHIFT:
- switch( val1 )
- {
- case 1: val2 = ELE_FIRE; break;
- case 2: val2 = ELE_EARTH; break;
- case 3: val2 = ELE_WIND; break;
- case 4: val2 = ELE_WATER; break;
- }
- break;
- case SC_ELECTRICSHOCKER:
- case SC_CRYSTALIZE:
- case SC_MEIKYOUSISUI:
- val4 = tick / 1000;
- if( val4 < 1 )
- val4 = 1;
- tick_time = 1000; // [GodLesZ] tick time
- break;
- case SC_CAMOUFLAGE:
- val4 = tick/1000;
- tick_time = 1000; // [GodLesZ] tick time
- break;
- case SC_WUGDASH:
- val4 = gettick(); //Store time at which you started running.
- tick = -1;
- break;
- case SC__SHADOWFORM: {
- struct map_session_data * s_sd = map_id2sd(val2);
- if( s_sd )
- s_sd->shadowform_id = bl->id;
- val4 = tick / 1000;
- val_flag |= 1|2|4;
- tick_time = 1000; // [GodLesZ] tick time
- }
- break;
- case SC__STRIPACCESSORY:
- if (!sd)
- val2 = 20;
- break;
- case SC__INVISIBILITY:
- val2 = 50 - 10 * val1; // ASPD
- val3 = 20 * val1; // CRITICAL
- val4 = tick / 1000;
- tick_time = 1000; // [GodLesZ] tick time
- val_flag |= 1|2;
- break;
- case SC__ENERVATION:
- val2 = 20 + 10 * val1; // ATK Reduction
- val_flag |= 1|2;
- if( sd ) pc_delspiritball(sd,sd->spiritball,0);
- break;
- case SC__GROOMY:
- val2 = 20 + 10 * val1; //ASPD. Need to confirm if Movement Speed reduction is the same. [Jobbie]
- val3 = 20 * val1; //HIT
- val_flag |= 1|2|4;
- if( sd )
- { // Removes Animals
- if( pc_isriding(sd) ) pc_setriding(sd, 0);
- if( pc_isridingdragon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_DRAGON);
- if( pc_iswug(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_WUG);
- if( pc_isridingwug(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_WUGRIDER);
- if( pc_isfalcon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_FALCON);
- if( sd->status.pet_id > 0 ) pet_menu(sd, 3);
- if( merc_is_hom_active(sd->hd) ) merc_hom_vaporize(sd,1);
- if( sd->md ) merc_delete(sd->md,3);
- }
- break;
- case SC__LAZINESS:
- val2 = 10 + 10 * val1; // Cast reduction
- val3 = 10 * val1; // Flee Reduction
- val_flag |= 1|2|4;
- break;
- case SC__UNLUCKY:
- val2 = 10 * val1; // Crit and Flee2 Reduction
- val_flag |= 1|2|4;
- break;
- case SC__WEAKNESS:
- val2 = 10 * val1;
- val_flag |= 1|2;
- // bypasses coating protection and MADO
- sc_start(bl,SC_STRIPWEAPON,100,val1,tick);
- sc_start(bl,SC_STRIPSHIELD,100,val1,tick);
- break;
- break;
- case SC_GN_CARTBOOST:
- if( val1 < 3 )
- val2 = 50;
- else if( val1 < 5 )
- val2 = 75;
- else
- val2 = 100;
- break;
- case SC_PROPERTYWALK:
- val_flag |= 1|2;
- val3 = 0;
- break;
- case SC_WARMER:
- status_change_end(bl, SC_FREEZE, INVALID_TIMER);
- status_change_end(bl, SC_FREEZING, INVALID_TIMER);
- status_change_end(bl, SC_CRYSTALIZE, INVALID_TIMER);
- break;
- case SC_STRIKING:
- val1 = 6 - val1;//spcost = 6 - level (lvl1:5 ... lvl 5: 1)
- val4 = tick / 1000;
- tick_time = 1000; // [GodLesZ] tick time
- break;
- case SC_BLOODSUCKER:
- val4 = tick / 1000;
- tick_time = 1000; // [GodLesZ] tick time
- break;
- case SC_VACUUM_EXTREME:
- tick -= (status->str / 20) * 1000;
- val4 = val3 = tick / 100;
- tick_time = 100; // [GodLesZ] tick time
- break;
- case SC_SWINGDANCE:
- val2 = 4 * val1; // Walk speed and aspd reduction.
- break;
- case SC_SYMPHONYOFLOVER:
- case SC_RUSHWINDMILL:
- case SC_ECHOSONG:
- val2 = 6 * val1;
- val2 += val3; //Adding 1% * Lesson Bonus
- val2 += (int)(val4*2/10); //Adding 0.2% per JobLevel
- break;
- case SC_MOONLITSERENADE:
- val2 = 10 * val1;
- break;
- case SC_HARMONIZE:
- val2 = 5 + 5 * val1;
- break;
- case SC_VOICEOFSIREN:
- val4 = tick / 2000;
- tick_time = 2000; // [GodLesZ] tick time
- break;
- case SC_DEEPSLEEP:
- val4 = tick / 2000;
- tick_time = 2000; // [GodLesZ] tick time
- break;
- case SC_SIRCLEOFNATURE:
- val2 = 1 + val1; //SP consume
- val3 = 40 * val1; //HP recovery
- val4 = tick / 1000;
- tick_time = 1000; // [GodLesZ] tick time
- break;
- case SC_SONGOFMANA:
- val3 = 10 + (2 * val2);
- val4 = tick/3000;
- tick_time = 3000; // [GodLesZ] tick time
- break;
- case SC_SATURDAYNIGHTFEVER:
- if (!val4) val4 = skill_get_time2(status_sc2skill(type),val1);
- if (!val4) val4 = 3000;
- val3 = tick/val4;
- tick_time = val4; // [GodLesZ] tick time
- break;
- case SC_GLOOMYDAY:
- val2 = 20 + 5 * val1; // Flee reduction.
- val3 = 15 + 5 * val1; // ASPD reduction.
- if( sd && rand()%100 < val1 ){ // (Skill Lv) %
- val4 = 1; // reduce walk speed by half.
- if( pc_isriding(sd) ) pc_setriding(sd, 0);
- if( pc_isridingdragon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_DRAGON);
- }
- break;
- case SC_GLOOMYDAY_SK:
- // Random number between [15 ~ (Voice Lesson Skill Level x 5) + (Skill Level x 10)] %.
- val2 = 15 + rand()%( (sd?pc_checkskill(sd, WM_LESSON)*5:0) + val1*10 );
- break;
- case SC_SITDOWN_FORCE:
- case SC_BANANA_BOMB_SITDOWN:
- if( sd && !pc_issit(sd) )
- {
- pc_setsit(sd);
- skill_sit(sd,1);
- clif_sitting(bl);
- }
- break;
- case SC_DANCEWITHWUG:
- val3 = (5 * val1) + (1 * val2); //Still need official value.
- break;
- case SC_LERADSDEW:
- val3 = (5 * val1) + (1 * val2);
- break;
- case SC_MELODYOFSINK:
- val3 = (5 * val1) + (1 * val2);
- break;
- case SC_BEYONDOFWARCRY:
- val3 = (5 * val1) + (1 * val2);
- break;
- case SC_UNLIMITEDHUMMINGVOICE:
- {
- struct unit_data *ud = unit_bl2ud(bl);
- if( ud == NULL ) return 0;
- ud->state.skillcastcancel = 0;
- val3 = 15 - (2 * val2);
- }
- break;
- case SC_REFLECTDAMAGE:
- val2 = 15 + 5 * val1;
- val3 = (val1==5)?20:(val1+4)*2; // SP consumption
- val4 = tick/10000;
- tick_time = 10000; // [GodLesZ] tick time
- break;
- case SC_FORCEOFVANGUARD: // This is not the official way to handle it but I think we should use it. [pakpil]
- val2 = 20 + 12 * (val1 - 1); // Chance
- val3 = 5 + (2 * val1); // Max rage counters
- tick = -1; //endless duration in the client
- tick_time = 6000; // [GodLesZ] tick time
- val_flag |= 1|2|4;
- break;
- case SC_EXEEDBREAK:
- val1 *= 150; // 150 * skill_lv
- if( sd && sd->inventory_data[sd->equip_index[EQI_HAND_R]] ) { // Chars.
- val1 += (sd->inventory_data[sd->equip_index[EQI_HAND_R]]->weight/10 * sd->inventory_data[sd->equip_index[EQI_HAND_R]]->wlv * status_get_lv(bl) / 100);
- val1 += 15 * (sd ? sd->status.job_level:50) + 100;
- }
- else // Mobs
- val1 += (400 * status_get_lv(bl) / 100) + (15 * (status_get_lv(bl) / 2)); // About 1138% at mob_lvl 99. Is an aproximation to a standard weapon. [pakpil]
- break;
- case SC_PRESTIGE: // Bassed on suggested formula in iRO Wiki and some test, still need more test. [pakpil]
- val2 = ((status->int_ + status->luk) / 6) + 5; // Chance to evade magic damage.
- val1 *= 15; // Defence added
- if( sd )
- val1 += 10 * pc_checkskill(sd,CR_DEFENDER);
- val_flag |= 1|2;
- break;
- case SC_BANDING:
- tick_time = 5000; // [GodLesZ] tick time
- val_flag |= 1;
- break;
- case SC_SHIELDSPELL_DEF:
- case SC_SHIELDSPELL_MDEF:
- case SC_SHIELDSPELL_REF:
- val_flag |= 1|2;
- break;
- case SC_MAGNETICFIELD:
- val3 = tick / 1000;
- tick_time = 1000; // [GodLesZ] tick time
- break;
- case SC_INSPIRATION:
- if( sd )
- {
- val2 = (40 * val1) + (3 * sd->status.job_level); // ATK bonus
- val3 = (sd->status.job_level / 10) * 2 + 12; // All stat bonus
- }
- val4 = tick / 1000;
- tick_time = 1000; // [GodLesZ] tick time
- status_change_clear_buffs(bl,3); //Remove buffs/debuffs
- break;
- case SC_SPELLFIST:
- case SC_CURSEDCIRCLE_ATKER:
- val_flag |= 1|2|4;
- break;
- case SC_CRESCENTELBOW:
- val2 = 94 + val1;
- val_flag |= 1|2;
- break;
- case SC_LIGHTNINGWALK: // [(Job Level / 2) + (40 + 5 * Skill Level)] %
- val1 = (sd?sd->status.job_level:2)/2 + 40 + 5 * val1;
- val_flag |= 1;
- break;
- case SC_RAISINGDRAGON:
- val3 = tick / 5000;
- tick_time = 5000; // [GodLesZ] tick time
- break;
- case SC_GT_CHANGE:
- {// take note there is no def increase as skill desc says. [malufett]
- struct block_list * src;
- val3 = status->agi * val1 / 60; // ASPD increase: [(Target AGI x Skill Level) / 60] %
- if( (src = map_id2bl(val2)) )
- val4 = ( 200/status_get_int(src) ) * val1;// MDEF decrease: MDEF [(200 / Caster INT) x Skill Level]
- }
- break;
- case SC_GT_REVITALIZE:
- {// take note there is no vit,aspd,speed increase as skill desc says. [malufett]
- struct block_list * src;
- val3 = val1 * 30 + 150; // Natural HP recovery increase: [(Skill Level x 30) + 50] %
- if( (src = map_id2bl(val2)) ) // the stat def is not shown in the status window and it is process differently
- val4 = ( status_get_vit(src)/4 ) * val1; // STAT DEF increase: [(Caster VIT / 4) x Skill Level]
- }
- break;
- case SC_PYROTECHNIC_OPTION:
- val_flag |= 1|2|4;
- break;
- case SC_HEATER_OPTION:
- val2 = 120; // Watk. TODO: Renewal (Atk2)
- val3 = 33; // % Increase effects.
- val4 = 3; // Change into fire element.
- val_flag |= 1|2|4;
- break;
- case SC_TROPIC_OPTION:
- val2 = 180; // Watk. TODO: Renewal (Atk2)
- val3 = MG_FIREBOLT;
- break;
- case SC_AQUAPLAY_OPTION:
- val2 = 40;
- val_flag |= 1|2|4;
- break;
- case SC_COOLER_OPTION:
- val2 = 80; // % Freezing chance
- val3 = 33; // % increased damage
- val4 = 1; // Change into water elemet
- val_flag |= 1|2|4;
- break;
- case SC_CHILLY_AIR_OPTION:
- val2 = 120; // Matk. TODO: Renewal (Matk1)
- val3 = MG_COLDBOLT;
- val_flag |= 1|2;
- break;
- case SC_GUST_OPTION:
- val_flag |= 1|2;
- break;
- case SC_WIND_STEP_OPTION:
- val2 = 50; // % Increase speed and flee.
- break;
- case SC_BLAST_OPTION:
- val2 = 20;
- val3 = ELE_WIND;
- val_flag |= 1|2|4;
- break;
- case SC_WILD_STORM_OPTION:
- val2 = MG_LIGHTNINGBOLT;
- val_flag |= 1|2;
- break;
- case SC_PETROLOGY_OPTION:
- val2 = 5;
- val3 = 50;
- val_flag |= 1|2|4;
- break;
- case SC_CURSED_SOIL_OPTION:
- val2 = 10;
- val3 = 33;
- val4 = 2;
- val_flag |= 1|2|4;
- break;
- case SC_UPHEAVAL_OPTION:
- val2 = WZ_EARTHSPIKE;
- val_flag |= 1|2;
- break;
- case SC_CIRCLE_OF_FIRE_OPTION:
- val2 = 300;
- val_flag |= 1|2;
- break;
- case SC_FIRE_CLOAK_OPTION:
- case SC_WATER_DROP_OPTION:
- case SC_WIND_CURTAIN_OPTION:
- case SC_STONE_SHIELD_OPTION:
- val2 = 20; // Elemental modifier. Not confirmed.
- break;
- case SC_CIRCLE_OF_FIRE:
- case SC_FIRE_CLOAK:
- case SC_WATER_DROP:
- case SC_WATER_SCREEN:
- case SC_WIND_CURTAIN:
- case SC_WIND_STEP:
- case SC_STONE_SHIELD:
- case SC_SOLID_SKIN:
- val2 = 10;
- tick_time = 2000; // [GodLesZ] tick time
- break;
- case SC_WATER_BARRIER:
- val2 = 40; // Increasement. Mdef1 ???
- val3 = 20; // Reductions. Atk2, Flee1, Matk1 ????
- val_flag |= 1|2|4;
- break;
- case SC_ZEPHYR:
- val2 = 22; // Flee.
- break;
- case SC_TIDAL_WEAPON:
- val2 = 20; // Increase Elemental's attack.
- break;
- case SC_ROCK_CRUSHER:
- case SC_ROCK_CRUSHER_ATK:
- case SC_POWER_OF_GAIA:
- val2 = 33;
- break;
- case SC_MELON_BOMB:
- case SC_BANANA_BOMB:
- val1 = 15;
- break;
- case SC_STOMACHACHE:
- val2 = 8; // SP consume.
- val4 = tick / 10000;
- tick_time = 10000; // [GodLesZ] tick time
- break;
- case SC_KYOUGAKU:
- val2 = 2*val1 + rand()%val1;
- clif_status_change(bl,SI_ACTIVE_MONSTER_TRANSFORM,1,0,1002,0,0);
- break;
- case SC_KAGEMUSYA:
- val3 = val1 * 2;
- case SC_IZAYOI:
- val2 = tick/1000;
- tick_time = 1000;
- break;
- case SC_ZANGETSU:
- if( (status_get_hp(bl)+status_get_sp(bl)) % 2 == 0)
- val2 = status_get_lv(bl) / 2 + 50;
- else
- val2 -= 50;
- break;
- case SC_GENSOU:
- {
- int hp = status_get_hp(bl), lv = 5;
- short per = 100 / (status_get_max_hp(bl) / hp);
-
- if( per <= 15 )
- lv = 1;
- else if( per <= 30 )
- lv = 2;
- else if( per <= 50 )
- lv = 3;
- else if( per <= 75 )
- lv = 4;
- if( hp % 2 == 0)
- status_heal(bl, hp * (6-lv) * 4 / 100, status_get_sp(bl) * (6-lv) * 3 / 100, 1);
- else
- status_zap(bl, hp * (lv*4) / 100, status_get_sp(bl) * (lv*3) / 100);
- }
- break;
- case SC_ANGRIFFS_MODUS:
- val2 = 50 + 20 * val1; //atk bonus
- val3 = 40 + 20 * val1; // Flee reduction.
- val4 = tick/1000; // hp/sp reduction timer
- tick_time = 1000;
- break;
- case SC_GOLDENE_FERSE:
- val2 = 10 + 10*val1; //max hp bonus
- val3 = 6 + 4 * val1; // Aspd Bonus
- val4 = 2 + 2 * val1; // Chance of holy attack
- break;
- case SC_OVERED_BOOST:
- val2 = 300 + 40*val1; //flee bonus
- val3 = 179 + 2*val1; //aspd bonus
- break;
- case SC_GRANITIC_ARMOR:
- val2 = 2*val1; //dmg reduction
- val3 = 6*val1; //dmg on status end
- break;
- case SC_MAGMA_FLOW:
- val2 = 3*val1; //activation chance
- break;
- case SC_PYROCLASTIC:
- val2 += 10*val1; //atk bonus
- break;
- case SC_PARALYSIS: //[Lighta] need real info
- val2 = 2*val1; //def reduction
- val3 = 500*val1; //varcast augmentation
- break;
- case SC_PAIN_KILLER: //[Lighta] need real info
- val2 = 2*val1; //aspd reduction %
- val3 = 2*val1; //dmg reduction %
- if(sc->data[SC_PARALYSIS])
- sc_start(bl, SC_ENDURE, 100, val1, tick); //start endure for same duration
- break;
- case SC_STYLE_CHANGE: //[Lighta] need real info
- tick = -1;
- if(val2 == MH_MD_FIGHTING) val2 = MH_MD_GRAPPLING;
- else val2 = MH_MD_FIGHTING;
- break;
- default:
- if( calc_flag == SCB_NONE && StatusSkillChangeTable[type] == 0 && StatusIconChangeTable[type] == 0 )
- { //Status change with no calc, no icon, and no skill associated...?
- ShowError("UnknownStatusChange [%d]\n", type);
- return 0;
- }
- }
- else //Special considerations when loading SC data.
- switch( type )
- {
- case SC_WEDDING:
- case SC_XMAS:
- case SC_SUMMER:
- clif_changelook(bl,LOOK_WEAPON,0);
- clif_changelook(bl,LOOK_SHIELD,0);
- clif_changelook(bl,LOOK_BASE,type==SC_WEDDING?JOB_WEDDING:type==SC_XMAS?JOB_XMAS:JOB_SUMMER);
- clif_changelook(bl,LOOK_CLOTHES_COLOR,val4);
- break;
- case SC_KAAHI:
- val4 = INVALID_TIMER;
- break;
- }
-
- //Those that make you stop attacking/walking....
- switch (type) {
- case SC_FREEZE:
- case SC_STUN:
- case SC_SLEEP:
- case SC_STONE:
- case SC_DEEPSLEEP:
- if (sd && pc_issit(sd)) //Avoid sprite sync problems.
- pc_setstand(sd);
- case SC_TRICKDEAD:
- status_change_end(bl, SC_DANCING, INVALID_TIMER);
- // Cancel cast when get status [LuzZza]
- if (battle_config.sc_castcancel&bl->type)
- unit_skillcastcancel(bl, 0);
- case SC_WHITEIMPRISON:
- unit_stop_attack(bl);
- case SC_STOP:
- case SC_CONFUSION:
- case SC_CLOSECONFINE:
- case SC_CLOSECONFINE2:
- case SC_ANKLE:
- case SC_SPIDERWEB:
- case SC_ELECTRICSHOCKER:
- case SC_BITE:
- case SC_THORNSTRAP:
- case SC__MANHOLE:
- case SC_CRYSTALIZE:
- case SC_CURSEDCIRCLE_ATKER:
- case SC_CURSEDCIRCLE_TARGET:
- case SC_FEAR:
- case SC_NETHERWORLD:
- case SC_MEIKYOUSISUI:
- case SC_KYOUGAKU:
- case SC_PARALYSIS:
- unit_stop_walking(bl,1);
- break;
- case SC_HIDING:
- case SC_CLOAKING:
- case SC_CLOAKINGEXCEED:
- case SC_CHASEWALK:
- case SC_WEIGHT90:
- case SC_CAMOUFLAGE:
- case SC_VOICEOFSIREN:
- unit_stop_attack(bl);
- break;
- case SC_SILENCE:
- if (battle_config.sc_castcancel&bl->type)
- unit_skillcastcancel(bl, 0);
- break;
- }
-
- // Set option as needed.
- opt_flag = 1;
- switch(type)
- {
- //OPT1
- case SC_STONE: sc->opt1 = OPT1_STONEWAIT; break;
- case SC_FREEZE: sc->opt1 = OPT1_FREEZE; break;
- case SC_STUN: sc->opt1 = OPT1_STUN; break;
- case SC_DEEPSLEEP: opt_flag = 0;
- case SC_SLEEP: sc->opt1 = OPT1_SLEEP; break;
- case SC_BURNING: sc->opt1 = OPT1_BURNING; break; // Burning need this to be showed correctly. [pakpil]
- case SC_WHITEIMPRISON: sc->opt1 = OPT1_IMPRISON; break;
- case SC_CRYSTALIZE: sc->opt1 = OPT1_CRYSTALIZE; break;
- //OPT2
- case SC_POISON: sc->opt2 |= OPT2_POISON; break;
- case SC_CURSE: sc->opt2 |= OPT2_CURSE; break;
- case SC_SILENCE: sc->opt2 |= OPT2_SILENCE; break;
-
- case SC_SIGNUMCRUCIS:
- sc->opt2 |= OPT2_SIGNUMCRUCIS;
- break;
-
- case SC_BLIND: sc->opt2 |= OPT2_BLIND; break;
- case SC_ANGELUS: sc->opt2 |= OPT2_ANGELUS; break;
- case SC_BLEEDING: sc->opt2 |= OPT2_BLEEDING; break;
- case SC_DPOISON: sc->opt2 |= OPT2_DPOISON; break;
- //OPT3
- case SC_TWOHANDQUICKEN:
- case SC_ONEHAND:
- case SC_SPEARQUICKEN:
- case SC_CONCENTRATION:
- case SC_MERC_QUICKEN:
- sc->opt3 |= OPT3_QUICKEN;
- opt_flag = 0;
- break;
- case SC_MAXOVERTHRUST:
- case SC_OVERTHRUST:
- case SC_SWOO: //Why does it shares the same opt as Overthrust? Perhaps we'll never know...
- sc->opt3 |= OPT3_OVERTHRUST;
- opt_flag = 0;
- break;
- case SC_ENERGYCOAT:
- case SC_SKE:
- sc->opt3 |= OPT3_ENERGYCOAT;
- opt_flag = 0;
- break;
- case SC_INCATKRATE:
- //Simulate Explosion Spirits effect for NPC_POWERUP [Skotlex]
- if (bl->type != BL_MOB) {
- opt_flag = 0;
- break;
- }
- case SC_EXPLOSIONSPIRITS:
- sc->opt3 |= OPT3_EXPLOSIONSPIRITS;
- opt_flag = 0;
- break;
- case SC_STEELBODY:
- case SC_SKA:
- sc->opt3 |= OPT3_STEELBODY;
- opt_flag = 0;
- break;
- case SC_BLADESTOP:
- sc->opt3 |= OPT3_BLADESTOP;
- opt_flag = 0;
- break;
- case SC_AURABLADE:
- sc->opt3 |= OPT3_AURABLADE;
- opt_flag = 0;
- break;
- case SC_BERSERK:
- opt_flag = 0;
-// case SC__BLOODYLUST:
- sc->opt3 |= OPT3_BERSERK;
- break;
-// case ???: // doesn't seem to do anything
-// sc->opt3 |= OPT3_LIGHTBLADE;
-// opt_flag = 0;
-// break;
- case SC_DANCING:
- if ((val1&0xFFFF) == CG_MOONLIT)
- sc->opt3 |= OPT3_MOONLIT;
- opt_flag = 0;
- break;
- case SC_MARIONETTE:
- case SC_MARIONETTE2:
- sc->opt3 |= OPT3_MARIONETTE;
- opt_flag = 0;
- break;
- case SC_ASSUMPTIO:
- sc->opt3 |= OPT3_ASSUMPTIO;
- opt_flag = 0;
- break;
- case SC_WARM: //SG skills [Komurka]
- sc->opt3 |= OPT3_WARM;
- opt_flag = 0;
- break;
- case SC_KAITE:
- sc->opt3 |= OPT3_KAITE;
- opt_flag = 0;
- break;
- case SC_BUNSINJYUTSU:
- sc->opt3 |= OPT3_BUNSIN;
- opt_flag = 0;
- break;
- case SC_SPIRIT:
- sc->opt3 |= OPT3_SOULLINK;
- opt_flag = 0;
- break;
- case SC_CHANGEUNDEAD:
- sc->opt3 |= OPT3_UNDEAD;
- opt_flag = 0;
- break;
-// case ???: // from DA_CONTRACT (looks like biolab mobs aura)
-// sc->opt3 |= OPT3_CONTRACT;
-// opt_flag = 0;
-// break;
- //OPTION
- case SC_HIDING:
- sc->option |= OPTION_HIDE;
- opt_flag = 2;
- break;
- case SC_CLOAKING:
- case SC_CLOAKINGEXCEED:
- case SC__INVISIBILITY:
- sc->option |= OPTION_CLOAK;
- opt_flag = 2;
- break;
- case SC_CHASEWALK:
- sc->option |= OPTION_CHASEWALK|OPTION_CLOAK;
- opt_flag = 2;
- break;
- case SC_SIGHT:
- sc->option |= OPTION_SIGHT;
- break;
- case SC_RUWACH:
- sc->option |= OPTION_RUWACH;
- break;
- case SC_WEDDING:
- sc->option |= OPTION_WEDDING;
- break;
- case SC_XMAS:
- sc->option |= OPTION_XMAS;
- break;
- case SC_SUMMER:
- sc->option |= OPTION_SUMMER;
- break;
- case SC_ORCISH:
- sc->option |= OPTION_ORCISH;
- break;
- case SC_FUSION:
- sc->option |= OPTION_FLYING;
- break;
- default:
- opt_flag = 0;
- }
-
- //On Aegis, when turning on a status change, first goes the option packet, then the sc packet.
- if(opt_flag)
- clif_changeoption(bl);
-
- if (calc_flag&SCB_DYE)
- { //Reset DYE color
- if (vd && vd->cloth_color)
- {
- val4 = vd->cloth_color;
- clif_changelook(bl,LOOK_CLOTHES_COLOR,0);
- }
- calc_flag&=~SCB_DYE;
- }
-
- clif_status_change(bl,StatusIconChangeTable[type],1,tick,(val_flag&1)?val1:1,(val_flag&2)?val2:0,(val_flag&4)?val3:0);
-
- /**
- * used as temporary storage for scs with interval ticks, so that the actual duration is sent to the client first.
- **/
- if( tick_time )
- tick = tick_time;
-
- //Don't trust the previous sce assignment, in case the SC ended somewhere between there and here.
- if((sce=sc->data[type])) {// reuse old sc
- if( sce->timer != INVALID_TIMER )
- delete_timer(sce->timer, status_change_timer);
- sc_isnew = false;
- } else {// new sc
- ++(sc->count);
- sce = sc->data[type] = ers_alloc(sc_data_ers, struct status_change_entry);
- }
- sce->val1 = val1;
- sce->val2 = val2;
- sce->val3 = val3;
- sce->val4 = val4;
- if (tick >= 0)
- sce->timer = add_timer(gettick() + tick, status_change_timer, bl->id, type);
- else
- sce->timer = INVALID_TIMER; //Infinite duration
-
- if (calc_flag)
- status_calc_bl(bl,calc_flag);
-
- if ( sc_isnew && StatusChangeStateTable[type] ) /* non-zero */
- status_calc_state(bl,sc,( enum scs_flag ) StatusChangeStateTable[type],true);
-
-
- if(sd && sd->pd)
- pet_sc_check(sd, type); //Skotlex: Pet Status Effect Healing
-
- switch (type) {
- case SC__BLOODYLUST:
- case SC_BERSERK:
- if (!(sce->val2)) { //don't heal if already set
- status_heal(bl, status->max_hp, 0, 1); //Do not use percent_heal as this healing must override BERSERK's block.
- status_set_sp(bl, 0, 0); //Damage all SP
- }
- sce->val2 = 5 * status->max_hp / 100;
- break;
- case SC_CHANGE:
- status_percent_heal(bl, 100, 100);
- break;
- case SC_RUN:
- {
- struct unit_data *ud = unit_bl2ud(bl);
- if( ud )
- ud->state.running = unit_run(bl);
- }
- break;
- case SC_BOSSMAPINFO:
- clif_bossmapinfo(sd->fd, map_id2boss(sce->val1), 0); // First Message
- break;
- case SC_MERC_HPUP:
- status_percent_heal(bl, 100, 0); // Recover Full HP
- break;
- case SC_MERC_SPUP:
- status_percent_heal(bl, 0, 100); // Recover Full SP
- break;
- /**
- * Ranger
- **/
- case SC_WUGDASH:
- {
- struct unit_data *ud = unit_bl2ud(bl);
- if( ud )
- ud->state.running = unit_wugdash(bl, sd);
- }
- break;
- case SC_COMBO:
- switch (sce->val1) {
- case TK_STORMKICK:
- clif_skill_nodamage(bl,bl,TK_READYSTORM,1,1);
- break;
- case TK_DOWNKICK:
- clif_skill_nodamage(bl,bl,TK_READYDOWN,1,1);
- break;
- case TK_TURNKICK:
- clif_skill_nodamage(bl,bl,TK_READYTURN,1,1);
- break;
- case TK_COUNTER:
- clif_skill_nodamage(bl,bl,TK_READYCOUNTER,1,1);
- break;
- case MO_COMBOFINISH:
- case CH_TIGERFIST:
- case CH_CHAINCRUSH:
- if (sd)
- clif_skillinfo(sd,MO_EXTREMITYFIST, INF_SELF_SKILL);
- break;
- case TK_JUMPKICK:
- if (sd)
- clif_skillinfo(sd,TK_JUMPKICK, INF_SELF_SKILL);
- break;
- case MO_TRIPLEATTACK:
- if (sd && pc_checkskill(sd, SR_DRAGONCOMBO) > 0)
- clif_skillinfo(sd,SR_DRAGONCOMBO, INF_SELF_SKILL);
- break;
- case SR_FALLENEMPIRE:
- if (sd){
- clif_skillinfo(sd,SR_GATEOFHELL, INF_SELF_SKILL);
- clif_skillinfo(sd,SR_TIGERCANNON, INF_SELF_SKILL);
- }
- break;
- }
- break;
- case SC_RAISINGDRAGON:
- sce->val2 = status->max_hp / 100;// Officially tested its 1%hp drain. [Jobbie]
- break;
- }
-
- if( opt_flag&2 && sd && sd->touching_id )
- npc_touchnext_areanpc(sd,false); // run OnTouch_ on next char in range
-
- return 1;
-}
-
-/*==========================================
- * Ending all status except those listed.
- * @TODO maybe usefull for dispel instead reseting a liste there.
- * type:
- * 0 - PC killed -> Place here statuses that do not dispel on death.
- * 1 - If for some reason status_change_end decides to still keep the status when quitting.
- * 2 - Do clif
- * 3 - Do not remove some permanent/time-independent effects
- *------------------------------------------*/
-int status_change_clear(struct block_list* bl, int type)
-{
- struct status_change* sc;
- int i;
-
- sc = status_get_sc(bl);
-
- if (!sc || !sc->count)
- return 0;
-
- for(i = 0; i < SC_MAX; i++)
- {
- if(!sc->data[i])
- continue;
-
- if(type == 0)
- switch (i)
- { //Type 0: PC killed -> Place here statuses that do not dispel on death.
- case SC_ELEMENTALCHANGE://Only when its Holy or Dark that it doesn't dispell on death
- if( sc->data[i]->val2 != ELE_HOLY && sc->data[i]->val2 != ELE_DARK )
- break;
- case SC_WEIGHT50:
- case SC_WEIGHT90:
- case SC_EDP:
- case SC_MELTDOWN:
- case SC_XMAS:
- case SC_SUMMER:
- case SC_NOCHAT:
- case SC_FUSION:
- case SC_EARTHSCROLL:
- case SC_READYSTORM:
- case SC_READYDOWN:
- case SC_READYCOUNTER:
- case SC_READYTURN:
- case SC_DODGE:
- case SC_JAILED:
- case SC_EXPBOOST:
- case SC_ITEMBOOST:
- case SC_HELLPOWER:
- case SC_JEXPBOOST:
- case SC_AUTOTRADE:
- case SC_WHISTLE:
- case SC_ASSNCROS:
- case SC_POEMBRAGI:
- case SC_APPLEIDUN:
- case SC_HUMMING:
- case SC_DONTFORGETME:
- case SC_FORTUNE:
- case SC_SERVICE4U:
- case SC_FOOD_STR_CASH:
- case SC_FOOD_AGI_CASH:
- case SC_FOOD_VIT_CASH:
- case SC_FOOD_DEX_CASH:
- case SC_FOOD_INT_CASH:
- case SC_FOOD_LUK_CASH:
- case SC_DEF_RATE:
- case SC_MDEF_RATE:
- case SC_INCHEALRATE:
- case SC_INCFLEE2:
- case SC_INCHIT:
- case SC_ATKPOTION:
- case SC_MATKPOTION:
- case SC_S_LIFEPOTION:
- case SC_L_LIFEPOTION:
- case SC_PUSH_CART:
- continue;
-
- }
-
- if( type == 3 )
- {
- switch (i)
- {// TODO: This list may be incomplete
- case SC_WEIGHT50:
- case SC_WEIGHT90:
- case SC_NOCHAT:
- case SC_PUSH_CART:
- continue;
- }
- }
-
- status_change_end(bl, (sc_type)i, INVALID_TIMER);
-
- if( type == 1 && sc->data[i] )
- { //If for some reason status_change_end decides to still keep the status when quitting. [Skotlex]
- (sc->count)--;
- if (sc->data[i]->timer != INVALID_TIMER)
- delete_timer(sc->data[i]->timer, status_change_timer);
- ers_free(sc_data_ers, sc->data[i]);
- sc->data[i] = NULL;
- }
- }
-
- sc->opt1 = 0;
- sc->opt2 = 0;
- sc->opt3 = 0;
- sc->option &= OPTION_MASK;
-
- if( type == 0 || type == 2 )
- clif_changeoption(bl);
-
- return 1;
-}
-
-/*==========================================
- * Special condition we want to effectuate, check before ending a status.
- *------------------------------------------*/
-int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const char* file, int line)
-{
- struct map_session_data *sd;
- struct status_change *sc;
- struct status_change_entry *sce;
- struct status_data *status;
- struct view_data *vd;
- int opt_flag=0, calc_flag;
-
- nullpo_ret(bl);
-
- sc = status_get_sc(bl);
- status = status_get_status_data(bl);
-
- if(type < 0 || type >= SC_MAX || !sc || !(sce = sc->data[type]))
- return 0;
-
- sd = BL_CAST(BL_PC,bl);
-
- if (sce->timer != tid && tid != INVALID_TIMER)
- return 0;
-
- if (tid == INVALID_TIMER) {
- if (type == SC_ENDURE && sce->val4)
- //Do not end infinite endure.
- return 0;
- if (sce->timer != INVALID_TIMER) //Could be a SC with infinite duration
- delete_timer(sce->timer,status_change_timer);
- if (sc->opt1)
- switch (type) {
- //"Ugly workaround" [Skotlex]
- //delays status change ending so that a skill that sets opt1 fails to
- //trigger when it also removed one
- case SC_STONE:
- sce->val3 = 0; //Petrify time counter.
- case SC_FREEZE:
- case SC_STUN:
- case SC_SLEEP:
- if (sce->val1) {
- //Removing the 'level' shouldn't affect anything in the code
- //since these SC are not affected by it, and it lets us know
- //if we have already delayed this attack or not.
- sce->val1 = 0;
- sce->timer = add_timer(gettick()+10, status_change_timer, bl->id, type);
- return 1;
- }
- }
- }
-
- (sc->count)--;
-
- if ( StatusChangeStateTable[type] )
- status_calc_state(bl,sc,( enum scs_flag ) StatusChangeStateTable[type],false);
-
- sc->data[type] = NULL;
-
- vd = status_get_viewdata(bl);
- calc_flag = StatusChangeFlagTable[type];
- switch(type){
- case SC_GRANITIC_ARMOR:{
- int dammage = status->max_hp*sce->val3/100;
- if(status->hp < dammage) //to not kill him
- dammage = status->hp-1;
- status_damage(NULL, bl, dammage,0,0,1);
- break;
- }
- case SC_PYROCLASTIC:
- if(bl->type == BL_PC)
- skill_break_equip(bl,EQP_WEAPON,10000,BCT_SELF);
- break;
- case SC_WEDDING:
- case SC_XMAS:
- case SC_SUMMER:
- if (!vd) break;
- if (sd)
- { //Load data from sd->status.* as the stored values could have changed.
- //Must remove OPTION to prevent class being rechanged.
- sc->option &= type==SC_WEDDING?~OPTION_WEDDING:type==SC_XMAS?~OPTION_XMAS:~OPTION_SUMMER;
- clif_changeoption(&sd->bl);
- status_set_viewdata(bl, sd->status.class_);
- } else {
- vd->class_ = sce->val1;
- vd->weapon = sce->val2;
- vd->shield = sce->val3;
- vd->cloth_color = sce->val4;
- }
- clif_changelook(bl,LOOK_BASE,vd->class_);
- clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color);
- clif_changelook(bl,LOOK_WEAPON,vd->weapon);
- clif_changelook(bl,LOOK_SHIELD,vd->shield);
- if(sd) clif_skillinfoblock(sd);
- break;
- case SC_RUN:
- {
- struct unit_data *ud = unit_bl2ud(bl);
- bool begin_spurt = true;
- if (ud) {
- if(!ud->state.running)
- begin_spurt = false;
- ud->state.running = 0;
- if (ud->walktimer != INVALID_TIMER)
- unit_stop_walking(bl,1);
- }
- if (begin_spurt && sce->val1 >= 7 &&
- DIFF_TICK(gettick(), sce->val4) <= 1000 &&
- (!sd || (sd->weapontype1 == 0 && sd->weapontype2 == 0))
- )
- sc_start(bl,SC_SPURT,100,sce->val1,skill_get_time2(status_sc2skill(type), sce->val1));
- }
- break;
- case SC_AUTOBERSERK:
- if (sc->data[SC_PROVOKE] && sc->data[SC_PROVOKE]->val2 == 1)
- status_change_end(bl, SC_PROVOKE, INVALID_TIMER);
- break;
-
- case SC_ENDURE:
- case SC_DEFENDER:
- case SC_REFLECTSHIELD:
- case SC_AUTOGUARD:
- {
- struct map_session_data *tsd;
- if( bl->type == BL_PC )
- { // Clear Status from others
- int i;
- for( i = 0; i < 5; i++ )
- {
- if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) && tsd->sc.data[type] )
- status_change_end(&tsd->bl, type, INVALID_TIMER);
- }
- }
- else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag )
- { // Clear Status from Master
- tsd = ((TBL_MER*)bl)->master;
- if( tsd && tsd->sc.data[type] )
- status_change_end(&tsd->bl, type, INVALID_TIMER);
- }
- }
- break;
- case SC_DEVOTION:
- {
- struct block_list *d_bl = map_id2bl(sce->val1);
- if( d_bl )
- {
- if( d_bl->type == BL_PC )
- ((TBL_PC*)d_bl)->devotion[sce->val2] = 0;
- else if( d_bl->type == BL_MER )
- ((TBL_MER*)d_bl)->devotion_flag = 0;
- clif_devotion(d_bl, NULL);
- }
-
- status_change_end(bl, SC_AUTOGUARD, INVALID_TIMER);
- status_change_end(bl, SC_DEFENDER, INVALID_TIMER);
- status_change_end(bl, SC_REFLECTSHIELD, INVALID_TIMER);
- status_change_end(bl, SC_ENDURE, INVALID_TIMER);
- }
- break;
-
- case SC_BLADESTOP:
- if(sce->val4)
- {
- int tid = sce->val4;
- struct block_list *tbl = map_id2bl(tid);
- struct status_change *tsc = status_get_sc(tbl);
- sce->val4 = 0;
- if(tbl && tsc && tsc->data[SC_BLADESTOP])
- {
- tsc->data[SC_BLADESTOP]->val4 = 0;
- status_change_end(tbl, SC_BLADESTOP, INVALID_TIMER);
- }
- clif_bladestop(bl, tid, 0);
- }
- break;
- case SC_DANCING:
- {
- const char* prevfile = "<unknown>";
- int prevline = 0;
- struct map_session_data *dsd;
- struct status_change_entry *dsc;
- struct skill_unit_group *group;
-
- if( sd )
- {
- if( sd->delunit_prevfile )
- {// initially this is NULL, when a character logs in
- prevfile = sd->delunit_prevfile;
- prevline = sd->delunit_prevline;
- }
- else
- {
- prevfile = "<none>";
- }
- sd->delunit_prevfile = file;
- sd->delunit_prevline = line;
- }
-
- if(sce->val4 && sce->val4 != BCT_SELF && (dsd=map_id2sd(sce->val4)))
- {// end status on partner as well
- dsc = dsd->sc.data[SC_DANCING];
- if(dsc) {
-
- //This will prevent recursive loops.
- dsc->val2 = dsc->val4 = 0;
-
- status_change_end(&dsd->bl, SC_DANCING, INVALID_TIMER);
- }
- }
-
- if(sce->val2)
- {// erase associated land skill
- group = skill_id2group(sce->val2);
-
- if( group == NULL )
- {
- ShowDebug("status_change_end: SC_DANCING is missing skill unit group (val1=%d, val2=%d, val3=%d, val4=%d, timer=%d, tid=%d, char_id=%d, map=%s, x=%d, y=%d, prev=%s:%d, from=%s:%d). Please report this! (#3504)\n",
- sce->val1, sce->val2, sce->val3, sce->val4, sce->timer, tid,
- sd ? sd->status.char_id : 0,
- mapindex_id2name(map_id2index(bl->m)), bl->x, bl->y,
- prevfile, prevline,
- file, line);
- }
-
- sce->val2 = 0;
- skill_delunitgroup(group);
- }
-
- if((sce->val1&0xFFFF) == CG_MOONLIT)
- clif_status_change(bl,SI_MOONLIT,0,0,0,0,0);
-
- status_change_end(bl, SC_LONGING, INVALID_TIMER);
- }
- break;
- case SC_NOCHAT:
- if (sd && sd->status.manner < 0 && tid != INVALID_TIMER)
- sd->status.manner = 0;
- if (sd && tid == INVALID_TIMER)
- {
- clif_changestatus(sd,SP_MANNER,sd->status.manner);
- clif_updatestatus(sd,SP_MANNER);
- }
- break;
- case SC_SPLASHER:
- {
- struct block_list *src=map_id2bl(sce->val3);
- if(src && tid != INVALID_TIMER)
- skill_castend_damage_id(src, bl, sce->val2, sce->val1, gettick(), SD_LEVEL );
- }
- break;
- case SC_CLOSECONFINE2:
- {
- struct block_list *src = sce->val2?map_id2bl(sce->val2):NULL;
- struct status_change *sc2 = src?status_get_sc(src):NULL;
- if (src && sc2 && sc2->data[SC_CLOSECONFINE]) {
- //If status was already ended, do nothing.
- //Decrease count
- if (--(sc2->data[SC_CLOSECONFINE]->val1) <= 0) //No more holds, free him up.
- status_change_end(src, SC_CLOSECONFINE, INVALID_TIMER);
- }
- }
- case SC_CLOSECONFINE:
- if (sce->val2 > 0) {
- //Caster has been unlocked... nearby chars need to be unlocked.
- int range = 1
- +skill_get_range2(bl, status_sc2skill(type), sce->val1)
- +skill_get_range2(bl, TF_BACKSLIDING, 1); //Since most people use this to escape the hold....
- map_foreachinarea(status_change_timer_sub,
- bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,BL_CHAR,bl,sce,type,gettick());
- }
- break;
- case SC_COMBO:
- if( sd )
- switch (sce->val1) {
- case MO_COMBOFINISH:
- case CH_TIGERFIST:
- case CH_CHAINCRUSH:
- clif_skillinfo(sd, MO_EXTREMITYFIST, 0);
- break;
- case TK_JUMPKICK:
- clif_skillinfo(sd, TK_JUMPKICK, 0);
- break;
- case MO_TRIPLEATTACK:
- if (pc_checkskill(sd, SR_DRAGONCOMBO) > 0)
- clif_skillinfo(sd, SR_DRAGONCOMBO, 0);
- break;
- case SR_FALLENEMPIRE:
- clif_skillinfo(sd, SR_GATEOFHELL, 0);
- clif_skillinfo(sd, SR_TIGERCANNON, 0);
- break;
- }
- break;
-
- case SC_MARIONETTE:
- case SC_MARIONETTE2: /// Marionette target
- if (sce->val1)
- { // check for partner and end their marionette status as well
- enum sc_type type2 = (type == SC_MARIONETTE) ? SC_MARIONETTE2 : SC_MARIONETTE;
- struct block_list *pbl = map_id2bl(sce->val1);
- struct status_change* sc2 = pbl?status_get_sc(pbl):NULL;
-
- if (sc2 && sc2->data[type2])
- {
- sc2->data[type2]->val1 = 0;
- status_change_end(pbl, type2, INVALID_TIMER);
- }
- }
- break;
-
- case SC_BERSERK:
- case SC_SATURDAYNIGHTFEVER:
- //If val2 is removed, no HP penalty (dispelled?) [Skotlex]
- if (status->hp > 100 && sce->val2)
- status_set_hp(bl, 100, 0);
- if(sc->data[SC_ENDURE] && sc->data[SC_ENDURE]->val4 == 2)
- {
- sc->data[SC_ENDURE]->val4 = 0;
- status_change_end(bl, SC_ENDURE, INVALID_TIMER);
- }
- case SC__BLOODYLUST:
- sc_start4(bl, SC_REGENERATION, 100, 10,0,0,(RGN_HP|RGN_SP), skill_get_time(LK_BERSERK, sce->val1));
- if( type == SC_SATURDAYNIGHTFEVER ) //Sit down force of Saturday Night Fever has the duration of only 3 seconds.
- sc_start(bl,SC_SITDOWN_FORCE,100,sce->val1,skill_get_time2(WM_SATURDAY_NIGHT_FEVER,sce->val1));
- break;
- case SC_GOSPEL:
- if (sce->val3) { //Clear the group.
- struct skill_unit_group* group = skill_id2group(sce->val3);
- sce->val3 = 0;
- skill_delunitgroup(group);
- }
- break;
- case SC_HERMODE:
- if(sce->val3 == BCT_SELF)
- skill_clear_unitgroup(bl);
- break;
- case SC_BASILICA: //Clear the skill area. [Skotlex]
- skill_clear_unitgroup(bl);
- break;
- case SC_TRICKDEAD:
- if (vd) vd->dead_sit = 0;
- break;
- case SC_WARM:
- case SC__MANHOLE:
- if (sce->val4) { //Clear the group.
- struct skill_unit_group* group = skill_id2group(sce->val4);
- sce->val4 = 0;
- if( group ) /* might have been cleared before status ended, e.g. land protector */
- skill_delunitgroup(group);
- }
- break;
- case SC_KAAHI:
- //Delete timer if it exists.
- if (sce->val4 != INVALID_TIMER)
- delete_timer(sce->val4,kaahi_heal_timer);
- break;
- case SC_JAILED:
- if(tid == INVALID_TIMER)
- break;
- //natural expiration.
- if(sd && sd->mapindex == sce->val2)
- pc_setpos(sd,(unsigned short)sce->val3,sce->val4&0xFFFF, sce->val4>>16, CLR_TELEPORT);
- break; //guess hes not in jail :P
- case SC_CHANGE:
- if (tid == INVALID_TIMER)
- break;
- // "lose almost all their HP and SP" on natural expiration.
- status_set_hp(bl, 10, 0);
- status_set_sp(bl, 10, 0);
- break;
- case SC_AUTOTRADE:
- if (tid == INVALID_TIMER)
- break;
- // Note: vending/buying is closed by unit_remove_map, no
- // need to do it here.
- map_quit(sd);
- // Because map_quit calls status_change_end with tid -1
- // from here it's not neccesary to continue
- return 1;
- break;
- case SC_STOP:
- if( sce->val2 )
- {
- struct block_list* tbl = map_id2bl(sce->val2);
- sce->val2 = 0;
- if( tbl && (sc = status_get_sc(tbl)) && sc->data[SC_STOP] && sc->data[SC_STOP]->val2 == bl->id )
- status_change_end(tbl, SC_STOP, INVALID_TIMER);
- }
- break;
- /**
- * 3rd Stuff
- **/
- case SC_MILLENNIUMSHIELD:
- clif_millenniumshield(sd,0);
- break;
- case SC_HALLUCINATIONWALK:
- sc_start(bl,SC_HALLUCINATIONWALK_POSTDELAY,100,sce->val1,skill_get_time2(GC_HALLUCINATIONWALK,sce->val1));
- break;
- case SC_WHITEIMPRISON:
- {
- struct block_list* src = map_id2bl(sce->val2);
- if( tid == -1 || !src)
- break; // Terminated by Damage
- status_fix_damage(src,bl,400*sce->val1,clif_damage(bl,bl,gettick(),0,0,400*sce->val1,0,0,0));
- }
- break;
- case SC_WUGDASH:
- {
- struct unit_data *ud = unit_bl2ud(bl);
- if (ud) {
- ud->state.running = 0;
- if (ud->walktimer != -1)
- unit_stop_walking(bl,1);
- }
- }
- break;
- case SC_ADORAMUS:
- status_change_end(bl, SC_BLIND, INVALID_TIMER);
- break;
- case SC__SHADOWFORM: {
- struct map_session_data *s_sd = map_id2sd(sce->val2);
- if( !s_sd )
- break;
- s_sd->shadowform_id = 0;
- }
- break;
- case SC_SITDOWN_FORCE:
- if( sd && pc_issit(sd) ) {
- pc_setstand(sd);
- clif_standing(bl);
- }
- break;
- case SC_NEUTRALBARRIER_MASTER:
- case SC_STEALTHFIELD_MASTER:
- if( sce->val2 ) {
- struct skill_unit_group* group = skill_id2group(sce->val2);
- sce->val2 = 0;
- if( group ) /* might have been cleared before status ended, e.g. land protector */
- skill_delunitgroup(group);
- }
- break;
- case SC_BANDING:
- if(sce->val4) {
- struct skill_unit_group *group = skill_id2group(sce->val4);
- sce->val4 = 0;
- if( group ) /* might have been cleared before status ended, e.g. land protector */
- skill_delunitgroup(group);
- }
- break;
- case SC_CURSEDCIRCLE_ATKER:
- if( sce->val2 ) // used the default area size cause there is a chance the caster could knock back and can't clear the target.
- map_foreachinrange(status_change_timer_sub, bl, battle_config.area_size,BL_CHAR, bl, sce, SC_CURSEDCIRCLE_TARGET, gettick());
- break;
- case SC_RAISINGDRAGON:
- if( sd && sce->val2 && !pc_isdead(sd) ) {
- int i;
- i = min(sd->spiritball,5);
- pc_delspiritball(sd, sd->spiritball, 0);
- status_change_end(bl, SC_EXPLOSIONSPIRITS, INVALID_TIMER);
- while( i > 0 ) {
- pc_addspiritball(sd, skill_get_time(MO_CALLSPIRITS, pc_checkskill(sd,MO_CALLSPIRITS)), 5);
- --i;
- }
- }
- break;
- case SC_CURSEDCIRCLE_TARGET:
- {
- struct block_list *src = map_id2bl(sce->val2);
- struct status_change *sc = status_get_sc(src);
- if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] && --(sc->data[SC_CURSEDCIRCLE_ATKER]->val2) == 0 ){
- status_change_end(src, SC_CURSEDCIRCLE_ATKER, INVALID_TIMER);
- clif_bladestop(bl, sce->val2, 0);
- }
- }
- break;
- case SC_BLOODSUCKER:
- if( sce->val2 ){
- struct block_list *src = map_id2bl(sce->val2);
- if(src){
- struct status_change *sc = status_get_sc(src);
- sc->bs_counter--;
- }
- }
- break;
- case SC_VACUUM_EXTREME:
- if(sc && sc->cant.move > 0) sc->cant.move--;
- break;
- case SC_KYOUGAKU:
- clif_status_load(bl, SI_KYOUGAKU, 0); // Avoid client crash
- clif_status_load(bl, SI_ACTIVE_MONSTER_TRANSFORM, 0);
- break;
- case SC_INTRAVISION:
- calc_flag = SCB_ALL;/* required for overlapping */
- break;
- }
-
- opt_flag = 1;
- switch(type){
- case SC_STONE:
- case SC_FREEZE:
- case SC_STUN:
- case SC_SLEEP:
- case SC_DEEPSLEEP:
- case SC_BURNING:
- case SC_WHITEIMPRISON:
- case SC_CRYSTALIZE:
- sc->opt1 = 0;
- break;
-
- case SC_POISON:
- case SC_CURSE:
- case SC_SILENCE:
- case SC_BLIND:
- sc->opt2 &= ~(1<<(type-SC_POISON));
- break;
- case SC_DPOISON:
- sc->opt2 &= ~OPT2_DPOISON;
- break;
- case SC_SIGNUMCRUCIS:
- sc->opt2 &= ~OPT2_SIGNUMCRUCIS;
- break;
-
- case SC_HIDING:
- sc->option &= ~OPTION_HIDE;
- opt_flag|= 2|4; //Check for warp trigger + AoE trigger
- break;
- case SC_CLOAKING:
- case SC_CLOAKINGEXCEED:
- case SC__INVISIBILITY:
- sc->option &= ~OPTION_CLOAK;
- case SC_CAMOUFLAGE:
- opt_flag|= 2;
- break;
- case SC_CHASEWALK:
- sc->option &= ~(OPTION_CHASEWALK|OPTION_CLOAK);
- opt_flag|= 2;
- break;
- case SC_SIGHT:
- sc->option &= ~OPTION_SIGHT;
- break;
- case SC_WEDDING:
- sc->option &= ~OPTION_WEDDING;
- break;
- case SC_XMAS:
- sc->option &= ~OPTION_XMAS;
- break;
- case SC_SUMMER:
- sc->option &= ~OPTION_SUMMER;
- break;
- case SC_ORCISH:
- sc->option &= ~OPTION_ORCISH;
- break;
- case SC_RUWACH:
- sc->option &= ~OPTION_RUWACH;
- break;
- case SC_FUSION:
- sc->option &= ~OPTION_FLYING;
- break;
- //opt3
- case SC_TWOHANDQUICKEN:
- case SC_ONEHAND:
- case SC_SPEARQUICKEN:
- case SC_CONCENTRATION:
- case SC_MERC_QUICKEN:
- sc->opt3 &= ~OPT3_QUICKEN;
- opt_flag = 0;
- break;
- case SC_OVERTHRUST:
- case SC_MAXOVERTHRUST:
- case SC_SWOO:
- sc->opt3 &= ~OPT3_OVERTHRUST;
- if( type == SC_SWOO )
- opt_flag = 8;
- else
- opt_flag = 0;
- break;
- case SC_ENERGYCOAT:
- case SC_SKE:
- sc->opt3 &= ~OPT3_ENERGYCOAT;
- opt_flag = 0;
- break;
- case SC_INCATKRATE: //Simulated Explosion spirits effect.
- if (bl->type != BL_MOB)
- {
- opt_flag = 0;
- break;
- }
- case SC_EXPLOSIONSPIRITS:
- sc->opt3 &= ~OPT3_EXPLOSIONSPIRITS;
- opt_flag = 0;
- break;
- case SC_STEELBODY:
- case SC_SKA:
- sc->opt3 &= ~OPT3_STEELBODY;
- opt_flag = 0;
- break;
- case SC_BLADESTOP:
- sc->opt3 &= ~OPT3_BLADESTOP;
- opt_flag = 0;
- break;
- case SC_AURABLADE:
- sc->opt3 &= ~OPT3_AURABLADE;
- opt_flag = 0;
- break;
- case SC_BERSERK:
- opt_flag = 0;
-// case SC__BLOODYLUST:
- sc->opt3 &= ~OPT3_BERSERK;
- break;
-// case ???: // doesn't seem to do anything
-// sc->opt3 &= ~OPT3_LIGHTBLADE;
-// opt_flag = 0;
-// break;
- case SC_DANCING:
- if ((sce->val1&0xFFFF) == CG_MOONLIT)
- sc->opt3 &= ~OPT3_MOONLIT;
- opt_flag = 0;
- break;
- case SC_MARIONETTE:
- case SC_MARIONETTE2:
- sc->opt3 &= ~OPT3_MARIONETTE;
- opt_flag = 0;
- break;
- case SC_ASSUMPTIO:
- sc->opt3 &= ~OPT3_ASSUMPTIO;
- opt_flag = 0;
- break;
- case SC_WARM: //SG skills [Komurka]
- sc->opt3 &= ~OPT3_WARM;
- opt_flag = 0;
- break;
- case SC_KAITE:
- sc->opt3 &= ~OPT3_KAITE;
- opt_flag = 0;
- break;
- case SC_BUNSINJYUTSU:
- sc->opt3 &= ~OPT3_BUNSIN;
- opt_flag = 0;
- break;
- case SC_SPIRIT:
- sc->opt3 &= ~OPT3_SOULLINK;
- opt_flag = 0;
- break;
- case SC_CHANGEUNDEAD:
- sc->opt3 &= ~OPT3_UNDEAD;
- opt_flag = 0;
- break;
-// case ???: // from DA_CONTRACT (looks like biolab mobs aura)
-// sc->opt3 &= ~OPT3_CONTRACT;
-// opt_flag = 0;
-// break;
- default:
- opt_flag = 0;
- }
-
- if (calc_flag&SCB_DYE)
- { //Restore DYE color
- if (vd && !vd->cloth_color && sce->val4)
- clif_changelook(bl,LOOK_CLOTHES_COLOR,sce->val4);
- calc_flag&=~SCB_DYE;
- }
-
- //On Aegis, when turning off a status change, first goes the sc packet, then the option packet.
- clif_status_change(bl,StatusIconChangeTable[type],0,0,0,0,0);
-
- if( opt_flag&8 ) //bugreport:681
- clif_changeoption2(bl);
- else if(opt_flag)
- clif_changeoption(bl);
-
- if (calc_flag)
- status_calc_bl(bl,calc_flag);
-
- if(opt_flag&4) //Out of hiding, invoke on place.
- skill_unit_move(bl,gettick(),1);
-
- if(opt_flag&2 && sd && map_getcell(bl->m,bl->x,bl->y,CELL_CHKNPC))
- npc_touch_areanpc(sd,bl->m,bl->x,bl->y); //Trigger on-touch event.
-
- ers_free(sc_data_ers, sce);
- return 1;
-}
-
-int kaahi_heal_timer(int tid, unsigned int tick, int id, intptr_t data)
-{
- struct block_list *bl;
- struct status_change *sc;
- struct status_change_entry *sce;
- struct status_data *status;
- int hp;
-
- if(!((bl=map_id2bl(id))&&
- (sc=status_get_sc(bl)) &&
- (sce = sc->data[SC_KAAHI])))
- return 0;
-
- if(sce->val4 != tid) {
- ShowError("kaahi_heal_timer: Timer mismatch: %d != %d\n", tid, sce->val4);
- sce->val4 = INVALID_TIMER;
- return 0;
- }
-
- status=status_get_status_data(bl);
- if(!status_charge(bl, 0, sce->val3)) {
- sce->val4 = INVALID_TIMER;
- return 0;
- }
-
- hp = status->max_hp - status->hp;
- if (hp > sce->val2)
- hp = sce->val2;
- if (hp)
- status_heal(bl, hp, 0, 2);
- sce->val4 = INVALID_TIMER;
- return 1;
-}
-
-/*==========================================
- * For recusive status, like for each 5s we drop sp etc.
- * Reseting the end timer.
- *------------------------------------------*/
-int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
-{
- enum sc_type type = (sc_type)data;
- struct block_list *bl;
- struct map_session_data *sd;
- struct status_data *status;
- struct status_change *sc;
- struct status_change_entry *sce;
-
- bl = map_id2bl(id);
- if(!bl)
- {
- ShowDebug("status_change_timer: Null pointer id: %d data: %d\n", id, data);
- return 0;
- }
- sc = status_get_sc(bl);
- status = status_get_status_data(bl);
-
- if(!(sc && (sce = sc->data[type])))
- {
- ShowDebug("status_change_timer: Null pointer id: %d data: %d bl-type: %d\n", id, data, bl->type);
- return 0;
- }
-
- if( sce->timer != tid )
- {
- ShowError("status_change_timer: Mismatch for type %d: %d != %d (bl id %d)\n",type,tid,sce->timer, bl->id);
- return 0;
- }
-
- sd = BL_CAST(BL_PC, bl);
-
-// set the next timer of the sce (don't assume the status still exists)
-#define sc_timer_next(t,f,i,d) \
- if( (sce=sc->data[type]) ) \
- sce->timer = add_timer(t,f,i,d); \
- else \
- ShowError("status_change_timer: Unexpected NULL status change id: %d data: %d\n", id, data)
-
- switch(type)
- {
- case SC_MAXIMIZEPOWER:
- case SC_CLOAKING:
- if(!status_charge(bl, 0, 1))
- break; //Not enough SP to continue.
- sc_timer_next(sce->val2+tick, status_change_timer, bl->id, data);
- return 0;
-
- case SC_CHASEWALK:
- if(!status_charge(bl, 0, sce->val4))
- break; //Not enough SP to continue.
-
- if (!sc->data[SC_INCSTR]) {
- sc_start(bl, SC_INCSTR,100,1<<(sce->val1-1),
- (sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_ROGUE?10:1) //SL bonus -> x10 duration
- *skill_get_time2(status_sc2skill(type),sce->val1));
- }
- sc_timer_next(sce->val2+tick, status_change_timer, bl->id, data);
- return 0;
- break;
-
- case SC_SKA:
- if(--(sce->val2)>0){
- sce->val3 = rnd()%100; //Random defense.
- sc_timer_next(1000+tick, status_change_timer,bl->id, data);
- return 0;
- }
- break;
-
- case SC_HIDING:
- if(--(sce->val2)>0){
-
- if(sce->val2 % sce->val4 == 0 && !status_charge(bl, 0, 1))
- break; //Fail if it's time to substract SP and there isn't.
-
- sc_timer_next(1000+tick, status_change_timer,bl->id, data);
- return 0;
- }
- break;
-
- case SC_SIGHT:
- case SC_RUWACH:
- case SC_SIGHTBLASTER:
- if(type == SC_SIGHTBLASTER)
- map_foreachinrange( status_change_timer_sub, bl, sce->val3, BL_CHAR|BL_SKILL, bl, sce, type, tick);
- else
- map_foreachinrange( status_change_timer_sub, bl, sce->val3, BL_CHAR, bl, sce, type, tick);
-
- if( --(sce->val2)>0 ){
- sce->val4 += 250; // use for Shadow Form 2 seconds checking.
- sc_timer_next(250+tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_PROVOKE:
- if(sce->val2) { //Auto-provoke (it is ended in status_heal)
- sc_timer_next(1000*60+tick,status_change_timer, bl->id, data );
- return 0;
- }
- break;
-
- case SC_STONE:
- if(sc->opt1 == OPT1_STONEWAIT && sce->val3) {
- sce->val4 = 0;
- unit_stop_walking(bl,1);
- unit_stop_attack(bl);
- sc->opt1 = OPT1_STONE;
- clif_changeoption(bl);
- sc_timer_next(1000+tick,status_change_timer, bl->id, data );
- status_calc_bl(bl, StatusChangeFlagTable[type]);
- return 0;
- }
- if(--(sce->val3) > 0) {
- if(++(sce->val4)%5 == 0 && status->hp > status->max_hp/4)
- status_percent_damage(NULL, bl, 1, 0, false);
- sc_timer_next(1000+tick,status_change_timer, bl->id, data );
- return 0;
- }
- break;
-
- case SC_POISON:
- if(status->hp <= max(status->max_hp>>2, sce->val4)) //Stop damaging after 25% HP left.
- break;
- case SC_DPOISON:
- if (--(sce->val3) > 0) {
- if (!sc->data[SC_SLOWPOISON]) {
- if( sce->val2 && bl->type == BL_MOB ) {
- struct block_list* src = map_id2bl(sce->val2);
- if( src )
- mob_log_damage((TBL_MOB*)bl,src,sce->val4);
- }
- map_freeblock_lock();
- status_zap(bl, sce->val4, 0);
- if (sc->data[type]) { // Check if the status still last ( can be dead since then ).
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data );
- }
- map_freeblock_unlock();
- }
- return 0;
- }
- break;
-
- case SC_TENSIONRELAX:
- if(status->max_hp > status->hp && --(sce->val3) > 0){
- sc_timer_next(sce->val4+tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_KNOWLEDGE:
- if (!sd) break;
- if(bl->m == sd->feel_map[0].m ||
- bl->m == sd->feel_map[1].m ||
- bl->m == sd->feel_map[2].m)
- { //Timeout will be handled by pc_setpos
- sce->timer = INVALID_TIMER;
- return 0;
- }
- break;
-
- case SC_BLEEDING:
- if (--(sce->val4) >= 0) {
- int hp = rnd()%600 + 200;
- map_freeblock_lock();
- status_fix_damage(NULL, bl, sd||hp<status->hp?hp:status->hp-1, 1);
- if( sc->data[type] ) {
- if( status->hp == 1 ) {
- map_freeblock_unlock();
- break;
- }
- sc_timer_next(10000 + tick, status_change_timer, bl->id, data);
- }
- map_freeblock_unlock();
- return 0;
- }
- break;
-
- case SC_S_LIFEPOTION:
- case SC_L_LIFEPOTION:
- if( sd && --(sce->val4) >= 0 )
- {
- // val1 < 0 = per max% | val1 > 0 = exact amount
- int hp = 0;
- if( status->hp < status->max_hp )
- hp = (sce->val1 < 0) ? (int)(sd->status.max_hp * -1 * sce->val1 / 100.) : sce->val1 ;
- status_heal(bl, hp, 0, 2);
- sc_timer_next((sce->val2 * 1000) + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_BOSSMAPINFO:
- if( sd && --(sce->val4) >= 0 )
- {
- struct mob_data *boss_md = map_id2boss(sce->val1);
- if( boss_md && sd->bl.m == boss_md->bl.m )
- {
- clif_bossmapinfo(sd->fd, boss_md, 1); // Update X - Y on minimap
- if (boss_md->bl.prev != NULL) {
- sc_timer_next(5000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- }
- }
- break;
-
- case SC_DANCING: //SP consumption by time of dancing skills
- {
- int s = 0;
- int sp = 1;
- if (--sce->val3 <= 0)
- break;
- switch(sce->val1&0xFFFF){
- case BD_RICHMANKIM:
- case BD_DRUMBATTLEFIELD:
- case BD_RINGNIBELUNGEN:
- case BD_SIEGFRIED:
- case BA_DISSONANCE:
- case BA_ASSASSINCROSS:
- case DC_UGLYDANCE:
- s=3;
- break;
- case BD_LULLABY:
- case BD_ETERNALCHAOS:
- case BD_ROKISWEIL:
- case DC_FORTUNEKISS:
- s=4;
- break;
- case CG_HERMODE:
- case BD_INTOABYSS:
- case BA_WHISTLE:
- case DC_HUMMING:
- case BA_POEMBRAGI:
- case DC_SERVICEFORYOU:
- s=5;
- break;
- case BA_APPLEIDUN:
- #ifdef RENEWAL
- s=5;
- #else
- s=6;
- #endif
- break;
- case CG_MOONLIT:
- //Moonlit's cost is 4sp*skill_lv [Skotlex]
- sp= 4*(sce->val1>>16);
- //Upkeep is also every 10 secs.
- case DC_DONTFORGETME:
- s=10;
- break;
- }
- if( s != 0 && sce->val3 % s == 0 )
- {
- if (sc->data[SC_LONGING])
- sp*= 3;
- if (!status_charge(bl, 0, sp))
- break;
- }
- sc_timer_next(1000+tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
- case SC__BLOODYLUST:
- case SC_BERSERK:
- // 5% every 10 seconds [DracoRPG]
- if( --( sce->val3 ) > 0 && status_charge(bl, sce->val2, 0) && status->hp > 100 )
- {
- sc_timer_next(sce->val4+tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_NOCHAT:
- if(sd){
- sd->status.manner++;
- clif_changestatus(sd,SP_MANNER,sd->status.manner);
- clif_updatestatus(sd,SP_MANNER);
- if (sd->status.manner < 0)
- { //Every 60 seconds your manner goes up by 1 until it gets back to 0.
- sc_timer_next(60000+tick, status_change_timer, bl->id, data);
- return 0;
- }
- }
- break;
-
- case SC_SPLASHER:
- // custom Venom Splasher countdown timer
- //if (sce->val4 % 1000 == 0) {
- // char timer[10];
- // snprintf (timer, 10, "%d", sce->val4/1000);
- // clif_message(bl, timer);
- //}
- if((sce->val4 -= 500) > 0) {
- sc_timer_next(500 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_MARIONETTE:
- case SC_MARIONETTE2:
- {
- struct block_list *pbl = map_id2bl(sce->val1);
- if( pbl && check_distance_bl(bl, pbl, 7) )
- {
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- }
- break;
-
- case SC_GOSPEL:
- if(sce->val4 == BCT_SELF && --(sce->val2) > 0)
- {
- int hp, sp;
- hp = (sce->val1 > 5) ? 45 : 30;
- sp = (sce->val1 > 5) ? 35 : 20;
- if(!status_charge(bl, hp, sp))
- break;
- sc_timer_next(10000+tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_JAILED:
- if(sce->val1 == INT_MAX || --(sce->val1) > 0)
- {
- sc_timer_next(60000+tick, status_change_timer, bl->id,data);
- return 0;
- }
- break;
-
- case SC_BLIND:
- if(sc->data[SC_FOGWALL])
- { //Blind lasts forever while you are standing on the fog.
- sc_timer_next(5000+tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
- case SC_ABUNDANCE:
- if(--(sce->val4) > 0) {
- status_heal(bl,0,60,0);
- sc_timer_next(10000+tick, status_change_timer, bl->id, data);
- }
- break;
-
- case SC_PYREXIA:
- if( --(sce->val4) >= 0 ) {
- map_freeblock_lock();
- clif_damage(bl,bl,tick,status_get_amotion(bl),status_get_dmotion(bl)+500,100,0,0,0);
- status_fix_damage(NULL,bl,100,0);
- if( sc->data[type] ) {
- sc_timer_next(3000+tick,status_change_timer,bl->id,data);
- }
- map_freeblock_unlock();
- return 0;
- }
- break;
-
- case SC_LEECHESEND:
- if( --(sce->val4) >= 0 ) {
- int damage = status->max_hp/100; // {Target VIT x (New Poison Research Skill Level - 3)} + (Target HP/100)
- damage += status->vit * (sce->val1 - 3);
- unit_skillcastcancel(bl,2);
- map_freeblock_lock();
- status_damage(bl, bl, damage, 0, clif_damage(bl,bl,tick,status_get_amotion(bl),status_get_dmotion(bl)+500,damage,1,0,0), 1);
- if( sc->data[type] ) {
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data );
- }
- map_freeblock_unlock();
- return 0;
- }
- break;
-
- case SC_MAGICMUSHROOM:
- if( --(sce->val4) >= 0 ) {
- bool flag = 0;
- int damage = status->max_hp * 3 / 100;
- if( status->hp <= damage )
- damage = status->hp - 1; // Cannot Kill
-
- if( damage > 0 ) { // 3% Damage each 4 seconds
- map_freeblock_lock();
- status_zap(bl,damage,0);
- flag = !sc->data[type]; // Killed? Should not
- map_freeblock_unlock();
- }
-
- if( !flag ) { // Random Skill Cast
- if (sd && !pc_issit(sd)) { //can't cast if sit
- int mushroom_skill_id = 0, i;
- unit_stop_attack(bl);
- unit_skillcastcancel(bl,1);
- do {
- i = rnd() % MAX_SKILL_MAGICMUSHROOM_DB;
- mushroom_skill_id = skill_magicmushroom_db[i].skill_id;
- }
- while( mushroom_skill_id == 0 );
-
- switch( skill_get_casttype(mushroom_skill_id) ) { // Magic Mushroom skills are buffs or area damage
- case CAST_GROUND:
- skill_castend_pos2(bl,bl->x,bl->y,mushroom_skill_id,1,tick,0);
- break;
- case CAST_NODAMAGE:
- skill_castend_nodamage_id(bl,bl,mushroom_skill_id,1,tick,0);
- break;
- case CAST_DAMAGE:
- skill_castend_damage_id(bl,bl,mushroom_skill_id,1,tick,0);
- break;
- }
- }
-
- clif_emotion(bl,E_HEH);
- sc_timer_next(4000+tick,status_change_timer,bl->id,data);
- }
- return 0;
- }
- break;
-
- case SC_TOXIN:
- if( --(sce->val4) >= 0 )
- { //Damage is every 10 seconds including 3%sp drain.
- map_freeblock_lock();
- clif_damage(bl,bl,tick,status_get_amotion(bl),1,1,0,0,0);
- status_damage(NULL, bl, 1, status->max_sp * 3 / 100, 0, 0); //cancel dmg only if cancelable
- if( sc->data[type] ) {
- sc_timer_next(10000 + tick, status_change_timer, bl->id, data );
- }
- map_freeblock_unlock();
- return 0;
- }
- break;
-
- case SC_OBLIVIONCURSE:
- if( --(sce->val4) >= 0 )
- {
- clif_emotion(bl,E_WHAT);
- sc_timer_next(3000 + tick, status_change_timer, bl->id, data );
- return 0;
- }
- break;
-
- case SC_WEAPONBLOCKING:
- if( --(sce->val4) >= 0 )
- {
- if( !status_charge(bl,0,3) )
- break;
- sc_timer_next(3000+tick,status_change_timer,bl->id,data);
- return 0;
- }
- break;
-
- case SC_CLOAKINGEXCEED:
- if(!status_charge(bl,0,10-sce->val1))
- break;
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- return 0;
-
- case SC_RENOVATIO:
- if( --(sce->val4) >= 0 )
- {
- int heal = status->max_hp * 3 / 100;
- if( sc && sc->data[SC_AKAITSUKI] && heal )
- heal = ~heal + 1;
- status_heal(bl, heal, 0, 2);
- sc_timer_next(5000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_BURNING:
- if( --(sce->val4) >= 0 )
- {
- struct block_list *src = map_id2bl(sce->val3);
- int damage = 1000 + 3 * status_get_max_hp(bl) / 100; // Deals fixed (1000 + 3%*MaxHP)
-
- map_freeblock_lock();
- clif_damage(bl,bl,tick,0,0,damage,1,9,0); //damage is like endure effect with no walk delay
- status_damage(src, bl, damage, 0, 0, 1);
-
- if( sc->data[type]){ // Target still lives. [LimitLine]
- sc_timer_next(2000 + tick, status_change_timer, bl->id, data);
- }
- map_freeblock_unlock();
- return 0;
- }
- break;
-
- case SC_FEAR:
- if( --(sce->val4) >= 0 )
- {
- if( sce->val2 > 0 )
- sce->val2--;
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_SPHERE_1:
- case SC_SPHERE_2:
- case SC_SPHERE_3:
- case SC_SPHERE_4:
- case SC_SPHERE_5:
- if( --(sce->val4) >= 0 )
- {
- if( !status_charge(bl, 0, 1) )
- break;
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_READING_SB:
- if( !status_charge(bl, 0, sce->val2) ){
- int i;
- for(i = SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++) // Also remove stored spell as well.
- status_change_end(bl, (sc_type)i, INVALID_TIMER);
- break;
- }
- sc_timer_next(5000 + tick, status_change_timer, bl->id, data);
- return 0;
-
- case SC_ELECTRICSHOCKER:
- if( --(sce->val4) >= 0 )
- {
- status_charge(bl, 0, status->max_sp / 100 * sce->val1 );
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_CAMOUFLAGE:
- if(--(sce->val4) > 0){
- status_charge(bl,0,7 - sce->val1);
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC__REPRODUCE:
- if(!status_charge(bl, 0, 1))
- break;
- sc_timer_next(1000+tick, status_change_timer, bl->id, data);
- return 0;
-
- case SC__SHADOWFORM:
- if( --(sce->val4) >= 0 )
- {
- if( !status_charge(bl, 0, sce->val1 - (sce->val1 - 1)) )
- break;
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC__INVISIBILITY:
- if( --(sce->val4) >= 0 )
- {
- if( !status_charge(bl, 0, (status->sp * 6 - sce->val1) / 100) )// 6% - skill_lv.
- break;
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_STRIKING:
- if( --(sce->val4) >= 0 )
- {
- if( !status_charge(bl,0, sce->val1 ) )
- break;
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
- case SC_VACUUM_EXTREME:
- if( --(sce->val4) >= 0 ){
- if( !unit_is_walking(bl) && !sce->val2 ){
- sc->cant.move++;
- sce->val2 = 1;
- }
- sc_timer_next(100 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
- case SC_BLOODSUCKER:
- if( --(sce->val4) >= 0 ) {
- struct block_list *src = map_id2bl(sce->val2);
- int damage;
- if( !src || (src && (status_isdead(src) || src->m != bl->m || distance_bl(src, bl) >= 12)) )
- break;
- map_freeblock_lock();
- damage = 200 + 100 * sce->val1 + status_get_int(src);
- status_damage(src, bl, damage, 0, clif_damage(bl,bl,tick,status->amotion,status->dmotion+200,damage,1,0,0), 1);
- unit_skillcastcancel(bl,1);
- if ( sc->data[type] ) {
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- }
- map_freeblock_unlock();
- status_heal(src, damage*(5 + 5 * sce->val1)/100, 0, 0); // 5 + 5% per level
- return 0;
- }
- break;
-
- case SC_VOICEOFSIREN:
- if( --(sce->val4) >= 0 )
- {
- clif_emotion(bl,E_LV);
- sc_timer_next(2000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_DEEPSLEEP:
- if( --(sce->val4) >= 0 )
- { // Recovers 1% HP/SP every 2 seconds.
- status_heal(bl, status->max_hp / 100, status->max_sp / 100, 2);
- sc_timer_next(2000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_SIRCLEOFNATURE:
- if( --(sce->val4) >= 0 )
- {
- if( !status_charge(bl,0,sce->val2) )
- break;
- status_heal(bl, sce->val3, 0, 1);
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_SONGOFMANA:
- if( --(sce->val4) >= 0 )
- {
- status_heal(bl,0,sce->val3,3);
- sc_timer_next(3000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
-
- case SC_SATURDAYNIGHTFEVER:
- // 1% HP/SP drain every val4 seconds [Jobbie]
- if( --(sce->val3) >= 0 )
- {
- int hp = status->hp / 100;
- int sp = status->sp / 100;
- if( !status_charge(bl, hp, sp) )
- break;
- sc_timer_next(sce->val4+tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_CRYSTALIZE:
- if( --(sce->val4) >= 0 )
- { // Drains 2% of HP and 1% of SP every seconds.
- if( bl->type != BL_MOB) // doesn't work on mobs
- status_charge(bl, status->max_hp * 2 / 100, status->max_sp / 100);
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_FORCEOFVANGUARD:
- if( !status_charge(bl,0,20) )
- break;
- sc_timer_next(6000 + tick, status_change_timer, bl->id, data);
- return 0;
-
- case SC_BANDING:
- if( status_charge(bl, 0, 7 - sce->val1) )
- {
- if( sd ) pc_banding(sd, sce->val1);
- sc_timer_next(5000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_REFLECTDAMAGE:
- if( --(sce->val4) >= 0 ) {
- if( !status_charge(bl,0,sce->val3) )
- break;
- sc_timer_next(10000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_OVERHEAT_LIMITPOINT:
- if( --(sce->val1) > 0 ) { // Cooling
- sc_timer_next(30000 + tick, status_change_timer, bl->id, data);
- }
- break;
-
- case SC_OVERHEAT:
- {
- int damage = status->max_hp / 100; // Suggestion 1% each second
- if( damage >= status->hp ) damage = status->hp - 1; // Do not kill, just keep you with 1 hp minimum
- map_freeblock_lock();
- status_fix_damage(NULL,bl,damage,clif_damage(bl,bl,tick,0,0,damage,0,0,0));
- if( sc->data[type] ) {
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- }
- map_freeblock_unlock();
- }
- break;
-
- case SC_MAGNETICFIELD:
- {
- if( --(sce->val3) <= 0 )
- break; // Time out
- if( sce->val2 == bl->id )
- {
- if( !status_charge(bl,0,14 + (3 * sce->val1)) )
- break; // No more SP status should end, and in the next second will end for the other affected players
- }
- else
- {
- struct block_list *src = map_id2bl(sce->val2);
- struct status_change *ssc;
- if( !src || (ssc = status_get_sc(src)) == NULL || !ssc->data[SC_MAGNETICFIELD] )
- break; // Source no more under Magnetic Field
- }
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- }
- break;
-
- case SC_INSPIRATION:
- if(--(sce->val4) >= 0)
- {
- int hp = status->max_hp * (7-sce->val1) / 100;
- int sp = status->max_sp * (9-sce->val1) / 100;
-
- if( !status_charge(bl,hp,sp) ) break;
-
- sc_timer_next(1000+tick,status_change_timer,bl->id, data);
- return 0;
- }
- break;
-
- case SC_RAISINGDRAGON:
- // 1% every 5 seconds [Jobbie]
- if( --(sce->val3)>0 && status_charge(bl, sce->val2, 0) )
- {
- if( !sc->data[type] ) return 0;
- sc_timer_next(5000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
-
- case SC_CIRCLE_OF_FIRE:
- case SC_FIRE_CLOAK:
- case SC_WATER_DROP:
- case SC_WATER_SCREEN:
- case SC_WIND_CURTAIN:
- case SC_WIND_STEP:
- case SC_STONE_SHIELD:
- case SC_SOLID_SKIN:
- if( !status_charge(bl,0,sce->val2) ){
- struct block_list *s_bl = battle_get_master(bl);
- if( s_bl )
- status_change_end(s_bl,type+1,INVALID_TIMER);
- status_change_end(bl,type,INVALID_TIMER);
- break;
- }
- sc_timer_next(2000 + tick, status_change_timer, bl->id, data);
- return 0;
-
- case SC_STOMACHACHE:
- if( --(sce->val4) > 0 ){
- status_charge(bl,0,sce->val2); // Reduce 8 every 10 seconds.
- if( sd && !pc_issit(sd) ) // Force to sit every 10 seconds.
- {
- pc_stop_walking(sd,1|4);
- pc_stop_attack(sd);
- pc_setsit(sd);
- clif_sitting(bl);
- }
- sc_timer_next(10000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
- case SC_LEADERSHIP:
- case SC_GLORYWOUNDS:
- case SC_SOULCOLD:
- case SC_HAWKEYES:
- /* they only end by status_change_end */
- sc_timer_next(600000 + tick, status_change_timer, bl->id, data);
- return 0;
- case SC_MEIKYOUSISUI:
- if( --(sce->val4) > 0 ){
- status_heal(bl, status->max_hp * (sce->val1+1) / 100, status->max_sp * sce->val1 / 100, 0);
- sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
- case SC_IZAYOI:
- case SC_KAGEMUSYA:
- if( --(sce->val2) > 0 ){
- if(!status_charge(bl, 0, 1)) break;
- sc_timer_next(1000+tick, status_change_timer, bl->id, data);
- return 0;
- }
- break;
- case SC_ANGRIFFS_MODUS:
- if(--(sce->val4) >= 0) { //drain hp/sp
- if( !status_charge(bl,100,20) ) break;
- sc_timer_next(1000+tick,status_change_timer,bl->id, data);
- return 0;
- }
- break;
- }
-
- // default for all non-handled control paths is to end the status
- return status_change_end( bl,type,tid );
-#undef sc_timer_next
-}
-
-/*==========================================
- * Foreach iteration of repetitive status
- *------------------------------------------*/
-int status_change_timer_sub(struct block_list* bl, va_list ap)
-{
- struct status_change* tsc;
-
- struct block_list* src = va_arg(ap,struct block_list*);
- struct status_change_entry* sce = va_arg(ap,struct status_change_entry*);
- enum sc_type type = (sc_type)va_arg(ap,int); //gcc: enum args get promoted to int
- unsigned int tick = va_arg(ap,unsigned int);
-
- if (status_isdead(bl))
- return 0;
-
- tsc = status_get_sc(bl);
-
- switch( type ) {
- case SC_SIGHT: /* Reveal hidden ennemy on 3*3 range */
- if( tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 >0 && sce->val4%2000 == 0) && // for every 2 seconds do the checking
- rnd()%100 < 100-tsc->data[SC__SHADOWFORM]->val1*10 ) // [100 - (Skill Level x 10)] %
- status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER);
- case SC_CONCENTRATE:
- status_change_end(bl, SC_HIDING, INVALID_TIMER);
- status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
- status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
- status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER);
- status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER);
- break;
- case SC_RUWACH: /* Reveal hidden target and deal little dammages if ennemy */
- if (tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING] ||
- tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_CLOAKINGEXCEED] ||
- tsc->data[SC__INVISIBILITY])) {
- status_change_end(bl, SC_HIDING, INVALID_TIMER);
- status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
- status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER);
- status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
- status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER);
- if(battle_check_target( src, bl, BCT_ENEMY ) > 0)
- skill_attack(BF_MAGIC,src,src,bl,AL_RUWACH,1,tick,0);
- }
- if( tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 >0 && sce->val4%2000 == 0) && // for every 2 seconds do the checking
- rnd()%100 < 100-tsc->data[SC__SHADOWFORM]->val1*10 ) // [100 - (Skill Level x 10)] %
- status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER);
- break;
- case SC_SIGHTBLASTER:
- if (battle_check_target( src, bl, BCT_ENEMY ) > 0 &&
- status_check_skilluse(src, bl, WZ_SIGHTBLASTER, 2))
- {
- skill_attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,1,tick,0);
- if (sce && !(bl->type&BL_SKILL)) //The hit is not counted if it's against a trap
- sce->val2 = 0; //This signals it to end.
- }
- break;
- case SC_CLOSECONFINE:
- //Lock char has released the hold on everyone...
- if (tsc && tsc->data[SC_CLOSECONFINE2] && tsc->data[SC_CLOSECONFINE2]->val2 == src->id) {
- tsc->data[SC_CLOSECONFINE2]->val2 = 0;
- status_change_end(bl, SC_CLOSECONFINE2, INVALID_TIMER);
- }
- break;
- case SC_CURSEDCIRCLE_TARGET:
- if( tsc && tsc->data[SC_CURSEDCIRCLE_TARGET] && tsc->data[SC_CURSEDCIRCLE_TARGET]->val2 == src->id ) {
- clif_bladestop(bl, tsc->data[SC_CURSEDCIRCLE_TARGET]->val2, 0);
- status_change_end(bl, type, INVALID_TIMER);
- }
- break;
- }
- return 0;
-}
-
-/*==========================================
- * Clears buffs/debuffs of a character.
- * type&1 -> buffs, type&2 -> debuffs
- * type&4 -> especific debuffs(implemented with refresh)
- *------------------------------------------*/
-int status_change_clear_buffs (struct block_list* bl, int type)
-{
- int i;
- struct status_change *sc= status_get_sc(bl);
-
- if (!sc || !sc->count)
- return 0;
-
- if (type&6) //Debuffs
- for (i = SC_COMMON_MIN; i <= SC_COMMON_MAX; i++)
- status_change_end(bl, (sc_type)i, INVALID_TIMER);
-
- for( i = SC_COMMON_MAX+1; i < SC_MAX; i++ )
- {
- if(!sc->data[i])
- continue;
-
- switch (i) {
- //Stuff that cannot be removed
- case SC_WEIGHT50:
- case SC_WEIGHT90:
- case SC_COMBO:
- case SC_SMA:
- case SC_DANCING:
- case SC_LEADERSHIP:
- case SC_GLORYWOUNDS:
- case SC_SOULCOLD:
- case SC_HAWKEYES:
- case SC_GUILDAURA:
- case SC_SAFETYWALL:
- case SC_PNEUMA:
- case SC_NOCHAT:
- case SC_JAILED:
- case SC_ANKLE:
- case SC_BLADESTOP:
- case SC_CP_WEAPON:
- case SC_CP_SHIELD:
- case SC_CP_ARMOR:
- case SC_CP_HELM:
- case SC_STRFOOD:
- case SC_AGIFOOD:
- case SC_VITFOOD:
- case SC_INTFOOD:
- case SC_DEXFOOD:
- case SC_LUKFOOD:
- case SC_HITFOOD:
- case SC_FLEEFOOD:
- case SC_BATKFOOD:
- case SC_WATKFOOD:
- case SC_MATKFOOD:
- case SC_FOOD_STR_CASH:
- case SC_FOOD_AGI_CASH:
- case SC_FOOD_VIT_CASH:
- case SC_FOOD_DEX_CASH:
- case SC_FOOD_INT_CASH:
- case SC_FOOD_LUK_CASH:
- case SC_EXPBOOST:
- case SC_JEXPBOOST:
- case SC_ITEMBOOST:
- case SC_ELECTRICSHOCKER:
- case SC__MANHOLE:
- case SC_GIANTGROWTH:
- case SC_MILLENNIUMSHIELD:
- case SC_REFRESH:
- case SC_STONEHARDSKIN:
- case SC_VITALITYACTIVATION:
- case SC_FIGHTINGSPIRIT:
- case SC_ABUNDANCE:
- case SC_CURSEDCIRCLE_ATKER:
- case SC_CURSEDCIRCLE_TARGET:
- continue;
-
- //Debuffs that can be removed.
- case SC_DEEPSLEEP:
- case SC_BURNING:
- case SC_FREEZING:
- case SC_CRYSTALIZE:
- case SC_TOXIN:
- case SC_PARALYSE:
- case SC_VENOMBLEED:
- case SC_MAGICMUSHROOM:
- case SC_DEATHHURT:
- case SC_PYREXIA:
- case SC_OBLIVIONCURSE:
- case SC_LEECHESEND:
- case SC_MARSHOFABYSS:
- case SC_MANDRAGORA:
- if(!(type&4))
- continue;
- break;
- case SC_HALLUCINATION:
- case SC_QUAGMIRE:
- case SC_SIGNUMCRUCIS:
- case SC_DECREASEAGI:
- case SC_SLOWDOWN:
- case SC_MINDBREAKER:
- case SC_WINKCHARM:
- case SC_STOP:
- case SC_ORCISH:
- case SC_STRIPWEAPON:
- case SC_STRIPSHIELD:
- case SC_STRIPARMOR:
- case SC_STRIPHELM:
- case SC_BITE:
- case SC_ADORAMUS:
- case SC_VACUUM_EXTREME:
- case SC_FEAR:
- case SC_MAGNETICFIELD:
- case SC_NETHERWORLD:
- if (!(type&2))
- continue;
- break;
- //The rest are buffs that can be removed.
- case SC__BLOODYLUST:
- case SC_BERSERK:
- case SC_SATURDAYNIGHTFEVER:
- if (!(type&1))
- continue;
- sc->data[i]->val2 = 0;
- break;
- default:
- if (!(type&1))
- continue;
- break;
- }
- status_change_end(bl, (sc_type)i, INVALID_TIMER);
- }
- return 0;
-}
-
-int status_change_spread( struct block_list *src, struct block_list *bl ) {
- int i, flag = 0;
- struct status_change *sc = status_get_sc(src);
- const struct TimerData *timer;
- unsigned int tick;
- struct status_change_data data;
-
- if( !sc || !sc->count )
- return 0;
-
- tick = gettick();
-
- for( i = SC_COMMON_MIN; i < SC_MAX; i++ ) {
- if( !sc->data[i] || i == SC_COMMON_MAX )
- continue;
-
- switch( i ) {
- //Debuffs that can be spreaded.
- // NOTE: We'll add/delte SCs when we are able to confirm it.
- case SC_CURSE:
- case SC_SILENCE:
- case SC_CONFUSION:
- case SC_BLIND:
- case SC_NOCHAT:
- case SC_HALLUCINATION:
- case SC_SIGNUMCRUCIS:
- case SC_DECREASEAGI:
- case SC_SLOWDOWN:
- case SC_MINDBREAKER:
- case SC_WINKCHARM:
- case SC_STOP:
- case SC_ORCISH:
- //case SC_STRIPWEAPON://Omg I got infected and had the urge to strip myself physically.
- //case SC_STRIPSHIELD://No this is stupid and shouldnt be spreadable at all.
- //case SC_STRIPARMOR:// Disabled until I can confirm if it does or not. [Rytech]
- //case SC_STRIPHELM:
- //case SC__STRIPACCESSORY:
- case SC_BITE:
- case SC_FREEZING:
- case SC_VENOMBLEED:
- case SC_DEATHHURT:
- case SC_PARALYSE:
- if( sc->data[i]->timer != INVALID_TIMER ) {
- timer = get_timer(sc->data[i]->timer);
- if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0)
- continue;
- data.tick = DIFF_TICK(timer->tick,tick);
- } else
- data.tick = INVALID_TIMER;
- break;
- // Special cases
- case SC_POISON:
- case SC_DPOISON:
- data.tick = sc->data[i]->val3 * 1000;
- break;
- case SC_FEAR:
- case SC_LEECHESEND:
- data.tick = sc->data[i]->val4 * 1000;
- break;
- case SC_BURNING:
- data.tick = sc->data[i]->val4 * 2000;
- break;
- case SC_PYREXIA:
- case SC_OBLIVIONCURSE:
- data.tick = sc->data[i]->val4 * 3000;
- break;
- case SC_MAGICMUSHROOM:
- data.tick = sc->data[i]->val4 * 4000;
- break;
- case SC_TOXIN:
- case SC_BLEEDING:
- data.tick = sc->data[i]->val4 * 10000;
- break;
- default:
- continue;
- break;
- }
- if( i ){
- data.val1 = sc->data[i]->val1;
- data.val2 = sc->data[i]->val2;
- data.val3 = sc->data[i]->val3;
- data.val4 = sc->data[i]->val4;
- status_change_start(bl,(sc_type)i,10000,data.val1,data.val2,data.val3,data.val4,data.tick,1|2|8);
- flag = 1;
- }
- }
-
- return flag;
-}
-
-//Natural regen related stuff.
-static unsigned int natural_heal_prev_tick,natural_heal_diff_tick;
-static int status_natural_heal(struct block_list* bl, va_list args)
-{
- struct regen_data *regen;
- struct status_data *status;
- struct status_change *sc;
- struct unit_data *ud;
- struct view_data *vd = NULL;
- struct regen_data_sub *sregen;
- struct map_session_data *sd;
- int val,rate,bonus = 0,flag;
-
- regen = status_get_regen_data(bl);
- if (!regen) return 0;
- status = status_get_status_data(bl);
- sc = status_get_sc(bl);
- if (sc && !sc->count)
- sc = NULL;
- sd = BL_CAST(BL_PC,bl);
-
- flag = regen->flag;
- if (flag&RGN_HP && (status->hp >= status->max_hp || regen->state.block&1))
- flag&=~(RGN_HP|RGN_SHP);
- if (flag&RGN_SP && (status->sp >= status->max_sp || regen->state.block&2))
- flag&=~(RGN_SP|RGN_SSP);
-
- if (flag && (
- status_isdead(bl) ||
- (sc && (sc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || sc->data[SC__INVISIBILITY]))
- ))
- flag=0;
-
- if (sd) {
- if (sd->hp_loss.value || sd->sp_loss.value)
- pc_bleeding(sd, natural_heal_diff_tick);
- if (sd->hp_regen.value || sd->sp_regen.value)
- pc_regen(sd, natural_heal_diff_tick);
- }
-
- if(flag&(RGN_SHP|RGN_SSP) && regen->ssregen &&
- (vd = status_get_viewdata(bl)) && vd->dead_sit == 2)
- { //Apply sitting regen bonus.
- sregen = regen->ssregen;
- if(flag&(RGN_SHP))
- { //Sitting HP regen
- val = natural_heal_diff_tick * sregen->rate.hp;
- if (regen->state.overweight)
- val>>=1; //Half as fast when overweight.
- sregen->tick.hp += val;
- while(sregen->tick.hp >= (unsigned int)battle_config.natural_heal_skill_interval)
- {
- sregen->tick.hp -= battle_config.natural_heal_skill_interval;
- if(status_heal(bl, sregen->hp, 0, 3) < sregen->hp)
- { //Full
- flag&=~(RGN_HP|RGN_SHP);
- break;
- }
- }
- }
- if(flag&(RGN_SSP))
- { //Sitting SP regen
- val = natural_heal_diff_tick * sregen->rate.sp;
- if (regen->state.overweight)
- val>>=1; //Half as fast when overweight.
- sregen->tick.sp += val;
- while(sregen->tick.sp >= (unsigned int)battle_config.natural_heal_skill_interval)
- {
- sregen->tick.sp -= battle_config.natural_heal_skill_interval;
- if(status_heal(bl, 0, sregen->sp, 3) < sregen->sp)
- { //Full
- flag&=~(RGN_SP|RGN_SSP);
- break;
- }
- }
- }
- }
-
- if (flag && regen->state.overweight)
- flag=0;
-
- ud = unit_bl2ud(bl);
-
- if (flag&(RGN_HP|RGN_SHP|RGN_SSP) && ud && ud->walktimer != INVALID_TIMER)
- {
- flag&=~(RGN_SHP|RGN_SSP);
- if(!regen->state.walk)
- flag&=~RGN_HP;
- }
-
- if (!flag)
- return 0;
-
- if (flag&(RGN_HP|RGN_SP))
- {
- if(!vd) vd = status_get_viewdata(bl);
- if(vd && vd->dead_sit == 2)
- bonus++;
- if(regen->state.gc)
- bonus++;
- }
-
- //Natural Hp regen
- if (flag&RGN_HP)
- {
- rate = natural_heal_diff_tick*(regen->rate.hp+bonus);
- if (ud && ud->walktimer != INVALID_TIMER)
- rate/=2;
- // Homun HP regen fix (they should regen as if they were sitting (twice as fast)
- if(bl->type==BL_HOM) rate *=2;
-
- regen->tick.hp += rate;
-
- if(regen->tick.hp >= (unsigned int)battle_config.natural_healhp_interval)
- {
- val = 0;
- do {
- val += regen->hp;
- regen->tick.hp -= battle_config.natural_healhp_interval;
- } while(regen->tick.hp >= (unsigned int)battle_config.natural_healhp_interval);
- if (status_heal(bl, val, 0, 1) < val)
- flag&=~RGN_SHP; //full.
- }
- }
-
- //Natural SP regen
- if(flag&RGN_SP)
- {
- rate = natural_heal_diff_tick*(regen->rate.sp+bonus);
- // Homun SP regen fix (they should regen as if they were sitting (twice as fast)
- if(bl->type==BL_HOM) rate *=2;
-
- regen->tick.sp += rate;
-
- if(regen->tick.sp >= (unsigned int)battle_config.natural_healsp_interval)
- {
- val = 0;
- do {
- val += regen->sp;
- regen->tick.sp -= battle_config.natural_healsp_interval;
- } while(regen->tick.sp >= (unsigned int)battle_config.natural_healsp_interval);
- if (status_heal(bl, 0, val, 1) < val)
- flag&=~RGN_SSP; //full.
- }
- }
-
- if (!regen->sregen)
- return flag;
-
- //Skill regen
- sregen = regen->sregen;
-
- if(flag&RGN_SHP)
- { //Skill HP regen
- sregen->tick.hp += natural_heal_diff_tick * sregen->rate.hp;
-
- while(sregen->tick.hp >= (unsigned int)battle_config.natural_heal_skill_interval)
- {
- sregen->tick.hp -= battle_config.natural_heal_skill_interval;
- if(status_heal(bl, sregen->hp, 0, 3) < sregen->hp)
- break; //Full
- }
- }
- if(flag&RGN_SSP)
- { //Skill SP regen
- sregen->tick.sp += natural_heal_diff_tick * sregen->rate.sp;
- while(sregen->tick.sp >= (unsigned int)battle_config.natural_heal_skill_interval)
- {
- val = sregen->sp;
- if (sd && sd->state.doridori) {
- val*=2;
- sd->state.doridori = 0;
- if ((rate = pc_checkskill(sd,TK_SPTIME)))
- sc_start(bl,status_skill2sc(TK_SPTIME),
- 100,rate,skill_get_time(TK_SPTIME, rate));
- if (
- (sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR &&
- rnd()%10000 < battle_config.sg_angel_skill_ratio
- ) { //Angel of the Sun/Moon/Star
- clif_feel_hate_reset(sd);
- pc_resethate(sd);
- pc_resetfeel(sd);
- }
- }
- sregen->tick.sp -= battle_config.natural_heal_skill_interval;
- if(status_heal(bl, 0, val, 3) < val)
- break; //Full
- }
- }
- return flag;
-}
-
-//Natural heal main timer.
-static int status_natural_heal_timer(int tid, unsigned int tick, int id, intptr_t data)
-{
- natural_heal_diff_tick = DIFF_TICK(tick,natural_heal_prev_tick);
- map_foreachregen(status_natural_heal);
- natural_heal_prev_tick = tick;
- return 0;
-}
-
-/**
- * Get the chance to upgrade a piece of equipment.
- * @param wlv The weapon type of the item to refine (see see enum refine_type)
- * @param refine The target refine level
- * @return The chance to refine the item, in percent (0~100)
- **/
-int status_get_refine_chance(enum refine_type wlv, int refine) {
-
- if ( refine < 0 || refine >= MAX_REFINE)
- return 0;
-
- return refine_info[wlv].chance[refine];
-}
-
-
-/*------------------------------------------
- * DB reading.
- * job_db1.txt - weight, hp, sp, aspd
- * job_db2.txt - job level stat bonuses
- * size_fix.txt - size adjustment table for weapons
- * refine_db.txt - refining data table
- *------------------------------------------*/
-static bool status_readdb_job1(char* fields[], int columns, int current)
-{// Job-specific values (weight, HP, SP, ASPD)
- int idx, class_;
- unsigned int i;
-
- class_ = atoi(fields[0]);
-
- if(!pcdb_checkid(class_))
- {
- ShowWarning("status_readdb_job1: Invalid job class %d specified.\n", class_);
- return false;
- }
- idx = pc_class2idx(class_);
-
- max_weight_base[idx] = atoi(fields[1]);
- hp_coefficient[idx] = atoi(fields[2]);
- hp_coefficient2[idx] = atoi(fields[3]);
- sp_coefficient[idx] = atoi(fields[4]);
-#ifdef RENEWAL_ASPD
- for(i = 0; i <= MAX_WEAPON_TYPE; i++)
-#else
- for(i = 0; i < MAX_WEAPON_TYPE; i++)
-#endif
- {
- aspd_base[idx][i] = atoi(fields[i+5]);
- }
- return true;
-}
-
-static bool status_readdb_job2(char* fields[], int columns, int current)
-{
- int idx, class_, i;
-
- class_ = atoi(fields[0]);
-
- if(!pcdb_checkid(class_))
- {
- ShowWarning("status_readdb_job2: Invalid job class %d specified.\n", class_);
- return false;
- }
- idx = pc_class2idx(class_);
-
- for(i = 1; i < columns; i++)
- {
- job_bonus[idx][i-1] = atoi(fields[i]);
- }
- return true;
-}
-
-static bool status_readdb_sizefix(char* fields[], int columns, int current)
-{
- unsigned int i;
-
- for(i = 0; i < MAX_WEAPON_TYPE; i++)
- {
- atkmods[current][i] = atoi(fields[i]);
- }
- return true;
-}
-
-static bool status_readdb_refine(char* fields[], int columns, int current)
-{
- int i, bonus_per_level, random_bonus, random_bonus_start_level;
-
- current = atoi(fields[0]);
-
- if (current < 0 || current >= REFINE_TYPE_MAX)
- return false;
-
- bonus_per_level = atoi(fields[1]);
- random_bonus_start_level = atoi(fields[2]);
- random_bonus = atoi(fields[3]);
-
- for(i = 0; i < MAX_REFINE; i++)
- {
- char* delim;
-
- if (!(delim = strchr(fields[4+i], ':')))
- return false;
-
- *delim = '\0';
-
- refine_info[current].chance[i] = atoi(fields[4+i]);
-
- if (i >= random_bonus_start_level - 1)
- refine_info[current].randombonus_max[i] = random_bonus * (i - random_bonus_start_level + 2);
-
- refine_info[current].bonus[i] = bonus_per_level + atoi(delim+1);
- if (i > 0)
- refine_info[current].bonus[i] += refine_info[current].bonus[i-1];
- }
- return true;
-}
-
-/*
-* Read status db
-* job1.txt
-* job2.txt
-* size_fixe.txt
-* refine_db.txt
-*/
-int status_readdb(void)
-{
- int i, j;
-
- // initialize databases to default
- //
-
- // reset job_db1.txt data
- memset(max_weight_base, 0, sizeof(max_weight_base));
- memset(hp_coefficient, 0, sizeof(hp_coefficient));
- memset(hp_coefficient2, 0, sizeof(hp_coefficient2));
- memset(sp_coefficient, 0, sizeof(sp_coefficient));
- memset(aspd_base, 0, sizeof(aspd_base));
- // reset job_db2.txt data
- memset(job_bonus,0,sizeof(job_bonus)); // Job-specific stats bonus
-
- // size_fix.txt
- for(i=0;i<ARRAYLENGTH(atkmods);i++)
- for(j=0;j<MAX_WEAPON_TYPE;j++)
- atkmods[i][j]=100;
-
- // refine_db.txt
- for(i=0;i<ARRAYLENGTH(refine_info);i++)
- {
- for(j=0;j<MAX_REFINE; j++)
- {
- refine_info[i].chance[j] = 100;
- refine_info[i].bonus[j] = 0;
- refine_info[i].randombonus_max[j] = 0;
- }
- }
-
- // read databases
- //
-
-
-#ifdef RENEWAL_ASPD
- sv_readdb(db_path, "re/job_db1.txt", ',', 6+MAX_WEAPON_TYPE, 6+MAX_WEAPON_TYPE, -1, &status_readdb_job1);
-#else
- sv_readdb(db_path, "pre-re/job_db1.txt", ',', 5+MAX_WEAPON_TYPE, 5+MAX_WEAPON_TYPE, -1, &status_readdb_job1);
-#endif
- sv_readdb(db_path, "job_db2.txt", ',', 1, 1+MAX_LEVEL, -1, &status_readdb_job2);
- sv_readdb(db_path, "size_fix.txt", ',', MAX_WEAPON_TYPE, MAX_WEAPON_TYPE, ARRAYLENGTH(atkmods), &status_readdb_sizefix);
- sv_readdb(db_path, DBPATH"refine_db.txt", ',', 4+MAX_REFINE, 4+MAX_REFINE, ARRAYLENGTH(refine_info), &status_readdb_refine);
-
- return 0;
-}
-
-/*==========================================
- * Status db init and destroy.
- *------------------------------------------*/
-int do_init_status(void)
-{
- add_timer_func_list(status_change_timer,"status_change_timer");
- add_timer_func_list(kaahi_heal_timer,"kaahi_heal_timer");
- add_timer_func_list(status_natural_heal_timer,"status_natural_heal_timer");
- initChangeTables();
- initDummyData();
- status_readdb();
- status_calc_sigma();
- natural_heal_prev_tick = gettick();
- sc_data_ers = ers_new(sizeof(struct status_change_entry),"status.c::sc_data_ers",ERS_OPT_NONE);
- add_timer_interval(natural_heal_prev_tick + NATURAL_HEAL_INTERVAL, status_natural_heal_timer, 0, 0, NATURAL_HEAL_INTERVAL);
- return 0;
-}
-void do_final_status(void)
-{
- ers_destroy(sc_data_ers);
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "../common/cbasetypes.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/random.h"
+#include "../common/showmsg.h"
+#include "../common/malloc.h"
+#include "../common/utils.h"
+#include "../common/ers.h"
+#include "../common/strlib.h"
+
+#include "map.h"
+#include "path.h"
+#include "pc.h"
+#include "pet.h"
+#include "npc.h"
+#include "mob.h"
+#include "clif.h"
+#include "guild.h"
+#include "skill.h"
+#include "itemdb.h"
+#include "battle.h"
+#include "chrif.h"
+#include "skill.h"
+#include "status.h"
+#include "script.h"
+#include "unit.h"
+#include "homunculus.h"
+#include "mercenary.h"
+#include "elemental.h"
+#include "vending.h"
+
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string.h>
+#include <math.h>
+
+//Regen related flags.
+enum e_regen
+{
+ RGN_HP = 0x01,
+ RGN_SP = 0x02,
+ RGN_SHP = 0x04,
+ RGN_SSP = 0x08,
+};
+
+static int max_weight_base[CLASS_COUNT];
+static int hp_coefficient[CLASS_COUNT];
+static int hp_coefficient2[CLASS_COUNT];
+static int hp_sigma_val[CLASS_COUNT][MAX_LEVEL+1];
+static int sp_coefficient[CLASS_COUNT];
+#ifdef RENEWAL_ASPD
+static int aspd_base[CLASS_COUNT][MAX_WEAPON_TYPE+1];
+#else
+static int aspd_base[CLASS_COUNT][MAX_WEAPON_TYPE]; //[blackhole89]
+#endif
+
+// bonus values and upgrade chances for refining equipment
+static struct {
+ int chance[MAX_REFINE]; // success chance
+ int bonus[MAX_REFINE]; // cumulative fixed bonus damage
+ int randombonus_max[MAX_REFINE]; // cumulative maximum random bonus damage
+} refine_info[REFINE_TYPE_MAX];
+
+static int atkmods[3][MAX_WEAPON_TYPE]; //ATK weapon modification for size (size_fix.txt)
+static char job_bonus[CLASS_COUNT][MAX_LEVEL];
+
+static struct eri *sc_data_ers; //For sc_data entries
+static struct status_data dummy_status;
+
+int current_equip_item_index; //Contains inventory index of an equipped item. To pass it into the EQUP_SCRIPT [Lupus]
+int current_equip_card_id; //To prevent card-stacking (from jA) [Skotlex]
+//we need it for new cards 15 Feb 2005, to check if the combo cards are insrerted into the CURRENT weapon only
+//to avoid cards exploits
+
+static sc_type SkillStatusChangeTable[MAX_SKILL]; // skill -> status
+static int StatusIconChangeTable[SC_MAX]; // status -> "icon" (icon is a bit of a misnomer, since there exist values with no icon associated)
+static unsigned int StatusChangeFlagTable[SC_MAX]; // status -> flags
+static int StatusSkillChangeTable[SC_MAX]; // status -> skill
+static int StatusRelevantBLTypes[SI_MAX]; // "icon" -> enum bl_type (for clif_status_change to identify for which bl types to send packets)
+static unsigned int StatusChangeStateTable[SC_MAX]; // status -> flags
+
+
+/**
+ * Returns the status change associated with a skill.
+ * @param skill The skill to look up
+ * @return The status registered for this skill
+ **/
+sc_type status_skill2sc(int skill)
+{
+ int idx = skill_get_index(skill);
+ if( idx == 0 ) {
+ ShowError("status_skill2sc: Unsupported skill id %d\n", skill);
+ return SC_NONE;
+ }
+ return SkillStatusChangeTable[idx];
+}
+
+/**
+ * Returns the FIRST skill (in order of definition in initChangeTables) to use a given status change.
+ * Utilized for various duration lookups. Use with caution!
+ * @param sc The status to look up
+ * @return A skill associated with the status
+ **/
+int status_sc2skill(sc_type sc)
+{
+ if( sc < 0 || sc >= SC_MAX ) {
+ ShowError("status_sc2skill: Unsupported status change id %d\n", sc);
+ return 0;
+ }
+
+ return StatusSkillChangeTable[sc];
+}
+
+/**
+ * Returns the status calculation flag associated with a given status change.
+ * @param sc The status to look up
+ * @return The scb_flag registered for this status (see enum scb_flag)
+ **/
+unsigned int status_sc2scb_flag(sc_type sc)
+{
+ if( sc < 0 || sc >= SC_MAX ) {
+ ShowError("status_sc2scb_flag: Unsupported status change id %d\n", sc);
+ return SCB_NONE;
+ }
+
+ return StatusChangeFlagTable[sc];
+}
+
+/**
+ * Returns the bl types which require a status change packet to be sent for a given client status identifier.
+ * @param type The client-side status identifier to look up (see enum si_type)
+ * @return The bl types relevant to the type (see enum bl_type)
+ **/
+int status_type2relevant_bl_types(int type)
+{
+ if( type < 0 || type >= SI_MAX ) {
+ ShowError("status_type2relevant_bl_types: Unsupported type %d\n", type);
+ return SI_BLANK;
+ }
+
+ return StatusRelevantBLTypes[type];
+}
+
+#define add_sc(skill,sc) set_sc(skill,sc,SI_BLANK,SCB_NONE)
+// indicates that the status displays a visual effect for the affected unit, and should be sent to the client for all supported units
+#define set_sc_with_vfx(skill, sc, icon, flag) set_sc((skill), (sc), (icon), (flag)); if((icon) < SI_MAX) StatusRelevantBLTypes[(icon)] |= BL_SCEFFECT
+
+static void set_sc(uint16 skill_id, sc_type sc, int icon, unsigned int flag)
+{
+ uint16 idx = skill_get_index(skill_id);
+ if( idx == 0 ) {
+ ShowError("set_sc: Unsupported skill id %d\n", skill_id);
+ return;
+ }
+ if( sc < 0 || sc >= SC_MAX ) {
+ ShowError("set_sc: Unsupported status change id %d\n", sc);
+ return;
+ }
+
+ if( StatusSkillChangeTable[sc] == 0 )
+ StatusSkillChangeTable[sc] = skill_id;
+ if( StatusIconChangeTable[sc] == SI_BLANK )
+ StatusIconChangeTable[sc] = icon;
+ StatusChangeFlagTable[sc] |= flag;
+
+ if( SkillStatusChangeTable[idx] == SC_NONE )
+ SkillStatusChangeTable[idx] = sc;
+}
+
+void initChangeTables(void) {
+ int i;
+
+ for (i = 0; i < SC_MAX; i++)
+ StatusIconChangeTable[i] = SI_BLANK;
+
+ for (i = 0; i < MAX_SKILL; i++)
+ SkillStatusChangeTable[i] = SC_NONE;
+
+ for (i = 0; i < SI_MAX; i++)
+ StatusRelevantBLTypes[i] = BL_PC;
+
+ memset(StatusSkillChangeTable, 0, sizeof(StatusSkillChangeTable));
+ memset(StatusChangeFlagTable, 0, sizeof(StatusChangeFlagTable));
+ memset(StatusChangeStateTable, 0, sizeof(StatusChangeStateTable));
+
+
+ //First we define the skill for common ailments. These are used in skill_additional_effect through sc cards. [Skotlex]
+ set_sc( NPC_PETRIFYATTACK , SC_STONE , SI_BLANK , SCB_DEF_ELE|SCB_DEF|SCB_MDEF );
+ set_sc( NPC_WIDEFREEZE , SC_FREEZE , SI_BLANK , SCB_DEF_ELE|SCB_DEF|SCB_MDEF );
+ set_sc( NPC_STUNATTACK , SC_STUN , SI_BLANK , SCB_NONE );
+ set_sc( NPC_SLEEPATTACK , SC_SLEEP , SI_BLANK , SCB_NONE );
+ set_sc( NPC_POISON , SC_POISON , SI_BLANK , SCB_DEF2|SCB_REGEN );
+ set_sc( NPC_CURSEATTACK , SC_CURSE , SI_BLANK , SCB_LUK|SCB_BATK|SCB_WATK|SCB_SPEED );
+ set_sc( NPC_SILENCEATTACK , SC_SILENCE , SI_BLANK , SCB_NONE );
+ set_sc( NPC_WIDECONFUSE , SC_CONFUSION , SI_BLANK , SCB_NONE );
+ set_sc( NPC_BLINDATTACK , SC_BLIND , SI_BLANK , SCB_HIT|SCB_FLEE );
+ set_sc( NPC_BLEEDING , SC_BLEEDING , SI_BLEEDING , SCB_REGEN );
+ set_sc( NPC_POISON , SC_DPOISON , SI_BLANK , SCB_DEF2|SCB_REGEN );
+
+ //The main status definitions
+ add_sc( SM_BASH , SC_STUN );
+ set_sc( SM_PROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK );
+ add_sc( SM_MAGNUM , SC_WATK_ELEMENT );
+ set_sc( SM_ENDURE , SC_ENDURE , SI_ENDURE , SCB_MDEF|SCB_DSPD );
+ add_sc( MG_SIGHT , SC_SIGHT );
+ add_sc( MG_SAFETYWALL , SC_SAFETYWALL );
+ add_sc( MG_FROSTDIVER , SC_FREEZE );
+ add_sc( MG_STONECURSE , SC_STONE );
+ add_sc( AL_RUWACH , SC_RUWACH );
+ add_sc( AL_PNEUMA , SC_PNEUMA );
+ set_sc( AL_INCAGI , SC_INCREASEAGI , SI_INCREASEAGI , SCB_AGI|SCB_SPEED );
+ set_sc( AL_DECAGI , SC_DECREASEAGI , SI_DECREASEAGI , SCB_AGI|SCB_SPEED );
+ set_sc( AL_CRUCIS , SC_SIGNUMCRUCIS , SI_SIGNUMCRUCIS , SCB_DEF );
+ set_sc( AL_ANGELUS , SC_ANGELUS , SI_ANGELUS , SCB_DEF2 );
+ set_sc( AL_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX );
+ set_sc( AC_CONCENTRATION , SC_CONCENTRATE , SI_CONCENTRATE , SCB_AGI|SCB_DEX );
+ set_sc( TF_HIDING , SC_HIDING , SI_HIDING , SCB_SPEED );
+ add_sc( TF_POISON , SC_POISON );
+ set_sc( KN_TWOHANDQUICKEN , SC_TWOHANDQUICKEN , SI_TWOHANDQUICKEN , SCB_ASPD );
+ add_sc( KN_AUTOCOUNTER , SC_AUTOCOUNTER );
+ set_sc( PR_IMPOSITIO , SC_IMPOSITIO , SI_IMPOSITIO , SCB_WATK );
+ set_sc( PR_SUFFRAGIUM , SC_SUFFRAGIUM , SI_SUFFRAGIUM , SCB_NONE );
+ set_sc( PR_ASPERSIO , SC_ASPERSIO , SI_ASPERSIO , SCB_ATK_ELE );
+ set_sc( PR_BENEDICTIO , SC_BENEDICTIO , SI_BENEDICTIO , SCB_DEF_ELE );
+ set_sc( PR_SLOWPOISON , SC_SLOWPOISON , SI_SLOWPOISON , SCB_REGEN );
+ set_sc( PR_KYRIE , SC_KYRIE , SI_KYRIE , SCB_NONE );
+ set_sc( PR_MAGNIFICAT , SC_MAGNIFICAT , SI_MAGNIFICAT , SCB_REGEN );
+ set_sc( PR_GLORIA , SC_GLORIA , SI_GLORIA , SCB_LUK );
+ add_sc( PR_LEXDIVINA , SC_SILENCE );
+ set_sc( PR_LEXAETERNA , SC_AETERNA , SI_AETERNA , SCB_NONE );
+ add_sc( WZ_METEOR , SC_STUN );
+ add_sc( WZ_VERMILION , SC_BLIND );
+ add_sc( WZ_FROSTNOVA , SC_FREEZE );
+ add_sc( WZ_STORMGUST , SC_FREEZE );
+ set_sc( WZ_QUAGMIRE , SC_QUAGMIRE , SI_QUAGMIRE , SCB_AGI|SCB_DEX|SCB_ASPD|SCB_SPEED );
+ set_sc( BS_ADRENALINE , SC_ADRENALINE , SI_ADRENALINE , SCB_ASPD );
+ set_sc( BS_WEAPONPERFECT , SC_WEAPONPERFECTION, SI_WEAPONPERFECTION, SCB_NONE );
+ set_sc( BS_OVERTHRUST , SC_OVERTHRUST , SI_OVERTHRUST , SCB_NONE );
+ set_sc( BS_MAXIMIZE , SC_MAXIMIZEPOWER , SI_MAXIMIZEPOWER , SCB_REGEN );
+ add_sc( HT_LANDMINE , SC_STUN );
+ add_sc( HT_ANKLESNARE , SC_ANKLE );
+ add_sc( HT_SANDMAN , SC_SLEEP );
+ add_sc( HT_FLASHER , SC_BLIND );
+ add_sc( HT_FREEZINGTRAP , SC_FREEZE );
+ set_sc( AS_CLOAKING , SC_CLOAKING , SI_CLOAKING , SCB_CRI|SCB_SPEED );
+ add_sc( AS_SONICBLOW , SC_STUN );
+ set_sc( AS_ENCHANTPOISON , SC_ENCPOISON , SI_ENCPOISON , SCB_ATK_ELE );
+ set_sc( AS_POISONREACT , SC_POISONREACT , SI_POISONREACT , SCB_NONE );
+ add_sc( AS_VENOMDUST , SC_POISON );
+ add_sc( AS_SPLASHER , SC_SPLASHER );
+ set_sc( NV_TRICKDEAD , SC_TRICKDEAD , SI_TRICKDEAD , SCB_REGEN );
+ set_sc( SM_AUTOBERSERK , SC_AUTOBERSERK , SI_AUTOBERSERK , SCB_NONE );
+ add_sc( TF_SPRINKLESAND , SC_BLIND );
+ add_sc( TF_THROWSTONE , SC_STUN );
+ set_sc( MC_LOUD , SC_LOUD , SI_LOUD , SCB_STR );
+ set_sc( MG_ENERGYCOAT , SC_ENERGYCOAT , SI_ENERGYCOAT , SCB_NONE );
+ set_sc( NPC_EMOTION , SC_MODECHANGE , SI_BLANK , SCB_MODE );
+ add_sc( NPC_EMOTION_ON , SC_MODECHANGE );
+ set_sc( NPC_ATTRICHANGE , SC_ELEMENTALCHANGE , SI_ARMOR_PROPERTY , SCB_DEF_ELE );
+ add_sc( NPC_CHANGEWATER , SC_ELEMENTALCHANGE );
+ add_sc( NPC_CHANGEGROUND , SC_ELEMENTALCHANGE );
+ add_sc( NPC_CHANGEFIRE , SC_ELEMENTALCHANGE );
+ add_sc( NPC_CHANGEWIND , SC_ELEMENTALCHANGE );
+ add_sc( NPC_CHANGEPOISON , SC_ELEMENTALCHANGE );
+ add_sc( NPC_CHANGEHOLY , SC_ELEMENTALCHANGE );
+ add_sc( NPC_CHANGEDARKNESS , SC_ELEMENTALCHANGE );
+ add_sc( NPC_CHANGETELEKINESIS, SC_ELEMENTALCHANGE );
+ add_sc( NPC_POISON , SC_POISON );
+ add_sc( NPC_BLINDATTACK , SC_BLIND );
+ add_sc( NPC_SILENCEATTACK , SC_SILENCE );
+ add_sc( NPC_STUNATTACK , SC_STUN );
+ add_sc( NPC_PETRIFYATTACK , SC_STONE );
+ add_sc( NPC_CURSEATTACK , SC_CURSE );
+ add_sc( NPC_SLEEPATTACK , SC_SLEEP );
+ add_sc( NPC_MAGICALATTACK , SC_MAGICALATTACK );
+ set_sc( NPC_KEEPING , SC_KEEPING , SI_BLANK , SCB_DEF );
+ add_sc( NPC_DARKBLESSING , SC_COMA );
+ set_sc( NPC_BARRIER , SC_BARRIER , SI_BLANK , SCB_MDEF|SCB_DEF );
+ add_sc( NPC_DEFENDER , SC_ARMOR );
+ add_sc( NPC_LICK , SC_STUN );
+ set_sc( NPC_HALLUCINATION , SC_HALLUCINATION , SI_HALLUCINATION , SCB_NONE );
+ add_sc( NPC_REBIRTH , SC_REBIRTH );
+ add_sc( RG_RAID , SC_STUN );
+#ifdef RENEWAL
+ add_sc( RG_RAID , SC_RAID );
+ add_sc( RG_BACKSTAP , SC_STUN );
+#endif
+ set_sc( RG_STRIPWEAPON , SC_STRIPWEAPON , SI_STRIPWEAPON , SCB_WATK );
+ set_sc( RG_STRIPSHIELD , SC_STRIPSHIELD , SI_STRIPSHIELD , SCB_DEF );
+ set_sc( RG_STRIPARMOR , SC_STRIPARMOR , SI_STRIPARMOR , SCB_VIT );
+ set_sc( RG_STRIPHELM , SC_STRIPHELM , SI_STRIPHELM , SCB_INT );
+ add_sc( AM_ACIDTERROR , SC_BLEEDING );
+ set_sc( AM_CP_WEAPON , SC_CP_WEAPON , SI_CP_WEAPON , SCB_NONE );
+ set_sc( AM_CP_SHIELD , SC_CP_SHIELD , SI_CP_SHIELD , SCB_NONE );
+ set_sc( AM_CP_ARMOR , SC_CP_ARMOR , SI_CP_ARMOR , SCB_NONE );
+ set_sc( AM_CP_HELM , SC_CP_HELM , SI_CP_HELM , SCB_NONE );
+ set_sc( CR_AUTOGUARD , SC_AUTOGUARD , SI_AUTOGUARD , SCB_NONE );
+ add_sc( CR_SHIELDCHARGE , SC_STUN );
+ set_sc( CR_REFLECTSHIELD , SC_REFLECTSHIELD , SI_REFLECTSHIELD , SCB_NONE );
+ add_sc( CR_HOLYCROSS , SC_BLIND );
+ add_sc( CR_GRANDCROSS , SC_BLIND );
+ add_sc( CR_DEVOTION , SC_DEVOTION );
+ set_sc( CR_PROVIDENCE , SC_PROVIDENCE , SI_PROVIDENCE , SCB_ALL );
+ set_sc( CR_DEFENDER , SC_DEFENDER , SI_DEFENDER , SCB_SPEED|SCB_ASPD );
+ set_sc( CR_SPEARQUICKEN , SC_SPEARQUICKEN , SI_SPEARQUICKEN , SCB_ASPD|SCB_CRI|SCB_FLEE );
+ set_sc( MO_STEELBODY , SC_STEELBODY , SI_STEELBODY , SCB_DEF|SCB_MDEF|SCB_ASPD|SCB_SPEED );
+ add_sc( MO_BLADESTOP , SC_BLADESTOP_WAIT );
+ add_sc( MO_BLADESTOP , SC_BLADESTOP );
+ set_sc( MO_EXPLOSIONSPIRITS , SC_EXPLOSIONSPIRITS, SI_EXPLOSIONSPIRITS, SCB_CRI|SCB_REGEN );
+ set_sc( MO_EXTREMITYFIST , SC_EXTREMITYFIST , SI_BLANK , SCB_REGEN );
+#ifdef RENEWAL
+ set_sc( MO_EXTREMITYFIST , SC_EXTREMITYFIST2 , SI_EXTREMITYFIST , SCB_NONE );
+#endif
+ add_sc( SA_MAGICROD , SC_MAGICROD );
+ set_sc( SA_AUTOSPELL , SC_AUTOSPELL , SI_AUTOSPELL , SCB_NONE );
+ set_sc( SA_FLAMELAUNCHER , SC_FIREWEAPON , SI_FIREWEAPON , SCB_ATK_ELE );
+ set_sc( SA_FROSTWEAPON , SC_WATERWEAPON , SI_WATERWEAPON , SCB_ATK_ELE );
+ set_sc( SA_LIGHTNINGLOADER , SC_WINDWEAPON , SI_WINDWEAPON , SCB_ATK_ELE );
+ set_sc( SA_SEISMICWEAPON , SC_EARTHWEAPON , SI_EARTHWEAPON , SCB_ATK_ELE );
+ set_sc( SA_VOLCANO , SC_VOLCANO , SI_LANDENDOW , SCB_WATK );
+ set_sc( SA_DELUGE , SC_DELUGE , SI_LANDENDOW , SCB_MAXHP );
+ set_sc( SA_VIOLENTGALE , SC_VIOLENTGALE , SI_LANDENDOW , SCB_FLEE );
+ add_sc( SA_REVERSEORCISH , SC_ORCISH );
+ add_sc( SA_COMA , SC_COMA );
+ set_sc( BD_ENCORE , SC_DANCING , SI_BLANK , SCB_SPEED|SCB_REGEN );
+ add_sc( BD_RICHMANKIM , SC_RICHMANKIM );
+ set_sc( BD_ETERNALCHAOS , SC_ETERNALCHAOS , SI_BLANK , SCB_DEF2 );
+ set_sc( BD_DRUMBATTLEFIELD , SC_DRUMBATTLE , SI_BLANK , SCB_WATK|SCB_DEF );
+ set_sc( BD_RINGNIBELUNGEN , SC_NIBELUNGEN , SI_BLANK , SCB_WATK );
+ add_sc( BD_ROKISWEIL , SC_ROKISWEIL );
+ add_sc( BD_INTOABYSS , SC_INTOABYSS );
+ set_sc( BD_SIEGFRIED , SC_SIEGFRIED , SI_BLANK , SCB_ALL );
+ add_sc( BA_FROSTJOKER , SC_FREEZE );
+ set_sc( BA_WHISTLE , SC_WHISTLE , SI_BLANK , SCB_FLEE|SCB_FLEE2 );
+ set_sc( BA_ASSASSINCROSS , SC_ASSNCROS , SI_BLANK , SCB_ASPD );
+ add_sc( BA_POEMBRAGI , SC_POEMBRAGI );
+ set_sc( BA_APPLEIDUN , SC_APPLEIDUN , SI_BLANK , SCB_MAXHP );
+ add_sc( DC_SCREAM , SC_STUN );
+ set_sc( DC_HUMMING , SC_HUMMING , SI_BLANK , SCB_HIT );
+ set_sc( DC_DONTFORGETME , SC_DONTFORGETME , SI_BLANK , SCB_SPEED|SCB_ASPD );
+ set_sc( DC_FORTUNEKISS , SC_FORTUNE , SI_BLANK , SCB_CRI );
+ set_sc( DC_SERVICEFORYOU , SC_SERVICE4U , SI_BLANK , SCB_ALL );
+ add_sc( NPC_DARKCROSS , SC_BLIND );
+ add_sc( NPC_GRANDDARKNESS , SC_BLIND );
+ set_sc( NPC_STOP , SC_STOP , SI_STOP , SCB_NONE );
+ set_sc( NPC_WEAPONBRAKER , SC_BROKENWEAPON , SI_BROKENWEAPON , SCB_NONE );
+ set_sc( NPC_ARMORBRAKE , SC_BROKENARMOR , SI_BROKENARMOR , SCB_NONE );
+ set_sc( NPC_CHANGEUNDEAD , SC_CHANGEUNDEAD , SI_UNDEAD , SCB_DEF_ELE );
+ set_sc( NPC_POWERUP , SC_INCHITRATE , SI_BLANK , SCB_HIT );
+ set_sc( NPC_AGIUP , SC_INCFLEERATE , SI_BLANK , SCB_FLEE );
+ add_sc( NPC_INVISIBLE , SC_CLOAKING );
+ set_sc( LK_AURABLADE , SC_AURABLADE , SI_AURABLADE , SCB_NONE );
+ set_sc( LK_PARRYING , SC_PARRYING , SI_PARRYING , SCB_NONE );
+ set_sc( LK_CONCENTRATION , SC_CONCENTRATION , SI_CONCENTRATION , SCB_BATK|SCB_WATK|SCB_HIT|SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_DSPD );
+ set_sc( LK_TENSIONRELAX , SC_TENSIONRELAX , SI_TENSIONRELAX , SCB_REGEN );
+ set_sc( LK_BERSERK , SC_BERSERK , SI_BERSERK , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP|SCB_REGEN );
+ set_sc( HP_ASSUMPTIO , SC_ASSUMPTIO , SI_ASSUMPTIO , SCB_NONE );
+ add_sc( HP_BASILICA , SC_BASILICA );
+ set_sc( HW_MAGICPOWER , SC_MAGICPOWER , SI_MAGICPOWER , SCB_MATK );
+ add_sc( PA_SACRIFICE , SC_SACRIFICE );
+ set_sc( PA_GOSPEL , SC_GOSPEL , SI_BLANK , SCB_SPEED|SCB_ASPD );
+ add_sc( PA_GOSPEL , SC_SCRESIST );
+ add_sc( CH_TIGERFIST , SC_STOP );
+ set_sc( ASC_EDP , SC_EDP , SI_EDP , SCB_NONE );
+ set_sc( SN_SIGHT , SC_TRUESIGHT , SI_TRUESIGHT , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK|SCB_CRI|SCB_HIT );
+ set_sc( SN_WINDWALK , SC_WINDWALK , SI_WINDWALK , SCB_FLEE|SCB_SPEED );
+ set_sc( WS_MELTDOWN , SC_MELTDOWN , SI_MELTDOWN , SCB_NONE );
+ set_sc( WS_CARTBOOST , SC_CARTBOOST , SI_CARTBOOST , SCB_SPEED );
+ set_sc( ST_CHASEWALK , SC_CHASEWALK , SI_BLANK , SCB_SPEED );
+ set_sc( ST_REJECTSWORD , SC_REJECTSWORD , SI_REJECTSWORD , SCB_NONE );
+ add_sc( ST_REJECTSWORD , SC_AUTOCOUNTER );
+ set_sc( CG_MARIONETTE , SC_MARIONETTE , SI_MARIONETTE , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK );
+ set_sc( CG_MARIONETTE , SC_MARIONETTE2 , SI_MARIONETTE2 , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK );
+ add_sc( LK_SPIRALPIERCE , SC_STOP );
+ add_sc( LK_HEADCRUSH , SC_BLEEDING );
+ set_sc( LK_JOINTBEAT , SC_JOINTBEAT , SI_JOINTBEAT , SCB_BATK|SCB_DEF2|SCB_SPEED|SCB_ASPD );
+ add_sc( HW_NAPALMVULCAN , SC_CURSE );
+ set_sc( PF_MINDBREAKER , SC_MINDBREAKER , SI_BLANK , SCB_MATK|SCB_MDEF2 );
+ add_sc( PF_MEMORIZE , SC_MEMORIZE );
+ add_sc( PF_FOGWALL , SC_FOGWALL );
+ set_sc( PF_SPIDERWEB , SC_SPIDERWEB , SI_BLANK , SCB_FLEE );
+ set_sc( WE_BABY , SC_BABY , SI_BABY , SCB_NONE );
+ set_sc( TK_RUN , SC_RUN , SI_RUN , SCB_SPEED|SCB_DSPD );
+ set_sc( TK_RUN , SC_SPURT , SI_SPURT , SCB_STR );
+ set_sc( TK_READYSTORM , SC_READYSTORM , SI_READYSTORM , SCB_NONE );
+ set_sc( TK_READYDOWN , SC_READYDOWN , SI_READYDOWN , SCB_NONE );
+ add_sc( TK_DOWNKICK , SC_STUN );
+ set_sc( TK_READYTURN , SC_READYTURN , SI_READYTURN , SCB_NONE );
+ set_sc( TK_READYCOUNTER , SC_READYCOUNTER , SI_READYCOUNTER , SCB_NONE );
+ set_sc( TK_DODGE , SC_DODGE , SI_DODGE , SCB_NONE );
+ set_sc( TK_SPTIME , SC_EARTHSCROLL , SI_EARTHSCROLL , SCB_NONE );
+ add_sc( TK_SEVENWIND , SC_SEVENWIND );
+ set_sc( TK_SEVENWIND , SC_GHOSTWEAPON , SI_GHOSTWEAPON , SCB_ATK_ELE );
+ set_sc( TK_SEVENWIND , SC_SHADOWWEAPON , SI_SHADOWWEAPON , SCB_ATK_ELE );
+ set_sc( SG_SUN_WARM , SC_WARM , SI_WARM , SCB_NONE );
+ add_sc( SG_MOON_WARM , SC_WARM );
+ add_sc( SG_STAR_WARM , SC_WARM );
+ set_sc( SG_SUN_COMFORT , SC_SUN_COMFORT , SI_SUN_COMFORT , SCB_DEF2 );
+ set_sc( SG_MOON_COMFORT , SC_MOON_COMFORT , SI_MOON_COMFORT , SCB_FLEE );
+ set_sc( SG_STAR_COMFORT , SC_STAR_COMFORT , SI_STAR_COMFORT , SCB_ASPD );
+ add_sc( SG_FRIEND , SC_SKILLRATE_UP );
+ set_sc( SG_KNOWLEDGE , SC_KNOWLEDGE , SI_BLANK , SCB_ALL );
+ set_sc( SG_FUSION , SC_FUSION , SI_BLANK , SCB_SPEED );
+ set_sc( BS_ADRENALINE2 , SC_ADRENALINE2 , SI_ADRENALINE2 , SCB_ASPD );
+ set_sc( SL_KAIZEL , SC_KAIZEL , SI_KAIZEL , SCB_NONE );
+ set_sc( SL_KAAHI , SC_KAAHI , SI_KAAHI , SCB_NONE );
+ set_sc( SL_KAUPE , SC_KAUPE , SI_KAUPE , SCB_NONE );
+ set_sc( SL_KAITE , SC_KAITE , SI_KAITE , SCB_NONE );
+ add_sc( SL_STUN , SC_STUN );
+ set_sc( SL_SWOO , SC_SWOO , SI_BLANK , SCB_SPEED );
+ set_sc( SL_SKE , SC_SKE , SI_BLANK , SCB_BATK|SCB_WATK|SCB_DEF|SCB_DEF2 );
+ set_sc( SL_SKA , SC_SKA , SI_BLANK , SCB_DEF|SCB_MDEF|SCB_ASPD );
+ set_sc( SL_SMA , SC_SMA , SI_SMA , SCB_NONE );
+ set_sc( SM_SELFPROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK );
+ set_sc( ST_PRESERVE , SC_PRESERVE , SI_PRESERVE , SCB_NONE );
+ set_sc( PF_DOUBLECASTING , SC_DOUBLECAST , SI_DOUBLECAST , SCB_NONE );
+ set_sc( HW_GRAVITATION , SC_GRAVITATION , SI_BLANK , SCB_ASPD );
+ add_sc( WS_CARTTERMINATION , SC_STUN );
+ set_sc( WS_OVERTHRUSTMAX , SC_MAXOVERTHRUST , SI_MAXOVERTHRUST , SCB_NONE );
+ set_sc( CG_LONGINGFREEDOM , SC_LONGING , SI_BLANK , SCB_SPEED|SCB_ASPD );
+ add_sc( CG_HERMODE , SC_HERMODE );
+ set_sc( ITEM_ENCHANTARMS , SC_ENCHANTARMS , SI_BLANK , SCB_ATK_ELE );
+ set_sc( SL_HIGH , SC_SPIRIT , SI_SPIRIT , SCB_ALL );
+ set_sc( KN_ONEHAND , SC_ONEHAND , SI_ONEHAND , SCB_ASPD );
+ set_sc( GS_FLING , SC_FLING , SI_BLANK , SCB_DEF|SCB_DEF2 );
+ add_sc( GS_CRACKER , SC_STUN );
+ add_sc( GS_DISARM , SC_STRIPWEAPON );
+ add_sc( GS_PIERCINGSHOT , SC_BLEEDING );
+ set_sc( GS_MADNESSCANCEL , SC_MADNESSCANCEL , SI_MADNESSCANCEL , SCB_BATK|SCB_ASPD );
+ set_sc( GS_ADJUSTMENT , SC_ADJUSTMENT , SI_ADJUSTMENT , SCB_HIT|SCB_FLEE );
+ set_sc( GS_INCREASING , SC_INCREASING , SI_ACCURACY , SCB_AGI|SCB_DEX|SCB_HIT );
+ set_sc( GS_GATLINGFEVER , SC_GATLINGFEVER , SI_GATLINGFEVER , SCB_BATK|SCB_FLEE|SCB_SPEED|SCB_ASPD );
+ set_sc( NJ_TATAMIGAESHI , SC_TATAMIGAESHI , SI_BLANK , SCB_NONE );
+ set_sc( NJ_SUITON , SC_SUITON , SI_BLANK , SCB_AGI|SCB_SPEED );
+ add_sc( NJ_HYOUSYOURAKU , SC_FREEZE );
+ set_sc( NJ_NEN , SC_NEN , SI_NEN , SCB_STR|SCB_INT );
+ set_sc( NJ_UTSUSEMI , SC_UTSUSEMI , SI_UTSUSEMI , SCB_NONE );
+ set_sc( NJ_BUNSINJYUTSU , SC_BUNSINJYUTSU , SI_BUNSINJYUTSU , SCB_DYE );
+
+ add_sc( NPC_ICEBREATH , SC_FREEZE );
+ add_sc( NPC_ACIDBREATH , SC_POISON );
+ add_sc( NPC_HELLJUDGEMENT , SC_CURSE );
+ add_sc( NPC_WIDESILENCE , SC_SILENCE );
+ add_sc( NPC_WIDEFREEZE , SC_FREEZE );
+ add_sc( NPC_WIDEBLEEDING , SC_BLEEDING );
+ add_sc( NPC_WIDESTONE , SC_STONE );
+ add_sc( NPC_WIDECONFUSE , SC_CONFUSION );
+ add_sc( NPC_WIDESLEEP , SC_SLEEP );
+ add_sc( NPC_WIDESIGHT , SC_SIGHT );
+ add_sc( NPC_EVILLAND , SC_BLIND );
+ add_sc( NPC_MAGICMIRROR , SC_MAGICMIRROR );
+ set_sc( NPC_SLOWCAST , SC_SLOWCAST , SI_SLOWCAST , SCB_NONE );
+ set_sc( NPC_CRITICALWOUND , SC_CRITICALWOUND , SI_CRITICALWOUND , SCB_NONE );
+ set_sc( NPC_STONESKIN , SC_ARMORCHANGE , SI_BLANK , SCB_DEF|SCB_MDEF );
+ add_sc( NPC_ANTIMAGIC , SC_ARMORCHANGE );
+ add_sc( NPC_WIDECURSE , SC_CURSE );
+ add_sc( NPC_WIDESTUN , SC_STUN );
+
+ set_sc( NPC_HELLPOWER , SC_HELLPOWER , SI_HELLPOWER , SCB_NONE );
+ set_sc( NPC_WIDEHELLDIGNITY , SC_HELLPOWER , SI_HELLPOWER , SCB_NONE );
+ set_sc( NPC_INVINCIBLE , SC_INVINCIBLE , SI_INVINCIBLE , SCB_SPEED );
+ set_sc( NPC_INVINCIBLEOFF , SC_INVINCIBLEOFF , SI_BLANK , SCB_SPEED );
+
+ set_sc( CASH_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX );
+ set_sc( CASH_INCAGI , SC_INCREASEAGI , SI_INCREASEAGI , SCB_AGI|SCB_SPEED );
+ set_sc( CASH_ASSUMPTIO , SC_ASSUMPTIO , SI_ASSUMPTIO , SCB_NONE );
+
+ set_sc( ALL_PARTYFLEE , SC_PARTYFLEE , SI_PARTYFLEE , SCB_NONE );
+ set_sc( ALL_ODINS_POWER , SC_ODINS_POWER , SI_ODINS_POWER , SCB_MATK|SCB_BATK|SCB_MDEF|SCB_DEF );
+
+ set_sc( CR_SHRINK , SC_SHRINK , SI_SHRINK , SCB_NONE );
+ set_sc( RG_CLOSECONFINE , SC_CLOSECONFINE2 , SI_CLOSECONFINE2 , SCB_NONE );
+ set_sc( RG_CLOSECONFINE , SC_CLOSECONFINE , SI_CLOSECONFINE , SCB_FLEE );
+ set_sc( WZ_SIGHTBLASTER , SC_SIGHTBLASTER , SI_SIGHTBLASTER , SCB_NONE );
+ set_sc( DC_WINKCHARM , SC_WINKCHARM , SI_WINKCHARM , SCB_NONE );
+ add_sc( MO_BALKYOUNG , SC_STUN );
+ add_sc( SA_ELEMENTWATER , SC_ELEMENTALCHANGE );
+ add_sc( SA_ELEMENTFIRE , SC_ELEMENTALCHANGE );
+ add_sc( SA_ELEMENTGROUND , SC_ELEMENTALCHANGE );
+ add_sc( SA_ELEMENTWIND , SC_ELEMENTALCHANGE );
+
+ set_sc( HLIF_AVOID , SC_AVOID , SI_BLANK , SCB_SPEED );
+ set_sc( HLIF_CHANGE , SC_CHANGE , SI_BLANK , SCB_VIT|SCB_INT );
+ set_sc( HFLI_FLEET , SC_FLEET , SI_BLANK , SCB_ASPD|SCB_BATK|SCB_WATK );
+ set_sc( HFLI_SPEED , SC_SPEED , SI_BLANK , SCB_FLEE );
+ set_sc( HAMI_DEFENCE , SC_DEFENCE , SI_BLANK , SCB_DEF );
+ set_sc( HAMI_BLOODLUST , SC_BLOODLUST , SI_BLANK , SCB_BATK|SCB_WATK );
+
+ // Homunculus S
+ add_sc(MH_STAHL_HORN, SC_STUN);
+ set_sc(MH_ANGRIFFS_MODUS, SC_ANGRIFFS_MODUS, SI_ANGRIFFS_MODUS, SCB_BATK | SCB_DEF | SCB_FLEE | SCB_MAXHP);
+ set_sc(MH_GOLDENE_FERSE, SC_GOLDENE_FERSE, SI_GOLDENE_FERSE, SCB_ASPD|SCB_MAXHP);
+ add_sc( MH_STEINWAND, SC_SAFETYWALL );
+ add_sc(MH_ERASER_CUTTER, SC_ERASER_CUTTER);
+ set_sc(MH_OVERED_BOOST, SC_OVERED_BOOST, SI_BLANK, SCB_FLEE|SCB_ASPD);
+ add_sc(MH_LIGHT_OF_REGENE, SC_LIGHT_OF_REGENE);
+ set_sc(MH_VOLCANIC_ASH, SC_ASH, SI_VOLCANIC_ASH, SCB_DEF|SCB_DEF2|SCB_HIT|SCB_BATK|SCB_FLEE);
+ set_sc(MH_GRANITIC_ARMOR, SC_GRANITIC_ARMOR, SI_GRANITIC_ARMOR, SCB_NONE);
+ set_sc(MH_MAGMA_FLOW, SC_MAGMA_FLOW, SI_MAGMA_FLOW, SCB_NONE);
+ set_sc(MH_PYROCLASTIC, SC_PYROCLASTIC, SI_PYROCLASTIC, SCB_BATK|SCB_ATK_ELE);
+ add_sc(MH_LAVA_SLIDE, SC_BURNING);
+ set_sc(MH_NEEDLE_OF_PARALYZE, SC_PARALYSIS, SI_NEEDLE_OF_PARALYZE, SCB_DEF2);
+ add_sc(MH_POISON_MIST, SC_BLIND);
+ set_sc(MH_PAIN_KILLER, SC_PAIN_KILLER, SI_PAIN_KILLER, SCB_ASPD);
+
+ add_sc(MH_STYLE_CHANGE, SC_STYLE_CHANGE);
+ set_sc( MH_TINDER_BREAKER , SC_CLOSECONFINE2 , SI_CLOSECONFINE2 , SCB_NONE );
+ set_sc( MH_TINDER_BREAKER , SC_CLOSECONFINE , SI_CLOSECONFINE , SCB_FLEE );
+
+
+ add_sc( MER_CRASH , SC_STUN );
+ set_sc( MER_PROVOKE , SC_PROVOKE , SI_PROVOKE , SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK );
+ add_sc( MS_MAGNUM , SC_WATK_ELEMENT );
+ add_sc( MER_SIGHT , SC_SIGHT );
+ set_sc( MER_DECAGI , SC_DECREASEAGI , SI_DECREASEAGI , SCB_AGI|SCB_SPEED );
+ set_sc( MER_MAGNIFICAT , SC_MAGNIFICAT , SI_MAGNIFICAT , SCB_REGEN );
+ add_sc( MER_LEXDIVINA , SC_SILENCE );
+ add_sc( MA_LANDMINE , SC_STUN );
+ add_sc( MA_SANDMAN , SC_SLEEP );
+ add_sc( MA_FREEZINGTRAP , SC_FREEZE );
+ set_sc( MER_AUTOBERSERK , SC_AUTOBERSERK , SI_AUTOBERSERK , SCB_NONE );
+ set_sc( ML_AUTOGUARD , SC_AUTOGUARD , SI_AUTOGUARD , SCB_NONE );
+ set_sc( MS_REFLECTSHIELD , SC_REFLECTSHIELD , SI_REFLECTSHIELD , SCB_NONE );
+ set_sc( ML_DEFENDER , SC_DEFENDER , SI_DEFENDER , SCB_SPEED|SCB_ASPD );
+ set_sc( MS_PARRYING , SC_PARRYING , SI_PARRYING , SCB_NONE );
+ set_sc( MS_BERSERK , SC_BERSERK , SI_BERSERK , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP|SCB_REGEN );
+ add_sc( ML_SPIRALPIERCE , SC_STOP );
+ set_sc( MER_QUICKEN , SC_MERC_QUICKEN , SI_BLANK , SCB_ASPD );
+ add_sc( ML_DEVOTION , SC_DEVOTION );
+ set_sc( MER_KYRIE , SC_KYRIE , SI_KYRIE , SCB_NONE );
+ set_sc( MER_BLESSING , SC_BLESSING , SI_BLESSING , SCB_STR|SCB_INT|SCB_DEX );
+ set_sc( MER_INCAGI , SC_INCREASEAGI , SI_INCREASEAGI , SCB_AGI|SCB_SPEED );
+
+ set_sc( GD_LEADERSHIP , SC_LEADERSHIP , SI_BLANK , SCB_STR );
+ set_sc( GD_GLORYWOUNDS , SC_GLORYWOUNDS , SI_BLANK , SCB_VIT );
+ set_sc( GD_SOULCOLD , SC_SOULCOLD , SI_BLANK , SCB_AGI );
+ set_sc( GD_HAWKEYES , SC_HAWKEYES , SI_BLANK , SCB_DEX );
+
+ set_sc( GD_BATTLEORDER , SC_BATTLEORDERS , SI_BLANK , SCB_STR|SCB_INT|SCB_DEX );
+ set_sc( GD_REGENERATION , SC_REGENERATION , SI_BLANK , SCB_REGEN );
+
+ /**
+ * Rune Knight
+ **/
+ set_sc( RK_ENCHANTBLADE , SC_ENCHANTBLADE , SI_ENCHANTBLADE , SCB_NONE );
+ set_sc( RK_DRAGONHOWLING , SC_FEAR , SI_BLANK , SCB_FLEE|SCB_HIT );
+ set_sc( RK_DEATHBOUND , SC_DEATHBOUND , SI_DEATHBOUND , SCB_NONE );
+ set_sc( RK_WINDCUTTER , SC_FEAR , SI_BLANK , SCB_FLEE|SCB_HIT );
+ add_sc( RK_DRAGONBREATH , SC_BURNING );
+ set_sc( RK_MILLENNIUMSHIELD , SC_MILLENNIUMSHIELD , SI_REUSE_MILLENNIUMSHIELD , SCB_NONE );
+ set_sc( RK_REFRESH , SC_REFRESH , SI_REFRESH , SCB_NONE );
+ set_sc( RK_GIANTGROWTH , SC_GIANTGROWTH , SI_GIANTGROWTH , SCB_STR );
+ set_sc( RK_STONEHARDSKIN , SC_STONEHARDSKIN , SI_STONEHARDSKIN , SCB_NONE );
+ set_sc( RK_VITALITYACTIVATION, SC_VITALITYACTIVATION, SI_VITALITYACTIVATION, SCB_REGEN );
+ set_sc( RK_FIGHTINGSPIRIT , SC_FIGHTINGSPIRIT , SI_FIGHTINGSPIRIT , SCB_WATK|SCB_ASPD );
+ set_sc( RK_ABUNDANCE , SC_ABUNDANCE , SI_ABUNDANCE , SCB_NONE );
+ set_sc( RK_CRUSHSTRIKE , SC_CRUSHSTRIKE , SI_CRUSHSTRIKE , SCB_NONE );
+ /**
+ * GC Guillotine Cross
+ **/
+ set_sc_with_vfx( GC_VENOMIMPRESS , SC_VENOMIMPRESS , SI_VENOMIMPRESS , SCB_NONE );
+ set_sc( GC_POISONINGWEAPON , SC_POISONINGWEAPON , SI_POISONINGWEAPON , SCB_NONE );
+ set_sc( GC_WEAPONBLOCKING , SC_WEAPONBLOCKING , SI_WEAPONBLOCKING , SCB_NONE );
+ set_sc( GC_CLOAKINGEXCEED , SC_CLOAKINGEXCEED , SI_CLOAKINGEXCEED , SCB_SPEED );
+ set_sc( GC_HALLUCINATIONWALK , SC_HALLUCINATIONWALK, SI_HALLUCINATIONWALK, SCB_FLEE );
+ set_sc( GC_ROLLINGCUTTER , SC_ROLLINGCUTTER , SI_ROLLINGCUTTER , SCB_NONE );
+ /**
+ * Arch Bishop
+ **/
+ set_sc( AB_ADORAMUS , SC_ADORAMUS , SI_ADORAMUS , SCB_AGI|SCB_SPEED );
+ add_sc( AB_CLEMENTIA , SC_BLESSING );
+ add_sc( AB_CANTO , SC_INCREASEAGI );
+ set_sc( AB_EPICLESIS , SC_EPICLESIS , SI_EPICLESIS , SCB_MAXHP );
+ add_sc( AB_PRAEFATIO , SC_KYRIE );
+ set_sc_with_vfx( AB_ORATIO , SC_ORATIO , SI_ORATIO , SCB_NONE );
+ set_sc( AB_LAUDAAGNUS , SC_LAUDAAGNUS , SI_LAUDAAGNUS , SCB_VIT );
+ set_sc( AB_LAUDARAMUS , SC_LAUDARAMUS , SI_LAUDARAMUS , SCB_LUK );
+ set_sc( AB_RENOVATIO , SC_RENOVATIO , SI_RENOVATIO , SCB_REGEN );
+ set_sc( AB_EXPIATIO , SC_EXPIATIO , SI_EXPIATIO , SCB_ATK_ELE );
+ set_sc( AB_DUPLELIGHT , SC_DUPLELIGHT , SI_DUPLELIGHT , SCB_NONE );
+ set_sc( AB_SECRAMENT , SC_SECRAMENT , SI_SECRAMENT , SCB_NONE );
+ /**
+ * Warlock
+ **/
+ add_sc( WL_WHITEIMPRISON , SC_WHITEIMPRISON );
+ set_sc_with_vfx( WL_FROSTMISTY , SC_FREEZING , SI_FROSTMISTY , SCB_ASPD|SCB_SPEED|SCB_DEF|SCB_DEF2 );
+ set_sc( WL_MARSHOFABYSS , SC_MARSHOFABYSS , SI_MARSHOFABYSS , SCB_SPEED|SCB_FLEE|SCB_DEF|SCB_MDEF );
+ set_sc(WL_RECOGNIZEDSPELL , SC_RECOGNIZEDSPELL , SI_RECOGNIZEDSPELL , SCB_MATK);
+ set_sc( WL_STASIS , SC_STASIS , SI_STASIS , SCB_NONE );
+ /**
+ * Ranger
+ **/
+ set_sc( RA_FEARBREEZE , SC_FEARBREEZE , SI_FEARBREEZE , SCB_NONE );
+ set_sc( RA_ELECTRICSHOCKER , SC_ELECTRICSHOCKER , SI_ELECTRICSHOCKER , SCB_NONE );
+ set_sc( RA_WUGDASH , SC_WUGDASH , SI_WUGDASH , SCB_SPEED );
+ set_sc( RA_CAMOUFLAGE , SC_CAMOUFLAGE , SI_CAMOUFLAGE , SCB_SPEED );
+ add_sc( RA_MAGENTATRAP , SC_ELEMENTALCHANGE );
+ add_sc( RA_COBALTTRAP , SC_ELEMENTALCHANGE );
+ add_sc( RA_MAIZETRAP , SC_ELEMENTALCHANGE );
+ add_sc( RA_VERDURETRAP , SC_ELEMENTALCHANGE );
+ add_sc( RA_FIRINGTRAP , SC_BURNING );
+ set_sc_with_vfx( RA_ICEBOUNDTRAP , SC_FREEZING , SI_FROSTMISTY , SCB_NONE );
+ /**
+ * Mechanic
+ **/
+ set_sc( NC_ACCELERATION , SC_ACCELERATION , SI_ACCELERATION , SCB_SPEED );
+ set_sc( NC_HOVERING , SC_HOVERING , SI_HOVERING , SCB_SPEED );
+ set_sc( NC_SHAPESHIFT , SC_SHAPESHIFT , SI_SHAPESHIFT , SCB_DEF_ELE );
+ set_sc( NC_INFRAREDSCAN , SC_INFRAREDSCAN , SI_INFRAREDSCAN , SCB_FLEE );
+ set_sc( NC_ANALYZE , SC_ANALYZE , SI_ANALYZE , SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2 );
+ set_sc( NC_MAGNETICFIELD , SC_MAGNETICFIELD , SI_MAGNETICFIELD , SCB_NONE );
+ set_sc( NC_NEUTRALBARRIER , SC_NEUTRALBARRIER , SI_NEUTRALBARRIER , SCB_NONE );
+ set_sc( NC_STEALTHFIELD , SC_STEALTHFIELD , SI_STEALTHFIELD , SCB_NONE );
+ /**
+ * Royal Guard
+ **/
+ set_sc( LG_REFLECTDAMAGE , SC_REFLECTDAMAGE , SI_LG_REFLECTDAMAGE, SCB_NONE );
+ set_sc( LG_FORCEOFVANGUARD , SC_FORCEOFVANGUARD , SI_FORCEOFVANGUARD , SCB_MAXHP|SCB_DEF );
+ set_sc( LG_EXEEDBREAK , SC_EXEEDBREAK , SI_EXEEDBREAK , SCB_NONE );
+ set_sc( LG_PRESTIGE , SC_PRESTIGE , SI_PRESTIGE , SCB_DEF );
+ set_sc( LG_BANDING , SC_BANDING , SI_BANDING , SCB_DEF2|SCB_WATK );// Renewal: atk2 & def2
+ set_sc( LG_PIETY , SC_BENEDICTIO , SI_BENEDICTIO , SCB_DEF_ELE );
+ set_sc( LG_EARTHDRIVE , SC_EARTHDRIVE , SI_EARTHDRIVE , SCB_DEF|SCB_ASPD );
+ set_sc( LG_INSPIRATION , SC_INSPIRATION , SI_INSPIRATION , SCB_MAXHP|SCB_WATK|SCB_HIT|SCB_VIT|SCB_AGI|SCB_STR|SCB_DEX|SCB_INT|SCB_LUK);
+ set_sc( LG_SHIELDSPELL , SC_SHIELDSPELL_DEF , SI_SHIELDSPELL_DEF , SCB_WATK );
+ set_sc( LG_SHIELDSPELL , SC_SHIELDSPELL_REF , SI_SHIELDSPELL_REF , SCB_DEF );
+ /**
+ * Shadow Chaser
+ **/
+ set_sc( SC_REPRODUCE , SC__REPRODUCE , SI_REPRODUCE , SCB_NONE );
+ set_sc( SC_AUTOSHADOWSPELL , SC__AUTOSHADOWSPELL, SI_AUTOSHADOWSPELL , SCB_NONE );
+ set_sc( SC_SHADOWFORM , SC__SHADOWFORM , SI_SHADOWFORM , SCB_NONE );
+ set_sc( SC_BODYPAINT , SC__BODYPAINT , SI_BODYPAINT , SCB_ASPD );
+ set_sc( SC_INVISIBILITY , SC__INVISIBILITY , SI_INVISIBILITY , SCB_ASPD|SCB_CRI|SCB_ATK_ELE );
+ set_sc( SC_DEADLYINFECT , SC__DEADLYINFECT , SI_DEADLYINFECT , SCB_NONE );
+ set_sc( SC_ENERVATION , SC__ENERVATION , SI_ENERVATION , SCB_BATK );
+ set_sc( SC_GROOMY , SC__GROOMY , SI_GROOMY , SCB_ASPD|SCB_HIT|SCB_SPEED );
+ set_sc( SC_IGNORANCE , SC__IGNORANCE , SI_IGNORANCE , SCB_NONE );
+ set_sc( SC_LAZINESS , SC__LAZINESS , SI_LAZINESS , SCB_FLEE );
+ set_sc( SC_UNLUCKY , SC__UNLUCKY , SI_UNLUCKY , SCB_CRI|SCB_FLEE2 );
+ set_sc( SC_WEAKNESS , SC__WEAKNESS , SI_WEAKNESS , SCB_FLEE2|SCB_MAXHP );
+ set_sc( SC_STRIPACCESSARY , SC__STRIPACCESSORY , SI_STRIPACCESSARY , SCB_DEX|SCB_INT|SCB_LUK );
+ set_sc_with_vfx( SC_MANHOLE , SC__MANHOLE , SI_MANHOLE , SCB_NONE );
+ add_sc( SC_CHAOSPANIC , SC_CONFUSION );
+ set_sc_with_vfx( SC_BLOODYLUST , SC__BLOODYLUST , SI_BLOODYLUST , SCB_DEF | SCB_DEF2 | SCB_MDEF | SCB_MDEF2 | SCB_FLEE | SCB_SPEED | SCB_ASPD | SCB_MAXHP | SCB_REGEN );
+ /**
+ * Sura
+ **/
+ add_sc( SR_DRAGONCOMBO , SC_STUN );
+ add_sc( SR_EARTHSHAKER , SC_STUN );
+ set_sc( SR_CRESCENTELBOW , SC_CRESCENTELBOW , SI_CRESCENTELBOW , SCB_NONE );
+ set_sc_with_vfx( SR_CURSEDCIRCLE , SC_CURSEDCIRCLE_TARGET, SI_CURSEDCIRCLE_TARGET , SCB_NONE );
+ set_sc( SR_LIGHTNINGWALK , SC_LIGHTNINGWALK , SI_LIGHTNINGWALK , SCB_NONE );
+ set_sc( SR_RAISINGDRAGON , SC_RAISINGDRAGON , SI_RAISINGDRAGON , SCB_REGEN|SCB_MAXHP|SCB_MAXSP );
+ set_sc( SR_GENTLETOUCH_ENERGYGAIN, SC_GT_ENERGYGAIN , SI_GENTLETOUCH_ENERGYGAIN, SCB_NONE );
+ set_sc( SR_GENTLETOUCH_CHANGE , SC_GT_CHANGE , SI_GENTLETOUCH_CHANGE , SCB_ASPD|SCB_MDEF|SCB_MAXHP );
+ set_sc( SR_GENTLETOUCH_REVITALIZE, SC_GT_REVITALIZE , SI_GENTLETOUCH_REVITALIZE, SCB_MAXHP|SCB_REGEN );
+ /**
+ * Wanderer / Minstrel
+ **/
+ set_sc( WA_SWING_DANCE , SC_SWINGDANCE , SI_SWINGDANCE , SCB_SPEED|SCB_ASPD );
+ set_sc( WA_SYMPHONY_OF_LOVER , SC_SYMPHONYOFLOVER , SI_SYMPHONYOFLOVERS , SCB_MDEF );
+ set_sc( WA_MOONLIT_SERENADE , SC_MOONLITSERENADE , SI_MOONLITSERENADE , SCB_MATK );
+ set_sc( MI_RUSH_WINDMILL , SC_RUSHWINDMILL , SI_RUSHWINDMILL , SCB_BATK );
+ set_sc( MI_ECHOSONG , SC_ECHOSONG , SI_ECHOSONG , SCB_DEF2 );
+ set_sc( MI_HARMONIZE , SC_HARMONIZE , SI_HARMONIZE , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK );
+ set_sc_with_vfx( WM_POEMOFNETHERWORLD , SC_NETHERWORLD , SI_NETHERWORLD , SCB_NONE );
+ set_sc_with_vfx( WM_VOICEOFSIREN , SC_VOICEOFSIREN , SI_VOICEOFSIREN , SCB_NONE );
+ set_sc_with_vfx( WM_LULLABY_DEEPSLEEP , SC_DEEPSLEEP , SI_DEEPSLEEP , SCB_NONE );
+ set_sc( WM_SIRCLEOFNATURE , SC_SIRCLEOFNATURE , SI_SIRCLEOFNATURE , SCB_NONE );
+ set_sc( WM_GLOOMYDAY , SC_GLOOMYDAY , SI_GLOOMYDAY , SCB_FLEE|SCB_ASPD );
+ set_sc( WM_SONG_OF_MANA , SC_SONGOFMANA , SI_SONGOFMANA , SCB_NONE );
+ set_sc( WM_DANCE_WITH_WUG , SC_DANCEWITHWUG , SI_DANCEWITHWUG , SCB_ASPD );
+ set_sc( WM_SATURDAY_NIGHT_FEVER , SC_SATURDAYNIGHTFEVER , SI_SATURDAYNIGHTFEVER , SCB_BATK|SCB_DEF|SCB_FLEE|SCB_REGEN );
+ set_sc( WM_LERADS_DEW , SC_LERADSDEW , SI_LERADSDEW , SCB_MAXHP );
+ set_sc( WM_MELODYOFSINK , SC_MELODYOFSINK , SI_MELODYOFSINK , SCB_BATK|SCB_MATK );
+ set_sc( WM_BEYOND_OF_WARCRY , SC_BEYONDOFWARCRY , SI_WARCRYOFBEYOND , SCB_BATK|SCB_MATK );
+ set_sc( WM_UNLIMITED_HUMMING_VOICE, SC_UNLIMITEDHUMMINGVOICE, SI_UNLIMITEDHUMMINGVOICE, SCB_NONE );
+ /**
+ * Sorcerer
+ **/
+ set_sc( SO_FIREWALK , SC_PROPERTYWALK , SI_PROPERTYWALK , SCB_NONE );
+ set_sc( SO_ELECTRICWALK , SC_PROPERTYWALK , SI_PROPERTYWALK , SCB_NONE );
+ set_sc( SO_SPELLFIST , SC_SPELLFIST , SI_SPELLFIST , SCB_NONE );
+ set_sc_with_vfx( SO_DIAMONDDUST , SC_CRYSTALIZE , SI_COLD , SCB_NONE ); // it does show the snow icon on mobs but doesn't affect it.
+ add_sc( SO_CLOUD_KILL , SC_POISON );
+ set_sc( SO_STRIKING , SC_STRIKING , SI_STRIKING , SCB_WATK|SCB_CRI );
+ set_sc( SO_WARMER , SC_WARMER , SI_WARMER , SCB_NONE );
+ set_sc( SO_VACUUM_EXTREME , SC_VACUUM_EXTREME , SI_VACUUM_EXTREME , SCB_NONE );
+ set_sc( SO_ARRULLO , SC_DEEPSLEEP , SI_DEEPSLEEP , SCB_NONE );
+ set_sc( SO_FIRE_INSIGNIA , SC_FIRE_INSIGNIA , SI_FIRE_INSIGNIA , SCB_MATK | SCB_BATK | SCB_WATK | SCB_ATK_ELE | SCB_REGEN );
+ set_sc( SO_WATER_INSIGNIA , SC_WATER_INSIGNIA , SI_WATER_INSIGNIA , SCB_WATK | SCB_ATK_ELE | SCB_REGEN );
+ set_sc( SO_WIND_INSIGNIA , SC_WIND_INSIGNIA , SI_WIND_INSIGNIA , SCB_WATK | SCB_ATK_ELE | SCB_REGEN );
+ set_sc( SO_EARTH_INSIGNIA , SC_EARTH_INSIGNIA , SI_EARTH_INSIGNIA , SCB_MDEF|SCB_DEF|SCB_MAXHP|SCB_MAXSP|SCB_WATK | SCB_ATK_ELE | SCB_REGEN );
+ /**
+ * Genetic
+ **/
+ set_sc( GN_CARTBOOST , SC_GN_CARTBOOST, SI_CARTSBOOST , SCB_SPEED );
+ set_sc( GN_THORNS_TRAP , SC_THORNSTRAP , SI_THORNTRAP , SCB_NONE );
+ set_sc_with_vfx( GN_BLOOD_SUCKER , SC_BLOODSUCKER , SI_BLOODSUCKER , SCB_NONE );
+ set_sc( GN_WALLOFTHORN , SC_STOP , SI_BLANK , SCB_NONE );
+ set_sc( GN_FIRE_EXPANSION_SMOKE_POWDER, SC_SMOKEPOWDER , SI_FIRE_EXPANSION_SMOKE_POWDER, SCB_NONE );
+ set_sc( GN_FIRE_EXPANSION_TEAR_GAS , SC_TEARGAS , SI_FIRE_EXPANSION_TEAR_GAS , SCB_NONE );
+ set_sc( GN_MANDRAGORA , SC_MANDRAGORA , SI_MANDRAGORA , SCB_INT );
+
+ // Elemental Spirit summoner's 'side' status changes.
+ set_sc( EL_CIRCLE_OF_FIRE , SC_CIRCLE_OF_FIRE_OPTION, SI_CIRCLE_OF_FIRE_OPTION, SCB_NONE );
+ set_sc( EL_FIRE_CLOAK , SC_FIRE_CLOAK_OPTION , SI_FIRE_CLOAK_OPTION , SCB_ALL );
+ set_sc( EL_WATER_SCREEN , SC_WATER_SCREEN_OPTION , SI_WATER_SCREEN_OPTION , SCB_NONE );
+ set_sc( EL_WATER_DROP , SC_WATER_DROP_OPTION , SI_WATER_DROP_OPTION , SCB_ALL );
+ set_sc( EL_WATER_BARRIER , SC_WATER_BARRIER , SI_WATER_BARRIER , SCB_MDEF|SCB_WATK|SCB_MATK|SCB_FLEE );
+ set_sc( EL_WIND_STEP , SC_WIND_STEP_OPTION , SI_WIND_STEP_OPTION , SCB_SPEED|SCB_FLEE );
+ set_sc( EL_WIND_CURTAIN , SC_WIND_CURTAIN_OPTION , SI_WIND_CURTAIN_OPTION , SCB_ALL );
+ set_sc( EL_ZEPHYR , SC_ZEPHYR , SI_ZEPHYR , SCB_FLEE );
+ set_sc( EL_SOLID_SKIN , SC_SOLID_SKIN_OPTION , SI_SOLID_SKIN_OPTION , SCB_DEF|SCB_MAXHP );
+ set_sc( EL_STONE_SHIELD , SC_STONE_SHIELD_OPTION , SI_STONE_SHIELD_OPTION , SCB_ALL );
+ set_sc( EL_POWER_OF_GAIA , SC_POWER_OF_GAIA , SI_POWER_OF_GAIA , SCB_MAXHP|SCB_DEF|SCB_SPEED );
+ set_sc( EL_PYROTECHNIC , SC_PYROTECHNIC_OPTION , SI_PYROTECHNIC_OPTION , SCB_WATK );
+ set_sc( EL_HEATER , SC_HEATER_OPTION , SI_HEATER_OPTION , SCB_WATK );
+ set_sc( EL_TROPIC , SC_TROPIC_OPTION , SI_TROPIC_OPTION , SCB_WATK );
+ set_sc( EL_AQUAPLAY , SC_AQUAPLAY_OPTION , SI_AQUAPLAY_OPTION , SCB_MATK );
+ set_sc( EL_COOLER , SC_COOLER_OPTION , SI_COOLER_OPTION , SCB_MATK );
+ set_sc( EL_CHILLY_AIR , SC_CHILLY_AIR_OPTION , SI_CHILLY_AIR_OPTION , SCB_MATK );
+ set_sc( EL_GUST , SC_GUST_OPTION , SI_GUST_OPTION , SCB_ASPD );
+ set_sc( EL_BLAST , SC_BLAST_OPTION , SI_BLAST_OPTION , SCB_ASPD );
+ set_sc( EL_WILD_STORM , SC_WILD_STORM_OPTION , SI_WILD_STORM_OPTION , SCB_ASPD );
+ set_sc( EL_PETROLOGY , SC_PETROLOGY_OPTION , SI_PETROLOGY_OPTION , SCB_MAXHP );
+ set_sc( EL_CURSED_SOIL , SC_CURSED_SOIL_OPTION , SI_CURSED_SOIL_OPTION , SCB_NONE );
+ set_sc( EL_UPHEAVAL , SC_UPHEAVAL_OPTION , SI_UPHEAVAL_OPTION , SCB_NONE );
+ set_sc( EL_TIDAL_WEAPON , SC_TIDAL_WEAPON_OPTION , SI_TIDAL_WEAPON_OPTION , SCB_ALL );
+ set_sc( EL_ROCK_CRUSHER , SC_ROCK_CRUSHER , SI_ROCK_CRUSHER , SCB_DEF );
+ set_sc( EL_ROCK_CRUSHER_ATK, SC_ROCK_CRUSHER_ATK , SI_ROCK_CRUSHER_ATK , SCB_SPEED );
+
+ add_sc( KO_YAMIKUMO , SC_HIDING );
+ set_sc_with_vfx( KO_JYUMONJIKIRI , SC_JYUMONJIKIRI , SI_KO_JYUMONJIKIRI , SCB_NONE );
+ add_sc( KO_MAKIBISHI , SC_STUN );
+ set_sc( KO_MEIKYOUSISUI , SC_MEIKYOUSISUI , SI_MEIKYOUSISUI , SCB_NONE );
+ set_sc( KO_KYOUGAKU , SC_KYOUGAKU , SI_KYOUGAKU , SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK );
+ add_sc( KO_JYUSATSU , SC_CURSE );
+ set_sc( KO_ZENKAI , SC_ZENKAI , SI_ZENKAI , SCB_NONE );
+ set_sc( KO_IZAYOI , SC_IZAYOI , SI_IZAYOI , SCB_MATK );
+ set_sc( KG_KYOMU , SC_KYOMU , SI_KYOMU , SCB_NONE );
+ set_sc( KG_KAGEMUSYA , SC_KAGEMUSYA , SI_KAGEMUSYA , SCB_NONE );
+ set_sc( KG_KAGEHUMI , SC_KAGEHUMI , SI_KG_KAGEHUMI , SCB_NONE );
+ set_sc( OB_ZANGETSU , SC_ZANGETSU , SI_ZANGETSU , SCB_MATK|SCB_BATK );
+ set_sc_with_vfx( OB_AKAITSUKI , SC_AKAITSUKI , SI_AKAITSUKI , SCB_NONE );
+ set_sc( OB_OBOROGENSOU , SC_GENSOU , SI_GENSOU , SCB_NONE );
+
+ // Storing the target job rather than simply SC_SPIRIT simplifies code later on.
+ SkillStatusChangeTable[SL_ALCHEMIST] = (sc_type)MAPID_ALCHEMIST,
+ SkillStatusChangeTable[SL_MONK] = (sc_type)MAPID_MONK,
+ SkillStatusChangeTable[SL_STAR] = (sc_type)MAPID_STAR_GLADIATOR,
+ SkillStatusChangeTable[SL_SAGE] = (sc_type)MAPID_SAGE,
+ SkillStatusChangeTable[SL_CRUSADER] = (sc_type)MAPID_CRUSADER,
+ SkillStatusChangeTable[SL_SUPERNOVICE] = (sc_type)MAPID_SUPER_NOVICE,
+ SkillStatusChangeTable[SL_KNIGHT] = (sc_type)MAPID_KNIGHT,
+ SkillStatusChangeTable[SL_WIZARD] = (sc_type)MAPID_WIZARD,
+ SkillStatusChangeTable[SL_PRIEST] = (sc_type)MAPID_PRIEST,
+ SkillStatusChangeTable[SL_BARDDANCER] = (sc_type)MAPID_BARDDANCER,
+ SkillStatusChangeTable[SL_ROGUE] = (sc_type)MAPID_ROGUE,
+ SkillStatusChangeTable[SL_ASSASIN] = (sc_type)MAPID_ASSASSIN,
+ SkillStatusChangeTable[SL_BLACKSMITH] = (sc_type)MAPID_BLACKSMITH,
+ SkillStatusChangeTable[SL_HUNTER] = (sc_type)MAPID_HUNTER,
+ SkillStatusChangeTable[SL_SOULLINKER] = (sc_type)MAPID_SOUL_LINKER,
+
+ //Status that don't have a skill associated.
+ StatusIconChangeTable[SC_WEIGHT50] = SI_WEIGHT50;
+ StatusIconChangeTable[SC_WEIGHT90] = SI_WEIGHT90;
+ StatusIconChangeTable[SC_ASPDPOTION0] = SI_ASPDPOTION0;
+ StatusIconChangeTable[SC_ASPDPOTION1] = SI_ASPDPOTION1;
+ StatusIconChangeTable[SC_ASPDPOTION2] = SI_ASPDPOTION2;
+ StatusIconChangeTable[SC_ASPDPOTION3] = SI_ASPDPOTIONINFINITY;
+ StatusIconChangeTable[SC_SPEEDUP0] = SI_MOVHASTE_HORSE;
+ StatusIconChangeTable[SC_SPEEDUP1] = SI_SPEEDPOTION1;
+ StatusIconChangeTable[SC_INCSTR] = SI_INCSTR;
+ StatusIconChangeTable[SC_MIRACLE] = SI_SPIRIT;
+ StatusIconChangeTable[SC_INTRAVISION] = SI_INTRAVISION;
+ StatusIconChangeTable[SC_STRFOOD] = SI_FOODSTR;
+ StatusIconChangeTable[SC_AGIFOOD] = SI_FOODAGI;
+ StatusIconChangeTable[SC_VITFOOD] = SI_FOODVIT;
+ StatusIconChangeTable[SC_INTFOOD] = SI_FOODINT;
+ StatusIconChangeTable[SC_DEXFOOD] = SI_FOODDEX;
+ StatusIconChangeTable[SC_LUKFOOD] = SI_FOODLUK;
+ StatusIconChangeTable[SC_FLEEFOOD]= SI_FOODFLEE;
+ StatusIconChangeTable[SC_HITFOOD] = SI_FOODHIT;
+ StatusIconChangeTable[SC_MANU_ATK] = SI_MANU_ATK;
+ StatusIconChangeTable[SC_MANU_DEF] = SI_MANU_DEF;
+ StatusIconChangeTable[SC_SPL_ATK] = SI_SPL_ATK;
+ StatusIconChangeTable[SC_SPL_DEF] = SI_SPL_DEF;
+ StatusIconChangeTable[SC_MANU_MATK] = SI_MANU_MATK;
+ StatusIconChangeTable[SC_SPL_MATK] = SI_SPL_MATK;
+ StatusIconChangeTable[SC_ATKPOTION] = SI_PLUSATTACKPOWER;
+ StatusIconChangeTable[SC_MATKPOTION] = SI_PLUSMAGICPOWER;
+ //Cash Items
+ StatusIconChangeTable[SC_FOOD_STR_CASH] = SI_FOOD_STR_CASH;
+ StatusIconChangeTable[SC_FOOD_AGI_CASH] = SI_FOOD_AGI_CASH;
+ StatusIconChangeTable[SC_FOOD_VIT_CASH] = SI_FOOD_VIT_CASH;
+ StatusIconChangeTable[SC_FOOD_DEX_CASH] = SI_FOOD_DEX_CASH;
+ StatusIconChangeTable[SC_FOOD_INT_CASH] = SI_FOOD_INT_CASH;
+ StatusIconChangeTable[SC_FOOD_LUK_CASH] = SI_FOOD_LUK_CASH;
+ StatusIconChangeTable[SC_EXPBOOST] = SI_EXPBOOST;
+ StatusIconChangeTable[SC_ITEMBOOST] = SI_ITEMBOOST;
+ StatusIconChangeTable[SC_JEXPBOOST] = SI_CASH_PLUSONLYJOBEXP;
+ StatusIconChangeTable[SC_LIFEINSURANCE] = SI_LIFEINSURANCE;
+ StatusIconChangeTable[SC_BOSSMAPINFO] = SI_BOSSMAPINFO;
+ StatusIconChangeTable[SC_DEF_RATE] = SI_DEF_RATE;
+ StatusIconChangeTable[SC_MDEF_RATE] = SI_MDEF_RATE;
+ StatusIconChangeTable[SC_INCCRI] = SI_INCCRI;
+ StatusIconChangeTable[SC_INCFLEE2] = SI_PLUSAVOIDVALUE;
+ StatusIconChangeTable[SC_INCHEALRATE] = SI_INCHEALRATE;
+ StatusIconChangeTable[SC_S_LIFEPOTION] = SI_S_LIFEPOTION;
+ StatusIconChangeTable[SC_L_LIFEPOTION] = SI_L_LIFEPOTION;
+ StatusIconChangeTable[SC_SPCOST_RATE] = SI_ATKER_BLOOD;
+ StatusIconChangeTable[SC_COMMONSC_RESIST] = SI_TARGET_BLOOD;
+ // Mercenary Bonus Effects
+ StatusIconChangeTable[SC_MERC_FLEEUP] = SI_MERC_FLEEUP;
+ StatusIconChangeTable[SC_MERC_ATKUP] = SI_MERC_ATKUP;
+ StatusIconChangeTable[SC_MERC_HPUP] = SI_MERC_HPUP;
+ StatusIconChangeTable[SC_MERC_SPUP] = SI_MERC_SPUP;
+ StatusIconChangeTable[SC_MERC_HITUP] = SI_MERC_HITUP;
+ // Warlock Spheres
+ StatusIconChangeTable[SC_SPHERE_1] = SI_SPHERE_1;
+ StatusIconChangeTable[SC_SPHERE_2] = SI_SPHERE_2;
+ StatusIconChangeTable[SC_SPHERE_3] = SI_SPHERE_3;
+ StatusIconChangeTable[SC_SPHERE_4] = SI_SPHERE_4;
+ StatusIconChangeTable[SC_SPHERE_5] = SI_SPHERE_5;
+ // Warlock Preserved spells
+ StatusIconChangeTable[SC_SPELLBOOK1] = SI_SPELLBOOK1;
+ StatusIconChangeTable[SC_SPELLBOOK2] = SI_SPELLBOOK2;
+ StatusIconChangeTable[SC_SPELLBOOK3] = SI_SPELLBOOK3;
+ StatusIconChangeTable[SC_SPELLBOOK4] = SI_SPELLBOOK4;
+ StatusIconChangeTable[SC_SPELLBOOK5] = SI_SPELLBOOK5;
+ StatusIconChangeTable[SC_SPELLBOOK6] = SI_SPELLBOOK6;
+ StatusIconChangeTable[SC_MAXSPELLBOOK] = SI_SPELLBOOK7;
+
+ StatusIconChangeTable[SC_NEUTRALBARRIER_MASTER] = SI_NEUTRALBARRIER_MASTER;
+ StatusIconChangeTable[SC_STEALTHFIELD_MASTER] = SI_STEALTHFIELD_MASTER;
+ StatusIconChangeTable[SC_OVERHEAT] = SI_OVERHEAT;
+ StatusIconChangeTable[SC_OVERHEAT_LIMITPOINT] = SI_OVERHEAT_LIMITPOINT;
+
+ StatusIconChangeTable[SC_HALLUCINATIONWALK_POSTDELAY] = SI_HALLUCINATIONWALK_POSTDELAY;
+ StatusIconChangeTable[SC_TOXIN] = SI_TOXIN;
+ StatusIconChangeTable[SC_PARALYSE] = SI_PARALYSE;
+ StatusIconChangeTable[SC_VENOMBLEED] = SI_VENOMBLEED;
+ StatusIconChangeTable[SC_MAGICMUSHROOM] = SI_MAGICMUSHROOM;
+ StatusIconChangeTable[SC_DEATHHURT] = SI_DEATHHURT;
+ StatusIconChangeTable[SC_PYREXIA] = SI_PYREXIA;
+ StatusIconChangeTable[SC_OBLIVIONCURSE] = SI_OBLIVIONCURSE;
+ StatusIconChangeTable[SC_LEECHESEND] = SI_LEECHESEND;
+
+ StatusIconChangeTable[SC_SHIELDSPELL_DEF] = SI_SHIELDSPELL_DEF;
+ StatusIconChangeTable[SC_SHIELDSPELL_MDEF] = SI_SHIELDSPELL_MDEF;
+ StatusIconChangeTable[SC_SHIELDSPELL_REF] = SI_SHIELDSPELL_REF;
+ StatusIconChangeTable[SC_BANDING_DEFENCE] = SI_BANDING_DEFENCE;
+
+ StatusIconChangeTable[SC_GLOOMYDAY_SK] = SI_GLOOMYDAY;
+
+ StatusIconChangeTable[SC_CURSEDCIRCLE_ATKER] = SI_CURSEDCIRCLE_ATKER;
+
+ StatusIconChangeTable[SC_STOMACHACHE] = SI_STOMACHACHE;
+ StatusIconChangeTable[SC_MYSTERIOUS_POWDER] = SI_MYSTERIOUS_POWDER;
+ StatusIconChangeTable[SC_MELON_BOMB] = SI_MELON_BOMB;
+ StatusIconChangeTable[SC_BANANA_BOMB] = SI_BANANA_BOMB;
+ StatusIconChangeTable[SC_BANANA_BOMB_SITDOWN] = SI_BANANA_BOMB_SITDOWN_POSTDELAY;
+
+ //Genetics New Food Items Status Icons
+ StatusIconChangeTable[SC_SAVAGE_STEAK] = SI_SAVAGE_STEAK;
+ StatusIconChangeTable[SC_COCKTAIL_WARG_BLOOD] = SI_COCKTAIL_WARG_BLOOD;
+ StatusIconChangeTable[SC_MINOR_BBQ] = SI_MINOR_BBQ;
+ StatusIconChangeTable[SC_SIROMA_ICE_TEA] = SI_SIROMA_ICE_TEA;
+ StatusIconChangeTable[SC_DROCERA_HERB_STEAMED] = SI_DROCERA_HERB_STEAMED;
+ StatusIconChangeTable[SC_PUTTI_TAILS_NOODLES] = SI_PUTTI_TAILS_NOODLES;
+
+ StatusIconChangeTable[SC_BOOST500] |= SI_BOOST500;
+ StatusIconChangeTable[SC_FULL_SWING_K] |= SI_FULL_SWING_K;
+ StatusIconChangeTable[SC_MANA_PLUS] |= SI_MANA_PLUS;
+ StatusIconChangeTable[SC_MUSTLE_M] |= SI_MUSTLE_M;
+ StatusIconChangeTable[SC_LIFE_FORCE_F] |= SI_LIFE_FORCE_F;
+ StatusIconChangeTable[SC_EXTRACT_WHITE_POTION_Z] |= SI_EXTRACT_WHITE_POTION_Z;
+ StatusIconChangeTable[SC_VITATA_500] |= SI_VITATA_500;
+ StatusIconChangeTable[SC_EXTRACT_SALAMINE_JUICE] |= SI_EXTRACT_SALAMINE_JUICE;
+
+ // Elemental Spirit's 'side' status change icons.
+ StatusIconChangeTable[SC_CIRCLE_OF_FIRE] = SI_CIRCLE_OF_FIRE;
+ StatusIconChangeTable[SC_FIRE_CLOAK] = SI_FIRE_CLOAK;
+ StatusIconChangeTable[SC_WATER_SCREEN] = SI_WATER_SCREEN;
+ StatusIconChangeTable[SC_WATER_DROP] = SI_WATER_DROP;
+ StatusIconChangeTable[SC_WIND_STEP] = SI_WIND_STEP;
+ StatusIconChangeTable[SC_WIND_CURTAIN] = SI_WIND_CURTAIN;
+ StatusIconChangeTable[SC_SOLID_SKIN] = SI_SOLID_SKIN;
+ StatusIconChangeTable[SC_STONE_SHIELD] = SI_STONE_SHIELD;
+ StatusIconChangeTable[SC_PYROTECHNIC] = SI_PYROTECHNIC;
+ StatusIconChangeTable[SC_HEATER] = SI_HEATER;
+ StatusIconChangeTable[SC_TROPIC] = SI_TROPIC;
+ StatusIconChangeTable[SC_AQUAPLAY] = SI_AQUAPLAY;
+ StatusIconChangeTable[SC_COOLER] = SI_COOLER;
+ StatusIconChangeTable[SC_CHILLY_AIR] = SI_CHILLY_AIR;
+ StatusIconChangeTable[SC_GUST] = SI_GUST;
+ StatusIconChangeTable[SC_BLAST] = SI_BLAST;
+ StatusIconChangeTable[SC_WILD_STORM] = SI_WILD_STORM;
+ StatusIconChangeTable[SC_PETROLOGY] = SI_PETROLOGY;
+ StatusIconChangeTable[SC_CURSED_SOIL] = SI_CURSED_SOIL;
+ StatusIconChangeTable[SC_UPHEAVAL] = SI_UPHEAVAL;
+ StatusIconChangeTable[SC_PUSH_CART] = SI_ON_PUSH_CART;
+
+ //Other SC which are not necessarily associated to skills.
+ StatusChangeFlagTable[SC_ASPDPOTION0] = SCB_ASPD;
+ StatusChangeFlagTable[SC_ASPDPOTION1] = SCB_ASPD;
+ StatusChangeFlagTable[SC_ASPDPOTION2] = SCB_ASPD;
+ StatusChangeFlagTable[SC_ASPDPOTION3] = SCB_ASPD;
+ StatusChangeFlagTable[SC_SPEEDUP0] = SCB_SPEED;
+ StatusChangeFlagTable[SC_SPEEDUP1] = SCB_SPEED;
+ StatusChangeFlagTable[SC_ATKPOTION] = SCB_BATK;
+ StatusChangeFlagTable[SC_MATKPOTION] = SCB_MATK;
+ StatusChangeFlagTable[SC_INCALLSTATUS] |= SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK;
+ StatusChangeFlagTable[SC_INCSTR] |= SCB_STR;
+ StatusChangeFlagTable[SC_INCAGI] |= SCB_AGI;
+ StatusChangeFlagTable[SC_INCVIT] |= SCB_VIT;
+ StatusChangeFlagTable[SC_INCINT] |= SCB_INT;
+ StatusChangeFlagTable[SC_INCDEX] |= SCB_DEX;
+ StatusChangeFlagTable[SC_INCLUK] |= SCB_LUK;
+ StatusChangeFlagTable[SC_INCHIT] |= SCB_HIT;
+ StatusChangeFlagTable[SC_INCHITRATE] |= SCB_HIT;
+ StatusChangeFlagTable[SC_INCFLEE] |= SCB_FLEE;
+ StatusChangeFlagTable[SC_INCFLEERATE] |= SCB_FLEE;
+ StatusChangeFlagTable[SC_INCCRI] |= SCB_CRI;
+ StatusChangeFlagTable[SC_INCASPDRATE] |= SCB_ASPD;
+ StatusChangeFlagTable[SC_INCFLEE2] |= SCB_FLEE2;
+ StatusChangeFlagTable[SC_INCMHPRATE] |= SCB_MAXHP;
+ StatusChangeFlagTable[SC_INCMSPRATE] |= SCB_MAXSP;
+ StatusChangeFlagTable[SC_INCMHP] |= SCB_MAXHP;
+ StatusChangeFlagTable[SC_INCMSP] |= SCB_MAXSP;
+ StatusChangeFlagTable[SC_INCATKRATE] |= SCB_BATK|SCB_WATK;
+ StatusChangeFlagTable[SC_INCMATKRATE] |= SCB_MATK;
+ StatusChangeFlagTable[SC_INCDEFRATE] |= SCB_DEF;
+ StatusChangeFlagTable[SC_STRFOOD] |= SCB_STR;
+ StatusChangeFlagTable[SC_AGIFOOD] |= SCB_AGI;
+ StatusChangeFlagTable[SC_VITFOOD] |= SCB_VIT;
+ StatusChangeFlagTable[SC_INTFOOD] |= SCB_INT;
+ StatusChangeFlagTable[SC_DEXFOOD] |= SCB_DEX;
+ StatusChangeFlagTable[SC_LUKFOOD] |= SCB_LUK;
+ StatusChangeFlagTable[SC_HITFOOD] |= SCB_HIT;
+ StatusChangeFlagTable[SC_FLEEFOOD] |= SCB_FLEE;
+ StatusChangeFlagTable[SC_BATKFOOD] |= SCB_BATK;
+ StatusChangeFlagTable[SC_WATKFOOD] |= SCB_WATK;
+ StatusChangeFlagTable[SC_MATKFOOD] |= SCB_MATK;
+ StatusChangeFlagTable[SC_ARMOR_ELEMENT] |= SCB_ALL;
+ StatusChangeFlagTable[SC_ARMOR_RESIST] |= SCB_ALL;
+ StatusChangeFlagTable[SC_SPCOST_RATE] |= SCB_ALL;
+ StatusChangeFlagTable[SC_WALKSPEED] |= SCB_SPEED;
+ StatusChangeFlagTable[SC_ITEMSCRIPT] |= SCB_ALL;
+ // Cash Items
+ StatusChangeFlagTable[SC_FOOD_STR_CASH] = SCB_STR;
+ StatusChangeFlagTable[SC_FOOD_AGI_CASH] = SCB_AGI;
+ StatusChangeFlagTable[SC_FOOD_VIT_CASH] = SCB_VIT;
+ StatusChangeFlagTable[SC_FOOD_DEX_CASH] = SCB_DEX;
+ StatusChangeFlagTable[SC_FOOD_INT_CASH] = SCB_INT;
+ StatusChangeFlagTable[SC_FOOD_LUK_CASH] = SCB_LUK;
+ // Mercenary Bonus Effects
+ StatusChangeFlagTable[SC_MERC_FLEEUP] |= SCB_FLEE;
+ StatusChangeFlagTable[SC_MERC_ATKUP] |= SCB_WATK;
+ StatusChangeFlagTable[SC_MERC_HPUP] |= SCB_MAXHP;
+ StatusChangeFlagTable[SC_MERC_SPUP] |= SCB_MAXSP;
+ StatusChangeFlagTable[SC_MERC_HITUP] |= SCB_HIT;
+ // Guillotine Cross Poison Effects
+ StatusChangeFlagTable[SC_PARALYSE] |= SCB_ASPD|SCB_FLEE|SCB_SPEED;
+ StatusChangeFlagTable[SC_DEATHHURT] |= SCB_REGEN;
+ StatusChangeFlagTable[SC_VENOMBLEED] |= SCB_MAXHP;
+ StatusChangeFlagTable[SC_OBLIVIONCURSE] |= SCB_REGEN;
+
+ StatusChangeFlagTable[SC_SAVAGE_STEAK] |= SCB_STR;
+ StatusChangeFlagTable[SC_COCKTAIL_WARG_BLOOD] |= SCB_INT;
+ StatusChangeFlagTable[SC_MINOR_BBQ] |= SCB_VIT;
+ StatusChangeFlagTable[SC_SIROMA_ICE_TEA] |= SCB_DEX;
+ StatusChangeFlagTable[SC_DROCERA_HERB_STEAMED] |= SCB_AGI;
+ StatusChangeFlagTable[SC_PUTTI_TAILS_NOODLES] |= SCB_LUK;
+ StatusChangeFlagTable[SC_BOOST500] |= SCB_ASPD;
+ StatusChangeFlagTable[SC_FULL_SWING_K] |= SCB_BATK;
+ StatusChangeFlagTable[SC_MANA_PLUS] |= SCB_MATK;
+ StatusChangeFlagTable[SC_MUSTLE_M] |= SCB_MAXHP;
+ StatusChangeFlagTable[SC_LIFE_FORCE_F] |= SCB_MAXSP;
+ StatusChangeFlagTable[SC_EXTRACT_WHITE_POTION_Z] |= SCB_REGEN;
+ StatusChangeFlagTable[SC_VITATA_500] |= SCB_REGEN;
+ StatusChangeFlagTable[SC_EXTRACT_SALAMINE_JUICE] |= SCB_ASPD;
+
+#ifdef RENEWAL_EDP
+ // renewal EDP increases your weapon atk
+ StatusChangeFlagTable[SC_EDP] |= SCB_WATK;
+#endif
+
+ if( !battle_config.display_hallucination ) //Disable Hallucination.
+ StatusIconChangeTable[SC_HALLUCINATION] = SI_BLANK;
+
+ /* StatusChangeState (SCS_) NOMOVE */
+ StatusChangeStateTable[SC_ANKLE] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_AUTOCOUNTER] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_TRICKDEAD] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_BLADESTOP] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_BLADESTOP_WAIT] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_GOSPEL] |= SCS_NOMOVE|SCS_NOMOVECOND;
+ StatusChangeStateTable[SC_BASILICA] |= SCS_NOMOVE|SCS_NOMOVECOND;
+ StatusChangeStateTable[SC_STOP] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_CLOSECONFINE] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_CLOSECONFINE2] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_MADNESSCANCEL] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_GRAVITATION] |= SCS_NOMOVE|SCS_NOMOVECOND;
+ StatusChangeStateTable[SC_WHITEIMPRISON] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_ELECTRICSHOCKER] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_BITE] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_THORNSTRAP] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_MAGNETICFIELD] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC__MANHOLE] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_CURSEDCIRCLE_ATKER] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_CURSEDCIRCLE_TARGET] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_CRYSTALIZE] |= SCS_NOMOVE|SCS_NOMOVECOND;
+ StatusChangeStateTable[SC_NETHERWORLD] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_CAMOUFLAGE] |= SCS_NOMOVE|SCS_NOMOVECOND;
+ StatusChangeStateTable[SC_MEIKYOUSISUI] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_KAGEHUMI] |= SCS_NOMOVE;
+ StatusChangeStateTable[SC_KYOUGAKU] |= SCS_NOMOVE;
+
+ /* StatusChangeState (SCS_) NOPICKUPITEMS */
+ StatusChangeStateTable[SC_HIDING] |= SCS_NOPICKITEM;
+ StatusChangeStateTable[SC_CLOAKING] |= SCS_NOPICKITEM;
+ StatusChangeStateTable[SC_TRICKDEAD] |= SCS_NOPICKITEM;
+ StatusChangeStateTable[SC_BLADESTOP] |= SCS_NOPICKITEM;
+ StatusChangeStateTable[SC_CLOAKINGEXCEED] |= SCS_NOPICKITEM;
+ StatusChangeStateTable[SC_NOCHAT] |= SCS_NOPICKITEM|SCS_NOPICKITEMCOND;
+
+ /* StatusChangeState (SCS_) NODROPITEMS */
+ StatusChangeStateTable[SC_AUTOCOUNTER] |= SCS_NODROPITEM;
+ StatusChangeStateTable[SC_BLADESTOP] |= SCS_NODROPITEM;
+ StatusChangeStateTable[SC_NOCHAT] |= SCS_NODROPITEM|SCS_NODROPITEMCOND;
+
+ /* StatusChangeState (SCS_) NOCAST (skills) */
+ StatusChangeStateTable[SC_SILENCE] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_STEELBODY] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_BERSERK] |= SCS_NOCAST;
+ StatusChangeStateTable[SC__BLOODYLUST] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_OBLIVIONCURSE] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_WHITEIMPRISON] |= SCS_NOCAST;
+ StatusChangeStateTable[SC__INVISIBILITY] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_CRYSTALIZE] |= SCS_NOCAST|SCS_NOCASTCOND;
+ StatusChangeStateTable[SC__IGNORANCE] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_DEEPSLEEP] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_SATURDAYNIGHTFEVER] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_CURSEDCIRCLE_TARGET] |= SCS_NOCAST;
+ StatusChangeStateTable[SC_SILENCE] |= SCS_NOCAST;
+
+ //Homon S
+ StatusChangeStateTable[SC_PARALYSIS] |= SCS_NOMOVE;
+
+}
+
+static void initDummyData(void)
+{
+ memset(&dummy_status, 0, sizeof(dummy_status));
+ dummy_status.hp =
+ dummy_status.max_hp =
+ dummy_status.max_sp =
+ dummy_status.str =
+ dummy_status.agi =
+ dummy_status.vit =
+ dummy_status.int_ =
+ dummy_status.dex =
+ dummy_status.luk =
+ dummy_status.hit = 1;
+ dummy_status.speed = 2000;
+ dummy_status.adelay = 4000;
+ dummy_status.amotion = 2000;
+ dummy_status.dmotion = 2000;
+ dummy_status.ele_lv = 1; //Min elemental level.
+ dummy_status.mode = MD_CANMOVE;
+}
+
+
+//For copying a status_data structure from b to a, without overwriting current Hp and Sp
+static inline void status_cpy(struct status_data* a, const struct status_data* b)
+{
+ memcpy((void*)&a->max_hp, (const void*)&b->max_hp, sizeof(struct status_data)-(sizeof(a->hp)+sizeof(a->sp)));
+}
+
+//Sets HP to given value. Flag is the flag passed to status_heal in case
+//final value is higher than current (use 2 to make a healing effect display
+//on players) It will always succeed (overrides Berserk block), but it can't kill.
+int status_set_hp(struct block_list *bl, unsigned int hp, int flag)
+{
+ struct status_data *status;
+ if (hp < 1) return 0;
+ status = status_get_status_data(bl);
+ if (status == &dummy_status)
+ return 0;
+
+ if (hp > status->max_hp) hp = status->max_hp;
+ if (hp == status->hp) return 0;
+ if (hp > status->hp)
+ return status_heal(bl, hp - status->hp, 0, 1|flag);
+ return status_zap(bl, status->hp - hp, 0);
+}
+
+//Sets SP to given value. Flag is the flag passed to status_heal in case
+//final value is higher than current (use 2 to make a healing effect display
+//on players)
+int status_set_sp(struct block_list *bl, unsigned int sp, int flag)
+{
+ struct status_data *status;
+
+ status = status_get_status_data(bl);
+ if (status == &dummy_status)
+ return 0;
+
+ if (sp > status->max_sp) sp = status->max_sp;
+ if (sp == status->sp) return 0;
+ if (sp > status->sp)
+ return status_heal(bl, 0, sp - status->sp, 1|flag);
+ return status_zap(bl, 0, status->sp - sp);
+}
+
+int status_charge(struct block_list* bl, int hp, int sp)
+{
+ if(!(bl->type&BL_CONSUME))
+ return hp+sp; //Assume all was charged so there are no 'not enough' fails.
+ return status_damage(NULL, bl, hp, sp, 0, 3);
+}
+
+//Inflicts damage on the target with the according walkdelay.
+//If flag&1, damage is passive and does not triggers cancelling status changes.
+//If flag&2, fail if target does not has enough to substract.
+//If flag&4, if killed, mob must not give exp/loot.
+//flag will be set to &8 when damaging sp of a dead character
+int status_damage(struct block_list *src,struct block_list *target,int hp, int sp, int walkdelay, int flag)
+{
+ struct status_data *status;
+ struct status_change *sc;
+
+ if(sp && !(target->type&BL_CONSUME))
+ sp = 0; //Not a valid SP target.
+
+ if (hp < 0) { //Assume absorbed damage.
+ status_heal(target, -hp, 0, 1);
+ hp = 0;
+ }
+
+ if (sp < 0) {
+ status_heal(target, 0, -sp, 1);
+ sp = 0;
+ }
+
+ if (target->type == BL_SKILL)
+ return skill_unit_ondamaged((struct skill_unit *)target, src, hp, gettick());
+
+ status = status_get_status_data(target);
+ if( status == &dummy_status )
+ return 0;
+
+ if ((unsigned int)hp >= status->hp) {
+ if (flag&2) return 0;
+ hp = status->hp;
+ }
+
+ if ((unsigned int)sp > status->sp) {
+ if (flag&2) return 0;
+ sp = status->sp;
+ }
+
+ if (!hp && !sp)
+ return 0;
+
+ if( !status->hp )
+ flag |= 8;
+
+// Let through. battle.c/skill.c have the whole logic of when it's possible or
+// not to hurt someone (and this check breaks pet catching) [Skotlex]
+// if (!target->prev && !(flag&2))
+// return 0; //Cannot damage a bl not on a map, except when "charging" hp/sp
+
+ sc = status_get_sc(target);
+ if( hp && battle_config.invincible_nodamage && src && sc && sc->data[SC_INVINCIBLE] && !sc->data[SC_INVINCIBLEOFF] )
+ hp = 1;
+
+ if( hp && !(flag&1) ) {
+ if( sc ) {
+ struct status_change_entry *sce;
+ if (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
+ status_change_end(target, SC_STONE, INVALID_TIMER);
+ status_change_end(target, SC_FREEZE, INVALID_TIMER);
+ status_change_end(target, SC_SLEEP, INVALID_TIMER);
+ status_change_end(target, SC_WINKCHARM, INVALID_TIMER);
+ status_change_end(target, SC_CONFUSION, INVALID_TIMER);
+ status_change_end(target, SC_TRICKDEAD, INVALID_TIMER);
+ status_change_end(target, SC_HIDING, INVALID_TIMER);
+ status_change_end(target, SC_CLOAKING, INVALID_TIMER);
+ status_change_end(target, SC_CHASEWALK, INVALID_TIMER);
+ status_change_end(target, SC_CAMOUFLAGE, INVALID_TIMER);
+ status_change_end(target, SC__INVISIBILITY, INVALID_TIMER);
+ status_change_end(target, SC_DEEPSLEEP, INVALID_TIMER);
+ if ((sce=sc->data[SC_ENDURE]) && !sce->val4) {
+ //Endure count is only reduced by non-players on non-gvg maps.
+ //val4 signals infinite endure. [Skotlex]
+ if (src && src->type != BL_PC && !map_flag_gvg(target->m) && !map[target->m].flag.battleground && --(sce->val2) < 0)
+ status_change_end(target, SC_ENDURE, INVALID_TIMER);
+ }
+ if ((sce=sc->data[SC_GRAVITATION]) && sce->val3 == BCT_SELF) {
+ struct skill_unit_group* sg = skill_id2group(sce->val4);
+ if (sg) {
+ skill_delunitgroup(sg);
+ sce->val4 = 0;
+ status_change_end(target, SC_GRAVITATION, INVALID_TIMER);
+ }
+ }
+ if(sc->data[SC_DANCING] && (unsigned int)hp > status->max_hp>>2)
+ status_change_end(target, SC_DANCING, INVALID_TIMER);
+ if(sc->data[SC_CLOAKINGEXCEED] && --(sc->data[SC_CLOAKINGEXCEED]->val2) <= 0)
+ status_change_end(target, SC_CLOAKINGEXCEED, INVALID_TIMER);
+ if(sc->data[SC_KAGEMUSYA] && --(sc->data[SC_KAGEMUSYA]->val3) <= 0)
+ status_change_end(target, SC_KAGEMUSYA, INVALID_TIMER);
+ }
+ unit_skillcastcancel(target, 2);
+ }
+
+ status->hp-= hp;
+ status->sp-= sp;
+
+ if (sc && hp && status->hp) {
+ if (sc->data[SC_AUTOBERSERK] &&
+ (!sc->data[SC_PROVOKE] || !sc->data[SC_PROVOKE]->val2) &&
+ status->hp < status->max_hp>>2)
+ sc_start4(target,SC_PROVOKE,100,10,1,0,0,0);
+ if (sc->data[SC_BERSERK] && status->hp <= 100)
+ status_change_end(target, SC_BERSERK, INVALID_TIMER);
+ if( sc->data[SC_RAISINGDRAGON] && status->hp <= 1000 )
+ status_change_end(target, SC_RAISINGDRAGON, INVALID_TIMER);
+ if (sc->data[SC_SATURDAYNIGHTFEVER] && status->hp <= 100)
+ status_change_end(target, SC_SATURDAYNIGHTFEVER, INVALID_TIMER);
+ if (sc->data[SC__BLOODYLUST] && status->hp <= 100)
+ status_change_end(target, SC__BLOODYLUST, INVALID_TIMER);
+ }
+
+ switch (target->type) {
+ case BL_PC: pc_damage((TBL_PC*)target,src,hp,sp); break;
+ case BL_MOB: mob_damage((TBL_MOB*)target, src, hp); break;
+ case BL_HOM: merc_damage((TBL_HOM*)target); break;
+ case BL_MER: mercenary_heal((TBL_MER*)target,hp,sp); break;
+ case BL_ELEM: elemental_heal((TBL_ELEM*)target,hp,sp); break;
+ }
+
+ if( src && target->type == BL_PC && ((TBL_PC*)target)->disguise ) {// stop walking when attacked in disguise to prevent walk-delay bug
+ unit_stop_walking( target, 1 );
+ }
+
+ if( status->hp || (flag&8) )
+ { //Still lives or has been dead before this damage.
+ if (walkdelay)
+ unit_set_walkdelay(target, gettick(), walkdelay, 0);
+ return hp+sp;
+ }
+
+ status->hp = 1; //To let the dead function cast skills and all that.
+ //NOTE: These dead functions should return: [Skotlex]
+ //0: Death cancelled, auto-revived.
+ //Non-zero: Standard death. Clear status, cancel move/attack, etc
+ //&2: Also remove object from map.
+ //&4: Also delete object from memory.
+ switch (target->type) {
+ case BL_PC: flag = pc_dead((TBL_PC*)target,src); break;
+ case BL_MOB: flag = mob_dead((TBL_MOB*)target, src, flag&4?3:0); break;
+ case BL_HOM: flag = merc_hom_dead((TBL_HOM*)target); break;
+ case BL_MER: flag = mercenary_dead((TBL_MER*)target); break;
+ case BL_ELEM: flag = elemental_dead((TBL_ELEM*)target); break;
+ default: //Unhandled case, do nothing to object.
+ flag = 0;
+ break;
+ }
+
+ if(!flag) //Death cancelled.
+ return hp+sp;
+
+ //Normal death
+ status->hp = 0;
+ if (battle_config.clear_unit_ondeath &&
+ battle_config.clear_unit_ondeath&target->type)
+ skill_clear_unitgroup(target);
+
+ if(target->type&BL_REGEN)
+ { //Reset regen ticks.
+ struct regen_data *regen = status_get_regen_data(target);
+ if (regen) {
+ memset(&regen->tick, 0, sizeof(regen->tick));
+ if (regen->sregen)
+ memset(&regen->sregen->tick, 0, sizeof(regen->sregen->tick));
+ if (regen->ssregen)
+ memset(&regen->ssregen->tick, 0, sizeof(regen->ssregen->tick));
+ }
+ }
+
+ if( sc && sc->data[SC_KAIZEL] && !map_flag_gvg(target->m) )
+ { //flag&8 = disable Kaizel
+ int time = skill_get_time2(SL_KAIZEL,sc->data[SC_KAIZEL]->val1);
+ //Look for Osiris Card's bonus effect on the character and revive 100% or revive normally
+ if ( target->type == BL_PC && BL_CAST(BL_PC,target)->special_state.restart_full_recover )
+ status_revive(target, 100, 100);
+ else
+ status_revive(target, sc->data[SC_KAIZEL]->val2, 0);
+ status_change_clear(target,0);
+ clif_skill_nodamage(target,target,ALL_RESURRECTION,1,1);
+ sc_start(target,status_skill2sc(PR_KYRIE),100,10,time);
+
+ if( target->type == BL_MOB )
+ ((TBL_MOB*)target)->state.rebirth = 1;
+
+ return hp+sp;
+ }
+ if(target->type == BL_PC){
+ TBL_PC *sd = BL_CAST(BL_PC,target);
+ TBL_HOM *hd = sd->hd;
+ if(hd && hd->sc.data[SC_LIGHT_OF_REGENE]){
+ clif_skillcasting(&hd->bl, hd->bl.id, target->id, 0,0, MH_LIGHT_OF_REGENE, skill_get_ele(MH_LIGHT_OF_REGENE, 1), 10); //just to display usage
+ clif_skill_nodamage(&sd->bl, target, ALL_RESURRECTION, 1, status_revive(&sd->bl,10*hd->sc.data[SC_LIGHT_OF_REGENE]->val1,0));
+ status_change_end(&sd->hd->bl,SC_LIGHT_OF_REGENE,INVALID_TIMER);
+ return hp + sp;
+ }
+ }
+ if (target->type == BL_MOB && sc && sc->data[SC_REBIRTH] && !((TBL_MOB*) target)->state.rebirth) {// Ensure the monster has not already rebirthed before doing so.
+ status_revive(target, sc->data[SC_REBIRTH]->val2, 0);
+ status_change_clear(target,0);
+ ((TBL_MOB*)target)->state.rebirth = 1;
+
+ return hp+sp;
+ }
+
+ status_change_clear(target,0);
+
+ if(flag&4) //Delete from memory. (also invokes map removal code)
+ unit_free(target,CLR_DEAD);
+ else
+ if(flag&2) //remove from map
+ unit_remove_map(target,CLR_DEAD);
+ else
+ { //Some death states that would normally be handled by unit_remove_map
+ unit_stop_attack(target);
+ unit_stop_walking(target,1);
+ unit_skillcastcancel(target,0);
+ clif_clearunit_area(target,CLR_DEAD);
+ skill_unit_move(target,gettick(),4);
+ skill_cleartimerskill(target);
+ }
+
+ return hp+sp;
+}
+
+//Heals a character. If flag&1, this is forced healing (otherwise stuff like Berserk can block it)
+//If flag&2, when the player is healed, show the HP/SP heal effect.
+int status_heal(struct block_list *bl,int hp,int sp, int flag)
+{
+ struct status_data *status;
+ struct status_change *sc;
+
+ status = status_get_status_data(bl);
+
+ if (status == &dummy_status || !status->hp)
+ return 0;
+
+ sc = status_get_sc(bl);
+ if (sc && !sc->count)
+ sc = NULL;
+
+ if (hp < 0) {
+ if (hp == INT_MIN) hp++; //-INT_MIN == INT_MIN in some architectures!
+ status_damage(NULL, bl, -hp, 0, 0, 1);
+ hp = 0;
+ }
+
+ if(hp) {
+ if( sc && (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) ) {
+ if( flag&1 )
+ flag &= ~2;
+ else
+ hp = 0;
+ }
+
+ if((unsigned int)hp > status->max_hp - status->hp)
+ hp = status->max_hp - status->hp;
+ }
+
+ if(sp < 0) {
+ if (sp==INT_MIN) sp++;
+ status_damage(NULL, bl, 0, -sp, 0, 1);
+ sp = 0;
+ }
+
+ if(sp) {
+ if((unsigned int)sp > status->max_sp - status->sp)
+ sp = status->max_sp - status->sp;
+ }
+
+ if(!sp && !hp) return 0;
+
+ status->hp+= hp;
+ status->sp+= sp;
+
+ if(hp && sc &&
+ sc->data[SC_AUTOBERSERK] &&
+ sc->data[SC_PROVOKE] &&
+ sc->data[SC_PROVOKE]->val2==1 &&
+ status->hp>=status->max_hp>>2
+ ) //End auto berserk.
+ status_change_end(bl, SC_PROVOKE, INVALID_TIMER);
+
+ // send hp update to client
+ switch(bl->type) {
+ case BL_PC: pc_heal((TBL_PC*)bl,hp,sp,flag&2?1:0); break;
+ case BL_MOB: mob_heal((TBL_MOB*)bl,hp); break;
+ case BL_HOM: merc_hom_heal((TBL_HOM*)bl); break;
+ case BL_MER: mercenary_heal((TBL_MER*)bl,hp,sp); break;
+ case BL_ELEM: elemental_heal((TBL_ELEM*)bl,hp,sp); break;
+ }
+
+ return hp+sp;
+}
+
+//Does percentual non-flinching damage/heal. If mob is killed this way,
+//no exp/drops will be awarded if there is no src (or src is target)
+//If rates are > 0, percent is of current HP/SP
+//If rates are < 0, percent is of max HP/SP
+//If !flag, this is heal, otherwise it is damage.
+//Furthermore, if flag==2, then the target must not die from the substraction.
+int status_percent_change(struct block_list *src,struct block_list *target,signed char hp_rate, signed char sp_rate, int flag)
+{
+ struct status_data *status;
+ unsigned int hp =0, sp = 0;
+
+ status = status_get_status_data(target);
+
+
+ //It's safe now [MarkZD]
+ if (hp_rate > 99)
+ hp = status->hp;
+ else if (hp_rate > 0)
+ hp = status->hp>10000?
+ hp_rate*(status->hp/100):
+ ((int64)hp_rate*status->hp)/100;
+ else if (hp_rate < -99)
+ hp = status->max_hp;
+ else if (hp_rate < 0)
+ hp = status->max_hp>10000?
+ (-hp_rate)*(status->max_hp/100):
+ ((int64)-hp_rate*status->max_hp)/100;
+ if (hp_rate && !hp)
+ hp = 1;
+
+ if (flag == 2 && hp >= status->hp)
+ hp = status->hp-1; //Must not kill target.
+
+ if (sp_rate > 99)
+ sp = status->sp;
+ else if (sp_rate > 0)
+ sp = ((int64)sp_rate*status->sp)/100;
+ else if (sp_rate < -99)
+ sp = status->max_sp;
+ else if (sp_rate < 0)
+ sp = ((int64)-sp_rate)*status->max_sp/100;
+ if (sp_rate && !sp)
+ sp = 1;
+
+ //Ugly check in case damage dealt is too much for the received args of
+ //status_heal / status_damage. [Skotlex]
+ if (hp > INT_MAX) {
+ hp -= INT_MAX;
+ if (flag)
+ status_damage(src, target, INT_MAX, 0, 0, (!src||src==target?5:1));
+ else
+ status_heal(target, INT_MAX, 0, 0);
+ }
+ if (sp > INT_MAX) {
+ sp -= INT_MAX;
+ if (flag)
+ status_damage(src, target, 0, INT_MAX, 0, (!src||src==target?5:1));
+ else
+ status_heal(target, 0, INT_MAX, 0);
+ }
+ if (flag)
+ return status_damage(src, target, hp, sp, 0, (!src||src==target?5:1));
+ return status_heal(target, hp, sp, 0);
+}
+
+int status_revive(struct block_list *bl, unsigned char per_hp, unsigned char per_sp)
+{
+ struct status_data *status;
+ unsigned int hp, sp;
+ if (!status_isdead(bl)) return 0;
+
+ status = status_get_status_data(bl);
+ if (status == &dummy_status)
+ return 0; //Invalid target.
+
+ hp = (int64)status->max_hp * per_hp/100;
+ sp = (int64)status->max_sp * per_sp/100;
+
+ if(hp > status->max_hp - status->hp)
+ hp = status->max_hp - status->hp;
+ else if (per_hp && !hp)
+ hp = 1;
+
+ if(sp > status->max_sp - status->sp)
+ sp = status->max_sp - status->sp;
+ else if (per_sp && !sp)
+ sp = 1;
+
+ status->hp += hp;
+ status->sp += sp;
+
+ if (bl->prev) //Animation only if character is already on a map.
+ clif_resurrection(bl, 1);
+ switch (bl->type) {
+ case BL_PC: pc_revive((TBL_PC*)bl, hp, sp); break;
+ case BL_MOB: mob_revive((TBL_MOB*)bl, hp); break;
+ case BL_HOM: merc_hom_revive((TBL_HOM*)bl, hp, sp); break;
+ }
+ return 1;
+}
+
+/*==========================================
+ * Checks whether the src can use the skill on the target,
+ * taking into account status/option of both source/target. [Skotlex]
+ * flag:
+ * 0 - Trying to use skill on target.
+ * 1 - Cast bar is done.
+ * 2 - Skill already pulled off, check is due to ground-based skills or splash-damage ones.
+ * src MAY be null to indicate we shouldn't check it, this is a ground-based skill attack.
+ * target MAY Be null, in which case the checks are only to see
+ * whether the source can cast or not the skill on the ground.
+ *------------------------------------------*/
+int status_check_skilluse(struct block_list *src, struct block_list *target, uint16 skill_id, int flag)
+{
+ struct status_data *status;
+ struct status_change *sc=NULL, *tsc;
+ int hide_flag;
+
+ status = src?status_get_status_data(src):&dummy_status;
+
+ if (src && src->type != BL_PC && status_isdead(src))
+ return 0;
+
+ if (!skill_id) { //Normal attack checks.
+ if (!(status->mode&MD_CANATTACK))
+ return 0; //This mode is only needed for melee attacking.
+ //Dead state is not checked for skills as some skills can be used
+ //on dead characters, said checks are left to skill.c [Skotlex]
+ if (target && status_isdead(target))
+ return 0;
+ if( src && (sc = status_get_sc(src)) && sc->data[SC_CRYSTALIZE] && src->type != BL_MOB)
+ return 0;
+ }
+
+ switch( skill_id ) {
+ case PA_PRESSURE:
+ if( flag && target ) {
+ //Gloria Avoids pretty much everything....
+ tsc = status_get_sc(target);
+ if(tsc && tsc->option&OPTION_HIDE)
+ return 0;
+ }
+ break;
+ case GN_WALLOFTHORN:
+ if( target && status_isdead(target) )
+ return 0;
+ break;
+ case AL_TELEPORT:
+ //Should fail when used on top of Land Protector [Skotlex]
+ if (src && map_getcell(src->m, src->x, src->y, CELL_CHKLANDPROTECTOR)
+ && !(status->mode&MD_BOSS)
+ && (src->type != BL_PC || ((TBL_PC*)src)->skillitem != skill_id))
+ return 0;
+ break;
+ default:
+ break;
+ }
+
+ if ( src ) sc = status_get_sc(src);
+
+ if( sc && sc->count ) {
+
+ if (skill_id != RK_REFRESH && sc->opt1 >0 && (sc->opt1 != OPT1_CRYSTALIZE && src->type != BL_MOB) && sc->opt1 != OPT1_BURNING && skill_id != SR_GENTLETOUCH_CURE) { //Stuned/Frozen/etc
+ if (flag != 1) //Can't cast, casted stuff can't damage.
+ return 0;
+ if (!(skill_get_inf(skill_id)&INF_GROUND_SKILL))
+ return 0; //Targetted spells can't come off.
+ }
+
+ if (
+ (sc->data[SC_TRICKDEAD] && skill_id != NV_TRICKDEAD)
+ || (sc->data[SC_AUTOCOUNTER] && !flag)
+ || (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF && skill_id != PA_GOSPEL)
+ || (sc->data[SC_GRAVITATION] && sc->data[SC_GRAVITATION]->val3 == BCT_SELF && flag != 2)
+ )
+ return 0;
+
+ if (sc->data[SC_WINKCHARM] && target && !flag) { //Prevents skill usage
+ if( unit_bl2ud(src) && (unit_bl2ud(src))->walktimer == INVALID_TIMER )
+ unit_walktobl(src, map_id2bl(sc->data[SC_WINKCHARM]->val2), 3, 1);
+ clif_emotion(src, E_LV);
+ return 0;
+ }
+
+ if (sc->data[SC_BLADESTOP]) {
+ switch (sc->data[SC_BLADESTOP]->val1)
+ {
+ case 5: if (skill_id == MO_EXTREMITYFIST) break;
+ case 4: if (skill_id == MO_CHAINCOMBO) break;
+ case 3: if (skill_id == MO_INVESTIGATE) break;
+ case 2: if (skill_id == MO_FINGEROFFENSIVE) break;
+ default: return 0;
+ }
+ }
+
+ if (sc->data[SC_DANCING] && flag!=2) {
+ if( src->type == BL_PC && skill_id >= WA_SWING_DANCE && skill_id <= WM_UNLIMITED_HUMMING_VOICE )
+ { // Lvl 5 Lesson or higher allow you use 3rd job skills while dancing.v
+ if( pc_checkskill((TBL_PC*)src,WM_LESSON) < 5 )
+ return 0;
+ } else if(sc->data[SC_LONGING]) { //Allow everything except dancing/re-dancing. [Skotlex]
+ if (skill_id == BD_ENCORE ||
+ skill_get_inf2(skill_id)&(INF2_SONG_DANCE|INF2_ENSEMBLE_SKILL)
+ )
+ return 0;
+ } else {
+ switch (skill_id) {
+ case BD_ADAPTATION:
+ case CG_LONGINGFREEDOM:
+ case BA_MUSICALSTRIKE:
+ case DC_THROWARROW:
+ break;
+ default:
+ return 0;
+ }
+ }
+ if ((sc->data[SC_DANCING]->val1&0xFFFF) == CG_HERMODE && skill_id == BD_ADAPTATION)
+ return 0; //Can't amp out of Wand of Hermode :/ [Skotlex]
+ }
+
+ if (skill_id && //Do not block item-casted skills.
+ (src->type != BL_PC || ((TBL_PC*)src)->skillitem != skill_id)
+ ) { //Skills blocked through status changes...
+ if (!flag && ( //Blocked only from using the skill (stuff like autospell may still go through
+ sc->cant.cast ||
+ (sc->data[SC_MARIONETTE] && skill_id != CG_MARIONETTE) || //Only skill you can use is marionette again to cancel it
+ (sc->data[SC_MARIONETTE2] && skill_id == CG_MARIONETTE) || //Cannot use marionette if you are being buffed by another
+ (sc->data[SC_STASIS] && skill_block_check(src, SC_STASIS, skill_id)) ||
+ (sc->data[SC_KAGEHUMI] && skill_block_check(src, SC_KAGEHUMI, skill_id))
+ ))
+ return 0;
+
+ //Skill blocking.
+ if (
+ (sc->data[SC_VOLCANO] && skill_id == WZ_ICEWALL) ||
+ (sc->data[SC_ROKISWEIL] && skill_id != BD_ADAPTATION) ||
+ (sc->data[SC_HERMODE] && skill_get_inf(skill_id) & INF_SUPPORT_SKILL) ||
+ (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOSKILL)
+ )
+ return 0;
+
+ if( sc->data[SC__MANHOLE] || ((tsc = status_get_sc(target)) && tsc->data[SC__MANHOLE]) ) {
+ switch(skill_id) {//##TODO## make this a flag in skill_db?
+ // Skills that can be used even under Man Hole effects.
+ case SC_SHADOWFORM:
+ case SC_STRIPACCESSARY:
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ }
+ }
+
+ if (sc && sc->option)
+ {
+ if (sc->option&OPTION_HIDE)
+ switch (skill_id) { //Usable skills while hiding.
+ case TF_HIDING:
+ case AS_GRIMTOOTH:
+ case RG_BACKSTAP:
+ case RG_RAID:
+ case NJ_SHADOWJUMP:
+ case NJ_KIRIKAGE:
+ case KO_YAMIKUMO:
+ break;
+ default:
+ //Non players can use all skills while hidden.
+ if (!skill_id || src->type == BL_PC)
+ return 0;
+ }
+ if (sc->option&OPTION_CHASEWALK && skill_id != ST_CHASEWALK)
+ return 0;
+ if(sc->option&OPTION_MOUNTING)
+ return 0;//New mounts can't attack nor use skills in the client; this check makes it cheat-safe [Ind]
+ }
+
+ if (target == NULL || target == src) //No further checking needed.
+ return 1;
+
+ tsc = status_get_sc(target);
+
+ if(tsc && tsc->count) {
+ /* attacks in invincible are capped to 1 damage and handled in batte.c; allow spell break and eske for sealed shrine GDB when in INVINCIBLE state. */
+ if( tsc->data[SC_INVINCIBLE] && !tsc->data[SC_INVINCIBLEOFF] && skill_id && !(skill_id&(SA_SPELLBREAKER|SL_SKE)) )
+ return 0;
+ if(!skill_id && tsc->data[SC_TRICKDEAD])
+ return 0;
+ if((skill_id == WZ_STORMGUST || skill_id == WZ_FROSTNOVA || skill_id == NJ_HYOUSYOURAKU)
+ && tsc->data[SC_FREEZE])
+ return 0;
+ if(skill_id == PR_LEXAETERNA && (tsc->data[SC_FREEZE] || (tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE)))
+ return 0;
+ }
+
+ //If targetting, cloak+hide protect you, otherwise only hiding does.
+ hide_flag = flag?OPTION_HIDE:(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK);
+
+ //You cannot hide from ground skills.
+ if( skill_get_ele(skill_id,1) == ELE_EARTH ) //TODO: Need Skill Lv here :/
+ hide_flag &= ~OPTION_HIDE;
+
+ switch( target->type ) {
+ case BL_PC: {
+ struct map_session_data *sd = (TBL_PC*) target;
+ bool is_boss = (status->mode&MD_BOSS);
+ bool is_detect = ((status->mode&MD_DETECTOR)?true:false);//god-knows-why gcc doesn't shut up until this happens
+ if (pc_isinvisible(sd))
+ return 0;
+ if (tsc->option&hide_flag && !is_boss &&
+ ((sd->special_state.perfect_hiding || !is_detect) ||
+ (tsc->data[SC_CLOAKINGEXCEED] && is_detect)))
+ return 0;
+ if( tsc->data[SC_CAMOUFLAGE] && !(is_boss || is_detect) && !skill_id )
+ return 0;
+ if( tsc->data[SC_STEALTHFIELD] && !is_boss )
+ return 0;
+ }
+ break;
+ case BL_ITEM: //Allow targetting of items to pick'em up (or in the case of mobs, to loot them).
+ //TODO: Would be nice if this could be used to judge whether the player can or not pick up the item it targets. [Skotlex]
+ if (status->mode&MD_LOOTER)
+ return 1;
+ return 0;
+ case BL_HOM:
+ case BL_MER:
+ case BL_ELEM:
+ if( target->type == BL_HOM && skill_id && battle_config.hom_setting&0x1 && skill_get_inf(skill_id)&INF_SUPPORT_SKILL && battle_get_master(target) != src )
+ return 0; // Can't use support skills on Homunculus (only Master/Self)
+ if( target->type == BL_MER && (skill_id == PR_ASPERSIO || (skill_id >= SA_FLAMELAUNCHER && skill_id <= SA_SEISMICWEAPON)) && battle_get_master(target) != src )
+ return 0; // Can't use Weapon endow skills on Mercenary (only Master)
+ if( skill_id == AM_POTIONPITCHER && ( target->type == BL_MER || target->type == BL_ELEM) )
+ return 0; // Can't use Potion Pitcher on Mercenaries
+ default:
+ //Check for chase-walk/hiding/cloaking opponents.
+ if( tsc ) {
+ if( tsc->option&hide_flag && !(status->mode&(MD_BOSS|MD_DETECTOR)))
+ return 0;
+ if( tsc->data[SC_STEALTHFIELD] && !(status->mode&MD_BOSS) )
+ return 0;
+ }
+ }
+ return 1;
+}
+
+//Checks whether the source can see and chase target.
+int status_check_visibility(struct block_list *src, struct block_list *target)
+{
+ int view_range;
+ struct status_data* status = status_get_status_data(src);
+ struct status_change* tsc = status_get_sc(target);
+ switch (src->type) {
+ case BL_MOB:
+ view_range = ((TBL_MOB*)src)->min_chase;
+ break;
+ case BL_PET:
+ view_range = ((TBL_PET*)src)->db->range2;
+ break;
+ default:
+ view_range = AREA_SIZE;
+ }
+
+ if (src->m != target->m || !check_distance_bl(src, target, view_range))
+ return 0;
+
+ if( tsc && tsc->data[SC_STEALTHFIELD] )
+ return 0;
+
+ switch (target->type)
+ { //Check for chase-walk/hiding/cloaking opponents.
+ case BL_PC:
+ if ( tsc->data[SC_CLOAKINGEXCEED] && !(status->mode&MD_BOSS) )
+ return 0;
+ if( (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC__INVISIBILITY] || tsc->data[SC_CAMOUFLAGE]) && !(status->mode&MD_BOSS) &&
+ ( ((TBL_PC*)target)->special_state.perfect_hiding || !(status->mode&MD_DETECTOR) ) )
+ return 0;
+ break;
+ default:
+ if( tsc && (tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || tsc->data[SC__INVISIBILITY] || tsc->data[SC_CAMOUFLAGE]) && !(status->mode&(MD_BOSS|MD_DETECTOR)) )
+ return 0;
+
+ }
+
+ return 1;
+}
+
+// Basic ASPD value
+int status_base_amotion_pc(struct map_session_data* sd, struct status_data* status)
+{
+ int amotion;
+#ifdef RENEWAL_ASPD
+ short mod = -1;
+
+ switch( sd->weapontype2 ){ // adjustment for dual weilding
+ case W_DAGGER: mod = 0; break; // 0, 1, 1
+ case W_1HSWORD:
+ case W_1HAXE: mod = 1;
+ if( (sd->class_&MAPID_THIRDMASK) == MAPID_GUILLOTINE_CROSS ) // 0, 2, 3
+ mod = sd->weapontype2 / W_1HSWORD + W_1HSWORD / sd->weapontype2 ;
+ }
+
+ amotion = ( sd->status.weapon < MAX_WEAPON_TYPE && mod < 0 )
+ ? (aspd_base[pc_class2idx(sd->status.class_)][sd->status.weapon]) // single weapon
+ : ((aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2] // dual-wield
+ + aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2]) * 6 / 10 + 10 * mod
+ - aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2]
+ + aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype1]);
+
+ if ( sd->status.shield )
+ amotion += ( 2000 - aspd_base[pc_class2idx(sd->status.class_)][W_FIST] ) +
+ ( aspd_base[pc_class2idx(sd->status.class_)][MAX_WEAPON_TYPE] - 2000 );
+
+#else
+ // base weapon delay
+ amotion = (sd->status.weapon < MAX_WEAPON_TYPE)
+ ? (aspd_base[pc_class2idx(sd->status.class_)][sd->status.weapon]) // single weapon
+ : (aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype1] + aspd_base[pc_class2idx(sd->status.class_)][sd->weapontype2])*7/10; // dual-wield
+
+ // percentual delay reduction from stats
+ amotion -= amotion * (4*status->agi + status->dex)/1000;
+#endif
+ // raw delay adjustment from bAspd bonus
+ amotion += sd->bonus.aspd_add;
+
+ return amotion;
+}
+
+static unsigned short status_base_atk(const struct block_list *bl, const struct status_data *status)
+{
+ int flag = 0, str, dex,
+#ifdef RENEWAL
+ rstr,
+#endif
+ dstr;
+
+
+ if(!(bl->type&battle_config.enable_baseatk))
+ return 0;
+
+ if (bl->type == BL_PC)
+ switch(((TBL_PC*)bl)->status.weapon){
+ case W_BOW:
+ case W_MUSICAL:
+ case W_WHIP:
+ case W_REVOLVER:
+ case W_RIFLE:
+ case W_GATLING:
+ case W_SHOTGUN:
+ case W_GRENADE:
+ flag = 1;
+ }
+ if (flag) {
+#ifdef RENEWAL
+ rstr =
+#endif
+ str = status->dex;
+ dex = status->str;
+ } else {
+#ifdef RENEWAL
+ rstr =
+#endif
+ str = status->str;
+ dex = status->dex;
+ }
+ //Normally only players have base-atk, but homunc have a different batk
+ // equation, hinting that perhaps non-players should use this for batk.
+ // [Skotlex]
+ dstr = str/10;
+ str += dstr*dstr;
+ if (bl->type == BL_PC)
+#ifdef RENEWAL
+ str = (rstr*10 + dex*10/5 + status->luk*10/3 + ((TBL_PC*)bl)->status.base_level*10/4)/10;
+#else
+ str+= dex/5 + status->luk/5;
+#endif
+ return cap_value(str, 0, USHRT_MAX);
+}
+
+#ifndef RENEWAL
+static inline unsigned short status_base_matk_min(const struct status_data* status){ return status->int_+(status->int_/7)*(status->int_/7); }
+static inline unsigned short status_base_matk_max(const struct status_data* status){ return status->int_+(status->int_/5)*(status->int_/5); }
+#else
+unsigned short status_base_matk(const struct status_data* status, int level){ return status->int_+(status->int_/2)+(status->dex/5)+(status->luk/3)+(level/4); }
+#endif
+
+//Fills in the misc data that can be calculated from the other status info (except for level)
+void status_calc_misc(struct block_list *bl, struct status_data *status, int level)
+{
+ //Non players get the value set, players need to stack with previous bonuses.
+ if( bl->type != BL_PC )
+ status->batk =
+ status->hit = status->flee =
+ status->def2 = status->mdef2 =
+ status->cri = status->flee2 = 0;
+
+#ifdef RENEWAL // renewal formulas
+ status->matk_min = status->matk_max = status_base_matk(status, level);
+ status->hit += level + status->dex + status->luk/3 + 175; //base level + ( every 1 dex = +1 hit ) + (every 3 luk = +1 hit) + 175
+ status->flee += level + status->agi + status->luk/5 + 100; //base level + ( every 1 agi = +1 flee ) + (every 5 luk = +1 flee) + 100
+ status->def2 += (int)(((float)level + status->vit)/2 + ((float)status->agi/5)); //base level + (every 2 vit = +1 def) + (every 5 agi = +1 def)
+ status->mdef2 += (int)(status->int_ + ((float)level/4) + ((float)status->dex/5) + ((float)status->vit/5)); //(every 4 base level = +1 mdef) + (every 1 int = +1 mdef) + (every 5 dex = +1 mdef) + (every 5 vit = +1 mdef)
+#else
+ status->matk_min = status_base_matk_min(status);
+ status->matk_max = status_base_matk_max(status);
+ status->hit += level + status->dex;
+ status->flee += level + status->agi;
+ status->def2 += status->vit;
+ status->mdef2 += status->int_ + (status->vit>>1);
+#endif
+
+ if( bl->type&battle_config.enable_critical )
+ status->cri += 10 + (status->luk*10/3); //(every 1 luk = +0.3 critical)
+ else
+ status->cri = 0;
+
+ if (bl->type&battle_config.enable_perfect_flee)
+ status->flee2 += status->luk + 10; //(every 10 luk = +1 perfect flee)
+ else
+ status->flee2 = 0;
+
+ if (status->batk) {
+ int temp = status->batk + status_base_atk(bl, status);
+ status->batk = cap_value(temp, 0, USHRT_MAX);
+ } else
+ status->batk = status_base_atk(bl, status);
+ if (status->cri)
+ switch (bl->type) {
+ case BL_MOB:
+ if(battle_config.mob_critical_rate != 100)
+ status->cri = status->cri*battle_config.mob_critical_rate/100;
+ if(!status->cri && battle_config.mob_critical_rate)
+ status->cri = 10;
+ break;
+ case BL_PC:
+ //Players don't have a critical adjustment setting as of yet.
+ break;
+ default:
+ if(battle_config.critical_rate != 100)
+ status->cri = status->cri*battle_config.critical_rate/100;
+ if (!status->cri && battle_config.critical_rate)
+ status->cri = 10;
+ }
+ if(bl->type&BL_REGEN)
+ status_calc_regen(bl, status, status_get_regen_data(bl));
+}
+
+//Skotlex: Calculates the initial status for the given mob
+//first will only be false when the mob leveled up or got a GuardUp level.
+int status_calc_mob_(struct mob_data* md, bool first)
+{
+ struct status_data *status;
+ struct block_list *mbl = NULL;
+ int flag=0;
+
+ if(first)
+ { //Set basic level on respawn.
+ if (md->level > 0 && md->level <= MAX_LEVEL && md->level != md->db->lv)
+ ;
+ else
+ md->level = md->db->lv;
+ }
+
+ //Check if we need custom base-status
+ if (battle_config.mobs_level_up && md->level > md->db->lv)
+ flag|=1;
+
+ if (md->special_state.size)
+ flag|=2;
+
+ if (md->guardian_data && md->guardian_data->guardup_lv)
+ flag|=4;
+ if (md->class_ == MOBID_EMPERIUM)
+ flag|=4;
+
+ if (battle_config.slaves_inherit_speed && md->master_id)
+ flag|=8;
+
+ if (md->master_id && md->special_state.ai>1)
+ flag|=16;
+
+ if (!flag)
+ { //No special status required.
+ if (md->base_status) {
+ aFree(md->base_status);
+ md->base_status = NULL;
+ }
+ if(first)
+ memcpy(&md->status, &md->db->status, sizeof(struct status_data));
+ return 0;
+ }
+ if (!md->base_status)
+ md->base_status = (struct status_data*)aCalloc(1, sizeof(struct status_data));
+
+ status = md->base_status;
+ memcpy(status, &md->db->status, sizeof(struct status_data));
+
+ if (flag&(8|16))
+ mbl = map_id2bl(md->master_id);
+
+ if (flag&8 && mbl) {
+ struct status_data *mstatus = status_get_base_status(mbl);
+ if (mstatus &&
+ battle_config.slaves_inherit_speed&(mstatus->mode&MD_CANMOVE?1:2))
+ status->speed = mstatus->speed;
+ if( status->speed < 2 ) /* minimum for the unit to function properly */
+ status->speed = 2;
+ }
+
+ if (flag&16 && mbl)
+ { //Max HP setting from Summon Flora/marine Sphere
+ struct unit_data *ud = unit_bl2ud(mbl);
+ //Remove special AI when this is used by regular mobs.
+ if (mbl->type == BL_MOB && !((TBL_MOB*)mbl)->special_state.ai)
+ md->special_state.ai = 0;
+ if (ud)
+ { // different levels of HP according to skill level
+ if (ud->skill_id == AM_SPHEREMINE) {
+ status->max_hp = 2000 + 400*ud->skill_lv;
+ } else if(ud->skill_id == KO_ZANZOU){
+ status->max_hp = 3000 + 3000 * ud->skill_lv;
+ } else { //AM_CANNIBALIZE
+ status->max_hp = 1500 + 200*ud->skill_lv + 10*status_get_lv(mbl);
+ status->mode|= MD_CANATTACK|MD_AGGRESSIVE;
+ }
+ status->hp = status->max_hp;
+ }
+ }
+
+ if (flag&1)
+ { // increase from mobs leveling up [Valaris]
+ int diff = md->level - md->db->lv;
+ status->str+= diff;
+ status->agi+= diff;
+ status->vit+= diff;
+ status->int_+= diff;
+ status->dex+= diff;
+ status->luk+= diff;
+ status->max_hp += diff*status->vit;
+ status->max_sp += diff*status->int_;
+ status->hp = status->max_hp;
+ status->sp = status->max_sp;
+ status->speed -= cap_value(diff, 0, status->speed - 10);
+ }
+
+
+ if (flag&2 && battle_config.mob_size_influence)
+ { // change for sized monsters [Valaris]
+ if (md->special_state.size==SZ_MEDIUM) {
+ status->max_hp>>=1;
+ status->max_sp>>=1;
+ if (!status->max_hp) status->max_hp = 1;
+ if (!status->max_sp) status->max_sp = 1;
+ status->hp=status->max_hp;
+ status->sp=status->max_sp;
+ status->str>>=1;
+ status->agi>>=1;
+ status->vit>>=1;
+ status->int_>>=1;
+ status->dex>>=1;
+ status->luk>>=1;
+ if (!status->str) status->str = 1;
+ if (!status->agi) status->agi = 1;
+ if (!status->vit) status->vit = 1;
+ if (!status->int_) status->int_ = 1;
+ if (!status->dex) status->dex = 1;
+ if (!status->luk) status->luk = 1;
+ } else if (md->special_state.size==SZ_BIG) {
+ status->max_hp<<=1;
+ status->max_sp<<=1;
+ status->hp=status->max_hp;
+ status->sp=status->max_sp;
+ status->str<<=1;
+ status->agi<<=1;
+ status->vit<<=1;
+ status->int_<<=1;
+ status->dex<<=1;
+ status->luk<<=1;
+ }
+ }
+
+ status_calc_misc(&md->bl, status, md->level);
+
+ if(flag&4)
+ { // Strengthen Guardians - custom value +10% / lv
+ struct guild_castle *gc;
+ gc=guild_mapname2gc(map[md->bl.m].name);
+ if (!gc)
+ ShowError("status_calc_mob: No castle set at map %s\n", map[md->bl.m].name);
+ else
+ if(gc->castle_id < 24 || md->class_ == MOBID_EMPERIUM) {
+#ifdef RENEWAL
+ status->max_hp += 50 * gc->defense;
+ status->max_sp += 70 * gc->defense;
+#else
+ status->max_hp += 1000 * gc->defense;
+ status->max_sp += 200 * gc->defense;
+#endif
+ status->hp = status->max_hp;
+ status->sp = status->max_sp;
+ status->def += (gc->defense+2)/3;
+ status->mdef += (gc->defense+2)/3;
+ }
+ if(md->class_ != MOBID_EMPERIUM) {
+ status->batk += status->batk * 10*md->guardian_data->guardup_lv/100;
+ status->rhw.atk += status->rhw.atk * 10*md->guardian_data->guardup_lv/100;
+ status->rhw.atk2 += status->rhw.atk2 * 10*md->guardian_data->guardup_lv/100;
+ status->aspd_rate -= 100*md->guardian_data->guardup_lv;
+ }
+ }
+
+ if( first ) //Initial battle status
+ memcpy(&md->status, status, sizeof(struct status_data));
+
+ return 1;
+}
+
+//Skotlex: Calculates the stats of the given pet.
+int status_calc_pet_(struct pet_data *pd, bool first)
+{
+ nullpo_ret(pd);
+
+ if (first) {
+ memcpy(&pd->status, &pd->db->status, sizeof(struct status_data));
+ pd->status.mode = MD_CANMOVE; // pets discard all modes, except walking
+ pd->status.speed = pd->petDB->speed;
+
+ if(battle_config.pet_attack_support || battle_config.pet_damage_support)
+ {// attack support requires the pet to be able to attack
+ pd->status.mode|= MD_CANATTACK;
+ }
+ }
+
+ if (battle_config.pet_lv_rate && pd->msd)
+ {
+ struct map_session_data *sd = pd->msd;
+ int lv;
+
+ lv =sd->status.base_level*battle_config.pet_lv_rate/100;
+ if (lv < 0)
+ lv = 1;
+ if (lv != pd->pet.level || first)
+ {
+ struct status_data *bstat = &pd->db->status, *status = &pd->status;
+ pd->pet.level = lv;
+ if (!first) //Lv Up animation
+ clif_misceffect(&pd->bl, 0);
+ status->rhw.atk = (bstat->rhw.atk*lv)/pd->db->lv;
+ status->rhw.atk2 = (bstat->rhw.atk2*lv)/pd->db->lv;
+ status->str = (bstat->str*lv)/pd->db->lv;
+ status->agi = (bstat->agi*lv)/pd->db->lv;
+ status->vit = (bstat->vit*lv)/pd->db->lv;
+ status->int_ = (bstat->int_*lv)/pd->db->lv;
+ status->dex = (bstat->dex*lv)/pd->db->lv;
+ status->luk = (bstat->luk*lv)/pd->db->lv;
+
+ status->rhw.atk = cap_value(status->rhw.atk, 1, battle_config.pet_max_atk1);
+ status->rhw.atk2 = cap_value(status->rhw.atk2, 2, battle_config.pet_max_atk2);
+ status->str = cap_value(status->str,1,battle_config.pet_max_stats);
+ status->agi = cap_value(status->agi,1,battle_config.pet_max_stats);
+ status->vit = cap_value(status->vit,1,battle_config.pet_max_stats);
+ status->int_= cap_value(status->int_,1,battle_config.pet_max_stats);
+ status->dex = cap_value(status->dex,1,battle_config.pet_max_stats);
+ status->luk = cap_value(status->luk,1,battle_config.pet_max_stats);
+
+ status_calc_misc(&pd->bl, &pd->status, lv);
+
+ if (!first) //Not done the first time because the pet is not visible yet
+ clif_send_petstatus(sd);
+ }
+ } else if (first) {
+ status_calc_misc(&pd->bl, &pd->status, pd->db->lv);
+ if (!battle_config.pet_lv_rate && pd->pet.level != pd->db->lv)
+ pd->pet.level = pd->db->lv;
+ }
+
+ //Support rate modifier (1000 = 100%)
+ pd->rate_fix = 1000*(pd->pet.intimate - battle_config.pet_support_min_friendly)/(1000- battle_config.pet_support_min_friendly) +500;
+ if(battle_config.pet_support_rate != 100)
+ pd->rate_fix = pd->rate_fix*battle_config.pet_support_rate/100;
+
+ return 1;
+}
+
+/// Helper function for status_base_pc_maxhp(), used to pre-calculate the hp_sigma_val[] array
+static void status_calc_sigma(void)
+{
+ int i,j;
+
+ for(i = 0; i < CLASS_COUNT; i++)
+ {
+ unsigned int k = 0;
+ hp_sigma_val[i][0] = hp_sigma_val[i][1] = 0;
+ for(j = 2; j <= MAX_LEVEL; j++)
+ {
+ k += (hp_coefficient[i]*j + 50) / 100;
+ hp_sigma_val[i][j] = k;
+ if (k >= INT_MAX)
+ break; //Overflow protection. [Skotlex]
+ }
+ for(; j <= MAX_LEVEL; j++)
+ hp_sigma_val[i][j] = INT_MAX;
+ }
+}
+
+/// Calculates base MaxHP value according to class and base level
+/// The recursive equation used to calculate level bonus is (using integer operations)
+/// f(0) = 35 | f(x+1) = f(x) + A + (x + B)*C/D
+/// which reduces to something close to
+/// f(x) = 35 + x*(A + B*C/D) + sum(i=2..x){ i*C/D }
+static unsigned int status_base_pc_maxhp(struct map_session_data* sd, struct status_data* status)
+{
+ uint64 val = pc_class2idx(sd->status.class_);
+ val = 35 + sd->status.base_level*(int64)hp_coefficient2[val]/100 + hp_sigma_val[val][sd->status.base_level];
+
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_NINJA || (sd->class_&MAPID_UPPERMASK) == MAPID_GUNSLINGER)
+ val += 100; //Since their HP can't be approximated well enough without this.
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON))
+ val *= 3; //Triple max HP for top ranking Taekwons over level 90.
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.base_level >= 99)
+ val += 2000; //Supernovice lvl99 hp bonus.
+
+ val += val * status->vit/100; // +1% per each point of VIT
+
+ if (sd->class_&JOBL_UPPER)
+ val += val * 25/100; //Trans classes get a 25% hp bonus
+ else if (sd->class_&JOBL_BABY)
+ val -= val * 30/100; //Baby classes get a 30% hp penalty
+ return (unsigned int)val;
+}
+
+static unsigned int status_base_pc_maxsp(struct map_session_data* sd, struct status_data *status)
+{
+ uint64 val;
+
+ val = 10 + sd->status.base_level*(int64)sp_coefficient[pc_class2idx(sd->status.class_)]/100;
+ val += val * status->int_/100;
+
+ if (sd->class_&JOBL_UPPER)
+ val += val * 25/100;
+ else if (sd->class_&JOBL_BABY)
+ val -= val * 30/100;
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON))
+ val *= 3; //Triple max SP for top ranking Taekwons over level 90.
+
+ return (unsigned int)val;
+}
+
+//Calculates player data from scratch without counting SC adjustments.
+//Should be invoked whenever players raise stats, learn passive skills or change equipment.
+int status_calc_pc_(struct map_session_data* sd, bool first)
+{
+ static int calculating = 0; //Check for recursive call preemption. [Skotlex]
+ struct status_data *status; // pointer to the player's base status
+ const struct status_change *sc = &sd->sc;
+ struct s_skill b_skill[MAX_SKILL]; // previous skill tree
+ int b_weight, b_max_weight, b_cart_weight_max, // previous weight
+ i, index, skill,refinedef=0;
+ int64 i64;
+
+ if (++calculating > 10) //Too many recursive calls!
+ return -1;
+
+ // remember player-specific values that are currently being shown to the client (for refresh purposes)
+ memcpy(b_skill, &sd->status.skill, sizeof(b_skill));
+ b_weight = sd->weight;
+ b_max_weight = sd->max_weight;
+ b_cart_weight_max = sd->cart_weight_max;
+
+ pc_calc_skilltree(sd); // SkillTree calculation
+
+ sd->max_weight = max_weight_base[pc_class2idx(sd->status.class_)]+sd->status.str*300;
+
+ if(first) {
+ //Load Hp/SP from char-received data.
+ sd->battle_status.hp = sd->status.hp;
+ sd->battle_status.sp = sd->status.sp;
+ sd->regen.sregen = &sd->sregen;
+ sd->regen.ssregen = &sd->ssregen;
+ sd->weight=0;
+ for(i=0;i<MAX_INVENTORY;i++){
+ if(sd->status.inventory[i].nameid==0 || sd->inventory_data[i] == NULL)
+ continue;
+ sd->weight += sd->inventory_data[i]->weight*sd->status.inventory[i].amount;
+ }
+ sd->cart_weight=0;
+ sd->cart_num=0;
+ for(i=0;i<MAX_CART;i++){
+ if(sd->status.cart[i].nameid==0)
+ continue;
+ sd->cart_weight+=itemdb_weight(sd->status.cart[i].nameid)*sd->status.cart[i].amount;
+ sd->cart_num++;
+ }
+ }
+
+ status = &sd->base_status;
+ // these are not zeroed. [zzo]
+ sd->hprate=100;
+ sd->sprate=100;
+ sd->castrate=100;
+ sd->delayrate=100;
+ sd->dsprate=100;
+ sd->hprecov_rate = 100;
+ sd->sprecov_rate = 100;
+ sd->matk_rate = 100;
+ sd->critical_rate = sd->hit_rate = sd->flee_rate = sd->flee2_rate = 100;
+ sd->def_rate = sd->def2_rate = sd->mdef_rate = sd->mdef2_rate = 100;
+ sd->regen.state.block = 0;
+
+ // zeroed arrays, order follows the order in pc.h.
+ // add new arrays to the end of zeroed area in pc.h (see comments) and size here. [zzo]
+ memset (sd->param_bonus, 0, sizeof(sd->param_bonus)
+ + sizeof(sd->param_equip)
+ + sizeof(sd->subele)
+ + sizeof(sd->subrace)
+ + sizeof(sd->subrace2)
+ + sizeof(sd->subsize)
+ + sizeof(sd->reseff)
+ + sizeof(sd->weapon_coma_ele)
+ + sizeof(sd->weapon_coma_race)
+ + sizeof(sd->weapon_atk)
+ + sizeof(sd->weapon_atk_rate)
+ + sizeof(sd->arrow_addele)
+ + sizeof(sd->arrow_addrace)
+ + sizeof(sd->arrow_addsize)
+ + sizeof(sd->magic_addele)
+ + sizeof(sd->magic_addrace)
+ + sizeof(sd->magic_addsize)
+ + sizeof(sd->magic_atk_ele)
+ + sizeof(sd->critaddrace)
+ + sizeof(sd->expaddrace)
+ + sizeof(sd->ignore_mdef)
+ + sizeof(sd->ignore_def)
+ + sizeof(sd->itemgrouphealrate)
+ + sizeof(sd->sp_gain_race)
+ + sizeof(sd->sp_gain_race_attack)
+ + sizeof(sd->hp_gain_race_attack)
+ );
+
+ memset (&sd->right_weapon.overrefine, 0, sizeof(sd->right_weapon) - sizeof(sd->right_weapon.atkmods));
+ memset (&sd->left_weapon.overrefine, 0, sizeof(sd->left_weapon) - sizeof(sd->left_weapon.atkmods));
+
+ if (sd->special_state.intravision && !sd->sc.data[SC_INTRAVISION]) //Clear intravision as long as nothing else is using it
+ clif_status_load(&sd->bl, SI_INTRAVISION, 0);
+
+ memset(&sd->special_state,0,sizeof(sd->special_state));
+ memset(&status->max_hp, 0, sizeof(struct status_data)-(sizeof(status->hp)+sizeof(status->sp)));
+
+ //FIXME: Most of these stuff should be calculated once, but how do I fix the memset above to do that? [Skotlex]
+ status->speed = DEFAULT_WALK_SPEED;
+ //Give them all modes except these (useful for clones)
+ status->mode = MD_MASK&~(MD_BOSS|MD_PLANT|MD_DETECTOR|MD_ANGRY|MD_TARGETWEAK);
+
+ status->size = (sd->class_&JOBL_BABY)?SZ_SMALL:SZ_MEDIUM;
+ if (battle_config.character_size && pc_isriding(sd)) { //[Lupus]
+ if (sd->class_&JOBL_BABY) {
+ if (battle_config.character_size&SZ_BIG)
+ status->size++;
+ } else
+ if(battle_config.character_size&SZ_MEDIUM)
+ status->size++;
+ }
+ status->aspd_rate = 1000;
+ status->ele_lv = 1;
+ status->race = RC_DEMIHUMAN;
+
+ //zero up structures...
+ memset(&sd->autospell,0,sizeof(sd->autospell)
+ + sizeof(sd->autospell2)
+ + sizeof(sd->autospell3)
+ + sizeof(sd->addeff)
+ + sizeof(sd->addeff2)
+ + sizeof(sd->addeff3)
+ + sizeof(sd->skillatk)
+ + sizeof(sd->skillusesprate)
+ + sizeof(sd->skillusesp)
+ + sizeof(sd->skillheal)
+ + sizeof(sd->skillheal2)
+ + sizeof(sd->hp_loss)
+ + sizeof(sd->sp_loss)
+ + sizeof(sd->hp_regen)
+ + sizeof(sd->sp_regen)
+ + sizeof(sd->skillblown)
+ + sizeof(sd->skillcast)
+ + sizeof(sd->add_def)
+ + sizeof(sd->add_mdef)
+ + sizeof(sd->add_mdmg)
+ + sizeof(sd->add_drop)
+ + sizeof(sd->itemhealrate)
+ + sizeof(sd->subele2)
+ + sizeof(sd->skillcooldown)
+ + sizeof(sd->skillfixcast)
+ + sizeof(sd->skillvarcast)
+ );
+
+ memset (&sd->bonus, 0,sizeof(sd->bonus));
+
+ // Autobonus
+ pc_delautobonus(sd,sd->autobonus,ARRAYLENGTH(sd->autobonus),true);
+ pc_delautobonus(sd,sd->autobonus2,ARRAYLENGTH(sd->autobonus2),true);
+ pc_delautobonus(sd,sd->autobonus3,ARRAYLENGTH(sd->autobonus3),true);
+
+ // Parse equipment.
+ for(i=0;i<EQI_MAX-1;i++) {
+ current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus]
+ if(index < 0)
+ continue;
+ if(i == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index)
+ continue;
+ if(i == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index)
+ continue;
+ if(i == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index))
+ continue;
+ if(i == EQI_COSTUME_MID && sd->equip_index[EQI_COSTUME_LOW] == index)
+ continue;
+ if(i == EQI_COSTUME_TOP && (sd->equip_index[EQI_COSTUME_MID] == index || sd->equip_index[EQI_COSTUME_LOW] == index))
+ continue;
+ if(!sd->inventory_data[index])
+ continue;
+
+ status->def += sd->inventory_data[index]->def;
+
+ if(first && sd->inventory_data[index]->equip_script)
+ { //Execute equip-script on login
+ run_script(sd->inventory_data[index]->equip_script,0,sd->bl.id,0);
+ if (!calculating)
+ return 1;
+ }
+
+ // sanitize the refine level in case someone decreased the value inbetween
+ if (sd->status.inventory[index].refine > MAX_REFINE)
+ sd->status.inventory[index].refine = MAX_REFINE;
+
+ if(sd->inventory_data[index]->type == IT_WEAPON) {
+ int r,wlv = sd->inventory_data[index]->wlv;
+ struct weapon_data *wd;
+ struct weapon_atk *wa;
+ if (wlv >= REFINE_TYPE_MAX)
+ wlv = REFINE_TYPE_MAX - 1;
+ if(i == EQI_HAND_L && sd->status.inventory[index].equip == EQP_HAND_L) {
+ wd = &sd->left_weapon; // Left-hand weapon
+ wa = &status->lhw;
+ } else {
+ wd = &sd->right_weapon;
+ wa = &status->rhw;
+ }
+ wa->atk += sd->inventory_data[index]->atk;
+ if ( (r = sd->status.inventory[index].refine) )
+ wa->atk2 = refine_info[wlv].bonus[r-1] / 100;
+
+#ifdef RENEWAL
+ wa->matk += sd->inventory_data[index]->matk;
+ wa->wlv = wlv;
+ if( r ) // renewal magic attack refine bonus
+ wa->matk += refine_info[wlv].bonus[r-1] / 100;
+#endif
+
+ //Overrefine bonus.
+ if (r)
+ wd->overrefine = refine_info[wlv].randombonus_max[r-1] / 100;
+
+ wa->range += sd->inventory_data[index]->range;
+ if(sd->inventory_data[index]->script) {
+ if (wd == &sd->left_weapon) {
+ sd->state.lr_flag = 1;
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ sd->state.lr_flag = 0;
+ } else
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ if (!calculating) //Abort, run_script retriggered this. [Skotlex]
+ return 1;
+ }
+
+ if(sd->status.inventory[index].card[0]==CARD0_FORGE)
+ { // Forged weapon
+ wd->star += (sd->status.inventory[index].card[1]>>8);
+ if(wd->star >= 15) wd->star = 40; // 3 Star Crumbs now give +40 dmg
+ if(pc_famerank(MakeDWord(sd->status.inventory[index].card[2],sd->status.inventory[index].card[3]) ,MAPID_BLACKSMITH))
+ wd->star += 10;
+
+ if (!wa->ele) //Do not overwrite element from previous bonuses.
+ wa->ele = (sd->status.inventory[index].card[1]&0x0f);
+ }
+ }
+ else if(sd->inventory_data[index]->type == IT_ARMOR) {
+ int r;
+ if ( (r = sd->status.inventory[index].refine) )
+ refinedef += refine_info[REFINE_TYPE_ARMOR].bonus[r-1];
+ if(sd->inventory_data[index]->script) {
+ if( i == EQI_HAND_L ) //Shield
+ sd->state.lr_flag = 3;
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ if( i == EQI_HAND_L ) //Shield
+ sd->state.lr_flag = 0;
+ if (!calculating) //Abort, run_script retriggered this. [Skotlex]
+ return 1;
+ }
+ }
+ }
+
+ if(sd->equip_index[EQI_AMMO] >= 0){
+ index = sd->equip_index[EQI_AMMO];
+ if(sd->inventory_data[index]){ // Arrows
+ sd->bonus.arrow_atk += sd->inventory_data[index]->atk;
+ sd->state.lr_flag = 2;
+ if( !itemdb_is_GNthrowable(sd->inventory_data[index]->nameid) ) //don't run scripts on throwable items
+ run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
+ sd->state.lr_flag = 0;
+ if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex]
+ return 1;
+ }
+ }
+
+ /* we've got combos to process */
+ if( sd->combos.count ) {
+ for( i = 0; i < sd->combos.count; i++ ) {
+ run_script(sd->combos.bonus[i],0,sd->bl.id,0);
+ if (!calculating) //Abort, run_script retriggered this.
+ return 1;
+ }
+ }
+
+ //Store equipment script bonuses
+ memcpy(sd->param_equip,sd->param_bonus,sizeof(sd->param_equip));
+ memset(sd->param_bonus, 0, sizeof(sd->param_bonus));
+
+ status->def += (refinedef+50)/100;
+
+ //Parse Cards
+ for(i=0;i<EQI_MAX-1;i++) {
+ current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus]
+ if(index < 0)
+ continue;
+ if(i == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == index)
+ continue;
+ if(i == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == index)
+ continue;
+ if(i == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == index || sd->equip_index[EQI_HEAD_LOW] == index))
+ continue;
+
+ if(sd->inventory_data[index]) {
+ int j,c;
+ struct item_data *data;
+
+ //Card script execution.
+ if(itemdb_isspecial(sd->status.inventory[index].card[0]))
+ continue;
+ for(j=0;j<MAX_SLOTS;j++){ // Uses MAX_SLOTS to support Soul Bound system [Inkfish]
+ current_equip_card_id= c= sd->status.inventory[index].card[j];
+ if(!c)
+ continue;
+ data = itemdb_exists(c);
+ if(!data)
+ continue;
+ if(first && data->equip_script)
+ { //Execute equip-script on login
+ run_script(data->equip_script,0,sd->bl.id,0);
+ if (!calculating)
+ return 1;
+ }
+ if(!data->script)
+ continue;
+ if(data->flag.no_equip) { //Card restriction checks.
+ if(map[sd->bl.m].flag.restricted && data->flag.no_equip&(8*map[sd->bl.m].zone))
+ continue;
+ if(!map_flag_vs(sd->bl.m) && data->flag.no_equip&1)
+ continue;
+ if(map[sd->bl.m].flag.pvp && data->flag.no_equip&2)
+ continue;
+ if(map_flag_gvg(sd->bl.m) && data->flag.no_equip&4)
+ continue;
+ if(map[sd->bl.m].flag.battleground && data->flag.no_equip&8)
+ continue;
+ }
+ if(i == EQI_HAND_L && sd->status.inventory[index].equip == EQP_HAND_L)
+ { //Left hand status.
+ sd->state.lr_flag = 1;
+ run_script(data->script,0,sd->bl.id,0);
+ sd->state.lr_flag = 0;
+ } else
+ run_script(data->script,0,sd->bl.id,0);
+ if (!calculating) //Abort, run_script his function. [Skotlex]
+ return 1;
+ }
+ }
+ }
+
+ if( sc->count && sc->data[SC_ITEMSCRIPT] )
+ {
+ struct item_data *data = itemdb_exists(sc->data[SC_ITEMSCRIPT]->val1);
+ if( data && data->script )
+ run_script(data->script,0,sd->bl.id,0);
+ }
+
+ if( sd->pd )
+ { // Pet Bonus
+ struct pet_data *pd = sd->pd;
+ if( pd && pd->petDB && pd->petDB->equip_script && pd->pet.intimate >= battle_config.pet_equip_min_friendly )
+ run_script(pd->petDB->equip_script,0,sd->bl.id,0);
+ if( pd && pd->pet.intimate > 0 && (!battle_config.pet_equip_required || pd->pet.equip > 0) && pd->state.skillbonus == 1 && pd->bonus )
+ pc_bonus(sd,pd->bonus->type, pd->bonus->val);
+ }
+
+ //param_bonus now holds card bonuses.
+ if(status->rhw.range < 1) status->rhw.range = 1;
+ if(status->lhw.range < 1) status->lhw.range = 1;
+ if(status->rhw.range < status->lhw.range)
+ status->rhw.range = status->lhw.range;
+
+ sd->bonus.double_rate += sd->bonus.double_add_rate;
+ sd->bonus.perfect_hit += sd->bonus.perfect_hit_add;
+ sd->bonus.splash_range += sd->bonus.splash_add_range;
+
+ // Damage modifiers from weapon type
+ sd->right_weapon.atkmods[0] = atkmods[0][sd->weapontype1];
+ sd->right_weapon.atkmods[1] = atkmods[1][sd->weapontype1];
+ sd->right_weapon.atkmods[2] = atkmods[2][sd->weapontype1];
+ sd->left_weapon.atkmods[0] = atkmods[0][sd->weapontype2];
+ sd->left_weapon.atkmods[1] = atkmods[1][sd->weapontype2];
+ sd->left_weapon.atkmods[2] = atkmods[2][sd->weapontype2];
+
+ if(pc_isriding(sd) &&
+ (sd->status.weapon==W_1HSPEAR || sd->status.weapon==W_2HSPEAR))
+ { //When Riding with spear, damage modifier to mid-class becomes
+ //same as versus large size.
+ sd->right_weapon.atkmods[1] = sd->right_weapon.atkmods[2];
+ sd->left_weapon.atkmods[1] = sd->left_weapon.atkmods[2];
+ }
+
+// ----- STATS CALCULATION -----
+
+ // Job bonuses
+ index = pc_class2idx(sd->status.class_);
+ for(i=0;i<(int)sd->status.job_level && i<MAX_LEVEL;i++){
+ if(!job_bonus[index][i])
+ continue;
+ switch(job_bonus[index][i]) {
+ case 1: status->str++; break;
+ case 2: status->agi++; break;
+ case 3: status->vit++; break;
+ case 4: status->int_++; break;
+ case 5: status->dex++; break;
+ case 6: status->luk++; break;
+ }
+ }
+
+ // If a Super Novice has never died and is at least joblv 70, he gets all stats +10
+ if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->die_counter == 0 && sd->status.job_level >= 70){
+ status->str += 10;
+ status->agi += 10;
+ status->vit += 10;
+ status->int_+= 10;
+ status->dex += 10;
+ status->luk += 10;
+ }
+
+ // Absolute modifiers from passive skills
+ if(pc_checkskill(sd,BS_HILTBINDING)>0)
+ status->str++;
+ if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0)
+ status->int_ += (skill+1)/2; // +1 INT / 2 lv
+ if((skill=pc_checkskill(sd,AC_OWL))>0)
+ status->dex += skill;
+ if((skill = pc_checkskill(sd,RA_RESEARCHTRAP))>0)
+ status->int_ += skill;
+
+ // Bonuses from cards and equipment as well as base stat, remember to avoid overflows.
+ i = status->str + sd->status.str + sd->param_bonus[0] + sd->param_equip[0];
+ status->str = cap_value(i,0,USHRT_MAX);
+ i = status->agi + sd->status.agi + sd->param_bonus[1] + sd->param_equip[1];
+ status->agi = cap_value(i,0,USHRT_MAX);
+ i = status->vit + sd->status.vit + sd->param_bonus[2] + sd->param_equip[2];
+ status->vit = cap_value(i,0,USHRT_MAX);
+ i = status->int_+ sd->status.int_+ sd->param_bonus[3] + sd->param_equip[3];
+ status->int_ = cap_value(i,0,USHRT_MAX);
+ i = status->dex + sd->status.dex + sd->param_bonus[4] + sd->param_equip[4];
+ status->dex = cap_value(i,0,USHRT_MAX);
+ i = status->luk + sd->status.luk + sd->param_bonus[5] + sd->param_equip[5];
+ status->luk = cap_value(i,0,USHRT_MAX);
+
+// ------ BASE ATTACK CALCULATION ------
+
+ // Base batk value is set on status_calc_misc
+ // weapon-type bonus (FIXME: Why is the weapon_atk bonus applied to base attack?)
+ if (sd->status.weapon < MAX_WEAPON_TYPE && sd->weapon_atk[sd->status.weapon])
+ status->batk += sd->weapon_atk[sd->status.weapon];
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,BS_HILTBINDING))>0)
+ status->batk += 4;
+
+// ----- HP MAX CALCULATION -----
+
+ // Basic MaxHP value
+ //We hold the standard Max HP here to make it faster to recalculate on vit changes.
+ sd->status.max_hp = status_base_pc_maxhp(sd,status);
+ //This is done to handle underflows from negative Max HP bonuses
+ i64 = sd->status.max_hp + (int)status->max_hp;
+ status->max_hp = (unsigned int)cap_value(i64, 0, INT_MAX);
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,CR_TRUST))>0)
+ status->max_hp += skill*200;
+
+ // Apply relative modifiers from equipment
+ if(sd->hprate < 0)
+ sd->hprate = 0;
+ if(sd->hprate!=100)
+ status->max_hp = (int64)status->max_hp * sd->hprate/100;
+ if(battle_config.hp_rate != 100)
+ status->max_hp = (int64)status->max_hp * battle_config.hp_rate/100;
+
+ if(status->max_hp > (unsigned int)battle_config.max_hp)
+ status->max_hp = battle_config.max_hp;
+ else if(!status->max_hp)
+ status->max_hp = 1;
+
+// ----- SP MAX CALCULATION -----
+
+ // Basic MaxSP value
+ sd->status.max_sp = status_base_pc_maxsp(sd,status);
+ //This is done to handle underflows from negative Max SP bonuses
+ i64 = sd->status.max_sp + (int)status->max_sp;
+ status->max_sp = (unsigned int)cap_value(i64, 0, INT_MAX);
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,SL_KAINA))>0)
+ status->max_sp += 30*skill;
+ if((skill=pc_checkskill(sd,HP_MEDITATIO))>0)
+ status->max_sp += (int64)status->max_sp * skill/100;
+ if((skill=pc_checkskill(sd,HW_SOULDRAIN))>0)
+ status->max_sp += (int64)status->max_sp * 2*skill/100;
+ if( (skill = pc_checkskill(sd,RA_RESEARCHTRAP)) > 0 )
+ status->max_sp += 200 + 20 * skill;
+ if( (skill = pc_checkskill(sd,WM_LESSON)) > 0 )
+ status->max_sp += 30 * skill;
+
+
+ // Apply relative modifiers from equipment
+ if(sd->sprate < 0)
+ sd->sprate = 0;
+ if(sd->sprate!=100)
+ status->max_sp = (int64)status->max_sp * sd->sprate/100;
+ if(battle_config.sp_rate != 100)
+ status->max_sp = (int64)status->max_sp * battle_config.sp_rate/100;
+
+ if(status->max_sp > (unsigned int)battle_config.max_sp)
+ status->max_sp = battle_config.max_sp;
+ else if(!status->max_sp)
+ status->max_sp = 1;
+
+// ----- RESPAWN HP/SP -----
+//
+ //Calc respawn hp and store it on base_status
+ if (sd->special_state.restart_full_recover)
+ {
+ status->hp = status->max_hp;
+ status->sp = status->max_sp;
+ } else {
+ if((sd->class_&MAPID_BASEMASK) == MAPID_NOVICE && !(sd->class_&JOBL_2)
+ && battle_config.restart_hp_rate < 50)
+ status->hp = status->max_hp>>1;
+ else
+ status->hp = (int64)status->max_hp * battle_config.restart_hp_rate/100;
+ if(!status->hp)
+ status->hp = 1;
+
+ status->sp = (int64)status->max_sp * battle_config.restart_sp_rate /100;
+
+ if( !status->sp ) /* the minimum for the respawn setting is SP:1 */
+ status->sp = 1;
+ }
+
+// ----- MISC CALCULATION -----
+ status_calc_misc(&sd->bl, status, sd->status.base_level);
+
+ //Equipment modifiers for misc settings
+ if(sd->matk_rate < 0)
+ sd->matk_rate = 0;
+
+ if(sd->matk_rate != 100){
+ status->matk_max = status->matk_max * sd->matk_rate/100;
+ status->matk_min = status->matk_min * sd->matk_rate/100;
+ }
+
+ if(sd->hit_rate < 0)
+ sd->hit_rate = 0;
+ if(sd->hit_rate != 100)
+ status->hit = status->hit * sd->hit_rate/100;
+
+ if(sd->flee_rate < 0)
+ sd->flee_rate = 0;
+ if(sd->flee_rate != 100)
+ status->flee = status->flee * sd->flee_rate/100;
+
+ if(sd->def2_rate < 0)
+ sd->def2_rate = 0;
+ if(sd->def2_rate != 100)
+ status->def2 = status->def2 * sd->def2_rate/100;
+
+ if(sd->mdef2_rate < 0)
+ sd->mdef2_rate = 0;
+ if(sd->mdef2_rate != 100)
+ status->mdef2 = status->mdef2 * sd->mdef2_rate/100;
+
+ if(sd->critical_rate < 0)
+ sd->critical_rate = 0;
+ if(sd->critical_rate != 100)
+ status->cri = status->cri * sd->critical_rate/100;
+
+ if(sd->flee2_rate < 0)
+ sd->flee2_rate = 0;
+ if(sd->flee2_rate != 100)
+ status->flee2 = status->flee2 * sd->flee2_rate/100;
+
+// ----- HIT CALCULATION -----
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,BS_WEAPONRESEARCH))>0)
+ status->hit += skill*2;
+ if((skill=pc_checkskill(sd,AC_VULTURE))>0){
+#ifndef RENEWAL
+ status->hit += skill;
+#endif
+ if(sd->status.weapon == W_BOW)
+ status->rhw.range += skill;
+ }
+ if(sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)
+ {
+ if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0)
+ status->hit += 2*skill;
+ if((skill=pc_checkskill(sd,GS_SNAKEEYE))>0) {
+ status->hit += skill;
+ status->rhw.range += skill;
+ }
+ }
+
+// ----- FLEE CALCULATION -----
+
+ // Absolute modifiers from passive skills
+ if((skill=pc_checkskill(sd,TF_MISS))>0)
+ status->flee += skill*(sd->class_&JOBL_2 && (sd->class_&MAPID_BASEMASK) == MAPID_THIEF? 4 : 3);
+ if((skill=pc_checkskill(sd,MO_DODGE))>0)
+ status->flee += (skill*3)>>1;
+// ----- EQUIPMENT-DEF CALCULATION -----
+
+ // Apply relative modifiers from equipment
+ if(sd->def_rate < 0)
+ sd->def_rate = 0;
+ if(sd->def_rate != 100) {
+ i = status->def * sd->def_rate/100;
+ status->def = cap_value(i, DEFTYPE_MIN, DEFTYPE_MAX);
+ }
+
+#ifndef RENEWAL
+ if (!battle_config.weapon_defense_type && status->def > battle_config.max_def)
+ {
+ status->def2 += battle_config.over_def_bonus*(status->def -battle_config.max_def);
+ status->def = (unsigned char)battle_config.max_def;
+ }
+#endif
+
+// ----- EQUIPMENT-MDEF CALCULATION -----
+
+ // Apply relative modifiers from equipment
+ if(sd->mdef_rate < 0)
+ sd->mdef_rate = 0;
+ if(sd->mdef_rate != 100) {
+ i = status->mdef * sd->mdef_rate/100;
+ status->mdef = cap_value(i, DEFTYPE_MIN, DEFTYPE_MAX);
+ }
+
+#ifndef RENEWAL
+ if (!battle_config.magic_defense_type && status->mdef > battle_config.max_def)
+ {
+ status->mdef2 += battle_config.over_def_bonus*(status->mdef -battle_config.max_def);
+ status->mdef = (signed char)battle_config.max_def;
+ }
+#endif
+
+// ----- ASPD CALCULATION -----
+// Unlike other stats, ASPD rate modifiers from skills/SCs/items/etc are first all added together, then the final modifier is applied
+
+ // Basic ASPD value
+ i = status_base_amotion_pc(sd,status);
+ status->amotion = cap_value(i,((sd->class_&JOBL_THIRD) ? battle_config.max_third_aspd : battle_config.max_aspd),2000);
+
+ // Relative modifiers from passive skills
+#ifndef RENEWAL_ASPD
+ if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0 && sd->status.weapon == W_BOOK)
+ status->aspd_rate -= 5*skill;
+ if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && !pc_nextjobexp(sd))
+ status->aspd_rate -= 30*skill;
+ if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0 &&
+ (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE))
+ status->aspd_rate -= ((skill+1)/2) * 10;
+ if(pc_isriding(sd))
+ status->aspd_rate += 500-100*pc_checkskill(sd,KN_CAVALIERMASTERY);
+ else if(pc_isridingdragon(sd))
+ status->aspd_rate += 250-50*pc_checkskill(sd,RK_DRAGONTRAINING);
+#else // needs more info
+ if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0 && sd->status.weapon == W_BOOK)
+ status->aspd_rate += 5*skill;
+ if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && !pc_nextjobexp(sd))
+ status->aspd_rate += 30*skill;
+ if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0 &&
+ (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE))
+ status->aspd_rate += ((skill+1)/2) * 10;
+ if(pc_isriding(sd))
+ status->aspd_rate -= 500-100*pc_checkskill(sd,KN_CAVALIERMASTERY);
+ else if(pc_isridingdragon(sd))
+ status->aspd_rate -= 250-50*pc_checkskill(sd,RK_DRAGONTRAINING);
+#endif
+ status->adelay = 2*status->amotion;
+
+
+// ----- DMOTION -----
+//
+ i = 800-status->agi*4;
+ status->dmotion = cap_value(i, 400, 800);
+ if(battle_config.pc_damage_delay_rate != 100)
+ status->dmotion = status->dmotion*battle_config.pc_damage_delay_rate/100;
+
+// ----- MISC CALCULATIONS -----
+
+ // Weight
+ if((skill=pc_checkskill(sd,MC_INCCARRY))>0)
+ sd->max_weight += 2000*skill;
+ if(pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0)
+ sd->max_weight += 10000;
+ else if(pc_isridingdragon(sd))
+ sd->max_weight += 5000+2000*pc_checkskill(sd,RK_DRAGONTRAINING);
+ if(sc->data[SC_KNOWLEDGE])
+ sd->max_weight += sd->max_weight*sc->data[SC_KNOWLEDGE]->val1/10;
+ if((skill=pc_checkskill(sd,ALL_INCCARRY))>0)
+ sd->max_weight += 2000*skill;
+
+ sd->cart_weight_max = battle_config.max_cart_weight + (pc_checkskill(sd, GN_REMODELING_CART)*5000);
+
+ if (pc_checkskill(sd,SM_MOVINGRECOVERY)>0)
+ sd->regen.state.walk = 1;
+ else
+ sd->regen.state.walk = 0;
+
+ // Skill SP cost
+ if((skill=pc_checkskill(sd,HP_MANARECHARGE))>0 )
+ sd->dsprate -= 4*skill;
+
+ if(sc->data[SC_SERVICE4U])
+ sd->dsprate -= sc->data[SC_SERVICE4U]->val3;
+
+ if(sc->data[SC_SPCOST_RATE])
+ sd->dsprate -= sc->data[SC_SPCOST_RATE]->val1;
+
+ //Underflow protections.
+ if(sd->dsprate < 0)
+ sd->dsprate = 0;
+ if(sd->castrate < 0)
+ sd->castrate = 0;
+ if(sd->delayrate < 0)
+ sd->delayrate = 0;
+ if(sd->hprecov_rate < 0)
+ sd->hprecov_rate = 0;
+ if(sd->sprecov_rate < 0)
+ sd->sprecov_rate = 0;
+
+ // Anti-element and anti-race
+ if((skill=pc_checkskill(sd,CR_TRUST))>0)
+ sd->subele[ELE_HOLY] += skill*5;
+ if((skill=pc_checkskill(sd,BS_SKINTEMPER))>0) {
+ sd->subele[ELE_NEUTRAL] += skill;
+ sd->subele[ELE_FIRE] += skill*4;
+ }
+ if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0 ){
+ skill = skill*4;
+ sd->right_weapon.addrace[RC_DRAGON]+=skill;
+ sd->left_weapon.addrace[RC_DRAGON]+=skill;
+ sd->magic_addrace[RC_DRAGON]+=skill;
+ sd->subrace[RC_DRAGON]+=skill;
+ }
+
+ if(sc->count){
+ if(sc->data[SC_CONCENTRATE]) { //Update the card-bonus data
+ sc->data[SC_CONCENTRATE]->val3 = sd->param_bonus[1]; //Agi
+ sc->data[SC_CONCENTRATE]->val4 = sd->param_bonus[4]; //Dex
+ }
+ if(sc->data[SC_SIEGFRIED]){
+ i = sc->data[SC_SIEGFRIED]->val2;
+ sd->subele[ELE_WATER] += i;
+ sd->subele[ELE_EARTH] += i;
+ sd->subele[ELE_FIRE] += i;
+ sd->subele[ELE_WIND] += i;
+ sd->subele[ELE_POISON] += i;
+ sd->subele[ELE_HOLY] += i;
+ sd->subele[ELE_DARK] += i;
+ sd->subele[ELE_GHOST] += i;
+ sd->subele[ELE_UNDEAD] += i;
+ }
+ if(sc->data[SC_PROVIDENCE]){
+ sd->subele[ELE_HOLY] += sc->data[SC_PROVIDENCE]->val2;
+ sd->subrace[RC_DEMON] += sc->data[SC_PROVIDENCE]->val2;
+ }
+ if(sc->data[SC_ARMOR_ELEMENT]) { //This status change should grant card-type elemental resist.
+ sd->subele[ELE_WATER] += sc->data[SC_ARMOR_ELEMENT]->val1;
+ sd->subele[ELE_EARTH] += sc->data[SC_ARMOR_ELEMENT]->val2;
+ sd->subele[ELE_FIRE] += sc->data[SC_ARMOR_ELEMENT]->val3;
+ sd->subele[ELE_WIND] += sc->data[SC_ARMOR_ELEMENT]->val4;
+ }
+ if(sc->data[SC_ARMOR_RESIST]) { // Undead Scroll
+ sd->subele[ELE_WATER] += sc->data[SC_ARMOR_RESIST]->val1;
+ sd->subele[ELE_EARTH] += sc->data[SC_ARMOR_RESIST]->val2;
+ sd->subele[ELE_FIRE] += sc->data[SC_ARMOR_RESIST]->val3;
+ sd->subele[ELE_WIND] += sc->data[SC_ARMOR_RESIST]->val4;
+ }
+ if( sc->data[SC_FIRE_CLOAK_OPTION] ) {
+ i = sc->data[SC_FIRE_CLOAK_OPTION]->val2;
+ sd->subele[ELE_FIRE] += i;
+ sd->subele[ELE_WATER] -= i;
+ }
+ if( sc->data[SC_WATER_DROP_OPTION] ) {
+ i = sc->data[SC_WATER_DROP_OPTION]->val2;
+ sd->subele[ELE_WATER] += i;
+ sd->subele[ELE_WIND] -= i;
+ }
+ if( sc->data[SC_WIND_CURTAIN_OPTION] ) {
+ i = sc->data[SC_WIND_CURTAIN_OPTION]->val2;
+ sd->subele[ELE_WIND] += i;
+ sd->subele[ELE_EARTH] -= i;
+ }
+ if( sc->data[SC_STONE_SHIELD_OPTION] ) {
+ i = sc->data[SC_STONE_SHIELD_OPTION]->val2;
+ sd->subele[ELE_EARTH] += i;
+ sd->subele[ELE_FIRE] -= i;
+ }
+ if( sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3 )
+ sd->magic_addele[ELE_FIRE] += 25;
+ if( sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 3 )
+ sd->magic_addele[ELE_WATER] += 25;
+ if( sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 3 )
+ sd->magic_addele[ELE_WIND] += 25;
+ if( sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3 )
+ sd->magic_addele[ELE_EARTH] += 25;
+ }
+ status_cpy(&sd->battle_status, status);
+
+// ----- CLIENT-SIDE REFRESH -----
+ if(!sd->bl.prev) {
+ //Will update on LoadEndAck
+ calculating = 0;
+ return 0;
+ }
+ if(memcmp(b_skill,sd->status.skill,sizeof(sd->status.skill)))
+ clif_skillinfoblock(sd);
+ if(b_weight != sd->weight)
+ clif_updatestatus(sd,SP_WEIGHT);
+ if(b_max_weight != sd->max_weight) {
+ clif_updatestatus(sd,SP_MAXWEIGHT);
+ pc_updateweightstatus(sd);
+ }
+ if( b_cart_weight_max != sd->cart_weight_max ) {
+ clif_updatestatus(sd,SP_CARTINFO);
+ }
+
+ calculating = 0;
+
+ return 0;
+}
+
+int status_calc_mercenary_(struct mercenary_data *md, bool first)
+{
+ struct status_data *status = &md->base_status;
+ struct s_mercenary *merc = &md->mercenary;
+
+ if( first )
+ {
+ memcpy(status, &md->db->status, sizeof(struct status_data));
+ status->mode = MD_CANMOVE|MD_CANATTACK;
+ status->hp = status->max_hp;
+ status->sp = status->max_sp;
+ md->battle_status.hp = merc->hp;
+ md->battle_status.sp = merc->sp;
+ }
+
+ status_calc_misc(&md->bl, status, md->db->lv);
+ status_cpy(&md->battle_status, status);
+
+ return 0;
+}
+
+int status_calc_homunculus_(struct homun_data *hd, bool first)
+{
+ struct status_data *status = &hd->base_status;
+ struct s_homunculus *hom = &hd->homunculus;
+ int skill;
+ int amotion;
+
+ status->str = hom->str / 10;
+ status->agi = hom->agi / 10;
+ status->vit = hom->vit / 10;
+ status->dex = hom->dex / 10;
+ status->int_ = hom->int_ / 10;
+ status->luk = hom->luk / 10;
+
+ if (first) { //[orn]
+ const struct s_homunculus_db *db = hd->homunculusDB;
+ status->def_ele = db->element;
+ status->ele_lv = 1;
+ status->race = db->race;
+ status->size = (hom->class_ == db->evo_class)?db->evo_size:db->base_size;
+ status->rhw.range = 1 + status->size;
+ status->mode = MD_CANMOVE|MD_CANATTACK;
+ status->speed = DEFAULT_WALK_SPEED;
+ if (battle_config.hom_setting&0x8 && hd->master)
+ status->speed = status_get_speed(&hd->master->bl);
+
+ status->hp = 1;
+ status->sp = 1;
+ }
+ skill = hom->level/10 + status->vit/5;
+ status->def = cap_value(skill, 0, 99);
+
+ skill = hom->level/10 + status->int_/5;
+ status->mdef = cap_value(skill, 0, 99);
+
+ status->max_hp = hom->max_hp ;
+ status->max_sp = hom->max_sp ;
+
+ merc_hom_calc_skilltree(hd, 0);
+
+ if((skill=merc_hom_checkskill(hd,HAMI_SKIN)) > 0)
+ status->def += skill * 4;
+
+ if((skill = merc_hom_checkskill(hd,HVAN_INSTRUCT)) > 0)
+ {
+ status->int_ += 1 +skill/2 +skill/4 +skill/5;
+ status->str += 1 +skill/3 +skill/3 +skill/4;
+ }
+
+ if((skill=merc_hom_checkskill(hd,HAMI_SKIN)) > 0)
+ status->max_hp += skill * 2 * status->max_hp / 100;
+
+ if((skill = merc_hom_checkskill(hd,HLIF_BRAIN)) > 0)
+ status->max_sp += (1 +skill/2 -skill/4 +skill/5) * status->max_sp / 100 ;
+
+ if (first) {
+ hd->battle_status.hp = hom->hp ;
+ hd->battle_status.sp = hom->sp ;
+ }
+
+ status->rhw.atk = status->dex;
+ status->rhw.atk2 = status->str + hom->level;
+
+ status->aspd_rate = 1000;
+
+ amotion = (1000 -4*status->agi -status->dex) * hd->homunculusDB->baseASPD/1000;
+ status->amotion = cap_value(amotion,battle_config.max_aspd,2000);
+ status->adelay = status->amotion; //It seems adelay = amotion for Homunculus.
+
+ status_calc_misc(&hd->bl, status, hom->level);
+
+#ifdef RENEWAL
+ status->matk_max = status->matk_min;
+#endif
+
+ status_cpy(&hd->battle_status, status);
+ return 1;
+}
+
+int status_calc_elemental_(struct elemental_data *ed, bool first) {
+ struct status_data *status = &ed->base_status;
+ struct s_elemental *ele = &ed->elemental;
+ struct map_session_data *sd = ed->master;
+
+ if( !sd )
+ return 0;
+
+ if( first ) {
+ memcpy(status, &ed->db->status, sizeof(struct status_data));
+ if( !ele->mode )
+ status->mode = EL_MODE_PASSIVE;
+ else
+ status->mode = ele->mode;
+
+ status_calc_misc(&ed->bl, status, 0);
+
+ status->max_hp = ele->max_hp;
+ status->max_sp = ele->max_sp;
+ status->hp = ele->hp;
+ status->sp = ele->sp;
+ status->rhw.atk = ele->atk;
+ status->rhw.atk2 = ele->atk2;
+
+ status->matk_min += ele->matk;
+ status->def += ele->def;
+ status->mdef += ele->mdef;
+ status->flee = ele->flee;
+ status->hit = ele->hit;
+
+ memcpy(&ed->battle_status,status,sizeof(struct status_data));
+ } else {
+ status_calc_misc(&ed->bl, status, 0);
+ status_cpy(&ed->battle_status, status);
+ }
+
+ return 0;
+}
+
+int status_calc_npc_(struct npc_data *nd, bool first) {
+ struct status_data *status = &nd->status;
+
+ if (!nd)
+ return 0;
+
+ if (first) {
+ status->hp = 1;
+ status->sp = 1;
+ status->max_hp = 1;
+ status->max_sp = 1;
+
+ status->def_ele = ELE_NEUTRAL;
+ status->ele_lv = 1;
+ status->race = RC_DEMIHUMAN;
+ status->size = nd->size;
+ status->rhw.range = 1 + status->size;
+ status->mode = (MD_CANMOVE|MD_CANATTACK);
+ status->speed = nd->speed;
+ }
+
+ status->str = nd->stat_point;
+ status->agi = nd->stat_point;
+ status->vit = nd->stat_point;
+ status->int_= nd->stat_point;
+ status->dex = nd->stat_point;
+ status->luk = nd->stat_point;
+
+ status_calc_misc(&nd->bl, status, nd->level);
+ status_cpy(&nd->status, status);
+
+ return 0;
+}
+
+static unsigned short status_calc_str(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_agi(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_vit(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_int(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_dex(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_luk(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_batk(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_watk(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_matk(struct block_list *,struct status_change *,int);
+static signed short status_calc_hit(struct block_list *,struct status_change *,int);
+static signed short status_calc_critical(struct block_list *,struct status_change *,int);
+static signed short status_calc_flee(struct block_list *,struct status_change *,int);
+static signed short status_calc_flee2(struct block_list *,struct status_change *,int);
+static defType status_calc_def(struct block_list *bl, struct status_change *sc, int);
+static signed short status_calc_def2(struct block_list *,struct status_change *,int);
+static defType status_calc_mdef(struct block_list *bl, struct status_change *sc, int);
+static signed short status_calc_mdef2(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_speed(struct block_list *,struct status_change *,int);
+static short status_calc_aspd_rate(struct block_list *,struct status_change *,int);
+static unsigned short status_calc_dmotion(struct block_list *bl, struct status_change *sc, int dmotion);
+#ifdef RENEWAL_ASPD
+static short status_calc_aspd(struct block_list *bl, struct status_change *sc, short flag);
+#endif
+static short status_calc_fix_aspd(struct block_list *bl, struct status_change *sc, int);
+static unsigned int status_calc_maxhp(struct block_list *,struct status_change *, uint64);
+static unsigned int status_calc_maxsp(struct block_list *,struct status_change *,unsigned int);
+static unsigned char status_calc_element(struct block_list *bl, struct status_change *sc, int element);
+static unsigned char status_calc_element_lv(struct block_list *bl, struct status_change *sc, int lv);
+static unsigned short status_calc_mode(struct block_list *bl, struct status_change *sc, int mode);
+#ifdef RENEWAL
+static unsigned short status_calc_ematk(struct block_list *,struct status_change *,int);
+#endif
+
+//Calculates base regen values.
+void status_calc_regen(struct block_list *bl, struct status_data *status, struct regen_data *regen)
+{
+ struct map_session_data *sd;
+ int val, skill, reg_flag;
+
+ if( !(bl->type&BL_REGEN) || !regen )
+ return;
+
+ sd = BL_CAST(BL_PC,bl);
+ val = 1 + (status->vit/5) + (status->max_hp/200);
+
+ if( sd && sd->hprecov_rate != 100 )
+ val = val*sd->hprecov_rate/100;
+
+ reg_flag = bl->type == BL_PC ? 0 : 1;
+
+ regen->hp = cap_value(val, reg_flag, SHRT_MAX);
+
+ val = 1 + (status->int_/6) + (status->max_sp/100);
+ if( status->int_ >= 120 )
+ val += ((status->int_-120)>>1) + 4;
+
+ if( sd && sd->sprecov_rate != 100 )
+ val = val*sd->sprecov_rate/100;
+
+ regen->sp = cap_value(val, reg_flag, SHRT_MAX);
+
+ if( sd )
+ {
+ struct regen_data_sub *sregen;
+ if( (skill=pc_checkskill(sd,HP_MEDITATIO)) > 0 )
+ {
+ val = regen->sp*(100+3*skill)/100;
+ regen->sp = cap_value(val, 1, SHRT_MAX);
+ }
+ //Only players have skill/sitting skill regen for now.
+ sregen = regen->sregen;
+
+ val = 0;
+ if( (skill=pc_checkskill(sd,SM_RECOVERY)) > 0 )
+ val += skill*5 + skill*status->max_hp/500;
+ sregen->hp = cap_value(val, 0, SHRT_MAX);
+
+ val = 0;
+ if( (skill=pc_checkskill(sd,MG_SRECOVERY)) > 0 )
+ val += skill*3 + skill*status->max_sp/500;
+ if( (skill=pc_checkskill(sd,NJ_NINPOU)) > 0 )
+ val += skill*3 + skill*status->max_sp/500;
+ if( (skill=pc_checkskill(sd,WM_LESSON)) > 0 )
+ val += 3 + 3 * skill;
+
+ sregen->sp = cap_value(val, 0, SHRT_MAX);
+
+ // Skill-related recovery (only when sit)
+ sregen = regen->ssregen;
+
+ val = 0;
+ if( (skill=pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0 )
+ val += skill*4 + skill*status->max_hp/500;
+
+ if( (skill=pc_checkskill(sd,TK_HPTIME)) > 0 && sd->state.rest )
+ val += skill*30 + skill*status->max_hp/500;
+ sregen->hp = cap_value(val, 0, SHRT_MAX);
+
+ val = 0;
+ if( (skill=pc_checkskill(sd,TK_SPTIME)) > 0 && sd->state.rest )
+ {
+ val += skill*3 + skill*status->max_sp/500;
+ if ((skill=pc_checkskill(sd,SL_KAINA)) > 0) //Power up Enjoyable Rest
+ val += (30+10*skill)*val/100;
+ }
+ if( (skill=pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0 )
+ val += skill*2 + skill*status->max_sp/500;
+ sregen->sp = cap_value(val, 0, SHRT_MAX);
+ }
+
+ if( bl->type == BL_HOM ) {
+ struct homun_data *hd = (TBL_HOM*)bl;
+ if( (skill = merc_hom_checkskill(hd,HAMI_SKIN)) > 0 ) {
+ val = regen->hp*(100+5*skill)/100;
+ regen->hp = cap_value(val, 1, SHRT_MAX);
+ }
+ if( (skill = merc_hom_checkskill(hd,HLIF_BRAIN)) > 0 ) {
+ val = regen->sp*(100+3*skill)/100;
+ regen->sp = cap_value(val, 1, SHRT_MAX);
+ }
+ } else if( bl->type == BL_MER ) {
+ val = (status->max_hp * status->vit / 10000 + 1) * 6;
+ regen->hp = cap_value(val, 1, SHRT_MAX);
+
+ val = (status->max_sp * (status->int_ + 10) / 750) + 1;
+ regen->sp = cap_value(val, 1, SHRT_MAX);
+ } else if( bl->type == BL_ELEM ) {
+ val = (status->max_hp * status->vit / 10000 + 1) * 6;
+ regen->hp = cap_value(val, 1, SHRT_MAX);
+
+ val = (status->max_sp * (status->int_ + 10) / 750) + 1;
+ regen->sp = cap_value(val, 1, SHRT_MAX);
+ }
+}
+
+//Calculates SC related regen rates.
+void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, struct status_change *sc)
+{
+ if (!(bl->type&BL_REGEN) || !regen)
+ return;
+
+ regen->flag = RGN_HP|RGN_SP;
+ if(regen->sregen)
+ {
+ if (regen->sregen->hp)
+ regen->flag|=RGN_SHP;
+
+ if (regen->sregen->sp)
+ regen->flag|=RGN_SSP;
+ regen->sregen->rate.hp = regen->sregen->rate.sp = 1;
+ }
+ if (regen->ssregen)
+ {
+ if (regen->ssregen->hp)
+ regen->flag|=RGN_SHP;
+
+ if (regen->ssregen->sp)
+ regen->flag|=RGN_SSP;
+ regen->ssregen->rate.hp = regen->ssregen->rate.sp = 1;
+ }
+ regen->rate.hp = regen->rate.sp = 1;
+
+ if (!sc || !sc->count)
+ return;
+
+ if (
+ (sc->data[SC_POISON] && !sc->data[SC_SLOWPOISON])
+ || (sc->data[SC_DPOISON] && !sc->data[SC_SLOWPOISON])
+ || sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]
+ || sc->data[SC_TRICKDEAD]
+ || sc->data[SC_BLEEDING]
+ || sc->data[SC_MAGICMUSHROOM]
+ || sc->data[SC_RAISINGDRAGON]
+ || sc->data[SC_SATURDAYNIGHTFEVER]
+ ) //No regen
+ regen->flag = 0;
+
+ if (
+ sc->data[SC_DANCING] || sc->data[SC_OBLIVIONCURSE] || sc->data[SC_MAXIMIZEPOWER]
+ || (
+ (bl->type == BL_PC && ((TBL_PC*)bl)->class_&MAPID_UPPERMASK) == MAPID_MONK &&
+ (sc->data[SC_EXTREMITYFIST] || (sc->data[SC_EXPLOSIONSPIRITS] && (!sc->data[SC_SPIRIT] || sc->data[SC_SPIRIT]->val2 != SL_MONK)))
+ )
+ ) //No natural SP regen
+ regen->flag &=~RGN_SP;
+
+ if(
+ sc->data[SC_TENSIONRELAX]
+ ) {
+ regen->rate.hp += 2;
+ if (regen->sregen)
+ regen->sregen->rate.hp += 3;
+ }
+ if (sc->data[SC_MAGNIFICAT])
+ {
+ regen->rate.hp += 1;
+ regen->rate.sp += 1;
+ }
+ if (sc->data[SC_REGENERATION])
+ {
+ const struct status_change_entry *sce = sc->data[SC_REGENERATION];
+ if (!sce->val4)
+ {
+ regen->rate.hp += sce->val2;
+ regen->rate.sp += sce->val3;
+ } else
+ regen->flag&=~sce->val4; //Remove regen as specified by val4
+ }
+ if(sc->data[SC_GT_REVITALIZE]){
+ regen->hp = cap_value(regen->hp*sc->data[SC_GT_REVITALIZE]->val3/100, 1, SHRT_MAX);
+ regen->state.walk= 1;
+ }
+ if ((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 1) //if insignia lvl 1
+ || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 1)
+ || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 1)
+ || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 1))
+ regen->rate.hp *= 2;
+
+}
+void status_calc_state( struct block_list *bl, struct status_change *sc, enum scs_flag flag, bool start ) {
+
+ /* no sc at all, we can zero without any extra weight over our conciousness */
+ if( !sc->count ) {
+ memset(&sc->cant, 0, sizeof (sc->cant));
+ return;
+ }
+
+ /* can move? */
+ if( flag&SCS_NOMOVE ) {
+ if( !(flag&SCS_NOMOVECOND) ) {
+ sc->cant.move += ( start ? 1 : -1 );
+ } else if(
+ (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF) // cannot move while gospel is in effect
+ || (sc->data[SC_BASILICA] && sc->data[SC_BASILICA]->val4 == bl->id) // Basilica caster cannot move
+ || (sc->data[SC_GRAVITATION] && sc->data[SC_GRAVITATION]->val3 == BCT_SELF)
+ || (sc->data[SC_CRYSTALIZE] && bl->type != BL_MOB)
+ || (sc->data[SC_CAMOUFLAGE] && sc->data[SC_CAMOUFLAGE]->val1 < 3
+ && !(sc->data[SC_CAMOUFLAGE]->val3&1))
+ ) {
+ sc->cant.move += ( start ? 1 : -1 );
+ }
+ }
+
+ /* can't use skills */
+ if( flag&SCS_NOCAST ) {
+ if( !(flag&SCS_NOCASTCOND) ) {
+ sc->cant.cast += ( start ? 1 : -1 );
+ } else if( (sc->data[SC_CRYSTALIZE] && bl->type != BL_MOB) ){
+ sc->cant.cast += ( start ? 1 : -1 );
+ }
+ }
+
+ /* player-only states */
+ if( bl->type == BL_PC ) {
+
+ /* can pick items? */
+ if( flag&SCS_NOPICKITEM ) {
+ if( !(flag&SCS_NOPICKITEMCOND) ) {
+ sc->cant.pickup += ( start ? 1 : -1 );
+ } else if( (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOITEM) ) {
+ sc->cant.pickup += ( start ? 1 : -1 );
+ }
+ }
+
+ /* can drop items? */
+ if( flag&SCS_NODROPITEM ) {
+ if( !(flag&SCS_NODROPITEMCOND) ) {
+ sc->cant.drop += ( start ? 1 : -1 );
+ } else if( (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOITEM) ) {
+ sc->cant.drop += ( start ? 1 : -1 );
+ }
+ }
+ }
+
+ return;
+}
+/// Recalculates parts of an object's battle status according to the specified flags.
+/// @param flag bitfield of values from enum scb_flag
+void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
+{
+ const struct status_data *b_status = status_get_base_status(bl);
+ struct status_data *status = status_get_status_data(bl);
+ struct status_change *sc = status_get_sc(bl);
+ TBL_PC *sd = BL_CAST(BL_PC,bl);
+ int temp;
+
+ if (!b_status || !status)
+ return;
+
+ if((!(bl->type&BL_REGEN)) && (!sc || !sc->count)) { //No difference.
+ status_cpy(status, b_status);
+ return;
+ }
+
+ if(flag&SCB_STR) {
+ status->str = status_calc_str(bl, sc, b_status->str);
+ flag|=SCB_BATK;
+ if( bl->type&BL_HOM )
+ flag |= SCB_WATK;
+ }
+
+ if(flag&SCB_AGI) {
+ status->agi = status_calc_agi(bl, sc, b_status->agi);
+ flag|=SCB_FLEE
+#ifdef RENEWAL
+ |SCB_DEF2
+#endif
+ ;
+ if( bl->type&(BL_PC|BL_HOM) )
+ flag |= SCB_ASPD|SCB_DSPD;
+ }
+
+ if(flag&SCB_VIT) {
+ status->vit = status_calc_vit(bl, sc, b_status->vit);
+ flag|=SCB_DEF2|SCB_MDEF2;
+ if( bl->type&(BL_PC|BL_HOM|BL_MER|BL_ELEM) )
+ flag |= SCB_MAXHP;
+ if( bl->type&BL_HOM )
+ flag |= SCB_DEF;
+ }
+
+ if(flag&SCB_INT) {
+ status->int_ = status_calc_int(bl, sc, b_status->int_);
+ flag|=SCB_MATK|SCB_MDEF2;
+ if( bl->type&(BL_PC|BL_HOM|BL_MER|BL_ELEM) )
+ flag |= SCB_MAXSP;
+ if( bl->type&BL_HOM )
+ flag |= SCB_MDEF;
+ }
+
+ if(flag&SCB_DEX) {
+ status->dex = status_calc_dex(bl, sc, b_status->dex);
+ flag|=SCB_BATK|SCB_HIT
+#ifdef RENEWAL
+ |SCB_MATK|SCB_MDEF2
+#endif
+ ;
+ if( bl->type&(BL_PC|BL_HOM) )
+ flag |= SCB_ASPD;
+ if( bl->type&BL_HOM )
+ flag |= SCB_WATK;
+ }
+
+ if(flag&SCB_LUK) {
+ status->luk = status_calc_luk(bl, sc, b_status->luk);
+ flag|=SCB_BATK|SCB_CRI|SCB_FLEE2
+#ifdef RENEWAL
+ |SCB_MATK|SCB_HIT|SCB_FLEE
+#endif
+ ;
+ }
+
+ if(flag&SCB_BATK && b_status->batk) {
+ status->batk = status_base_atk(bl,status);
+ temp = b_status->batk - status_base_atk(bl,b_status);
+ if (temp)
+ {
+ temp += status->batk;
+ status->batk = cap_value(temp, 0, USHRT_MAX);
+ }
+ status->batk = status_calc_batk(bl, sc, status->batk);
+ }
+
+ if(flag&SCB_WATK) {
+
+ status->rhw.atk = status_calc_watk(bl, sc, b_status->rhw.atk);
+ if (!sd) //Should not affect weapon refine bonus
+ status->rhw.atk2 = status_calc_watk(bl, sc, b_status->rhw.atk2);
+
+ if(b_status->lhw.atk) {
+ if (sd) {
+ sd->state.lr_flag = 1;
+ status->lhw.atk = status_calc_watk(bl, sc, b_status->lhw.atk);
+ sd->state.lr_flag = 0;
+ } else {
+ status->lhw.atk = status_calc_watk(bl, sc, b_status->lhw.atk);
+ status->lhw.atk2= status_calc_watk(bl, sc, b_status->lhw.atk2);
+ }
+ }
+
+ if( bl->type&BL_HOM )
+ {
+ status->rhw.atk += (status->dex - b_status->dex);
+ status->rhw.atk2 += (status->str - b_status->str);
+ if( status->rhw.atk2 < status->rhw.atk )
+ status->rhw.atk2 = status->rhw.atk;
+ }
+ }
+
+ if(flag&SCB_HIT) {
+ if (status->dex == b_status->dex
+#ifdef RENEWAL
+ && status->luk == b_status->luk
+#endif
+ )
+ status->hit = status_calc_hit(bl, sc, b_status->hit);
+ else
+ status->hit = status_calc_hit(bl, sc, b_status->hit + (status->dex - b_status->dex)
+#ifdef RENEWAL
+ + (status->luk/3 - b_status->luk/3)
+#endif
+ );
+ }
+
+ if(flag&SCB_FLEE) {
+ if (status->agi == b_status->agi
+#ifdef RENEWAL
+ && status->luk == b_status->luk
+#endif
+ )
+ status->flee = status_calc_flee(bl, sc, b_status->flee);
+ else
+ status->flee = status_calc_flee(bl, sc, b_status->flee +(status->agi - b_status->agi)
+#ifdef RENEWAL
+ + (status->luk/5 - b_status->luk/5)
+#endif
+ );
+ }
+
+ if(flag&SCB_DEF)
+ {
+ status->def = status_calc_def(bl, sc, b_status->def);
+
+ if( bl->type&BL_HOM )
+ status->def += (status->vit/5 - b_status->vit/5);
+ }
+
+ if(flag&SCB_DEF2) {
+ if (status->vit == b_status->vit
+#ifdef RENEWAL
+ && status->agi == b_status->agi
+#endif
+ )
+ status->def2 = status_calc_def2(bl, sc, b_status->def2);
+ else
+ status->def2 = status_calc_def2(bl, sc, b_status->def2
+#ifdef RENEWAL
+ + (int)( ((float)status->vit/2 + (float)b_status->vit/2) + ((float)status->agi/5 + (float)b_status->agi/5) )
+#else
+ + (status->vit - b_status->vit)
+#endif
+ );
+ }
+
+ if(flag&SCB_MDEF)
+ {
+ status->mdef = status_calc_mdef(bl, sc, b_status->mdef);
+
+ if( bl->type&BL_HOM )
+ status->mdef += (status->int_/5 - b_status->int_/5);
+ }
+
+ if(flag&SCB_MDEF2) {
+ if (status->int_ == b_status->int_ && status->vit == b_status->vit
+#ifdef RENEWAL
+ && status->dex == b_status->dex
+#endif
+ )
+ status->mdef2 = status_calc_mdef2(bl, sc, b_status->mdef2);
+ else
+ status->mdef2 = status_calc_mdef2(bl, sc, b_status->mdef2 +(status->int_ - b_status->int_)
+#ifdef RENEWAL
+ + (int)( ((float)status->dex/5 - (float)b_status->dex/5) + ((float)status->vit/5 + (float)b_status->vit/5) )
+#else
+ + ((status->vit - b_status->vit)>>1)
+#endif
+ );
+ }
+
+ if(flag&SCB_SPEED) {
+ struct unit_data *ud = unit_bl2ud(bl);
+ status->speed = status_calc_speed(bl, sc, b_status->speed);
+
+ //Re-walk to adjust speed (we do not check if walktimer != INVALID_TIMER
+ //because if you step on something while walking, the moment this
+ //piece of code triggers the walk-timer is set on INVALID_TIMER) [Skotlex]
+ if (ud)
+ ud->state.change_walk_target = ud->state.speed_changed = 1;
+
+ if( bl->type&BL_PC && status->speed < battle_config.max_walk_speed )
+ status->speed = battle_config.max_walk_speed;
+
+ if( bl->type&BL_HOM && battle_config.hom_setting&0x8 && ((TBL_HOM*)bl)->master)
+ status->speed = status_get_speed(&((TBL_HOM*)bl)->master->bl);
+
+
+ }
+
+ if(flag&SCB_CRI && b_status->cri) {
+ if (status->luk == b_status->luk)
+ status->cri = status_calc_critical(bl, sc, b_status->cri);
+ else
+ status->cri = status_calc_critical(bl, sc, b_status->cri + 3*(status->luk - b_status->luk));
+ /**
+ * after status_calc_critical so the bonus is applied despite if you have or not a sc bugreport:5240
+ **/
+ if( bl->type == BL_PC && ((TBL_PC*)bl)->status.weapon == W_KATAR )
+ status->cri <<= 1;
+
+ }
+
+ if(flag&SCB_FLEE2 && b_status->flee2) {
+ if (status->luk == b_status->luk)
+ status->flee2 = status_calc_flee2(bl, sc, b_status->flee2);
+ else
+ status->flee2 = status_calc_flee2(bl, sc, b_status->flee2 +(status->luk - b_status->luk));
+ }
+
+ if(flag&SCB_ATK_ELE) {
+ status->rhw.ele = status_calc_attack_element(bl, sc, b_status->rhw.ele);
+ if (sd) sd->state.lr_flag = 1;
+ status->lhw.ele = status_calc_attack_element(bl, sc, b_status->lhw.ele);
+ if (sd) sd->state.lr_flag = 0;
+ }
+
+ if(flag&SCB_DEF_ELE) {
+ status->def_ele = status_calc_element(bl, sc, b_status->def_ele);
+ status->ele_lv = status_calc_element_lv(bl, sc, b_status->ele_lv);
+ }
+
+ if(flag&SCB_MODE)
+ {
+ status->mode = status_calc_mode(bl, sc, b_status->mode);
+ //Since mode changed, reset their state.
+ if (!(status->mode&MD_CANATTACK))
+ unit_stop_attack(bl);
+ if (!(status->mode&MD_CANMOVE))
+ unit_stop_walking(bl,1);
+ }
+
+// No status changes alter these yet.
+// if(flag&SCB_SIZE)
+// if(flag&SCB_RACE)
+// if(flag&SCB_RANGE)
+
+ if(flag&SCB_MAXHP) {
+ if( bl->type&BL_PC )
+ {
+ status->max_hp = status_base_pc_maxhp(sd,status);
+ status->max_hp += b_status->max_hp - sd->status.max_hp;
+
+ status->max_hp = status_calc_maxhp(bl, sc, status->max_hp);
+
+ if( status->max_hp > (unsigned int)battle_config.max_hp )
+ status->max_hp = (unsigned int)battle_config.max_hp;
+ }
+ else
+ {
+ status->max_hp = status_calc_maxhp(bl, sc, b_status->max_hp);
+ }
+
+ if( status->hp > status->max_hp ) //FIXME: Should perhaps a status_zap should be issued?
+ {
+ status->hp = status->max_hp;
+ if( sd ) clif_updatestatus(sd,SP_HP);
+ }
+ }
+
+ if(flag&SCB_MAXSP) {
+ if( bl->type&BL_PC )
+ {
+ status->max_sp = status_base_pc_maxsp(sd,status);
+ status->max_sp += b_status->max_sp - sd->status.max_sp;
+
+ status->max_sp = status_calc_maxsp(&sd->bl, &sd->sc, status->max_sp);
+
+ if( status->max_sp > (unsigned int)battle_config.max_sp )
+ status->max_sp = (unsigned int)battle_config.max_sp;
+ }
+ else
+ {
+ status->max_sp = status_calc_maxsp(bl, sc, b_status->max_sp);
+ }
+
+ if( status->sp > status->max_sp )
+ {
+ status->sp = status->max_sp;
+ if( sd ) clif_updatestatus(sd,SP_SP);
+ }
+ }
+
+ if(flag&SCB_MATK) {
+#ifndef RENEWAL
+ status->matk_min = status_base_matk_min(status) + (sd?sd->bonus.ematk:0);
+ status->matk_max = status_base_matk_max(status) + (sd?sd->bonus.ematk:0);
+#else
+ /**
+ * RE MATK Formula (from irowiki:http://irowiki.org/wiki/MATK)
+ * MATK = (sMATK + wMATK + eMATK) * Multiplicative Modifiers
+ **/
+ status->matk_min = status->matk_max = status_base_matk(status, status_get_lv(bl));
+ if( bl->type&BL_PC ){
+ // Any +MATK you get from skills and cards, including cards in weapon, is added here.
+ if( sd->bonus.ematk > 0 ){
+ status->matk_max += sd->bonus.ematk;
+ status->matk_min += sd->bonus.ematk;
+ }
+ status->matk_min = status_calc_ematk(bl, sc, status->matk_min);
+ status->matk_max = status_calc_ematk(bl, sc, status->matk_max);
+ //This is the only portion in MATK that varies depending on the weapon level and refinement rate.
+ if( status->rhw.matk > 0 ){
+ int wMatk = status->rhw.matk;
+ int variance = wMatk * status->rhw.wlv / 10;
+ status->matk_min += wMatk - variance;
+ status->matk_max += wMatk + variance;
+ }
+ }
+#endif
+ if (bl->type&BL_PC && sd->matk_rate != 100) {
+ status->matk_max = status->matk_max * sd->matk_rate/100;
+ status->matk_min = status->matk_min * sd->matk_rate/100;
+ }
+
+ status->matk_min = status_calc_matk(bl, sc, status->matk_min);
+ status->matk_max = status_calc_matk(bl, sc, status->matk_max);
+
+ if ((bl->type&BL_HOM && battle_config.hom_setting&0x20) //Hom Min Matk is always the same as Max Matk
+ || sc->data[SC_RECOGNIZEDSPELL])
+ status->matk_min = status->matk_max;
+
+#ifdef RENEWAL
+ if( sd && sd->right_weapon.overrefine > 0){
+ status->matk_min++;
+ status->matk_max += sd->right_weapon.overrefine - 1;
+ }
+#endif
+
+ }
+
+ if(flag&SCB_ASPD) {
+ int amotion;
+ if( bl->type&BL_PC )
+ {
+ amotion = status_base_amotion_pc(sd,status);
+#ifndef RENEWAL_ASPD
+ status->aspd_rate = status_calc_aspd_rate(bl, sc, b_status->aspd_rate);
+
+ if(status->aspd_rate != 1000)
+ amotion = amotion*status->aspd_rate/1000;
+#else
+ // aspd = baseaspd + floor(sqrt((agi^2/2) + (dex^2/5))/4 + (potskillbonus*agi/200))
+ amotion -= (int)(sqrt( (pow(status->agi, 2) / 2) + (pow(status->dex, 2) / 5) ) / 4 + (status_calc_aspd(bl, sc, 1) * status->agi / 200)) * 10;
+
+ if( (status_calc_aspd(bl, sc, 2) + status->aspd_rate2) != 0 ) // RE ASPD percertage modifier
+ amotion -= ( amotion - ((sd->class_&JOBL_THIRD) ? battle_config.max_third_aspd : battle_config.max_aspd) )
+ * (status_calc_aspd(bl, sc, 2) + status->aspd_rate2) / 100;
+
+ if(status->aspd_rate != 1000) // absolute percentage modifier
+ amotion = ( 200 - (200-amotion/10) * status->aspd_rate / 1000 ) * 10;
+#endif
+ amotion = status_calc_fix_aspd(bl, sc, amotion);
+ status->amotion = cap_value(amotion,((sd->class_&JOBL_THIRD) ? battle_config.max_third_aspd : battle_config.max_aspd),2000);
+
+ status->adelay = 2*status->amotion;
+ }
+ else
+ if( bl->type&BL_HOM )
+ {
+ amotion = (1000 -4*status->agi -status->dex) * ((TBL_HOM*)bl)->homunculusDB->baseASPD/1000;
+ status->aspd_rate = status_calc_aspd_rate(bl, sc, b_status->aspd_rate);
+
+ if(status->aspd_rate != 1000)
+ amotion = amotion*status->aspd_rate/1000;
+
+ amotion = status_calc_fix_aspd(bl, sc, amotion);
+ status->amotion = cap_value(amotion,battle_config.max_aspd,2000);
+
+ status->adelay = status->amotion;
+ }
+ else // mercenary and mobs
+ {
+ amotion = b_status->amotion;
+ status->aspd_rate = status_calc_aspd_rate(bl, sc, b_status->aspd_rate);
+
+ if(status->aspd_rate != 1000)
+ amotion = amotion*status->aspd_rate/1000;
+
+ amotion = status_calc_fix_aspd(bl, sc, amotion);
+ status->amotion = cap_value(amotion, battle_config.monster_max_aspd, 2000);
+
+ temp = b_status->adelay*status->aspd_rate/1000;
+ status->adelay = cap_value(temp, battle_config.monster_max_aspd*2, 4000);
+ }
+ }
+
+ if(flag&SCB_DSPD) {
+ int dmotion;
+ if( bl->type&BL_PC )
+ {
+ if (b_status->agi == status->agi)
+ status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion);
+ else {
+ dmotion = 800-status->agi*4;
+ status->dmotion = cap_value(dmotion, 400, 800);
+ if(battle_config.pc_damage_delay_rate != 100)
+ status->dmotion = status->dmotion*battle_config.pc_damage_delay_rate/100;
+ //It's safe to ignore b_status->dmotion since no bonus affects it.
+ status->dmotion = status_calc_dmotion(bl, sc, status->dmotion);
+ }
+ }
+ else
+ if( bl->type&BL_HOM )
+ {
+ dmotion = 800-status->agi*4;
+ status->dmotion = cap_value(dmotion, 400, 800);
+ status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion);
+ }
+ else // mercenary and mobs
+ {
+ status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion);
+ }
+ }
+
+ if(flag&(SCB_VIT|SCB_MAXHP|SCB_INT|SCB_MAXSP) && bl->type&BL_REGEN)
+ status_calc_regen(bl, status, status_get_regen_data(bl));
+
+ if(flag&SCB_REGEN && bl->type&BL_REGEN)
+ status_calc_regen_rate(bl, status_get_regen_data(bl), sc);
+}
+/// Recalculates parts of an object's base status and battle status according to the specified flags.
+/// Also sends updates to the client wherever applicable.
+/// @param flag bitfield of values from enum scb_flag
+/// @param first if true, will cause status_calc_* functions to run their base status initialization code
+void status_calc_bl_(struct block_list* bl, enum scb_flag flag, bool first)
+{
+ struct status_data b_status; // previous battle status
+ struct status_data* status; // pointer to current battle status
+
+ // remember previous values
+ status = status_get_status_data(bl);
+ memcpy(&b_status, status, sizeof(struct status_data));
+
+ if( flag&SCB_BASE ) {// calculate the object's base status too
+ switch( bl->type ) {
+ case BL_PC: status_calc_pc_(BL_CAST(BL_PC,bl), first); break;
+ case BL_MOB: status_calc_mob_(BL_CAST(BL_MOB,bl), first); break;
+ case BL_PET: status_calc_pet_(BL_CAST(BL_PET,bl), first); break;
+ case BL_HOM: status_calc_homunculus_(BL_CAST(BL_HOM,bl), first); break;
+ case BL_MER: status_calc_mercenary_(BL_CAST(BL_MER,bl), first); break;
+ case BL_ELEM: status_calc_elemental_(BL_CAST(BL_ELEM,bl), first); break;
+ case BL_NPC: status_calc_npc_(BL_CAST(BL_NPC,bl), first); break;
+ }
+ }
+
+ if( bl->type == BL_PET )
+ return; // pets are not affected by statuses
+
+ if( first && bl->type == BL_MOB )
+ return; // assume there will be no statuses active
+
+ status_calc_bl_main(bl, flag);
+
+ if( first && bl->type == BL_HOM )
+ return; // client update handled by caller
+
+ // compare against new values and send client updates
+ if( bl->type == BL_PC )
+ {
+ TBL_PC* sd = BL_CAST(BL_PC, bl);
+ if(b_status.str != status->str)
+ clif_updatestatus(sd,SP_STR);
+ if(b_status.agi != status->agi)
+ clif_updatestatus(sd,SP_AGI);
+ if(b_status.vit != status->vit)
+ clif_updatestatus(sd,SP_VIT);
+ if(b_status.int_ != status->int_)
+ clif_updatestatus(sd,SP_INT);
+ if(b_status.dex != status->dex)
+ clif_updatestatus(sd,SP_DEX);
+ if(b_status.luk != status->luk)
+ clif_updatestatus(sd,SP_LUK);
+ if(b_status.hit != status->hit)
+ clif_updatestatus(sd,SP_HIT);
+ if(b_status.flee != status->flee)
+ clif_updatestatus(sd,SP_FLEE1);
+ if(b_status.amotion != status->amotion)
+ clif_updatestatus(sd,SP_ASPD);
+ if(b_status.speed != status->speed)
+ clif_updatestatus(sd,SP_SPEED);
+
+ if(b_status.batk != status->batk
+#ifndef RENEWAL
+ || b_status.rhw.atk != status->rhw.atk || b_status.lhw.atk != status->lhw.atk
+#endif
+ )
+ clif_updatestatus(sd,SP_ATK1);
+
+ if(b_status.def != status->def){
+ clif_updatestatus(sd,SP_DEF1);
+#ifdef RENEWAL
+ clif_updatestatus(sd,SP_DEF2);
+#endif
+ }
+
+ if(b_status.rhw.atk2 != status->rhw.atk2 || b_status.lhw.atk2 != status->lhw.atk2
+#ifdef RENEWAL
+ || b_status.rhw.atk != status->rhw.atk || b_status.lhw.atk != status->lhw.atk
+#endif
+ )
+ clif_updatestatus(sd,SP_ATK2);
+
+ if(b_status.def2 != status->def2){
+ clif_updatestatus(sd,SP_DEF2);
+#ifdef RENEWAL
+ clif_updatestatus(sd,SP_DEF1);
+#endif
+ }
+ if(b_status.flee2 != status->flee2)
+ clif_updatestatus(sd,SP_FLEE2);
+ if(b_status.cri != status->cri)
+ clif_updatestatus(sd,SP_CRITICAL);
+#ifndef RENEWAL
+ if(b_status.matk_max != status->matk_max)
+ clif_updatestatus(sd,SP_MATK1);
+ if(b_status.matk_min != status->matk_min)
+ clif_updatestatus(sd,SP_MATK2);
+#else
+ if(b_status.matk_max != status->matk_max || b_status.matk_min != status->matk_min){
+ clif_updatestatus(sd,SP_MATK2);
+ clif_updatestatus(sd,SP_MATK1);
+ }
+#endif
+ if(b_status.mdef != status->mdef){
+ clif_updatestatus(sd,SP_MDEF1);
+#ifdef RENEWAL
+ clif_updatestatus(sd,SP_MDEF2);
+#endif
+ }
+ if(b_status.mdef2 != status->mdef2){
+ clif_updatestatus(sd,SP_MDEF2);
+#ifdef RENEWAL
+ clif_updatestatus(sd,SP_MDEF1);
+#endif
+ }
+ if(b_status.rhw.range != status->rhw.range)
+ clif_updatestatus(sd,SP_ATTACKRANGE);
+ if(b_status.max_hp != status->max_hp)
+ clif_updatestatus(sd,SP_MAXHP);
+ if(b_status.max_sp != status->max_sp)
+ clif_updatestatus(sd,SP_MAXSP);
+ if(b_status.hp != status->hp)
+ clif_updatestatus(sd,SP_HP);
+ if(b_status.sp != status->sp)
+ clif_updatestatus(sd,SP_SP);
+ } else if( bl->type == BL_HOM ) {
+ TBL_HOM* hd = BL_CAST(BL_HOM, bl);
+ if( hd->master && memcmp(&b_status, status, sizeof(struct status_data)) != 0 )
+ clif_hominfo(hd->master,hd,0);
+ } else if( bl->type == BL_MER ) {
+ TBL_MER* md = BL_CAST(BL_MER, bl);
+ if( b_status.rhw.atk != status->rhw.atk || b_status.rhw.atk2 != status->rhw.atk2 )
+ clif_mercenary_updatestatus(md->master, SP_ATK1);
+ if( b_status.matk_max != status->matk_max )
+ clif_mercenary_updatestatus(md->master, SP_MATK1);
+ if( b_status.hit != status->hit )
+ clif_mercenary_updatestatus(md->master, SP_HIT);
+ if( b_status.cri != status->cri )
+ clif_mercenary_updatestatus(md->master, SP_CRITICAL);
+ if( b_status.def != status->def )
+ clif_mercenary_updatestatus(md->master, SP_DEF1);
+ if( b_status.mdef != status->mdef )
+ clif_mercenary_updatestatus(md->master, SP_MDEF1);
+ if( b_status.flee != status->flee )
+ clif_mercenary_updatestatus(md->master, SP_MERCFLEE);
+ if( b_status.amotion != status->amotion )
+ clif_mercenary_updatestatus(md->master, SP_ASPD);
+ if( b_status.max_hp != status->max_hp )
+ clif_mercenary_updatestatus(md->master, SP_MAXHP);
+ if( b_status.max_sp != status->max_sp )
+ clif_mercenary_updatestatus(md->master, SP_MAXSP);
+ if( b_status.hp != status->hp )
+ clif_mercenary_updatestatus(md->master, SP_HP);
+ if( b_status.sp != status->sp )
+ clif_mercenary_updatestatus(md->master, SP_SP);
+ } else if( bl->type == BL_ELEM ) {
+ TBL_ELEM* ed = BL_CAST(BL_ELEM, bl);
+ if( b_status.max_hp != status->max_hp )
+ clif_elemental_updatestatus(ed->master, SP_MAXHP);
+ if( b_status.max_sp != status->max_sp )
+ clif_elemental_updatestatus(ed->master, SP_MAXSP);
+ if( b_status.hp != status->hp )
+ clif_elemental_updatestatus(ed->master, SP_HP);
+ if( b_status.sp != status->sp )
+ clif_mercenary_updatestatus(ed->master, SP_SP);
+ }
+}
+
+/*==========================================
+ * Apply shared stat mods from status changes [DracoRPG]
+ *------------------------------------------*/
+static unsigned short status_calc_str(struct block_list *bl, struct status_change *sc, int str)
+{
+ if(!sc || !sc->count)
+ return cap_value(str,0,USHRT_MAX);
+
+ if(sc->data[SC_HARMONIZE]) {
+ str -= sc->data[SC_HARMONIZE]->val2;
+ return (unsigned short)cap_value(str,0,USHRT_MAX);
+ }
+ if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && str < 50)
+ return 50;
+ if(sc->data[SC_INCALLSTATUS])
+ str += sc->data[SC_INCALLSTATUS]->val1;
+ if(sc->data[SC_INCSTR])
+ str += sc->data[SC_INCSTR]->val1;
+ if(sc->data[SC_STRFOOD])
+ str += sc->data[SC_STRFOOD]->val1;
+ if(sc->data[SC_FOOD_STR_CASH])
+ str += sc->data[SC_FOOD_STR_CASH]->val1;
+ if(sc->data[SC_BATTLEORDERS])
+ str += 5;
+ if(sc->data[SC_LEADERSHIP])
+ str += sc->data[SC_LEADERSHIP]->val1;
+ if(sc->data[SC_LOUD])
+ str += 4;
+ if(sc->data[SC_TRUESIGHT])
+ str += 5;
+ if(sc->data[SC_SPURT])
+ str += 10;
+ if(sc->data[SC_NEN])
+ str += sc->data[SC_NEN]->val1;
+ if(sc->data[SC_BLESSING]){
+ if(sc->data[SC_BLESSING]->val2)
+ str += sc->data[SC_BLESSING]->val2;
+ else
+ str >>= 1;
+ }
+ if(sc->data[SC_MARIONETTE])
+ str -= ((sc->data[SC_MARIONETTE]->val3)>>16)&0xFF;
+ if(sc->data[SC_MARIONETTE2])
+ str += ((sc->data[SC_MARIONETTE2]->val3)>>16)&0xFF;
+ if(sc->data[SC_GIANTGROWTH])
+ str += 30;
+ if(sc->data[SC_SAVAGE_STEAK])
+ str += sc->data[SC_SAVAGE_STEAK]->val1;
+ if(sc->data[SC_INSPIRATION])
+ str += sc->data[SC_INSPIRATION]->val3;
+ if(sc->data[SC_STOMACHACHE])
+ str -= sc->data[SC_STOMACHACHE]->val1;
+ if(sc->data[SC_KYOUGAKU])
+ str -= sc->data[SC_KYOUGAKU]->val2;
+
+ return (unsigned short)cap_value(str,0,USHRT_MAX);
+}
+
+static unsigned short status_calc_agi(struct block_list *bl, struct status_change *sc, int agi)
+{
+ if(!sc || !sc->count)
+ return cap_value(agi,0,USHRT_MAX);
+
+ if(sc->data[SC_HARMONIZE]) {
+ agi -= sc->data[SC_HARMONIZE]->val2;
+ return (unsigned short)cap_value(agi,0,USHRT_MAX);
+ }
+ if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && agi < 50)
+ return 50;
+ if(sc->data[SC_CONCENTRATE] && !sc->data[SC_QUAGMIRE])
+ agi += (agi-sc->data[SC_CONCENTRATE]->val3)*sc->data[SC_CONCENTRATE]->val2/100;
+ if(sc->data[SC_INCALLSTATUS])
+ agi += sc->data[SC_INCALLSTATUS]->val1;
+ if(sc->data[SC_INCAGI])
+ agi += sc->data[SC_INCAGI]->val1;
+ if(sc->data[SC_AGIFOOD])
+ agi += sc->data[SC_AGIFOOD]->val1;
+ if(sc->data[SC_FOOD_AGI_CASH])
+ agi += sc->data[SC_FOOD_AGI_CASH]->val1;
+ if(sc->data[SC_SOULCOLD])
+ agi += sc->data[SC_SOULCOLD]->val1;
+ if(sc->data[SC_TRUESIGHT])
+ agi += 5;
+ if(sc->data[SC_INCREASEAGI])
+ agi += sc->data[SC_INCREASEAGI]->val2;
+ if(sc->data[SC_INCREASING])
+ agi += 4; // added based on skill updates [Reddozen]
+ if(sc->data[SC_DECREASEAGI])
+ agi -= sc->data[SC_DECREASEAGI]->val2;
+ if(sc->data[SC_QUAGMIRE])
+ agi -= sc->data[SC_QUAGMIRE]->val2;
+ if(sc->data[SC_SUITON] && sc->data[SC_SUITON]->val3)
+ agi -= sc->data[SC_SUITON]->val2;
+ if(sc->data[SC_MARIONETTE])
+ agi -= ((sc->data[SC_MARIONETTE]->val3)>>8)&0xFF;
+ if(sc->data[SC_MARIONETTE2])
+ agi += ((sc->data[SC_MARIONETTE2]->val3)>>8)&0xFF;
+ if(sc->data[SC_ADORAMUS])
+ agi -= sc->data[SC_ADORAMUS]->val2;
+ if(sc->data[SC_DROCERA_HERB_STEAMED])
+ agi += sc->data[SC_DROCERA_HERB_STEAMED]->val1;
+ if(sc->data[SC_INSPIRATION])
+ agi += sc->data[SC_INSPIRATION]->val3;
+ if(sc->data[SC_STOMACHACHE])
+ agi -= sc->data[SC_STOMACHACHE]->val1;
+ if(sc->data[SC_KYOUGAKU])
+ agi -= sc->data[SC_KYOUGAKU]->val2;
+
+ return (unsigned short)cap_value(agi,0,USHRT_MAX);
+}
+
+static unsigned short status_calc_vit(struct block_list *bl, struct status_change *sc, int vit)
+{
+ if(!sc || !sc->count)
+ return cap_value(vit,0,USHRT_MAX);
+
+ if(sc->data[SC_HARMONIZE]) {
+ vit -= sc->data[SC_HARMONIZE]->val2;
+ return (unsigned short)cap_value(vit,0,USHRT_MAX);
+ }
+ if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && vit < 50)
+ return 50;
+ if(sc->data[SC_INCALLSTATUS])
+ vit += sc->data[SC_INCALLSTATUS]->val1;
+ if(sc->data[SC_INCVIT])
+ vit += sc->data[SC_INCVIT]->val1;
+ if(sc->data[SC_VITFOOD])
+ vit += sc->data[SC_VITFOOD]->val1;
+ if(sc->data[SC_FOOD_VIT_CASH])
+ vit += sc->data[SC_FOOD_VIT_CASH]->val1;
+ if(sc->data[SC_CHANGE])
+ vit += sc->data[SC_CHANGE]->val2;
+ if(sc->data[SC_GLORYWOUNDS])
+ vit += sc->data[SC_GLORYWOUNDS]->val1;
+ if(sc->data[SC_TRUESIGHT])
+ vit += 5;
+ if(sc->data[SC_MARIONETTE])
+ vit -= sc->data[SC_MARIONETTE]->val3&0xFF;
+ if(sc->data[SC_MARIONETTE2])
+ vit += sc->data[SC_MARIONETTE2]->val3&0xFF;
+ if(sc->data[SC_LAUDAAGNUS])
+ vit += 4 + sc->data[SC_LAUDAAGNUS]->val1;
+ if(sc->data[SC_MINOR_BBQ])
+ vit += sc->data[SC_MINOR_BBQ]->val1;
+ if(sc->data[SC_INSPIRATION])
+ vit += sc->data[SC_INSPIRATION]->val3;
+ if(sc->data[SC_STOMACHACHE])
+ vit -= sc->data[SC_STOMACHACHE]->val1;
+ if(sc->data[SC_KYOUGAKU])
+ vit -= sc->data[SC_KYOUGAKU]->val2;
+
+ if(sc->data[SC_STRIPARMOR])
+ vit -= vit * sc->data[SC_STRIPARMOR]->val2/100;
+
+ return (unsigned short)cap_value(vit,0,USHRT_MAX);
+}
+
+static unsigned short status_calc_int(struct block_list *bl, struct status_change *sc, int int_)
+{
+ if(!sc || !sc->count)
+ return cap_value(int_,0,USHRT_MAX);
+
+ if(sc->data[SC_HARMONIZE]) {
+ int_ -= sc->data[SC_HARMONIZE]->val2;
+ return (unsigned short)cap_value(int_,0,USHRT_MAX);
+ }
+ if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && int_ < 50)
+ return 50;
+ if(sc->data[SC_INCALLSTATUS])
+ int_ += sc->data[SC_INCALLSTATUS]->val1;
+ if(sc->data[SC_INCINT])
+ int_ += sc->data[SC_INCINT]->val1;
+ if(sc->data[SC_INTFOOD])
+ int_ += sc->data[SC_INTFOOD]->val1;
+ if(sc->data[SC_FOOD_INT_CASH])
+ int_ += sc->data[SC_FOOD_INT_CASH]->val1;
+ if(sc->data[SC_CHANGE])
+ int_ += sc->data[SC_CHANGE]->val3;
+ if(sc->data[SC_BATTLEORDERS])
+ int_ += 5;
+ if(sc->data[SC_TRUESIGHT])
+ int_ += 5;
+ if(sc->data[SC_BLESSING]){
+ if (sc->data[SC_BLESSING]->val2)
+ int_ += sc->data[SC_BLESSING]->val2;
+ else
+ int_ >>= 1;
+ }
+ if(sc->data[SC_NEN])
+ int_ += sc->data[SC_NEN]->val1;
+ if(sc->data[SC_MARIONETTE])
+ int_ -= ((sc->data[SC_MARIONETTE]->val4)>>16)&0xFF;
+ if(sc->data[SC_MARIONETTE2])
+ int_ += ((sc->data[SC_MARIONETTE2]->val4)>>16)&0xFF;
+ if(sc->data[SC_MANDRAGORA])
+ int_ -= 5 + 5 * sc->data[SC_MANDRAGORA]->val1;
+ if(sc->data[SC_COCKTAIL_WARG_BLOOD])
+ int_ += sc->data[SC_COCKTAIL_WARG_BLOOD]->val1;
+ if(sc->data[SC_INSPIRATION])
+ int_ += sc->data[SC_INSPIRATION]->val3;
+ if(sc->data[SC_STOMACHACHE])
+ int_ -= sc->data[SC_STOMACHACHE]->val1;
+ if(sc->data[SC_KYOUGAKU])
+ int_ -= sc->data[SC_KYOUGAKU]->val2;
+
+ if(sc->data[SC_STRIPHELM])
+ int_ -= int_ * sc->data[SC_STRIPHELM]->val2/100;
+ if(sc->data[SC__STRIPACCESSORY])
+ int_ -= int_ * sc->data[SC__STRIPACCESSORY]->val2 / 100;
+
+ return (unsigned short)cap_value(int_,0,USHRT_MAX);
+}
+
+static unsigned short status_calc_dex(struct block_list *bl, struct status_change *sc, int dex)
+{
+ if(!sc || !sc->count)
+ return cap_value(dex,0,USHRT_MAX);
+
+ if(sc->data[SC_HARMONIZE]) {
+ dex -= sc->data[SC_HARMONIZE]->val2;
+ return (unsigned short)cap_value(dex,0,USHRT_MAX);
+ }
+ if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && dex < 50)
+ return 50;
+ if(sc->data[SC_CONCENTRATE] && !sc->data[SC_QUAGMIRE])
+ dex += (dex-sc->data[SC_CONCENTRATE]->val4)*sc->data[SC_CONCENTRATE]->val2/100;
+ if(sc->data[SC_INCALLSTATUS])
+ dex += sc->data[SC_INCALLSTATUS]->val1;
+ if(sc->data[SC_INCDEX])
+ dex += sc->data[SC_INCDEX]->val1;
+ if(sc->data[SC_DEXFOOD])
+ dex += sc->data[SC_DEXFOOD]->val1;
+ if(sc->data[SC_FOOD_DEX_CASH])
+ dex += sc->data[SC_FOOD_DEX_CASH]->val1;
+ if(sc->data[SC_BATTLEORDERS])
+ dex += 5;
+ if(sc->data[SC_HAWKEYES])
+ dex += sc->data[SC_HAWKEYES]->val1;
+ if(sc->data[SC_TRUESIGHT])
+ dex += 5;
+ if(sc->data[SC_QUAGMIRE])
+ dex -= sc->data[SC_QUAGMIRE]->val2;
+ if(sc->data[SC_BLESSING]){
+ if (sc->data[SC_BLESSING]->val2)
+ dex += sc->data[SC_BLESSING]->val2;
+ else
+ dex >>= 1;
+ }
+ if(sc->data[SC_INCREASING])
+ dex += 4; // added based on skill updates [Reddozen]
+ if(sc->data[SC_MARIONETTE])
+ dex -= ((sc->data[SC_MARIONETTE]->val4)>>8)&0xFF;
+ if(sc->data[SC_MARIONETTE2])
+ dex += ((sc->data[SC_MARIONETTE2]->val4)>>8)&0xFF;
+ if(sc->data[SC_SIROMA_ICE_TEA])
+ dex += sc->data[SC_SIROMA_ICE_TEA]->val1;
+ if(sc->data[SC_INSPIRATION])
+ dex += sc->data[SC_INSPIRATION]->val3;
+ if(sc->data[SC_STOMACHACHE])
+ dex -= sc->data[SC_STOMACHACHE]->val1;
+ if(sc->data[SC_KYOUGAKU])
+ dex -= sc->data[SC_KYOUGAKU]->val2;
+
+ if(sc->data[SC__STRIPACCESSORY])
+ dex -= dex * sc->data[SC__STRIPACCESSORY]->val2 / 100;
+
+ return (unsigned short)cap_value(dex,0,USHRT_MAX);
+}
+
+static unsigned short status_calc_luk(struct block_list *bl, struct status_change *sc, int luk)
+{
+ if(!sc || !sc->count)
+ return cap_value(luk,0,USHRT_MAX);
+
+ if(sc->data[SC_HARMONIZE]) {
+ luk -= sc->data[SC_HARMONIZE]->val2;
+ return (unsigned short)cap_value(luk,0,USHRT_MAX);
+ }
+ if(sc->data[SC_CURSE])
+ return 0;
+ if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH && luk < 50)
+ return 50;
+ if(sc->data[SC_INCALLSTATUS])
+ luk += sc->data[SC_INCALLSTATUS]->val1;
+ if(sc->data[SC_INCLUK])
+ luk += sc->data[SC_INCLUK]->val1;
+ if(sc->data[SC_LUKFOOD])
+ luk += sc->data[SC_LUKFOOD]->val1;
+ if(sc->data[SC_FOOD_LUK_CASH])
+ luk += sc->data[SC_FOOD_LUK_CASH]->val1;
+ if(sc->data[SC_TRUESIGHT])
+ luk += 5;
+ if(sc->data[SC_GLORIA])
+ luk += 30;
+ if(sc->data[SC_MARIONETTE])
+ luk -= sc->data[SC_MARIONETTE]->val4&0xFF;
+ if(sc->data[SC_MARIONETTE2])
+ luk += sc->data[SC_MARIONETTE2]->val4&0xFF;
+ if(sc->data[SC_PUTTI_TAILS_NOODLES])
+ luk += sc->data[SC_PUTTI_TAILS_NOODLES]->val1;
+ if(sc->data[SC_INSPIRATION])
+ luk += sc->data[SC_INSPIRATION]->val3;
+ if(sc->data[SC_STOMACHACHE])
+ luk -= sc->data[SC_STOMACHACHE]->val1;
+ if(sc->data[SC_KYOUGAKU])
+ luk -= sc->data[SC_KYOUGAKU]->val2;
+ if(sc->data[SC_LAUDARAMUS])
+ luk += 4 + sc->data[SC_LAUDARAMUS]->val1;
+
+ if(sc->data[SC__STRIPACCESSORY])
+ luk -= luk * sc->data[SC__STRIPACCESSORY]->val2 / 100;
+ if(sc->data[SC_BANANA_BOMB])
+ luk -= luk * sc->data[SC_BANANA_BOMB]->val1 / 100;
+
+ return (unsigned short)cap_value(luk,0,USHRT_MAX);
+}
+
+static unsigned short status_calc_batk(struct block_list *bl, struct status_change *sc, int batk)
+{
+ if(!sc || !sc->count)
+ return cap_value(batk,0,USHRT_MAX);
+
+ if(sc->data[SC_ATKPOTION])
+ batk += sc->data[SC_ATKPOTION]->val1;
+ if(sc->data[SC_BATKFOOD])
+ batk += sc->data[SC_BATKFOOD]->val1;
+ if(sc->data[SC_GATLINGFEVER])
+ batk += sc->data[SC_GATLINGFEVER]->val3;
+ if(sc->data[SC_MADNESSCANCEL])
+ batk += 100;
+ if(sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2)
+ batk += 50;
+ if(bl->type == BL_ELEM
+ && ((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 1)
+ || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 1)
+ || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 1)
+ || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 1))
+ )
+ batk += batk / 5;
+ if(sc->data[SC_FULL_SWING_K])
+ batk += sc->data[SC_FULL_SWING_K]->val1;
+ if(sc->data[SC_ODINS_POWER])
+ batk += 70;
+ if(sc->data[SC_ASH] && (bl->type==BL_MOB)){
+ if(status_get_element(bl) == ELE_WATER) //water type
+ batk /= 2;
+ }
+ if(sc->data[SC_PYROCLASTIC])
+ batk += sc->data[SC_PYROCLASTIC]->val2;
+ if (sc->data[SC_ANGRIFFS_MODUS])
+ batk += sc->data[SC_ANGRIFFS_MODUS]->val2;
+
+ if(sc->data[SC_INCATKRATE])
+ batk += batk * sc->data[SC_INCATKRATE]->val1/100;
+ if(sc->data[SC_PROVOKE])
+ batk += batk * sc->data[SC_PROVOKE]->val3/100;
+ if(sc->data[SC_CONCENTRATION])
+ batk += batk * sc->data[SC_CONCENTRATION]->val2/100;
+ if(sc->data[SC_SKE])
+ batk += batk * 3;
+ if(sc->data[SC_BLOODLUST])
+ batk += batk * sc->data[SC_BLOODLUST]->val2/100;
+ if(sc->data[SC_JOINTBEAT] && sc->data[SC_JOINTBEAT]->val2&BREAK_WAIST)
+ batk -= batk * 25/100;
+ if(sc->data[SC_CURSE])
+ batk -= batk * 25/100;
+//Curse shouldn't effect on this? <- Curse OR Bleeding??
+// if(sc->data[SC_BLEEDING])
+// batk -= batk * 25/100;
+ if(sc->data[SC_FLEET])
+ batk += batk * sc->data[SC_FLEET]->val3/100;
+ if(sc->data[SC__ENERVATION])
+ batk -= batk * sc->data[SC__ENERVATION]->val2 / 100;
+ if(sc->data[SC_RUSHWINDMILL])
+ batk += batk * sc->data[SC_RUSHWINDMILL]->val2/100;
+ if(sc->data[SC_SATURDAYNIGHTFEVER])
+ batk += 100 * sc->data[SC_SATURDAYNIGHTFEVER]->val1;
+ if(sc->data[SC_MELODYOFSINK])
+ batk -= batk * sc->data[SC_MELODYOFSINK]->val3/100;
+ if(sc->data[SC_BEYONDOFWARCRY])
+ batk += batk * sc->data[SC_BEYONDOFWARCRY]->val3/100;
+ if( sc->data[SC_ZANGETSU] )
+ batk += batk * sc->data[SC_ZANGETSU]->val2 / 100;
+
+ return (unsigned short)cap_value(batk,0,USHRT_MAX);
+}
+
+static unsigned short status_calc_watk(struct block_list *bl, struct status_change *sc, int watk)
+{
+ if(!sc || !sc->count)
+ return cap_value(watk,0,USHRT_MAX);
+
+ if(sc->data[SC_IMPOSITIO])
+ watk += sc->data[SC_IMPOSITIO]->val2;
+ if(sc->data[SC_WATKFOOD])
+ watk += sc->data[SC_WATKFOOD]->val1;
+ if(sc->data[SC_DRUMBATTLE])
+ watk += sc->data[SC_DRUMBATTLE]->val2;
+ if(sc->data[SC_VOLCANO])
+ watk += sc->data[SC_VOLCANO]->val2;
+ if(sc->data[SC_MERC_ATKUP])
+ watk += sc->data[SC_MERC_ATKUP]->val2;
+ if(sc->data[SC_FIGHTINGSPIRIT])
+ watk += sc->data[SC_FIGHTINGSPIRIT]->val1;
+ if(sc->data[SC_STRIKING])
+ watk += sc->data[SC_STRIKING]->val2;
+ if(sc->data[SC_SHIELDSPELL_DEF] && sc->data[SC_SHIELDSPELL_DEF]->val1 == 3)
+ watk += sc->data[SC_SHIELDSPELL_DEF]->val2;
+ if(sc->data[SC_INSPIRATION])
+ watk += sc->data[SC_INSPIRATION]->val2;
+ if( sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 0 )
+ watk += (10 + 10 * sc->data[SC_BANDING]->val1) * (sc->data[SC_BANDING]->val2);
+ if( sc->data[SC_TROPIC_OPTION] )
+ watk += sc->data[SC_TROPIC_OPTION]->val2;
+ if( sc->data[SC_HEATER_OPTION] )
+ watk += sc->data[SC_HEATER_OPTION]->val2;
+ if( sc->data[SC_WATER_BARRIER] )
+ watk -= sc->data[SC_WATER_BARRIER]->val3;
+ if( sc->data[SC_PYROTECHNIC_OPTION] )
+ watk += sc->data[SC_PYROTECHNIC_OPTION]->val2;
+ if(sc->data[SC_NIBELUNGEN]) {
+ if (bl->type != BL_PC)
+ watk += sc->data[SC_NIBELUNGEN]->val2;
+ else {
+ #ifndef RENEWAL
+ TBL_PC *sd = (TBL_PC*)bl;
+ int index = sd->equip_index[sd->state.lr_flag?EQI_HAND_L:EQI_HAND_R];
+ if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 4)
+ #endif
+ watk += sc->data[SC_NIBELUNGEN]->val2;
+ }
+ }
+
+ if(sc->data[SC_INCATKRATE])
+ watk += watk * sc->data[SC_INCATKRATE]->val1/100;
+ if(sc->data[SC_PROVOKE])
+ watk += watk * sc->data[SC_PROVOKE]->val3/100;
+ if(sc->data[SC_CONCENTRATION])
+ watk += watk * sc->data[SC_CONCENTRATION]->val2/100;
+ if(sc->data[SC_SKE])
+ watk += watk * 3;
+ if(sc->data[SC__ENERVATION])
+ watk -= watk * sc->data[SC__ENERVATION]->val2 / 100;
+ if(sc->data[SC_FLEET])
+ watk += watk * sc->data[SC_FLEET]->val3/100;
+ if(sc->data[SC_CURSE])
+ watk -= watk * 25/100;
+ if(sc->data[SC_STRIPWEAPON])
+ watk -= watk * sc->data[SC_STRIPWEAPON]->val2/100;
+ if(sc->data[SC__ENERVATION])
+ watk -= watk * sc->data[SC__ENERVATION]->val2 / 100;
+ if((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2)
+ || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2)
+ || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 2)
+ || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2)
+ )
+ watk += watk / 10;
+ if( sc && sc->data[SC_TIDAL_WEAPON] )
+ watk += watk * sc->data[SC_TIDAL_WEAPON]->val2 / 100;
+ if(sc->data[SC_ANGRIFFS_MODUS])
+ watk += watk * sc->data[SC_ANGRIFFS_MODUS]->val2/100;
+#ifdef RENEWAL_EDP
+ if( sc->data[SC_EDP] )
+ watk = watk * (100 + sc->data[SC_EDP]->val1 * 80) / 100;
+#endif
+
+ return (unsigned short)cap_value(watk,0,USHRT_MAX);
+}
+#ifdef RENEWAL
+static unsigned short status_calc_ematk(struct block_list *bl, struct status_change *sc, int matk)
+{
+
+ if (!sc || !sc->count)
+ return cap_value(matk,0,USHRT_MAX);
+ if (sc->data[SC_MATKPOTION])
+ matk += sc->data[SC_MATKPOTION]->val1;
+ if (sc->data[SC_MATKFOOD])
+ matk += sc->data[SC_MATKFOOD]->val1;
+ if(sc->data[SC_MANA_PLUS])
+ matk += sc->data[SC_MANA_PLUS]->val1;
+ if(sc->data[SC_AQUAPLAY_OPTION])
+ matk += sc->data[SC_AQUAPLAY_OPTION]->val2;
+ if(sc->data[SC_CHILLY_AIR_OPTION])
+ matk += sc->data[SC_CHILLY_AIR_OPTION]->val2;
+ if(sc->data[SC_WATER_BARRIER])
+ matk -= sc->data[SC_WATER_BARRIER]->val3;
+ if(sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3)
+ matk += 50;
+ if(sc->data[SC_ODINS_POWER])
+ matk += 40 + 30 * sc->data[SC_ODINS_POWER]->val1; //70 lvl1, 100lvl2
+ if(sc->data[SC_IZAYOI])
+ matk += 50 * sc->data[SC_IZAYOI]->val1;
+ return (unsigned short)cap_value(matk,0,USHRT_MAX);
+}
+#endif
+static unsigned short status_calc_matk(struct block_list *bl, struct status_change *sc, int matk)
+{
+ if(!sc || !sc->count)
+ return cap_value(matk,0,USHRT_MAX);
+#ifndef RENEWAL
+ // take note fixed value first before % modifiers
+ if (sc->data[SC_MATKPOTION])
+ matk += sc->data[SC_MATKPOTION]->val1;
+ if (sc->data[SC_MATKFOOD])
+ matk += sc->data[SC_MATKFOOD]->val1;
+ if (sc->data[SC_MANA_PLUS])
+ matk += sc->data[SC_MANA_PLUS]->val1;
+ if (sc->data[SC_AQUAPLAY_OPTION])
+ matk += sc->data[SC_AQUAPLAY_OPTION]->val2;
+ if (sc->data[SC_CHILLY_AIR_OPTION])
+ matk += sc->data[SC_CHILLY_AIR_OPTION]->val2;
+ if (sc->data[SC_WATER_BARRIER])
+ matk -= sc->data[SC_WATER_BARRIER]->val3;
+ if (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3)
+ matk += 50;
+ if (sc->data[SC_ODINS_POWER])
+ matk += 40 + 30 * sc->data[SC_ODINS_POWER]->val1; //70 lvl1, 100lvl2
+ if (sc->data[SC_IZAYOI])
+ matk += 50 * sc->data[SC_IZAYOI]->val1;
+#endif
+ if (sc->data[SC_MAGICPOWER])
+ matk += matk * sc->data[SC_MAGICPOWER]->val3/100;
+ if (sc->data[SC_MINDBREAKER])
+ matk += matk * sc->data[SC_MINDBREAKER]->val2/100;
+ if (sc->data[SC_INCMATKRATE])
+ matk += matk * sc->data[SC_INCMATKRATE]->val1/100;
+ if (sc->data[SC_MOONLITSERENADE])
+ matk += matk * sc->data[SC_MOONLITSERENADE]->val2/100;
+ if (sc->data[SC_MELODYOFSINK])
+ matk += matk * sc->data[SC_MELODYOFSINK]->val3/100;
+ if (sc->data[SC_BEYONDOFWARCRY])
+ matk -= matk * sc->data[SC_BEYONDOFWARCRY]->val3/100;
+ if( sc->data[SC_ZANGETSU] )
+ matk += matk * sc->data[SC_ZANGETSU]->val2 / 100;
+
+ return (unsigned short)cap_value(matk,0,USHRT_MAX);
+}
+
+static signed short status_calc_critical(struct block_list *bl, struct status_change *sc, int critical) {
+
+ if(!sc || !sc->count)
+ return cap_value(critical,10,SHRT_MAX);
+
+ if (sc->data[SC_INCCRI])
+ critical += sc->data[SC_INCCRI]->val2;
+ if (sc->data[SC_EXPLOSIONSPIRITS])
+ critical += sc->data[SC_EXPLOSIONSPIRITS]->val2;
+ if (sc->data[SC_FORTUNE])
+ critical += sc->data[SC_FORTUNE]->val2;
+ if (sc->data[SC_TRUESIGHT])
+ critical += sc->data[SC_TRUESIGHT]->val2;
+ if(sc->data[SC_CLOAKING])
+ critical += critical;
+ if(sc->data[SC_STRIKING])
+ critical += sc->data[SC_STRIKING]->val1;
+#ifdef RENEWAL
+ if (sc->data[SC_SPEARQUICKEN])
+ critical += 3*sc->data[SC_SPEARQUICKEN]->val1*10;
+#endif
+
+ if(sc->data[SC__INVISIBILITY])
+ critical += critical * sc->data[SC__INVISIBILITY]->val3 / 100;
+ if(sc->data[SC__UNLUCKY])
+ critical -= critical * sc->data[SC__UNLUCKY]->val2 / 100;
+
+ return (short)cap_value(critical,10,SHRT_MAX);
+}
+
+static signed short status_calc_hit(struct block_list *bl, struct status_change *sc, int hit)
+{
+
+ if(!sc || !sc->count)
+ return cap_value(hit,1,SHRT_MAX);
+
+ if(sc->data[SC_INCHIT])
+ hit += sc->data[SC_INCHIT]->val1;
+ if(sc->data[SC_HITFOOD])
+ hit += sc->data[SC_HITFOOD]->val1;
+ if(sc->data[SC_TRUESIGHT])
+ hit += sc->data[SC_TRUESIGHT]->val3;
+ if(sc->data[SC_HUMMING])
+ hit += sc->data[SC_HUMMING]->val2;
+ if(sc->data[SC_CONCENTRATION])
+ hit += sc->data[SC_CONCENTRATION]->val3;
+ if(sc->data[SC_INSPIRATION])
+ hit += 5 * sc->data[SC_INSPIRATION]->val1;
+ if(sc->data[SC_ADJUSTMENT])
+ hit -= 30;
+ if(sc->data[SC_INCREASING])
+ hit += 20; // RockmanEXE; changed based on updated [Reddozen]
+ if(sc->data[SC_MERC_HITUP])
+ hit += sc->data[SC_MERC_HITUP]->val2;
+
+ if(sc->data[SC_INCHITRATE])
+ hit += hit * sc->data[SC_INCHITRATE]->val1/100;
+ if(sc->data[SC_BLIND])
+ hit -= hit * 25/100;
+ if(sc->data[SC__GROOMY])
+ hit -= hit * sc->data[SC__GROOMY]->val3 / 100;
+ if(sc->data[SC_FEAR])
+ hit -= hit * 20 / 100;
+ if (sc->data[SC_ASH])
+ hit /= 2;
+
+ return (short)cap_value(hit,1,SHRT_MAX);
+}
+
+static signed short status_calc_flee(struct block_list *bl, struct status_change *sc, int flee)
+{
+ if( bl->type == BL_PC )
+ {
+ if( map_flag_gvg(bl->m) )
+ flee -= flee * battle_config.gvg_flee_penalty/100;
+ else if( map[bl->m].flag.battleground )
+ flee -= flee * battle_config.bg_flee_penalty/100;
+ }
+
+ if(!sc || !sc->count)
+ return cap_value(flee,1,SHRT_MAX);
+
+ if(sc->data[SC_INCFLEE])
+ flee += sc->data[SC_INCFLEE]->val1;
+ if(sc->data[SC_FLEEFOOD])
+ flee += sc->data[SC_FLEEFOOD]->val1;
+ if(sc->data[SC_WHISTLE])
+ flee += sc->data[SC_WHISTLE]->val2;
+ if(sc->data[SC_WINDWALK])
+ flee += sc->data[SC_WINDWALK]->val2;
+ if(sc->data[SC_VIOLENTGALE])
+ flee += sc->data[SC_VIOLENTGALE]->val2;
+ if(sc->data[SC_MOON_COMFORT]) //SG skill [Komurka]
+ flee += sc->data[SC_MOON_COMFORT]->val2;
+ if(sc->data[SC_CLOSECONFINE])
+ flee += 10;
+ if (sc->data[SC_ANGRIFFS_MODUS])
+ flee -= sc->data[SC_ANGRIFFS_MODUS]->val3;
+ if (sc->data[SC_OVERED_BOOST])
+ flee = max(flee,sc->data[SC_OVERED_BOOST]->val2);
+ if(sc->data[SC_ADJUSTMENT])
+ flee += 30;
+ if(sc->data[SC_SPEED])
+ flee += 10 + sc->data[SC_SPEED]->val1 * 10;
+ if(sc->data[SC_GATLINGFEVER])
+ flee -= sc->data[SC_GATLINGFEVER]->val4;
+ if(sc->data[SC_PARTYFLEE])
+ flee += sc->data[SC_PARTYFLEE]->val1 * 10;
+ if(sc->data[SC_MERC_FLEEUP])
+ flee += sc->data[SC_MERC_FLEEUP]->val2;
+ if( sc->data[SC_HALLUCINATIONWALK] )
+ flee += sc->data[SC_HALLUCINATIONWALK]->val2;
+ if( sc->data[SC_WATER_BARRIER] )
+ flee -= sc->data[SC_WATER_BARRIER]->val3;
+ if( sc->data[SC_MARSHOFABYSS] )
+ flee -= (9 * sc->data[SC_MARSHOFABYSS]->val3 / 10 + sc->data[SC_MARSHOFABYSS]->val2 / 10) * (bl->type == BL_MOB ? 2 : 1);
+#ifdef RENEWAL
+ if( sc->data[SC_SPEARQUICKEN] )
+ flee += 2 * sc->data[SC_SPEARQUICKEN]->val1;
+#endif
+
+ if(sc->data[SC_INCFLEERATE])
+ flee += flee * sc->data[SC_INCFLEERATE]->val1/100;
+ if(sc->data[SC_SPIDERWEB] && sc->data[SC_SPIDERWEB]->val1)
+ flee -= flee * 50/100;
+ if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+ flee -= flee * 50/100;
+ if(sc->data[SC_BLIND])
+ flee -= flee * 25/100;
+ if(sc->data[SC_FEAR])
+ flee -= flee * 20 / 100;
+ if(sc->data[SC_PARALYSE])
+ flee -= flee * 10 / 100; // 10% Flee reduction
+ if(sc->data[SC_INFRAREDSCAN])
+ flee -= flee * 30 / 100;
+ if( sc->data[SC__LAZINESS] )
+ flee -= flee * sc->data[SC__LAZINESS]->val3 / 100;
+ if( sc->data[SC_GLOOMYDAY] )
+ flee -= flee * sc->data[SC_GLOOMYDAY]->val2 / 100;
+ if( sc->data[SC_SATURDAYNIGHTFEVER] )
+ flee -= flee * (40 + 10 * sc->data[SC_SATURDAYNIGHTFEVER]->val1) / 100;
+ if( sc->data[SC_WIND_STEP_OPTION] )
+ flee += flee * sc->data[SC_WIND_STEP_OPTION]->val2 / 100;
+ if( sc->data[SC_ZEPHYR] )
+ flee += flee * sc->data[SC_ZEPHYR]->val2 / 100;
+ if(sc->data[SC_ASH] && (bl->type==BL_MOB)){ //mob
+ if(status_get_element(bl) == ELE_WATER) //water type
+ flee /= 2;
+ }
+
+ return (short)cap_value(flee,1,SHRT_MAX);
+}
+
+static signed short status_calc_flee2(struct block_list *bl, struct status_change *sc, int flee2)
+{
+ if(!sc || !sc->count)
+ return cap_value(flee2,10,SHRT_MAX);
+
+ if(sc->data[SC_INCFLEE2])
+ flee2 += sc->data[SC_INCFLEE2]->val2;
+ if(sc->data[SC_WHISTLE])
+ flee2 += sc->data[SC_WHISTLE]->val3*10;
+ if(sc->data[SC__UNLUCKY])
+ flee2 -= flee2 * sc->data[SC__UNLUCKY]->val2 / 100;
+
+ return (short)cap_value(flee2,10,SHRT_MAX);
+}
+static defType status_calc_def(struct block_list *bl, struct status_change *sc, int def) {
+
+ if(!sc || !sc->count)
+ return (defType)cap_value(def,DEFTYPE_MIN,DEFTYPE_MAX);
+
+ if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+ return 0;
+ if(sc->data[SC_SKA])
+ return sc->data[SC_SKA]->val3;
+ if(sc->data[SC_BARRIER])
+ return 100;
+ if(sc->data[SC_KEEPING])
+ return 90;
+#ifndef RENEWAL // does not provide 90 DEF in renewal mode
+ if(sc->data[SC_STEELBODY])
+ return 90;
+#endif
+
+ if(sc->data[SC_ARMORCHANGE])
+ def += sc->data[SC_ARMORCHANGE]->val2;
+ if(sc->data[SC_DRUMBATTLE])
+ def += sc->data[SC_DRUMBATTLE]->val3;
+ if(sc->data[SC_DEFENCE]) //[orn]
+ def += sc->data[SC_DEFENCE]->val2 ;
+ if(sc->data[SC_INCDEFRATE])
+ def += def * sc->data[SC_INCDEFRATE]->val1/100;
+ if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2)
+ def += 50;
+ if(sc->data[SC_ODINS_POWER])
+ def -= 20;
+ if( sc->data[SC_ANGRIFFS_MODUS] )
+ def -= 30 + 20 * sc->data[SC_ANGRIFFS_MODUS]->val1;
+ if(sc->data[SC_STONEHARDSKIN])// Final DEF increase divided by 10 since were using classic (pre-renewal) mechanics. [Rytech]
+ def += sc->data[SC_STONEHARDSKIN]->val1;
+ if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
+ def >>=1;
+ if(sc->data[SC_FREEZE])
+ def >>=1;
+ if(sc->data[SC_SIGNUMCRUCIS])
+ def -= def * sc->data[SC_SIGNUMCRUCIS]->val2/100;
+ if(sc->data[SC_CONCENTRATION])
+ def -= def * sc->data[SC_CONCENTRATION]->val4/100;
+ if(sc->data[SC_SKE])
+ def >>=1;
+ if(sc->data[SC_PROVOKE] && bl->type != BL_PC) // Provoke doesn't alter player defense->
+ def -= def * sc->data[SC_PROVOKE]->val4/100;
+ if(sc->data[SC_STRIPSHIELD])
+ def -= def * sc->data[SC_STRIPSHIELD]->val2/100;
+ if (sc->data[SC_FLING])
+ def -= def * (sc->data[SC_FLING]->val2)/100;
+ if( sc->data[SC_FREEZING] )
+ def -= def * 10 / 100;
+ if( sc->data[SC_MARSHOFABYSS] )
+ def -= def * ( 6 + 6 * sc->data[SC_MARSHOFABYSS]->val3/10 + (bl->type == BL_MOB ? 5 : 3) * sc->data[SC_MARSHOFABYSS]->val2/36 ) / 100;
+ if( sc->data[SC_ANALYZE] )
+ def -= def * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100;
+ if( sc->data[SC_FORCEOFVANGUARD] )
+ def += def * 2 * sc->data[SC_FORCEOFVANGUARD]->val1 / 100;
+ if(sc->data[SC_SATURDAYNIGHTFEVER])
+ def -= def * (10 + 10 * sc->data[SC_SATURDAYNIGHTFEVER]->val1) / 100;
+ if(sc->data[SC_EARTHDRIVE])
+ def -= def * 25 / 100;
+ if( sc->data[SC_ROCK_CRUSHER] )
+ def -= def * sc->data[SC_ROCK_CRUSHER]->val2 / 100;
+ if( sc->data[SC_POWER_OF_GAIA] )
+ def += def * sc->data[SC_POWER_OF_GAIA]->val2 / 100;
+ if( sc->data[SC_PRESTIGE] )
+ def += def * sc->data[SC_PRESTIGE]->val1 / 100;
+ if(sc->data[SC_ASH] && (bl->type==BL_MOB)){
+ if(status_get_race(bl)==RC_PLANT)
+ def /= 2;
+ }
+
+ return (defType)cap_value(def,DEFTYPE_MIN,DEFTYPE_MAX);;
+}
+
+static signed short status_calc_def2(struct block_list *bl, struct status_change *sc, int def2)
+{
+ if(!sc || !sc->count)
+#ifdef RENEWAL
+ return (short)cap_value(def2,SHRT_MIN,SHRT_MAX);
+#else
+ return (short)cap_value(def2,1,SHRT_MAX);
+#endif
+
+ if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+ return 0;
+ if(sc->data[SC_ETERNALCHAOS])
+ return 0;
+ if(sc->data[SC_SUN_COMFORT])
+ def2 += sc->data[SC_SUN_COMFORT]->val2;
+ if( sc->data[SC_SHIELDSPELL_REF] && sc->data[SC_SHIELDSPELL_REF]->val1 == 1 )
+ def2 += sc->data[SC_SHIELDSPELL_REF]->val2;
+ if( sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 0 )
+ def2 += (5 + sc->data[SC_BANDING]->val1) * (sc->data[SC_BANDING]->val2);
+
+ if(sc->data[SC_ANGELUS])
+#ifdef RENEWAL //in renewal only the VIT stat bonus is boosted by angelus
+ def2 += status_get_vit(bl) / 2 * sc->data[SC_ANGELUS]->val2/100;
+#else
+ def2 += def2 * sc->data[SC_ANGELUS]->val2/100;
+#endif
+ if(sc->data[SC_CONCENTRATION])
+ def2 -= def2 * sc->data[SC_CONCENTRATION]->val4/100;
+ if(sc->data[SC_POISON])
+ def2 -= def2 * 25/100;
+ if(sc->data[SC_DPOISON])
+ def2 -= def2 * 25/100;
+ if(sc->data[SC_SKE])
+ def2 -= def2 * 50/100;
+ if(sc->data[SC_PROVOKE])
+ def2 -= def2 * sc->data[SC_PROVOKE]->val4/100;
+ if(sc->data[SC_JOINTBEAT])
+ def2 -= def2 * ( sc->data[SC_JOINTBEAT]->val2&BREAK_SHOULDER ? 50 : 0 ) / 100
+ + def2 * ( sc->data[SC_JOINTBEAT]->val2&BREAK_WAIST ? 25 : 0 ) / 100;
+ if(sc->data[SC_FLING])
+ def2 -= def2 * (sc->data[SC_FLING]->val3)/100;
+ if( sc->data[SC_FREEZING] )
+ def2 -= def2 * 3 / 10;
+ if(sc->data[SC_ANALYZE])
+ def2 -= def2 * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100;
+ if( sc->data[SC_ECHOSONG] )
+ def2 += def2 * sc->data[SC_ECHOSONG]->val2/100;
+ if(sc->data[SC_ASH] && (bl->type==BL_MOB)){
+ if(status_get_race(bl)==RC_PLANT)
+ def2 /= 2;
+ }
+ if (sc->data[SC_PARALYSIS])
+ def2 -= def2 * sc->data[SC_PARALYSIS]->val2 / 100;
+
+#ifdef RENEWAL
+ return (short)cap_value(def2,SHRT_MIN,SHRT_MAX);
+#else
+ return (short)cap_value(def2,1,SHRT_MAX);
+#endif
+}
+
+
+static defType status_calc_mdef(struct block_list *bl, struct status_change *sc, int mdef) {
+
+ if(!sc || !sc->count)
+ return (defType)cap_value(mdef,DEFTYPE_MIN,DEFTYPE_MAX);
+
+ if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+ return 0;
+ if(sc->data[SC_BARRIER])
+ return 100;
+
+#ifndef RENEWAL // no longer provides 90 MDEF in renewal mode
+ if(sc->data[SC_STEELBODY])
+ return 90;
+#endif
+
+ if(sc->data[SC_ARMORCHANGE])
+ mdef += sc->data[SC_ARMORCHANGE]->val3;
+ if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3)
+ mdef += 50;
+ if(sc->data[SC_ENDURE])// It has been confirmed that eddga card grants 1 MDEF, not 0, not 10, but 1.
+ mdef += (sc->data[SC_ENDURE]->val4 == 0) ? sc->data[SC_ENDURE]->val1 : 1;
+ if(sc->data[SC_CONCENTRATION])
+ mdef += 1; //Skill info says it adds a fixed 1 Mdef point.
+ if(sc->data[SC_STONEHARDSKIN])// Final MDEF increase divided by 10 since were using classic (pre-renewal) mechanics. [Rytech]
+ mdef += sc->data[SC_STONEHARDSKIN]->val1;
+ if(sc->data[SC_WATER_BARRIER])
+ mdef += sc->data[SC_WATER_BARRIER]->val2;
+ if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
+ mdef += 25*mdef/100;
+ if(sc->data[SC_FREEZE])
+ mdef += 25*mdef/100;
+ if( sc->data[SC_MARSHOFABYSS] )
+ mdef -= mdef * ( 6 + 6 * sc->data[SC_MARSHOFABYSS]->val3/10 + (bl->type == BL_MOB ? 5 : 3) * sc->data[SC_MARSHOFABYSS]->val2/36 ) / 100;
+ if(sc->data[SC_ANALYZE])
+ mdef -= mdef * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100;
+ if(sc->data[SC_SYMPHONYOFLOVER])
+ mdef += mdef * sc->data[SC_SYMPHONYOFLOVER]->val2 / 100;
+ if(sc->data[SC_GT_CHANGE] && sc->data[SC_GT_CHANGE]->val4)
+ mdef -= mdef * sc->data[SC_GT_CHANGE]->val4 / 100;
+ if (sc->data[SC_ODINS_POWER])
+ mdef -= 20 * sc->data[SC_ODINS_POWER]->val1;
+
+ return (defType)cap_value(mdef,DEFTYPE_MIN,DEFTYPE_MAX);
+}
+
+static signed short status_calc_mdef2(struct block_list *bl, struct status_change *sc, int mdef2)
+{
+ if(!sc || !sc->count)
+#ifdef RENEWAL
+ return (short)cap_value(mdef2,SHRT_MIN,SHRT_MAX);
+#else
+ return (short)cap_value(mdef2,1,SHRT_MAX);
+#endif
+
+
+ if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+ return 0;
+ if(sc->data[SC_SKA])
+ return 90;
+ if(sc->data[SC_MINDBREAKER])
+ mdef2 -= mdef2 * sc->data[SC_MINDBREAKER]->val3/100;
+ if(sc->data[SC_ANALYZE])
+ mdef2 -= mdef2 * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100;
+
+#ifdef RENEWAL
+ return (short)cap_value(mdef2,SHRT_MIN,SHRT_MAX);
+#else
+ return (short)cap_value(mdef2,1,SHRT_MAX);
+#endif
+}
+
+static unsigned short status_calc_speed(struct block_list *bl, struct status_change *sc, int speed)
+{
+ TBL_PC* sd = BL_CAST(BL_PC, bl);
+ int speed_rate;
+
+ if( sc == NULL )
+ return cap_value(speed,10,USHRT_MAX);
+
+ if( sd && sd->ud.skilltimer != INVALID_TIMER && (pc_checkskill(sd,SA_FREECAST) > 0 || sd->ud.skill_id == LG_EXEEDBREAK) )
+ {
+ if( sd->ud.skill_id == LG_EXEEDBREAK )
+ speed_rate = 100 + 60 - (sd->ud.skill_lv * 10);
+ else
+ speed_rate = 175 - 5 * pc_checkskill(sd,SA_FREECAST);
+ }
+ else
+ {
+ speed_rate = 100;
+
+ //GetMoveHasteValue2()
+ {
+ int val = 0;
+
+ if( sc->data[SC_FUSION] )
+ val = 25;
+ else if( sd ) {
+ if( pc_isriding(sd) || sd->sc.option&(OPTION_DRAGON|OPTION_MOUNTING) )
+ val = 25;//Same bonus
+ else if( pc_isridingwug(sd) )
+ val = 15 + 5 * pc_checkskill(sd, RA_WUGRIDER);
+ else if( pc_ismadogear(sd) ) {
+ val = (- 10 * (5 - pc_checkskill(sd,NC_MADOLICENCE)));
+ if( sc->data[SC_ACCELERATION] )
+ val += 25;
+ }
+ }
+
+ speed_rate -= val;
+ }
+
+ //GetMoveSlowValue()
+ {
+ int val = 0;
+
+ if( sd && sc->data[SC_HIDING] && pc_checkskill(sd,RG_TUNNELDRIVE) > 0 )
+ val = 120 - 6 * pc_checkskill(sd,RG_TUNNELDRIVE);
+ else
+ if( sd && sc->data[SC_CHASEWALK] && sc->data[SC_CHASEWALK]->val3 < 0 )
+ val = sc->data[SC_CHASEWALK]->val3;
+ else
+ {
+ // Longing for Freedom cancels song/dance penalty
+ if( sc->data[SC_LONGING] )
+ val = max( val, 50 - 10 * sc->data[SC_LONGING]->val1 );
+ else
+ if( sd && sc->data[SC_DANCING] )
+ val = max( val, 500 - (40 + 10 * (sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_BARDDANCER)) * pc_checkskill(sd,(sd->status.sex?BA_MUSICALLESSON:DC_DANCINGLESSON)) );
+
+ if( sc->data[SC_DECREASEAGI] )
+ val = max( val, 25 );
+ if( sc->data[SC_QUAGMIRE] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY] || (sc->data[SC_GLOOMYDAY] && sc->data[SC_GLOOMYDAY]->val4) )
+ val = max( val, 50 );
+ if( sc->data[SC_DONTFORGETME] )
+ val = max( val, sc->data[SC_DONTFORGETME]->val3 );
+ if( sc->data[SC_CURSE] )
+ val = max( val, 300 );
+ if( sc->data[SC_CHASEWALK] )
+ val = max( val, sc->data[SC_CHASEWALK]->val3 );
+ if( sc->data[SC_WEDDING] )
+ val = max( val, 100 );
+ if( sc->data[SC_JOINTBEAT] && sc->data[SC_JOINTBEAT]->val2&(BREAK_ANKLE|BREAK_KNEE) )
+ val = max( val, (sc->data[SC_JOINTBEAT]->val2&BREAK_ANKLE ? 50 : 0) + (sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE ? 30 : 0) );
+ if( sc->data[SC_CLOAKING] && (sc->data[SC_CLOAKING]->val4&1) == 0 )
+ val = max( val, sc->data[SC_CLOAKING]->val1 < 3 ? 300 : 30 - 3 * sc->data[SC_CLOAKING]->val1 );
+ if( sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY )
+ val = max( val, 75 );
+ if( sc->data[SC_SLOWDOWN] ) // Slow Potion
+ val = max( val, 100 );
+ if( sc->data[SC_GATLINGFEVER] )
+ val = max( val, 100 );
+ if( sc->data[SC_SUITON] )
+ val = max( val, sc->data[SC_SUITON]->val3 );
+ if( sc->data[SC_SWOO] )
+ val = max( val, 300 );
+ if( sc->data[SC_FREEZING] )
+ val = max( val, 70 );
+ if( sc->data[SC_MARSHOFABYSS] )
+ val = max( val, 40 + 10 * sc->data[SC_MARSHOFABYSS]->val1 );
+ if( sc->data[SC_CAMOUFLAGE] && (sc->data[SC_CAMOUFLAGE]->val3&1) == 0 )
+ val = max( val, sc->data[SC_CAMOUFLAGE]->val1 < 3 ? 0 : 25 * (5 - sc->data[SC_CAMOUFLAGE]->val1) );
+ if( sc->data[SC__GROOMY] )
+ val = max( val, sc->data[SC__GROOMY]->val2);
+ if( sc->data[SC_STEALTHFIELD_MASTER] )
+ val = max( val, 30 );
+ if( sc->data[SC_BANDING_DEFENCE] )
+ val = max( val, sc->data[SC_BANDING_DEFENCE]->val1 );//+90% walking speed.
+ if( sc->data[SC_ROCK_CRUSHER_ATK] )
+ val = max( val, sc->data[SC_ROCK_CRUSHER_ATK]->val2 );
+ if( sc->data[SC_POWER_OF_GAIA] )
+ val = max( val, sc->data[SC_POWER_OF_GAIA]->val2 );
+ if( sc->data[SC_MELON_BOMB] )
+ val = max( val, sc->data[SC_MELON_BOMB]->val1 );
+
+ if( sd && sd->bonus.speed_rate + sd->bonus.speed_add_rate > 0 ) // permanent item-based speedup
+ val = max( val, sd->bonus.speed_rate + sd->bonus.speed_add_rate );
+ }
+
+ speed_rate += val;
+ }
+
+ //GetMoveHasteValue1()
+ {
+ int val = 0;
+
+ if( sc->data[SC_SPEEDUP1] ) //FIXME: used both by NPC_AGIUP and Speed Potion script
+ val = max( val, 50 );
+ if( sc->data[SC_INCREASEAGI] )
+ val = max( val, 25 );
+ if( sc->data[SC_WINDWALK] )
+ val = max( val, 2 * sc->data[SC_WINDWALK]->val1 );
+ if( sc->data[SC_CARTBOOST] )
+ val = max( val, 20 );
+ if( sd && (sd->class_&MAPID_UPPERMASK) == MAPID_ASSASSIN && pc_checkskill(sd,TF_MISS) > 0 )
+ val = max( val, 1 * pc_checkskill(sd,TF_MISS) );
+ if( sc->data[SC_CLOAKING] && (sc->data[SC_CLOAKING]->val4&1) == 1 )
+ val = max( val, sc->data[SC_CLOAKING]->val1 >= 10 ? 25 : 3 * sc->data[SC_CLOAKING]->val1 - 3 );
+ if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+ val = max( val, 25 );
+ if( sc->data[SC_RUN] )
+ val = max( val, 55 );
+ if( sc->data[SC_AVOID] )
+ val = max( val, 10 * sc->data[SC_AVOID]->val1 );
+ if( sc->data[SC_INVINCIBLE] && !sc->data[SC_INVINCIBLEOFF] )
+ val = max( val, 75 );
+ if( sc->data[SC_CLOAKINGEXCEED] )
+ val = max( val, sc->data[SC_CLOAKINGEXCEED]->val3);
+ if( sc->data[SC_HOVERING] )
+ val = max( val, 10 );
+ if( sc->data[SC_GN_CARTBOOST] )
+ val = max( val, sc->data[SC_GN_CARTBOOST]->val2 );
+ if( sc->data[SC_SWINGDANCE] )
+ val = max( val, sc->data[SC_SWINGDANCE]->val2 );
+ if( sc->data[SC_WIND_STEP_OPTION] )
+ val = max( val, sc->data[SC_WIND_STEP_OPTION]->val2 );
+
+ //FIXME: official items use a single bonus for this [ultramage]
+ if( sc->data[SC_SPEEDUP0] ) // temporary item-based speedup
+ val = max( val, 25 );
+ if( sd && sd->bonus.speed_rate + sd->bonus.speed_add_rate < 0 ) // permanent item-based speedup
+ val = max( val, -(sd->bonus.speed_rate + sd->bonus.speed_add_rate) );
+
+ speed_rate -= val;
+ }
+
+ if( speed_rate < 40 )
+ speed_rate = 40;
+ }
+
+ //GetSpeed()
+ {
+ if( sd && pc_iscarton(sd) )
+ speed += speed * (50 - 5 * pc_checkskill(sd,MC_PUSHCART)) / 100;
+ if( sc->data[SC_PARALYSE] )
+ speed += speed * 50 / 100;
+ if( speed_rate != 100 )
+ speed = speed * speed_rate / 100;
+ if( sc->data[SC_STEELBODY] )
+ speed = 200;
+ if( sc->data[SC_DEFENDER] )
+ speed = max(speed, 200);
+ if( sc->data[SC_WALKSPEED] && sc->data[SC_WALKSPEED]->val1 > 0 ) // ChangeSpeed
+ speed = speed * 100 / sc->data[SC_WALKSPEED]->val1;
+ }
+
+ return (short)cap_value(speed,10,USHRT_MAX);
+}
+
+#ifdef RENEWAL_ASPD
+// flag&1 - fixed value [malufett]
+// flag&2 - percentage value
+static short status_calc_aspd(struct block_list *bl, struct status_change *sc, short flag)
+{
+ int i, pots = 0, skills1 = 0, skills2 = 0;
+
+ if(!sc || !sc->count)
+ return 0;
+
+ if(sc->data[i=SC_ASPDPOTION3] ||
+ sc->data[i=SC_ASPDPOTION2] ||
+ sc->data[i=SC_ASPDPOTION1] ||
+ sc->data[i=SC_ASPDPOTION0])
+ pots += sc->data[i]->val1;
+
+ if( !sc->data[SC_QUAGMIRE] ){
+ if(sc->data[SC_STAR_COMFORT])
+ skills1 = 5; // needs more info
+
+ if(sc->data[SC_TWOHANDQUICKEN] && skills1 < 7)
+ skills1 = 7;
+
+ if(sc->data[SC_ONEHAND] && skills1 < 7) skills1 = 7;
+
+ if(sc->data[SC_MERC_QUICKEN] && skills1 < 7) // needs more info
+ skills1 = 7;
+
+ if(sc->data[SC_ADRENALINE2] && skills1 < 6)
+ skills1 = 6;
+
+ if(sc->data[SC_ADRENALINE] && skills1 < 7)
+ skills1 = 7;
+
+ if(sc->data[SC_SPEARQUICKEN] && skills1 < 7)
+ skills1 = 7;
+
+ if(sc->data[SC_GATLINGFEVER] && skills1 < 9) // needs more info
+ skills1 = 9;
+
+ if(sc->data[SC_FLEET] && skills1 < 5)
+ skills1 = 5;
+
+ if(sc->data[SC_ASSNCROS] &&
+ skills1 < 5+1*sc->data[SC_ASSNCROS]->val1) // needs more info
+ {
+ if (bl->type!=BL_PC)
+ skills1 = 4+1*sc->data[SC_ASSNCROS]->val1;
+ else
+ switch(((TBL_PC*)bl)->status.weapon)
+ {
+ case W_BOW:
+ case W_REVOLVER:
+ case W_RIFLE:
+ case W_GATLING:
+ case W_SHOTGUN:
+ case W_GRENADE:
+ break;
+ default:
+ skills1 = 5+1*sc->data[SC_ASSNCROS]->val1;
+ }
+ }
+ }
+
+ if((sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]) && skills1 < 15)
+ skills1 = 15;
+ else if(sc->data[SC_MADNESSCANCEL] && skills1 < 15) // needs more info
+ skills1 = 15;
+
+ if(sc->data[SC_DONTFORGETME])
+ skills2 -= sc->data[SC_DONTFORGETME]->val2; // needs more info
+ if(sc->data[SC_LONGING])
+ skills2 -= sc->data[SC_LONGING]->val2; // needs more info
+ if(sc->data[SC_STEELBODY])
+ skills2 -= 25;
+ if(sc->data[SC_SKA])
+ skills2 -= 25;
+ if(sc->data[SC_DEFENDER])
+ skills2 -= sc->data[SC_DEFENDER]->val4; // needs more info
+ if(sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY) // needs more info
+ skills2 -= 25;
+ if(sc->data[SC_GRAVITATION])
+ skills2 -= sc->data[SC_GRAVITATION]->val2; // needs more info
+ if(sc->data[SC_JOINTBEAT]) { // needs more info
+ if( sc->data[SC_JOINTBEAT]->val2&BREAK_WRIST )
+ skills2 -= 25;
+ if( sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE )
+ skills2 -= 10;
+ }
+ if( sc->data[SC_FREEZING] )
+ skills2 -= 30;
+ if( sc->data[SC_HALLUCINATIONWALK_POSTDELAY] )
+ skills2 -= 50;
+ if( sc->data[SC_PARALYSE] )
+ skills2 -= 10;
+ if( sc->data[SC__BODYPAINT] )
+ skills2 -= 2 + 5 * sc->data[SC__BODYPAINT]->val1;
+ if( sc->data[SC__INVISIBILITY] )
+ skills2 -= sc->data[SC__INVISIBILITY]->val2 ;
+ if( sc->data[SC__GROOMY] )
+ skills2 -= sc->data[SC__GROOMY]->val2;
+ if( sc->data[SC_SWINGDANCE] )
+ skills2 += sc->data[SC_SWINGDANCE]->val2;
+ if( sc->data[SC_DANCEWITHWUG] )
+ skills2 += sc->data[SC_DANCEWITHWUG]->val3;
+ if( sc->data[SC_GLOOMYDAY] )
+ skills2 -= sc->data[SC_GLOOMYDAY]->val3;
+ if( sc->data[SC_EARTHDRIVE] )
+ skills2 -= 25;
+ if( sc->data[SC_GT_CHANGE] )
+ skills2 += sc->data[SC_GT_CHANGE]->val3;
+ if( sc->data[SC_MELON_BOMB] )
+ skills2 -= sc->data[SC_MELON_BOMB]->val1;
+ if( sc->data[SC_BOOST500] )
+ skills2 += sc->data[SC_BOOST500]->val1;
+ if( sc->data[SC_EXTRACT_SALAMINE_JUICE] )
+ skills2 += sc->data[SC_EXTRACT_SALAMINE_JUICE]->val1;
+ if( sc->data[SC_INCASPDRATE] )
+ skills2 += sc->data[SC_INCASPDRATE]->val1;
+
+ return ( flag&1? (skills1 + pots) : skills2 );
+}
+#endif
+
+static short status_calc_fix_aspd(struct block_list *bl, struct status_change *sc, int aspd) {
+ if (!sc || !sc->count)
+ return cap_value(aspd, 0, 2000);
+
+ if (!sc->data[SC_QUAGMIRE]) {
+ if (sc->data[SC_OVERED_BOOST])
+ aspd = 2000 - sc->data[SC_OVERED_BOOST]->val3*10;
+ }
+
+ if ((sc->data[SC_GUST_OPTION] || sc->data[SC_BLAST_OPTION]
+ || sc->data[SC_WILD_STORM_OPTION]))
+ aspd -= 50; // +5 ASPD
+ if( sc && sc->data[SC_FIGHTINGSPIRIT] && sc->data[SC_FIGHTINGSPIRIT]->val2 )
+ aspd -= (bl->type==BL_PC?pc_checkskill((TBL_PC *)bl, RK_RUNEMASTERY):10) / 10 * 40;
+
+ return cap_value(aspd, 0, 2000); // will be recap for proper bl anyway
+}
+
+/// Calculates an object's ASPD modifier (alters the base amotion value).
+/// Note that the scale of aspd_rate is 1000 = 100%.
+static short status_calc_aspd_rate(struct block_list *bl, struct status_change *sc, int aspd_rate)
+{
+ int i;
+
+ if(!sc || !sc->count)
+ return cap_value(aspd_rate,0,SHRT_MAX);
+
+ if( !sc->data[SC_QUAGMIRE] ){
+ int max = 0;
+ if(sc->data[SC_STAR_COMFORT])
+ max = sc->data[SC_STAR_COMFORT]->val2;
+
+ if(sc->data[SC_TWOHANDQUICKEN] &&
+ max < sc->data[SC_TWOHANDQUICKEN]->val2)
+ max = sc->data[SC_TWOHANDQUICKEN]->val2;
+
+ if(sc->data[SC_ONEHAND] &&
+ max < sc->data[SC_ONEHAND]->val2)
+ max = sc->data[SC_ONEHAND]->val2;
+
+ if(sc->data[SC_MERC_QUICKEN] &&
+ max < sc->data[SC_MERC_QUICKEN]->val2)
+ max = sc->data[SC_MERC_QUICKEN]->val2;
+
+ if(sc->data[SC_ADRENALINE2] &&
+ max < sc->data[SC_ADRENALINE2]->val3)
+ max = sc->data[SC_ADRENALINE2]->val3;
+
+ if(sc->data[SC_ADRENALINE] &&
+ max < sc->data[SC_ADRENALINE]->val3)
+ max = sc->data[SC_ADRENALINE]->val3;
+
+ if(sc->data[SC_SPEARQUICKEN] &&
+ max < sc->data[SC_SPEARQUICKEN]->val2)
+ max = sc->data[SC_SPEARQUICKEN]->val2;
+
+ if(sc->data[SC_GATLINGFEVER] &&
+ max < sc->data[SC_GATLINGFEVER]->val2)
+ max = sc->data[SC_GATLINGFEVER]->val2;
+
+ if(sc->data[SC_FLEET] &&
+ max < sc->data[SC_FLEET]->val2)
+ max = sc->data[SC_FLEET]->val2;
+
+ if(sc->data[SC_ASSNCROS] &&
+ max < sc->data[SC_ASSNCROS]->val2)
+ {
+ if (bl->type!=BL_PC)
+ max = sc->data[SC_ASSNCROS]->val2;
+ else
+ switch(((TBL_PC*)bl)->status.weapon)
+ {
+ case W_BOW:
+ case W_REVOLVER:
+ case W_RIFLE:
+ case W_GATLING:
+ case W_SHOTGUN:
+ case W_GRENADE:
+ break;
+ default:
+ max = sc->data[SC_ASSNCROS]->val2;
+ }
+ }
+ aspd_rate -= max;
+
+ if((sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]))
+ aspd_rate -= 300;
+ else if(sc->data[SC_MADNESSCANCEL])
+ aspd_rate -= 200;
+ }
+
+ if( sc->data[i=SC_ASPDPOTION3] ||
+ sc->data[i=SC_ASPDPOTION2] ||
+ sc->data[i=SC_ASPDPOTION1] ||
+ sc->data[i=SC_ASPDPOTION0] )
+ aspd_rate -= sc->data[i]->val2;
+
+ if(sc->data[SC_DONTFORGETME])
+ aspd_rate += 10 * sc->data[SC_DONTFORGETME]->val2;
+ if(sc->data[SC_LONGING])
+ aspd_rate += sc->data[SC_LONGING]->val2;
+ if(sc->data[SC_STEELBODY])
+ aspd_rate += 250;
+ if(sc->data[SC_SKA])
+ aspd_rate += 250;
+ if(sc->data[SC_DEFENDER])
+ aspd_rate += sc->data[SC_DEFENDER]->val4;
+ if(sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY)
+ aspd_rate += 250;
+ if(sc->data[SC_GRAVITATION])
+ aspd_rate += sc->data[SC_GRAVITATION]->val2;
+ if(sc->data[SC_JOINTBEAT]) {
+ if( sc->data[SC_JOINTBEAT]->val2&BREAK_WRIST )
+ aspd_rate += 250;
+ if( sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE )
+ aspd_rate += 100;
+ }
+ if( sc->data[SC_FREEZING] )
+ aspd_rate += 300;
+ if( sc->data[SC_HALLUCINATIONWALK_POSTDELAY] )
+ aspd_rate += 500;
+ if( sc->data[SC_FIGHTINGSPIRIT] && sc->data[SC_FIGHTINGSPIRIT]->val2 )
+ aspd_rate -= sc->data[SC_FIGHTINGSPIRIT]->val2;
+ if( sc->data[SC_PARALYSE] )
+ aspd_rate += 100;
+ if( sc->data[SC__BODYPAINT] )
+ aspd_rate += 200 + 50 * sc->data[SC__BODYPAINT]->val1;
+ if( sc->data[SC__INVISIBILITY] )
+ aspd_rate += sc->data[SC__INVISIBILITY]->val2 * 10 ;
+ if( sc->data[SC__GROOMY] )
+ aspd_rate += sc->data[SC__GROOMY]->val2 * 10;
+ if( sc->data[SC_SWINGDANCE] )
+ aspd_rate -= sc->data[SC_SWINGDANCE]->val2 * 10;
+ if( sc->data[SC_DANCEWITHWUG] )
+ aspd_rate -= sc->data[SC_DANCEWITHWUG]->val3 * 10;
+ if( sc->data[SC_GLOOMYDAY] )
+ aspd_rate += sc->data[SC_GLOOMYDAY]->val3 * 10;
+ if( sc->data[SC_EARTHDRIVE] )
+ aspd_rate += 250;
+ if( sc->data[SC_GT_CHANGE] )
+ aspd_rate -= sc->data[SC_GT_CHANGE]->val3 * 10;
+ if( sc->data[SC_MELON_BOMB] )
+ aspd_rate += sc->data[SC_MELON_BOMB]->val1 * 10;
+ if( sc->data[SC_BOOST500] )
+ aspd_rate -= sc->data[SC_BOOST500]->val1 *10;
+ if( sc->data[SC_EXTRACT_SALAMINE_JUICE] )
+ aspd_rate -= sc->data[SC_EXTRACT_SALAMINE_JUICE]->val1 * 10;
+ if( sc->data[SC_INCASPDRATE] )
+ aspd_rate -= sc->data[SC_INCASPDRATE]->val1 * 10;
+ if( sc->data[SC_PAIN_KILLER])
+ aspd_rate += sc->data[SC_PAIN_KILLER]->val2 * 10;
+ if( sc->data[SC_GOLDENE_FERSE])
+ aspd_rate -= sc->data[SC_GOLDENE_FERSE]->val3 * 10;
+
+ return (short)cap_value(aspd_rate,0,SHRT_MAX);
+}
+
+static unsigned short status_calc_dmotion(struct block_list *bl, struct status_change *sc, int dmotion)
+{
+ if( !sc || !sc->count || map_flag_gvg(bl->m) || map[bl->m].flag.battleground )
+ return cap_value(dmotion,0,USHRT_MAX);
+ /**
+ * It has been confirmed on official servers that MvP mobs have no dmotion even without endure
+ **/
+ if( sc->data[SC_ENDURE] || ( bl->type == BL_MOB && (((TBL_MOB*)bl)->status.mode&MD_BOSS) ) )
+ return 0;
+ if( sc->data[SC_CONCENTRATION] )
+ return 0;
+ if( sc->data[SC_RUN] || sc->data[SC_WUGDASH] )
+ return 0;
+
+ return (unsigned short)cap_value(dmotion,0,USHRT_MAX);
+}
+
+static unsigned int status_calc_maxhp(struct block_list *bl, struct status_change *sc, uint64 maxhp)
+{
+ if(!sc || !sc->count)
+ return (unsigned int)cap_value(maxhp,1,UINT_MAX);
+
+ if(sc->data[SC_INCMHPRATE])
+ maxhp += maxhp * sc->data[SC_INCMHPRATE]->val1/100;
+ if(sc->data[SC_INCMHP])
+ maxhp += (sc->data[SC_INCMHP]->val1);
+ if(sc->data[SC_APPLEIDUN])
+ maxhp += maxhp * sc->data[SC_APPLEIDUN]->val2/100;
+ if(sc->data[SC_DELUGE])
+ maxhp += maxhp * sc->data[SC_DELUGE]->val2/100;
+ if (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST])
+ maxhp += maxhp * 2;
+ if(sc->data[SC_MARIONETTE])
+ maxhp -= 1000;
+ if(sc->data[SC_SOLID_SKIN_OPTION])
+ maxhp += 2000;// Fix amount.
+ if(sc->data[SC_POWER_OF_GAIA])
+ maxhp += 3000;
+ if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2)
+ maxhp += 500;
+
+ if(sc->data[SC_MERC_HPUP])
+ maxhp += maxhp * sc->data[SC_MERC_HPUP]->val2/100;
+
+ if(sc->data[SC_EPICLESIS])
+ maxhp += maxhp * 5 * sc->data[SC_EPICLESIS]->val1 / 100;
+ if(sc->data[SC_VENOMBLEED])
+ maxhp -= maxhp * 15 / 100;
+ if(sc->data[SC__WEAKNESS])
+ maxhp -= maxhp * sc->data[SC__WEAKNESS]->val2 / 100;
+ if(sc->data[SC_LERADSDEW])
+ maxhp += maxhp * sc->data[SC_LERADSDEW]->val3 / 100;
+ if(sc->data[SC_FORCEOFVANGUARD])
+ maxhp += maxhp * 3 * sc->data[SC_FORCEOFVANGUARD]->val1 / 100;
+ if(sc->data[SC_INSPIRATION]) //Custom value.
+ maxhp += maxhp * 3 * sc->data[SC_INSPIRATION]->val1 / 100;
+ if(sc->data[SC_RAISINGDRAGON])
+ maxhp += maxhp * (2 + sc->data[SC_RAISINGDRAGON]->val1) / 100;
+ if(sc->data[SC_GT_CHANGE]) // Max HP decrease: [Skill Level x 4] %
+ maxhp -= maxhp * (4 * sc->data[SC_GT_CHANGE]->val1) / 100;
+ if(sc->data[SC_GT_REVITALIZE])// Max HP increase: [Skill Level x 2] %
+ maxhp += maxhp * (2 * sc->data[SC_GT_REVITALIZE]->val1) / 100;
+ if(sc->data[SC_MUSTLE_M])
+ maxhp += maxhp * sc->data[SC_MUSTLE_M]->val1/100;
+ if(sc->data[SC_MYSTERIOUS_POWDER])
+ maxhp -= sc->data[SC_MYSTERIOUS_POWDER]->val1 / 100;
+ if(sc->data[SC_PETROLOGY_OPTION])
+ maxhp += maxhp * sc->data[SC_PETROLOGY_OPTION]->val2 / 100;
+ if (sc->data[SC_ANGRIFFS_MODUS])
+ maxhp += maxhp * 5 * sc->data[SC_ANGRIFFS_MODUS]->val1 /100;
+ if (sc->data[SC_GOLDENE_FERSE])
+ maxhp += maxhp * sc->data[SC_GOLDENE_FERSE]->val2 / 100;
+
+ return (unsigned int)cap_value(maxhp,1,UINT_MAX);
+}
+
+static unsigned int status_calc_maxsp(struct block_list *bl, struct status_change *sc, unsigned int maxsp)
+{
+ if(!sc || !sc->count)
+ return cap_value(maxsp,1,UINT_MAX);
+
+ if(sc->data[SC_INCMSPRATE])
+ maxsp += maxsp * sc->data[SC_INCMSPRATE]->val1/100;
+ if(sc->data[SC_INCMSP])
+ maxsp += (sc->data[SC_INCMSP]->val1);
+ if(sc->data[SC_SERVICE4U])
+ maxsp += maxsp * sc->data[SC_SERVICE4U]->val2/100;
+ if(sc->data[SC_MERC_SPUP])
+ maxsp += maxsp * sc->data[SC_MERC_SPUP]->val2/100;
+ if(sc->data[SC_RAISINGDRAGON])
+ maxsp += maxsp * (2 + sc->data[SC_RAISINGDRAGON]->val1) / 100;
+ if(sc->data[SC_LIFE_FORCE_F])
+ maxsp += maxsp * sc->data[SC_LIFE_FORCE_F]->val1/100;
+ if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3)
+ maxsp += 50;
+
+ return cap_value(maxsp,1,UINT_MAX);
+}
+
+static unsigned char status_calc_element(struct block_list *bl, struct status_change *sc, int element)
+{
+ if(!sc || !sc->count)
+ return element;
+
+ if(sc->data[SC_FREEZE])
+ return ELE_WATER;
+ if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
+ return ELE_EARTH;
+ if(sc->data[SC_BENEDICTIO])
+ return ELE_HOLY;
+ if(sc->data[SC_CHANGEUNDEAD])
+ return ELE_UNDEAD;
+ if(sc->data[SC_ELEMENTALCHANGE])
+ return sc->data[SC_ELEMENTALCHANGE]->val2;
+ if(sc->data[SC_SHAPESHIFT])
+ return sc->data[SC_SHAPESHIFT]->val2;
+
+ return (unsigned char)cap_value(element,0,UCHAR_MAX);
+}
+
+static unsigned char status_calc_element_lv(struct block_list *bl, struct status_change *sc, int lv)
+{
+ if(!sc || !sc->count)
+ return lv;
+
+ if(sc->data[SC_FREEZE])
+ return 1;
+ if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
+ return 1;
+ if(sc->data[SC_BENEDICTIO])
+ return 1;
+ if(sc->data[SC_CHANGEUNDEAD])
+ return 1;
+ if(sc->data[SC_ELEMENTALCHANGE])
+ return sc->data[SC_ELEMENTALCHANGE]->val1;
+ if(sc->data[SC_SHAPESHIFT])
+ return 1;
+ if(sc->data[SC__INVISIBILITY])
+ return 1;
+
+ return (unsigned char)cap_value(lv,1,4);
+}
+
+
+unsigned char status_calc_attack_element(struct block_list *bl, struct status_change *sc, int element)
+{
+ if(!sc || !sc->count)
+ return element;
+ if(sc->data[SC_ENCHANTARMS])
+ return sc->data[SC_ENCHANTARMS]->val2;
+ if(sc->data[SC_WATERWEAPON]
+ || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2) )
+ return ELE_WATER;
+ if(sc->data[SC_EARTHWEAPON]
+ || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2) )
+ return ELE_EARTH;
+ if(sc->data[SC_FIREWEAPON]
+ || (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2) )
+ return ELE_FIRE;
+ if(sc->data[SC_WINDWEAPON]
+ || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 2) )
+ return ELE_WIND;
+ if(sc->data[SC_ENCPOISON])
+ return ELE_POISON;
+ if(sc->data[SC_ASPERSIO])
+ return ELE_HOLY;
+ if(sc->data[SC_SHADOWWEAPON])
+ return ELE_DARK;
+ if(sc->data[SC_GHOSTWEAPON] || sc->data[SC__INVISIBILITY])
+ return ELE_GHOST;
+ if(sc->data[SC_TIDAL_WEAPON_OPTION] || sc->data[SC_TIDAL_WEAPON] )
+ return ELE_WATER;
+ if(sc->data[SC_PYROCLASTIC])
+ return ELE_FIRE;
+ return (unsigned char)cap_value(element,0,UCHAR_MAX);
+}
+
+static unsigned short status_calc_mode(struct block_list *bl, struct status_change *sc, int mode)
+{
+ if(!sc || !sc->count)
+ return mode;
+ if(sc->data[SC_MODECHANGE]) {
+ if (sc->data[SC_MODECHANGE]->val2)
+ mode = sc->data[SC_MODECHANGE]->val2; //Set mode
+ if (sc->data[SC_MODECHANGE]->val3)
+ mode|= sc->data[SC_MODECHANGE]->val3; //Add mode
+ if (sc->data[SC_MODECHANGE]->val4)
+ mode&=~sc->data[SC_MODECHANGE]->val4; //Del mode
+ }
+ return cap_value(mode,0,USHRT_MAX);
+}
+
+const char* status_get_name(struct block_list *bl) {
+ nullpo_ret(bl);
+ switch (bl->type) {
+ case BL_PC: return ((TBL_PC *)bl)->fakename[0] != '\0' ? ((TBL_PC*)bl)->fakename : ((TBL_PC*)bl)->status.name;
+ case BL_MOB: return ((TBL_MOB*)bl)->name;
+ case BL_PET: return ((TBL_PET*)bl)->pet.name;
+ case BL_HOM: return ((TBL_HOM*)bl)->homunculus.name;
+ case BL_NPC: return ((TBL_NPC*)bl)->name;
+ }
+ return "Unknown";
+}
+
+/*==========================================
+ * Get the class of the current bl
+ * return
+ * 0 = fail
+ * class_id = success
+ *------------------------------------------*/
+int status_get_class(struct block_list *bl) {
+ nullpo_ret(bl);
+ switch( bl->type ) {
+ case BL_PC: return ((TBL_PC*)bl)->status.class_;
+ case BL_MOB: return ((TBL_MOB*)bl)->vd->class_; //Class used on all code should be the view class of the mob.
+ case BL_PET: return ((TBL_PET*)bl)->pet.class_;
+ case BL_HOM: return ((TBL_HOM*)bl)->homunculus.class_;
+ case BL_MER: return ((TBL_MER*)bl)->mercenary.class_;
+ case BL_NPC: return ((TBL_NPC*)bl)->class_;
+ case BL_ELEM: return ((TBL_ELEM*)bl)->elemental.class_;
+ }
+ return 0;
+}
+/*==========================================
+ * Get the base level of the current bl
+ * return
+ * 1 = fail
+ * level = success
+ *------------------------------------------*/
+int status_get_lv(struct block_list *bl) {
+ nullpo_ret(bl);
+ switch (bl->type) {
+ case BL_PC: return ((TBL_PC*)bl)->status.base_level;
+ case BL_MOB: return ((TBL_MOB*)bl)->level;
+ case BL_PET: return ((TBL_PET*)bl)->pet.level;
+ case BL_HOM: return ((TBL_HOM*)bl)->homunculus.level;
+ case BL_MER: return ((TBL_MER*)bl)->db->lv;
+ case BL_ELEM: return ((TBL_ELEM*)bl)->db->lv;
+ case BL_NPC: return ((TBL_NPC*)bl)->level;
+ }
+ return 1;
+}
+
+struct regen_data *status_get_regen_data(struct block_list *bl)
+{
+ nullpo_retr(NULL, bl);
+ switch (bl->type) {
+ case BL_PC: return &((TBL_PC*)bl)->regen;
+ case BL_HOM: return &((TBL_HOM*)bl)->regen;
+ case BL_MER: return &((TBL_MER*)bl)->regen;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->regen;
+ default:
+ return NULL;
+ }
+}
+
+struct status_data *status_get_status_data(struct block_list *bl)
+{
+ nullpo_retr(&dummy_status, bl);
+
+ switch (bl->type) {
+ case BL_PC: return &((TBL_PC*)bl)->battle_status;
+ case BL_MOB: return &((TBL_MOB*)bl)->status;
+ case BL_PET: return &((TBL_PET*)bl)->status;
+ case BL_HOM: return &((TBL_HOM*)bl)->battle_status;
+ case BL_MER: return &((TBL_MER*)bl)->battle_status;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->battle_status;
+ case BL_NPC: return &((TBL_NPC*)bl)->status;
+ default:
+ return &dummy_status;
+ }
+}
+
+struct status_data *status_get_base_status(struct block_list *bl)
+{
+ nullpo_retr(NULL, bl);
+ switch (bl->type) {
+ case BL_PC: return &((TBL_PC*)bl)->base_status;
+ case BL_MOB: return ((TBL_MOB*)bl)->base_status ? ((TBL_MOB*)bl)->base_status : &((TBL_MOB*)bl)->db->status;
+ case BL_PET: return &((TBL_PET*)bl)->db->status;
+ case BL_HOM: return &((TBL_HOM*)bl)->base_status;
+ case BL_MER: return &((TBL_MER*)bl)->base_status;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->base_status;
+ case BL_NPC: return &((TBL_NPC*)bl)->status;
+ default:
+ return NULL;
+ }
+}
+defType status_get_def(struct block_list *bl) {
+ struct unit_data *ud;
+ struct status_data *status = status_get_status_data(bl);
+ int def = status?status->def:0;
+ ud = unit_bl2ud(bl);
+ if (ud && ud->skilltimer != INVALID_TIMER)
+ def -= def * skill_get_castdef(ud->skill_id)/100;
+
+ return cap_value(def, DEFTYPE_MIN, DEFTYPE_MAX);
+}
+
+unsigned short status_get_speed(struct block_list *bl)
+{
+ return status_get_status_data(bl)->speed;
+}
+
+int status_get_party_id(struct block_list *bl) {
+ nullpo_ret(bl);
+ switch (bl->type) {
+ case BL_PC:
+ return ((TBL_PC*)bl)->status.party_id;
+ case BL_PET:
+ if (((TBL_PET*)bl)->msd)
+ return ((TBL_PET*)bl)->msd->status.party_id;
+ break;
+ case BL_MOB: {
+ struct mob_data *md=(TBL_MOB*)bl;
+ if( md->master_id > 0 ) {
+ struct map_session_data *msd;
+ if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
+ return msd->status.party_id;
+ return -md->master_id;
+ }
+ }
+ break;
+ case BL_HOM:
+ if (((TBL_HOM*)bl)->master)
+ return ((TBL_HOM*)bl)->master->status.party_id;
+ break;
+ case BL_MER:
+ if (((TBL_MER*)bl)->master)
+ return ((TBL_MER*)bl)->master->status.party_id;
+ break;
+ case BL_SKILL:
+ return ((TBL_SKILL*)bl)->group->party_id;
+ case BL_ELEM:
+ if (((TBL_ELEM*)bl)->master)
+ return ((TBL_ELEM*)bl)->master->status.party_id;
+ break;
+ }
+ return 0;
+}
+
+int status_get_guild_id(struct block_list *bl) {
+ nullpo_ret(bl);
+ switch (bl->type) {
+ case BL_PC:
+ return ((TBL_PC*)bl)->status.guild_id;
+ case BL_PET:
+ if (((TBL_PET*)bl)->msd)
+ return ((TBL_PET*)bl)->msd->status.guild_id;
+ break;
+ case BL_MOB: {
+ struct map_session_data *msd;
+ struct mob_data *md = (struct mob_data *)bl;
+ if (md->guardian_data) //Guardian's guild [Skotlex]
+ return md->guardian_data->guild_id;
+ if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
+ return msd->status.guild_id; //Alchemist's mobs [Skotlex]
+ }
+ break;
+ case BL_HOM:
+ if (((TBL_HOM*)bl)->master)
+ return ((TBL_HOM*)bl)->master->status.guild_id;
+ break;
+ case BL_MER:
+ if (((TBL_MER*)bl)->master)
+ return ((TBL_MER*)bl)->master->status.guild_id;
+ break;
+ case BL_NPC:
+ if (((TBL_NPC*)bl)->subtype == SCRIPT)
+ return ((TBL_NPC*)bl)->u.scr.guild_id;
+ break;
+ case BL_SKILL:
+ return ((TBL_SKILL*)bl)->group->guild_id;
+ case BL_ELEM:
+ if (((TBL_ELEM*)bl)->master)
+ return ((TBL_ELEM*)bl)->master->status.guild_id;
+ break;
+ }
+ return 0;
+}
+
+int status_get_emblem_id(struct block_list *bl) {
+ nullpo_ret(bl);
+ switch (bl->type) {
+ case BL_PC:
+ return ((TBL_PC*)bl)->guild_emblem_id;
+ case BL_PET:
+ if (((TBL_PET*)bl)->msd)
+ return ((TBL_PET*)bl)->msd->guild_emblem_id;
+ break;
+ case BL_MOB: {
+ struct map_session_data *msd;
+ struct mob_data *md = (struct mob_data *)bl;
+ if (md->guardian_data) //Guardian's guild [Skotlex]
+ return md->guardian_data->emblem_id;
+ if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
+ return msd->guild_emblem_id; //Alchemist's mobs [Skotlex]
+ }
+ break;
+ case BL_HOM:
+ if (((TBL_HOM*)bl)->master)
+ return ((TBL_HOM*)bl)->master->guild_emblem_id;
+ break;
+ case BL_MER:
+ if (((TBL_MER*)bl)->master)
+ return ((TBL_MER*)bl)->master->guild_emblem_id;
+ break;
+ case BL_NPC:
+ if (((TBL_NPC*)bl)->subtype == SCRIPT && ((TBL_NPC*)bl)->u.scr.guild_id > 0) {
+ struct guild *g = guild_search(((TBL_NPC*)bl)->u.scr.guild_id);
+ if (g)
+ return g->emblem_id;
+ }
+ break;
+ case BL_ELEM:
+ if (((TBL_ELEM*)bl)->master)
+ return ((TBL_ELEM*)bl)->master->guild_emblem_id;
+ break;
+ }
+ return 0;
+}
+
+int status_get_mexp(struct block_list *bl)
+{
+ nullpo_ret(bl);
+ if(bl->type==BL_MOB)
+ return ((struct mob_data *)bl)->db->mexp;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->mexp;
+ return 0;
+}
+int status_get_race2(struct block_list *bl)
+{
+ nullpo_ret(bl);
+ if(bl->type == BL_MOB)
+ return ((struct mob_data *)bl)->db->race2;
+ if(bl->type==BL_PET)
+ return ((struct pet_data *)bl)->db->race2;
+ return 0;
+}
+
+int status_isdead(struct block_list *bl)
+{
+ nullpo_ret(bl);
+ return status_get_status_data(bl)->hp == 0;
+}
+
+int status_isimmune(struct block_list *bl)
+{
+ struct status_change *sc =status_get_sc(bl);
+ if (sc && sc->data[SC_HERMODE])
+ return 100;
+
+ if (bl->type == BL_PC &&
+ ((TBL_PC*)bl)->special_state.no_magic_damage >= battle_config.gtb_sc_immunity)
+ return ((TBL_PC*)bl)->special_state.no_magic_damage;
+ return 0;
+}
+
+struct view_data* status_get_viewdata(struct block_list *bl)
+{
+ nullpo_retr(NULL, bl);
+ switch (bl->type) {
+ case BL_PC: return &((TBL_PC*)bl)->vd;
+ case BL_MOB: return ((TBL_MOB*)bl)->vd;
+ case BL_PET: return &((TBL_PET*)bl)->vd;
+ case BL_NPC: return ((TBL_NPC*)bl)->vd;
+ case BL_HOM: return ((TBL_HOM*)bl)->vd;
+ case BL_MER: return ((TBL_MER*)bl)->vd;
+ case BL_ELEM: return ((TBL_ELEM*)bl)->vd;
+ }
+ return NULL;
+}
+
+void status_set_viewdata(struct block_list *bl, int class_)
+{
+ struct view_data* vd;
+ nullpo_retv(bl);
+ if (mobdb_checkid(class_) || mob_is_clone(class_))
+ vd = mob_get_viewdata(class_);
+ else if (npcdb_checkid(class_) || (bl->type == BL_NPC && class_ == WARP_CLASS))
+ vd = npc_get_viewdata(class_);
+ else if (homdb_checkid(class_))
+ vd = merc_get_hom_viewdata(class_);
+ else if (merc_class(class_))
+ vd = merc_get_viewdata(class_);
+ else if (elemental_class(class_))
+ vd = elemental_get_viewdata(class_);
+ else
+ vd = NULL;
+
+ switch (bl->type) {
+ case BL_PC:
+ {
+ TBL_PC* sd = (TBL_PC*)bl;
+ if (pcdb_checkid(class_)) {
+ if (sd->sc.option&OPTION_WEDDING)
+ class_ = JOB_WEDDING;
+ else if (sd->sc.option&OPTION_SUMMER)
+ class_ = JOB_SUMMER;
+ else if (sd->sc.option&OPTION_XMAS)
+ class_ = JOB_XMAS;
+ else if (sd->sc.option&OPTION_RIDING) {
+ switch (class_) { //Adapt class to a Mounted one.
+ case JOB_KNIGHT:
+ class_ = JOB_KNIGHT2;
+ break;
+ case JOB_CRUSADER:
+ class_ = JOB_CRUSADER2;
+ break;
+ case JOB_LORD_KNIGHT:
+ class_ = JOB_LORD_KNIGHT2;
+ break;
+ case JOB_PALADIN:
+ class_ = JOB_PALADIN2;
+ break;
+ case JOB_BABY_KNIGHT:
+ class_ = JOB_BABY_KNIGHT2;
+ break;
+ case JOB_BABY_CRUSADER:
+ class_ = JOB_BABY_CRUSADER2;
+ break;
+ }
+ }
+ sd->vd.class_ = class_;
+ clif_get_weapon_view(sd, &sd->vd.weapon, &sd->vd.shield);
+ sd->vd.head_top = sd->status.head_top;
+ sd->vd.head_mid = sd->status.head_mid;
+ sd->vd.head_bottom = sd->status.head_bottom;
+ sd->vd.hair_style = cap_value(sd->status.hair,0,battle_config.max_hair_style);
+ sd->vd.hair_color = cap_value(sd->status.hair_color,0,battle_config.max_hair_color);
+ sd->vd.cloth_color = cap_value(sd->status.clothes_color,0,battle_config.max_cloth_color);
+ sd->vd.sex = sd->status.sex;
+ } else if (vd)
+ memcpy(&sd->vd, vd, sizeof(struct view_data));
+ else
+ ShowError("status_set_viewdata (PC): No view data for class %d\n", class_);
+ }
+ break;
+ case BL_MOB:
+ {
+ TBL_MOB* md = (TBL_MOB*)bl;
+ if (vd)
+ md->vd = vd;
+ else
+ ShowError("status_set_viewdata (MOB): No view data for class %d\n", class_);
+ }
+ break;
+ case BL_PET:
+ {
+ TBL_PET* pd = (TBL_PET*)bl;
+ if (vd) {
+ memcpy(&pd->vd, vd, sizeof(struct view_data));
+ if (!pcdb_checkid(vd->class_)) {
+ pd->vd.hair_style = battle_config.pet_hair_style;
+ if(pd->pet.equip) {
+ pd->vd.head_bottom = itemdb_viewid(pd->pet.equip);
+ if (!pd->vd.head_bottom)
+ pd->vd.head_bottom = pd->pet.equip;
+ }
+ }
+ } else
+ ShowError("status_set_viewdata (PET): No view data for class %d\n", class_);
+ }
+ break;
+ case BL_NPC:
+ {
+ TBL_NPC* nd = (TBL_NPC*)bl;
+ if (vd)
+ nd->vd = vd;
+ else
+ ShowError("status_set_viewdata (NPC): No view data for class %d\n", class_);
+ }
+ break;
+ case BL_HOM: //[blackhole89]
+ {
+ struct homun_data *hd = (struct homun_data*)bl;
+ if (vd)
+ hd->vd = vd;
+ else
+ ShowError("status_set_viewdata (HOMUNCULUS): No view data for class %d\n", class_);
+ }
+ break;
+ case BL_MER:
+ {
+ struct mercenary_data *md = (struct mercenary_data*)bl;
+ if (vd)
+ md->vd = vd;
+ else
+ ShowError("status_set_viewdata (MERCENARY): No view data for class %d\n", class_);
+ }
+ break;
+ case BL_ELEM:
+ {
+ struct elemental_data *ed = (struct elemental_data*)bl;
+ if (vd)
+ ed->vd = vd;
+ else
+ ShowError("status_set_viewdata (ELEMENTAL): No view data for class %d\n", class_);
+ }
+ break;
+ }
+ vd = status_get_viewdata(bl);
+ if (vd && vd->cloth_color && (
+ (vd->class_==JOB_WEDDING && battle_config.wedding_ignorepalette)
+ || (vd->class_==JOB_XMAS && battle_config.xmas_ignorepalette)
+ || (vd->class_==JOB_SUMMER && battle_config.summer_ignorepalette)
+ ))
+ vd->cloth_color = 0;
+}
+
+/// Returns the status_change data of bl or NULL if it doesn't exist.
+struct status_change *status_get_sc(struct block_list *bl) {
+ if( bl )
+ switch (bl->type) {
+ case BL_PC: return &((TBL_PC*)bl)->sc;
+ case BL_MOB: return &((TBL_MOB*)bl)->sc;
+ case BL_NPC: return &((TBL_NPC*)bl)->sc;
+ case BL_HOM: return &((TBL_HOM*)bl)->sc;
+ case BL_MER: return &((TBL_MER*)bl)->sc;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->sc;
+ }
+ return NULL;
+}
+
+void status_change_init(struct block_list *bl)
+{
+ struct status_change *sc = status_get_sc(bl);
+ nullpo_retv(sc);
+ memset(sc, 0, sizeof (struct status_change));
+}
+
+//Applies SC defense to a given status change.
+//Returns the adjusted duration based on flag values.
+//the flag values are the same as in status_change_start.
+int status_get_sc_def(struct block_list *bl, enum sc_type type, int rate, int tick, int flag)
+{
+ int sc_def = 0, tick_def = 0;
+ struct status_data* status;
+ struct status_change* sc;
+ struct map_session_data *sd;
+
+ nullpo_ret(bl);
+
+ //Status that are blocked by Golden Thief Bug card or Wand of Hermod
+ if (status_isimmune(bl))
+ switch (type) {
+ case SC_DECREASEAGI:
+ case SC_SILENCE:
+ case SC_COMA:
+ case SC_INCREASEAGI:
+ case SC_BLESSING:
+ case SC_SLOWPOISON:
+ case SC_IMPOSITIO:
+ case SC_AETERNA:
+ case SC_SUFFRAGIUM:
+ case SC_BENEDICTIO:
+ case SC_PROVIDENCE:
+ case SC_KYRIE:
+ case SC_ASSUMPTIO:
+ case SC_ANGELUS:
+ case SC_MAGNIFICAT:
+ case SC_GLORIA:
+ case SC_WINDWALK:
+ case SC_MAGICROD:
+ case SC_HALLUCINATION:
+ case SC_STONE:
+ case SC_QUAGMIRE:
+ case SC_SUITON:
+ case SC_SWINGDANCE:
+ case SC__ENERVATION:
+ case SC__GROOMY:
+ case SC__IGNORANCE:
+ case SC__LAZINESS:
+ case SC__UNLUCKY:
+ case SC__WEAKNESS:
+ case SC__BLOODYLUST:
+ return 0;
+ }
+
+ sd = BL_CAST(BL_PC,bl);
+ status = status_get_status_data(bl);
+ sc = status_get_sc(bl);
+ if( sc && !sc->count )
+ sc = NULL;
+ switch (type) {
+ case SC_STUN:
+ case SC_POISON:
+ if( sc && sc->data[SC__UNLUCKY] )
+ return tick;
+ case SC_DPOISON:
+ case SC_SILENCE:
+ case SC_BLEEDING:
+ sc_def = 3 +status->vit;
+ break;
+ case SC_SLEEP:
+ sc_def = 3 +status->int_;
+ break;
+ case SC_DEEPSLEEP:
+ tick_def = status->int_ / 10 + status_get_lv(bl) * 65 / 1000; // Seems to be -1 sec every 10 int and -5% chance every 10 int.
+ sc_def = 5 * status->int_ /10;
+ break;
+ case SC_DECREASEAGI:
+ case SC_ADORAMUS://Arch Bishop
+ if (sd) tick>>=1; //Half duration for players.
+ case SC_STONE:
+ case SC_FREEZE:
+ sc_def = 3 +status->mdef;
+ break;
+ case SC_CURSE:
+ //Special property: inmunity when luk is greater than level or zero
+ if (status->luk > status_get_lv(bl) || status->luk == 0)
+ return 0;
+ else
+ sc_def = 3 +status->luk;
+ tick_def = status->vit;
+ break;
+ case SC_BLIND:
+ if( sc && sc->data[SC__UNLUCKY] )
+ return tick;
+ sc_def = 3 +(status->vit + status->int_)/2;
+ break;
+ case SC_CONFUSION:
+ sc_def = 3 +(status->str + status->int_)/2;
+ break;
+ case SC_ANKLE:
+ if(status->mode&MD_BOSS) // Lasts 5 times less on bosses
+ tick /= 5;
+ sc_def = status->agi / 2;
+ break;
+ case SC_MAGICMIRROR:
+ case SC_ARMORCHANGE:
+ if (sd) //Duration greatly reduced for players.
+ tick /= 15;
+ //No defense against it (buff).
+ rate -= (status_get_lv(bl) / 5 + status->vit / 4 + status->agi / 10)*100; // Lineal Reduction of Rate
+ break;
+ case SC_MARSHOFABYSS:
+ //5 second (Fixed) + 25 second - {( INT + LUK ) / 20 second }
+ tick -= (status->int_ + status->luk) / 20 * 1000;
+ break;
+ case SC_STASIS:
+ //5 second (fixed) + { Stasis Skill level * 5 - (Target�s VIT + DEX) / 20 }
+ tick -= (status->vit + status->dex) / 20 * 1000;
+ break;
+ case SC_WHITEIMPRISON:
+ if( tick == 5000 ) // 100% on caster
+ break;
+ if( bl->type == BL_PC )
+ tick -= (status_get_lv(bl) / 5 + status->vit / 4 + status->agi / 10)*100;
+ else
+ tick -= (status->vit + status->luk) / 20 * 1000;
+ break;
+ case SC_BURNING:
+ // From iROwiki : http://forums.irowiki.org/showpost.php?p=577240&postcount=583
+ tick -= 50*status->luk + 60*status->int_ + 170*status->vit;
+ tick = max(tick,10000); // Minimum Duration 10s.
+ break;
+ case SC_FREEZING:
+ tick -= 1000 * ((status->vit + status->dex) / 20);
+ tick = max(tick,10000); // Minimum Duration 10s.
+ break;
+ case SC_OBLIVIONCURSE: // 100% - (100 - 0.8 x INT)
+ sc_def = 100 - ( 100 - status->int_* 8 / 10 );
+ sc_def = max(sc_def, 5); // minimum of 5%
+ break;
+ case SC_BITE: // {(Base Success chance) - (Target's AGI / 4)}
+ rate -= status->agi*1000/4;
+ rate = max(rate,50000); // minimum of 50%
+ break;
+ case SC_ELECTRICSHOCKER:
+ if( bl->type == BL_MOB )
+ tick -= 1000 * (status->agi/10);
+ break;
+ case SC_CRYSTALIZE:
+ tick -= (1000*(status->vit/10))+(status_get_lv(bl)/50);
+ break;
+ case SC_MANDRAGORA:
+ sc_def = (status->vit+status->luk)/5;
+ break;
+ case SC_KYOUGAKU:
+ tick -= 30*status->int_;
+ break;
+ case SC_PARALYSIS:
+ tick -= 50 * (status->vit + status->luk); //(1000/20);
+ break;
+ default:
+ //Effect that cannot be reduced? Likely a buff.
+ if (!(rnd()%10000 < rate))
+ return 0;
+ return tick?tick:1;
+ }
+
+ if (sd) {
+
+ if (battle_config.pc_sc_def_rate != 100)
+ sc_def = sc_def*battle_config.pc_sc_def_rate/100;
+
+ if (sc_def < battle_config.pc_max_sc_def)
+ sc_def += (battle_config.pc_max_sc_def - sc_def)*
+ status->luk/battle_config.pc_luk_sc_def;
+ else
+ sc_def = battle_config.pc_max_sc_def;
+
+ if (tick_def) {
+ if (battle_config.pc_sc_def_rate != 100)
+ tick_def = tick_def*battle_config.pc_sc_def_rate/100;
+ }
+
+ } else {
+
+ if (battle_config.mob_sc_def_rate != 100)
+ sc_def = sc_def*battle_config.mob_sc_def_rate/100;
+
+ if (sc_def < battle_config.mob_max_sc_def)
+ sc_def += (battle_config.mob_max_sc_def - sc_def)*
+ status->luk/battle_config.mob_luk_sc_def;
+ else
+ sc_def = battle_config.mob_max_sc_def;
+
+ if (tick_def) {
+ if (battle_config.mob_sc_def_rate != 100)
+ tick_def = tick_def*battle_config.mob_sc_def_rate/100;
+ }
+ }
+
+ if (sc) {
+ if (sc->data[SC_SCRESIST])
+ sc_def += sc->data[SC_SCRESIST]->val1; //Status resist
+ else if (sc->data[SC_SIEGFRIED])
+ sc_def += sc->data[SC_SIEGFRIED]->val3; //Status resistance.
+ }
+
+ //When no tick def, reduction is the same for both.
+ if( !tick_def && type != SC_STONE ) //Recent tests show duration of petrify isn't reduced by MDEF. [Inkfish]
+ tick_def = sc_def;
+
+ //Natural resistance
+ if (!(flag&8)) {
+ rate -= rate*sc_def/100;
+
+ //Item resistance (only applies to rate%)
+ if(sd && SC_COMMON_MIN <= type && type <= SC_COMMON_MAX)
+ {
+ if( sd->reseff[type-SC_COMMON_MIN] > 0 )
+ rate -= rate*sd->reseff[type-SC_COMMON_MIN]/10000;
+ if( sd->sc.data[SC_COMMONSC_RESIST] )
+ rate -= rate*sd->sc.data[SC_COMMONSC_RESIST]->val1/100;
+ }
+ }
+ if (!(rnd()%10000 < rate))
+ return 0;
+
+ //Why would a status start with no duration? Presume it has
+ //duration defined elsewhere.
+ if (!tick) return 1;
+
+ //Rate reduction
+ if (flag&2)
+ return tick;
+
+ tick -= tick*tick_def/100;
+ // Changed to 5 seconds according to recent tests [Playtester]
+ if (type == SC_ANKLE && tick < 5000)
+ tick = 5000;
+ return tick<=0?0:tick;
+}
+
+/*==========================================
+ * Starts a status change.
+ * 'type' = type, 'val1~4' depend on the type.
+ * 'rate' = base success rate. 10000 = 100%
+ * 'tick' is base duration
+ * 'flag':
+ * &1: Cannot be avoided (it has to start)
+ * &2: Tick should not be reduced (by vit, luk, lv, etc)
+ * &4: sc_data loaded, no value has to be altered.
+ * &8: rate should not be reduced
+ *------------------------------------------*/
+int status_change_start(struct block_list* bl,enum sc_type type,int rate,int val1,int val2,int val3,int val4,int tick,int flag)
+{
+ struct map_session_data *sd = NULL;
+ struct status_change* sc;
+ struct status_change_entry* sce;
+ struct status_data *status;
+ struct view_data *vd;
+ int opt_flag, calc_flag, undead_flag, val_flag = 0, tick_time = 0;
+ bool sc_isnew = true;
+
+ nullpo_ret(bl);
+ sc = status_get_sc(bl);
+ status = status_get_status_data(bl);
+
+ if( type <= SC_NONE || type >= SC_MAX )
+ {
+ ShowError("status_change_start: invalid status change (%d)!\n", type);
+ return 0;
+ }
+
+ if( !sc )
+ return 0; //Unable to receive status changes
+
+ if( status_isdead(bl) && type != SC_NOCHAT ) // SC_NOCHAT should work even on dead characters
+ return 0;
+
+ if( bl->type == BL_MOB)
+ {
+ struct mob_data *md = BL_CAST(BL_MOB,bl);
+ if(md && (md->class_ == MOBID_EMPERIUM || mob_is_battleground(md)) && type != SC_SAFETYWALL && type != SC_PNEUMA)
+ return 0; //Emperium/BG Monsters can't be afflicted by status changes
+ // if(md && mob_is_gvg(md) && status_sc2scb_flag(type)&SCB_MAXHP)
+ // return 0; //prevent status addinh hp to gvg mob (like bloodylust=hp*3 etc...
+ }
+
+ if( sc->data[SC_REFRESH] ) {
+ if( type >= SC_COMMON_MIN && type <= SC_COMMON_MAX) // Confirmed.
+ return 0; // Immune to status ailements
+ switch( type ) {
+ case SC_QUAGMIRE://Tester said it protects against this and decrease agi.
+ case SC_DECREASEAGI:
+ case SC_BURNING:
+ case SC_FREEZING:
+ //case SC_WHITEIMPRISON://Need confirm. Protected against this in the past. [Rytech]
+ case SC_MARSHOFABYSS:
+ case SC_TOXIN:
+ case SC_PARALYSE:
+ case SC_VENOMBLEED:
+ case SC_MAGICMUSHROOM:
+ case SC_DEATHHURT:
+ case SC_PYREXIA:
+ case SC_OBLIVIONCURSE:
+ case SC_LEECHESEND:
+ case SC_CRYSTALIZE: ////08/31/2011 - Class Balance Changes
+ case SC_DEEPSLEEP:
+ case SC_MANDRAGORA:
+ return 0;
+ }
+ }
+ else if( sc->data[SC_INSPIRATION] ) {
+ if( type >= SC_COMMON_MIN && type <= SC_COMMON_MAX )
+ return 0; // Immune to status ailements
+ switch( type ) {
+ case SC_DEEPSLEEP:
+ case SC_SATURDAYNIGHTFEVER:
+ case SC_PYREXIA:
+ case SC_DEATHHURT:
+ case SC_MAGICMUSHROOM:
+ case SC_VENOMBLEED:
+ case SC_TOXIN:
+ case SC_OBLIVIONCURSE:
+ case SC_LEECHESEND:
+ case SC__ENERVATION:
+ case SC__GROOMY:
+ case SC__LAZINESS:
+ case SC__UNLUCKY:
+ case SC__WEAKNESS:
+ case SC__BODYPAINT:
+ case SC__IGNORANCE:
+ return 0;
+ }
+ }
+
+ sd = BL_CAST(BL_PC, bl);
+
+ //Adjust tick according to status resistances
+ if( !(flag&(1|4)) )
+ {
+ tick = status_get_sc_def(bl, type, rate, tick, flag);
+ if( !tick ) return 0;
+ }
+
+ undead_flag = battle_check_undead(status->race,status->def_ele);
+ //Check for inmunities / sc fails
+ switch (type) {
+ case SC_ANGRIFFS_MODUS:
+ case SC_GOLDENE_FERSE:
+ if ((type==SC_GOLDENE_FERSE && sc->data[SC_ANGRIFFS_MODUS])
+ || (type==SC_ANGRIFFS_MODUS && sc->data[SC_GOLDENE_FERSE])
+ )
+ return 0;
+ case SC_STONE:
+ if(sc->data[SC_POWER_OF_GAIA])
+ return 0;
+ case SC_FREEZE:
+ //Undead are immune to Freeze/Stone
+ if (undead_flag && !(flag&1))
+ return 0;
+ case SC_DEEPSLEEP:
+ case SC_SLEEP:
+ case SC_STUN:
+ case SC_FREEZING:
+ case SC_CRYSTALIZE:
+ if (sc->opt1)
+ return 0; //Cannot override other opt1 status changes. [Skotlex]
+ if((type == SC_FREEZE || type == SC_FREEZING || type == SC_CRYSTALIZE) && sc->data[SC_WARMER])
+ return 0; //Immune to Frozen and Freezing status if under Warmer status. [Jobbie]
+ break;
+
+ //There all like berserk, do not everlap each other
+ case SC__BLOODYLUST:
+ if(!sd) return 0; //should only affect player
+ case SC_BERSERK:
+ if (((type == SC_BERSERK) && (sc->data[SC_SATURDAYNIGHTFEVER] || sc->data[SC__BLOODYLUST]))
+ || ((type == SC__BLOODYLUST) && (sc->data[SC_SATURDAYNIGHTFEVER] || sc->data[SC_BERSERK]))
+ )
+ return 0;
+ break;
+
+ case SC_BURNING:
+ if(sc->opt1 || sc->data[SC_FREEZING])
+ return 0;
+ break;
+
+ case SC_SIGNUMCRUCIS:
+ //Only affects demons and undead element (but not players)
+ if((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC)
+ return 0;
+ break;
+ case SC_AETERNA:
+ if( (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) || sc->data[SC_FREEZE] )
+ return 0;
+ break;
+ case SC_KYRIE:
+ if (bl->type == BL_MOB)
+ return 0;
+ break;
+ case SC_OVERTHRUST:
+ if (sc->data[SC_MAXOVERTHRUST])
+ return 0; //Overthrust can't take effect if under Max Overthrust. [Skotlex]
+ case SC_MAXOVERTHRUST:
+ if( sc->option&OPTION_MADOGEAR )
+ return 0;//Overthrust and Overthrust Max cannot be used on Mado Gear [Ind]
+ break;
+ case SC_ADRENALINE:
+ if(sd && !pc_check_weapontype(sd,skill_get_weapontype(BS_ADRENALINE)))
+ return 0;
+ if (sc->data[SC_QUAGMIRE] ||
+ sc->data[SC_DECREASEAGI] ||
+ sc->option&OPTION_MADOGEAR //Adrenaline doesn't affect Mado Gear [Ind]
+ )
+ return 0;
+ break;
+ case SC_ADRENALINE2:
+ if(sd && !pc_check_weapontype(sd,skill_get_weapontype(BS_ADRENALINE2)))
+ return 0;
+ if (sc->data[SC_QUAGMIRE] ||
+ sc->data[SC_DECREASEAGI]
+ )
+ return 0;
+ break;
+ case SC_MAGNIFICAT:
+ if( sc->option&OPTION_MADOGEAR ) //Mado is immune to magnificat
+ return 0;
+ break;
+ case SC_ONEHAND:
+ case SC_MERC_QUICKEN:
+ case SC_TWOHANDQUICKEN:
+ if(sc->data[SC_DECREASEAGI])
+ return 0;
+
+ case SC_INCREASEAGI:
+ if(sd && pc_issit(sd)){
+ pc_setstand(sd);
+ }
+
+ case SC_CONCENTRATE:
+ case SC_SPEARQUICKEN:
+ case SC_TRUESIGHT:
+ case SC_WINDWALK:
+ case SC_CARTBOOST:
+ case SC_ASSNCROS:
+ if (sc->data[SC_QUAGMIRE])
+ return 0;
+ if(sc->option&OPTION_MADOGEAR)
+ return 0;//Mado is immune to increase agi, wind walk, cart boost, etc (others above) [Ind]
+ break;
+ case SC_CLOAKING:
+ //Avoid cloaking with no wall and low skill level. [Skotlex]
+ //Due to the cloaking card, we have to check the wall versus to known
+ //skill level rather than the used one. [Skotlex]
+ //if (sd && val1 < 3 && skill_check_cloaking(bl,NULL))
+ if( sd && pc_checkskill(sd, AS_CLOAKING) < 3 && !skill_check_cloaking(bl,NULL) )
+ return 0;
+ break;
+ case SC_MODECHANGE:
+ {
+ int mode;
+ struct status_data *bstatus = status_get_base_status(bl);
+ if (!bstatus) return 0;
+ if (sc->data[type])
+ { //Pile up with previous values.
+ if(!val2) val2 = sc->data[type]->val2;
+ val3 |= sc->data[type]->val3;
+ val4 |= sc->data[type]->val4;
+ }
+ mode = val2?val2:bstatus->mode; //Base mode
+ if (val4) mode&=~val4; //Del mode
+ if (val3) mode|= val3; //Add mode
+ if (mode == bstatus->mode) { //No change.
+ if (sc->data[type]) //Abort previous status
+ return status_change_end(bl, type, INVALID_TIMER);
+ return 0;
+ }
+ }
+ break;
+ //Strip skills, need to divest something or it fails.
+ case SC_STRIPWEAPON:
+ if (sd && !(flag&4)) { //apply sc anyway if loading saved sc_data
+ int i;
+ opt_flag = 0; //Reuse to check success condition.
+ if(sd->bonus.unstripable_equip&EQP_WEAPON)
+ return 0;
+ i = sd->equip_index[EQI_HAND_L];
+ if (i>=0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_WEAPON) {
+ opt_flag|=1;
+ pc_unequipitem(sd,i,3); //L-hand weapon
+ }
+
+ i = sd->equip_index[EQI_HAND_R];
+ if (i>=0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_WEAPON) {
+ opt_flag|=2;
+ pc_unequipitem(sd,i,3);
+ }
+ if (!opt_flag) return 0;
+ }
+ if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
+ break;
+ case SC_STRIPSHIELD:
+ if( val2 == 1 ) val2 = 0; //GX effect. Do not take shield off..
+ else
+ if (sd && !(flag&4)) {
+ int i;
+ if(sd->bonus.unstripable_equip&EQP_SHIELD)
+ return 0;
+ i = sd->equip_index[EQI_HAND_L];
+ if ( i < 0 || !sd->inventory_data[i] || sd->inventory_data[i]->type != IT_ARMOR )
+ return 0;
+ pc_unequipitem(sd,i,3);
+ }
+ if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
+ break;
+ case SC_STRIPARMOR:
+ if (sd && !(flag&4)) {
+ int i;
+ if(sd->bonus.unstripable_equip&EQP_ARMOR)
+ return 0;
+ i = sd->equip_index[EQI_ARMOR];
+ if ( i < 0 || !sd->inventory_data[i] )
+ return 0;
+ pc_unequipitem(sd,i,3);
+ }
+ if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
+ break;
+ case SC_STRIPHELM:
+ if (sd && !(flag&4)) {
+ int i;
+ if(sd->bonus.unstripable_equip&EQP_HELM)
+ return 0;
+ i = sd->equip_index[EQI_HEAD_TOP];
+ if ( i < 0 || !sd->inventory_data[i] )
+ return 0;
+ pc_unequipitem(sd,i,3);
+ }
+ if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
+ break;
+ case SC_MERC_FLEEUP:
+ case SC_MERC_ATKUP:
+ case SC_MERC_HPUP:
+ case SC_MERC_SPUP:
+ case SC_MERC_HITUP:
+ if( bl->type != BL_MER )
+ return 0; // Stats only for Mercenaries
+ break;
+ case SC_STRFOOD:
+ if (sc->data[SC_FOOD_STR_CASH] && sc->data[SC_FOOD_STR_CASH]->val1 > val1)
+ return 0;
+ break;
+ case SC_AGIFOOD:
+ if (sc->data[SC_FOOD_AGI_CASH] && sc->data[SC_FOOD_AGI_CASH]->val1 > val1)
+ return 0;
+ break;
+ case SC_VITFOOD:
+ if (sc->data[SC_FOOD_VIT_CASH] && sc->data[SC_FOOD_VIT_CASH]->val1 > val1)
+ return 0;
+ break;
+ case SC_INTFOOD:
+ if (sc->data[SC_FOOD_INT_CASH] && sc->data[SC_FOOD_INT_CASH]->val1 > val1)
+ return 0;
+ break;
+ case SC_DEXFOOD:
+ if (sc->data[SC_FOOD_DEX_CASH] && sc->data[SC_FOOD_DEX_CASH]->val1 > val1)
+ return 0;
+ break;
+ case SC_LUKFOOD:
+ if (sc->data[SC_FOOD_LUK_CASH] && sc->data[SC_FOOD_LUK_CASH]->val1 > val1)
+ return 0;
+ break;
+ case SC_FOOD_STR_CASH:
+ if (sc->data[SC_STRFOOD] && sc->data[SC_STRFOOD]->val1 > val1)
+ return 0;
+ break;
+ case SC_FOOD_AGI_CASH:
+ if (sc->data[SC_AGIFOOD] && sc->data[SC_AGIFOOD]->val1 > val1)
+ return 0;
+ break;
+ case SC_FOOD_VIT_CASH:
+ if (sc->data[SC_VITFOOD] && sc->data[SC_VITFOOD]->val1 > val1)
+ return 0;
+ break;
+ case SC_FOOD_INT_CASH:
+ if (sc->data[SC_INTFOOD] && sc->data[SC_INTFOOD]->val1 > val1)
+ return 0;
+ break;
+ case SC_FOOD_DEX_CASH:
+ if (sc->data[SC_DEXFOOD] && sc->data[SC_DEXFOOD]->val1 > val1)
+ return 0;
+ break;
+ case SC_FOOD_LUK_CASH:
+ if (sc->data[SC_LUKFOOD] && sc->data[SC_LUKFOOD]->val1 > val1)
+ return 0;
+ break;
+ case SC_CAMOUFLAGE:
+ if( sd && pc_checkskill(sd, RA_CAMOUFLAGE) < 3 && !skill_check_camouflage(bl,NULL) )
+ return 0;
+ break;
+ case SC__STRIPACCESSORY:
+ if( sd ) {
+ int i = -1;
+ if( !(sd->bonus.unstripable_equip&EQI_ACC_L) ) {
+ i = sd->equip_index[EQI_ACC_L];
+ if( i >= 0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_ARMOR )
+ pc_unequipitem(sd,i,3); //L-Accessory
+ } if( !(sd->bonus.unstripable_equip&EQI_ACC_R) ) {
+ i = sd->equip_index[EQI_ACC_R];
+ if( i >= 0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_ARMOR )
+ pc_unequipitem(sd,i,3); //R-Accessory
+ }
+ if( i < 0 )
+ return 0;
+ }
+ if (tick == 1) return 1; //Minimal duration: Only strip without causing the SC
+ break;
+ case SC_TOXIN:
+ case SC_PARALYSE:
+ case SC_VENOMBLEED:
+ case SC_MAGICMUSHROOM:
+ case SC_DEATHHURT:
+ case SC_PYREXIA:
+ case SC_OBLIVIONCURSE:
+ case SC_LEECHESEND:
+ { // it doesn't stack or even renewed
+ int i = SC_TOXIN;
+ for(; i<= SC_LEECHESEND; i++)
+ if(sc->data[i]) return 0;
+ }
+ break;
+ case SC_SATURDAYNIGHTFEVER:
+ if (sc->data[SC_BERSERK] || sc->data[SC_INSPIRATION] || sc->data[SC__BLOODYLUST])
+ return 0;
+ break;
+ }
+
+ //Check for BOSS resistances
+ if(status->mode&MD_BOSS && !(flag&1)) {
+ if (type>=SC_COMMON_MIN && type <= SC_COMMON_MAX)
+ return 0;
+ switch (type) {
+ case SC_BLESSING:
+ case SC_DECREASEAGI:
+ case SC_PROVOKE:
+ case SC_COMA:
+ case SC_GRAVITATION:
+ case SC_SUITON:
+ case SC_RICHMANKIM:
+ case SC_ROKISWEIL:
+ case SC_FOGWALL:
+ case SC_FREEZING:
+ case SC_BURNING:
+ case SC_MARSHOFABYSS:
+ case SC_ADORAMUS:
+ case SC_PARALYSIS:
+ case SC_DEEPSLEEP:
+ case SC_CRYSTALIZE:
+
+ // Exploit prevention - kRO Fix
+ case SC_PYREXIA:
+ case SC_DEATHHURT:
+ case SC_TOXIN:
+ case SC_PARALYSE:
+ case SC_VENOMBLEED:
+ case SC_MAGICMUSHROOM:
+ case SC_OBLIVIONCURSE:
+ case SC_LEECHESEND:
+
+ // Ranger Effects
+ case SC_BITE:
+ case SC_ELECTRICSHOCKER:
+ case SC_MAGNETICFIELD:
+
+ return 0;
+ }
+ }
+
+ //Before overlapping fail, one must check for status cured.
+ switch (type) {
+ case SC_BLESSING:
+ //TO-DO Blessing and Agi up should do 1 damage against players on Undead Status, even on PvM
+ //but cannot be plagiarized (this requires aegis investigation on packets and official behavior) [Brainstorm]
+ if ((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC) {
+ status_change_end(bl, SC_CURSE, INVALID_TIMER);
+ if (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ }
+ break;
+ case SC_INCREASEAGI:
+ status_change_end(bl, SC_DECREASEAGI, INVALID_TIMER);
+ break;
+ case SC_QUAGMIRE:
+ status_change_end(bl, SC_CONCENTRATE, INVALID_TIMER);
+ status_change_end(bl, SC_TRUESIGHT, INVALID_TIMER);
+ status_change_end(bl, SC_WINDWALK, INVALID_TIMER);
+ //Also blocks the ones below...
+ case SC_DECREASEAGI:
+ status_change_end(bl, SC_CARTBOOST, INVALID_TIMER);
+ //Also blocks the ones below...
+ case SC_DONTFORGETME:
+ status_change_end(bl, SC_INCREASEAGI, INVALID_TIMER);
+ status_change_end(bl, SC_ADRENALINE, INVALID_TIMER);
+ status_change_end(bl, SC_ADRENALINE2, INVALID_TIMER);
+ status_change_end(bl, SC_SPEARQUICKEN, INVALID_TIMER);
+ status_change_end(bl, SC_TWOHANDQUICKEN, INVALID_TIMER);
+ status_change_end(bl, SC_ONEHAND, INVALID_TIMER);
+ status_change_end(bl, SC_MERC_QUICKEN, INVALID_TIMER);
+ status_change_end(bl, SC_ACCELERATION, INVALID_TIMER);
+ break;
+ case SC_ONEHAND:
+ //Removes the Aspd potion effect, as reported by Vicious. [Skotlex]
+ status_change_end(bl, SC_ASPDPOTION0, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION1, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION2, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION3, INVALID_TIMER);
+ break;
+ case SC_MAXOVERTHRUST:
+ //Cancels Normal Overthrust. [Skotlex]
+ status_change_end(bl, SC_OVERTHRUST, INVALID_TIMER);
+ break;
+ case SC_KYRIE:
+ //Cancels Assumptio
+ status_change_end(bl, SC_ASSUMPTIO, INVALID_TIMER);
+ break;
+ case SC_DELUGE:
+ if (sc->data[SC_FOGWALL] && sc->data[SC_BLIND])
+ status_change_end(bl, SC_BLIND, INVALID_TIMER);
+ break;
+ case SC_SILENCE:
+ if (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF)
+ status_change_end(bl, SC_GOSPEL, INVALID_TIMER);
+ break;
+ case SC_HIDING:
+ status_change_end(bl, SC_CLOSECONFINE, INVALID_TIMER);
+ status_change_end(bl, SC_CLOSECONFINE2, INVALID_TIMER);
+ break;
+ case SC__BLOODYLUST:
+ case SC_BERSERK:
+ if(battle_config.berserk_cancels_buffs) {
+ status_change_end(bl, SC_ONEHAND, INVALID_TIMER);
+ status_change_end(bl, SC_TWOHANDQUICKEN, INVALID_TIMER);
+ status_change_end(bl, SC_CONCENTRATION, INVALID_TIMER);
+ status_change_end(bl, SC_PARRYING, INVALID_TIMER);
+ status_change_end(bl, SC_AURABLADE, INVALID_TIMER);
+ status_change_end(bl, SC_MERC_QUICKEN, INVALID_TIMER);
+ }
+#ifdef RENEWAL
+ else {
+ status_change_end(bl, SC_TWOHANDQUICKEN, INVALID_TIMER);
+ }
+#endif
+ break;
+ case SC_ASSUMPTIO:
+ status_change_end(bl, SC_KYRIE, INVALID_TIMER);
+ status_change_end(bl, SC_KAITE, INVALID_TIMER);
+ break;
+ case SC_KAITE:
+ status_change_end(bl, SC_ASSUMPTIO, INVALID_TIMER);
+ break;
+ case SC_CARTBOOST:
+ if(sc->data[SC_DECREASEAGI])
+ { //Cancel Decrease Agi, but take no further effect [Skotlex]
+ status_change_end(bl, SC_DECREASEAGI, INVALID_TIMER);
+ return 0;
+ }
+ break;
+ case SC_FUSION:
+ status_change_end(bl, SC_SPIRIT, INVALID_TIMER);
+ break;
+ case SC_ADJUSTMENT:
+ status_change_end(bl, SC_MADNESSCANCEL, INVALID_TIMER);
+ break;
+ case SC_MADNESSCANCEL:
+ status_change_end(bl, SC_ADJUSTMENT, INVALID_TIMER);
+ break;
+ //NPC_CHANGEUNDEAD will debuff Blessing and Agi Up
+ case SC_CHANGEUNDEAD:
+ status_change_end(bl, SC_BLESSING, INVALID_TIMER);
+ status_change_end(bl, SC_INCREASEAGI, INVALID_TIMER);
+ break;
+ case SC_STRFOOD:
+ status_change_end(bl, SC_FOOD_STR_CASH, INVALID_TIMER);
+ break;
+ case SC_AGIFOOD:
+ status_change_end(bl, SC_FOOD_AGI_CASH, INVALID_TIMER);
+ break;
+ case SC_VITFOOD:
+ status_change_end(bl, SC_FOOD_VIT_CASH, INVALID_TIMER);
+ break;
+ case SC_INTFOOD:
+ status_change_end(bl, SC_FOOD_INT_CASH, INVALID_TIMER);
+ break;
+ case SC_DEXFOOD:
+ status_change_end(bl, SC_FOOD_DEX_CASH, INVALID_TIMER);
+ break;
+ case SC_LUKFOOD:
+ status_change_end(bl, SC_FOOD_LUK_CASH, INVALID_TIMER);
+ break;
+ case SC_FOOD_STR_CASH:
+ status_change_end(bl, SC_STRFOOD, INVALID_TIMER);
+ break;
+ case SC_FOOD_AGI_CASH:
+ status_change_end(bl, SC_AGIFOOD, INVALID_TIMER);
+ break;
+ case SC_FOOD_VIT_CASH:
+ status_change_end(bl, SC_VITFOOD, INVALID_TIMER);
+ break;
+ case SC_FOOD_INT_CASH:
+ status_change_end(bl, SC_INTFOOD, INVALID_TIMER);
+ break;
+ case SC_FOOD_DEX_CASH:
+ status_change_end(bl, SC_DEXFOOD, INVALID_TIMER);
+ break;
+ case SC_FOOD_LUK_CASH:
+ status_change_end(bl, SC_LUKFOOD, INVALID_TIMER);
+ break;
+ case SC_FIGHTINGSPIRIT:
+ status_change_end(bl, type, INVALID_TIMER); // Remove previous one.
+ break;
+ case SC_MARSHOFABYSS:
+ status_change_end(bl, SC_INCAGI, INVALID_TIMER);
+ status_change_end(bl, SC_WINDWALK, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION0, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION1, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION2, INVALID_TIMER);
+ status_change_end(bl, SC_ASPDPOTION3, INVALID_TIMER);
+ break;
+ case SC_SWINGDANCE:
+ case SC_SYMPHONYOFLOVER:
+ case SC_MOONLITSERENADE:
+ case SC_RUSHWINDMILL:
+ case SC_ECHOSONG:
+ case SC_HARMONIZE: //group A doesn't overlap
+ if (type != SC_SWINGDANCE) status_change_end(bl, SC_SWINGDANCE, INVALID_TIMER);
+ if (type != SC_SYMPHONYOFLOVER) status_change_end(bl, SC_SYMPHONYOFLOVER, INVALID_TIMER);
+ if (type != SC_MOONLITSERENADE) status_change_end(bl, SC_MOONLITSERENADE, INVALID_TIMER);
+ if (type != SC_RUSHWINDMILL) status_change_end(bl, SC_RUSHWINDMILL, INVALID_TIMER);
+ if (type != SC_ECHOSONG) status_change_end(bl, SC_ECHOSONG, INVALID_TIMER);
+ if (type != SC_HARMONIZE) status_change_end(bl, SC_HARMONIZE, INVALID_TIMER);
+ break;
+ case SC_VOICEOFSIREN:
+ case SC_DEEPSLEEP:
+ case SC_GLOOMYDAY:
+ case SC_SONGOFMANA:
+ case SC_DANCEWITHWUG:
+ case SC_SATURDAYNIGHTFEVER:
+ case SC_LERADSDEW:
+ case SC_MELODYOFSINK:
+ case SC_BEYONDOFWARCRY:
+ case SC_UNLIMITEDHUMMINGVOICE: //group B
+ if (type != SC_VOICEOFSIREN) status_change_end(bl, SC_VOICEOFSIREN, INVALID_TIMER);
+ if (type != SC_DEEPSLEEP) status_change_end(bl, SC_DEEPSLEEP, INVALID_TIMER);
+ if (type != SC_LERADSDEW) status_change_end(bl, SC_LERADSDEW, INVALID_TIMER);
+ if (type != SC_MELODYOFSINK) status_change_end(bl, SC_MELODYOFSINK, INVALID_TIMER);
+ if (type != SC_BEYONDOFWARCRY) status_change_end(bl, SC_BEYONDOFWARCRY, INVALID_TIMER);
+ if (type != SC_UNLIMITEDHUMMINGVOICE) status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER);
+ if (type != SC_GLOOMYDAY) {
+ status_change_end(bl, SC_GLOOMYDAY, INVALID_TIMER);
+ status_change_end(bl, SC_GLOOMYDAY_SK, INVALID_TIMER);
+ }
+ if (type != SC_SONGOFMANA) status_change_end(bl, SC_SONGOFMANA, INVALID_TIMER);
+ if (type != SC_DANCEWITHWUG) status_change_end(bl, SC_DANCEWITHWUG, INVALID_TIMER);
+ if (type != SC_SATURDAYNIGHTFEVER) {
+ if (sc->data[SC_SATURDAYNIGHTFEVER]) {
+ sc->data[SC_SATURDAYNIGHTFEVER]->val2 = 0; //mark to not lose hp
+ status_change_end(bl, SC_SATURDAYNIGHTFEVER, INVALID_TIMER);
+ }
+ }
+ break;
+ case SC_REFLECTSHIELD:
+ status_change_end(bl, SC_REFLECTDAMAGE, INVALID_TIMER);
+ break;
+ case SC_REFLECTDAMAGE:
+ status_change_end(bl, SC_REFLECTSHIELD, INVALID_TIMER);
+ break;
+ case SC_SHIELDSPELL_DEF:
+ case SC_SHIELDSPELL_MDEF:
+ case SC_SHIELDSPELL_REF:
+ status_change_end(bl, SC_MAGNIFICAT, INVALID_TIMER);
+ if( type != SC_SHIELDSPELL_DEF )
+ status_change_end(bl, SC_SHIELDSPELL_DEF, INVALID_TIMER);
+ if( type != SC_SHIELDSPELL_MDEF )
+ status_change_end(bl, SC_SHIELDSPELL_MDEF, INVALID_TIMER);
+ if( type != SC_SHIELDSPELL_REF )
+ status_change_end(bl, SC_SHIELDSPELL_REF, INVALID_TIMER);
+ break;
+ case SC_GT_ENERGYGAIN:
+ case SC_GT_CHANGE:
+ case SC_GT_REVITALIZE:
+ if( type != SC_GT_REVITALIZE )
+ status_change_end(bl, SC_GT_REVITALIZE, INVALID_TIMER);
+ if( type != SC_GT_ENERGYGAIN )
+ status_change_end(bl, SC_GT_ENERGYGAIN, INVALID_TIMER);
+ if( type != SC_GT_CHANGE )
+ status_change_end(bl, SC_GT_CHANGE, INVALID_TIMER);
+ break;
+ case SC_INVINCIBLE:
+ status_change_end(bl, SC_INVINCIBLEOFF, INVALID_TIMER);
+ break;
+ case SC_INVINCIBLEOFF:
+ status_change_end(bl, SC_INVINCIBLE, INVALID_TIMER);
+ break;
+ case SC_MAGICPOWER:
+ status_change_end(bl, type, INVALID_TIMER);
+ break;
+ }
+
+ //Check for overlapping fails
+ if( (sce = sc->data[type]) ) {
+ switch( type ) {
+ case SC_MERC_FLEEUP:
+ case SC_MERC_ATKUP:
+ case SC_MERC_HPUP:
+ case SC_MERC_SPUP:
+ case SC_MERC_HITUP:
+ if( sce->val1 > val1 )
+ val1 = sce->val1;
+ break;
+ case SC_ADRENALINE:
+ case SC_ADRENALINE2:
+ case SC_WEAPONPERFECTION:
+ case SC_OVERTHRUST:
+ if (sce->val2 > val2)
+ return 0;
+ break;
+ case SC_S_LIFEPOTION:
+ case SC_L_LIFEPOTION:
+ case SC_BOSSMAPINFO:
+ case SC_STUN:
+ case SC_SLEEP:
+ case SC_POISON:
+ case SC_CURSE:
+ case SC_SILENCE:
+ case SC_CONFUSION:
+ case SC_BLIND:
+ case SC_BLEEDING:
+ case SC_DPOISON:
+ case SC_CLOSECONFINE2: //Can't be re-closed in.
+ case SC_MARIONETTE:
+ case SC_MARIONETTE2:
+ case SC_NOCHAT:
+ case SC_CHANGE: //Otherwise your Hp/Sp would get refilled while still within effect of the last invocation.
+ case SC__INVISIBILITY:
+ case SC__ENERVATION:
+ case SC__GROOMY:
+ case SC__IGNORANCE:
+ case SC__LAZINESS:
+ case SC__WEAKNESS:
+ case SC__UNLUCKY:
+ return 0;
+ case SC_COMBO:
+ case SC_DANCING:
+ case SC_DEVOTION:
+ case SC_ASPDPOTION0:
+ case SC_ASPDPOTION1:
+ case SC_ASPDPOTION2:
+ case SC_ASPDPOTION3:
+ case SC_ATKPOTION:
+ case SC_MATKPOTION:
+ case SC_ENCHANTARMS:
+ case SC_ARMOR_ELEMENT:
+ case SC_ARMOR_RESIST:
+ break;
+ case SC_GOSPEL:
+ //Must not override a casting gospel char.
+ if(sce->val4 == BCT_SELF)
+ return 0;
+ if(sce->val1 > val1)
+ return 1;
+ break;
+ case SC_ENDURE:
+ if(sce->val4 && !val4)
+ return 1; //Don't let you override infinite endure.
+ if(sce->val1 > val1)
+ return 1;
+ break;
+ case SC_KAAHI:
+ //Kaahi overwrites previous level regardless of existing level.
+ //Delete timer if it exists.
+ if (sce->val4 != INVALID_TIMER) {
+ delete_timer(sce->val4,kaahi_heal_timer);
+ sce->val4 = INVALID_TIMER;
+ }
+ break;
+ case SC_JAILED:
+ //When a player is already jailed, do not edit the jail data.
+ val2 = sce->val2;
+ val3 = sce->val3;
+ val4 = sce->val4;
+ break;
+ case SC_LERADSDEW:
+ if (sc && (sc->data[SC_BERSERK] || sc->data[SC__BLOODYLUST]))
+ return 0;
+ case SC_SHAPESHIFT:
+ case SC_PROPERTYWALK:
+ break;
+ case SC_LEADERSHIP:
+ case SC_GLORYWOUNDS:
+ case SC_SOULCOLD:
+ case SC_HAWKEYES:
+ if( sce->val4 && !val4 )//you cannot override master guild aura
+ return 0;
+ break;
+ case SC_JOINTBEAT:
+ val2 |= sce->val2; // stackable ailments
+ default:
+ if(sce->val1 > val1)
+ return 1; //Return true to not mess up skill animations. [Skotlex]
+ }
+ }
+
+ vd = status_get_viewdata(bl);
+ calc_flag = StatusChangeFlagTable[type];
+ if(!(flag&4)) //&4 - Do not parse val settings when loading SCs
+ switch(type)
+ {
+ case SC_DECREASEAGI:
+ case SC_INCREASEAGI:
+ val2 = 2 + val1; //Agi change
+ break;
+ case SC_ENDURE:
+ val2 = 7; // Hit-count [Celest]
+ if( !(flag&1) && (bl->type&(BL_PC|BL_MER)) && !map_flag_gvg(bl->m) && !map[bl->m].flag.battleground && !val4 )
+ {
+ struct map_session_data *tsd;
+ if( sd )
+ {
+ int i;
+ for( i = 0; i < 5; i++ )
+ {
+ if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
+ status_change_start(&tsd->bl, type, 10000, val1, val2, val3, val4, tick, 1);
+ }
+ }
+ else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
+ status_change_start(&tsd->bl, type, 10000, val1, val2, val3, val4, tick, 1);
+ }
+ //val4 signals infinite endure (if val4 == 2 it is infinite endure from Berserk)
+ if( val4 )
+ tick = -1;
+ break;
+ case SC_AUTOBERSERK:
+ if (status->hp < status->max_hp>>2 &&
+ (!sc->data[SC_PROVOKE] || sc->data[SC_PROVOKE]->val2==0))
+ sc_start4(bl,SC_PROVOKE,100,10,1,0,0,60000);
+ tick = -1;
+ break;
+ case SC_SIGNUMCRUCIS:
+ val2 = 10 + 4*val1; //Def reduction
+ tick = -1;
+ clif_emotion(bl,E_SWT);
+ break;
+ case SC_MAXIMIZEPOWER:
+ tick_time = val2 = tick>0?tick:60000;
+ tick = -1; // duration sent to the client should be infinite
+ break;
+ case SC_EDP: // [Celest]
+ val2 = val1 + 2; //Chance to Poison enemies.
+#ifndef RENEWAL_EDP
+ val3 = 50*(val1+1); //Damage increase (+50 +50*lv%)
+#endif
+ if( sd )//[Ind] - iROwiki says each level increases its duration by 3 seconds
+ tick += pc_checkskill(sd,GC_RESEARCHNEWPOISON)*3000;
+ break;
+ case SC_POISONREACT:
+ val2=(val1+1)/2 + val1/10; // Number of counters [Skotlex]
+ val3=50; // + 5*val1; //Chance to counter. [Skotlex]
+ break;
+ case SC_MAGICROD:
+ val2 = val1*20; //SP gained
+ break;
+ case SC_KYRIE:
+ val2 = (int64)status->max_hp * (val1 * 2 + 10) / 100; //%Max HP to absorb
+ val3 = (val1 / 2 + 5); //Hits
+ break;
+ case SC_MAGICPOWER:
+ //val1: Skill lv
+ val2 = 1; //Lasts 1 invocation
+ val3 = 5*val1; //Matk% increase
+ val4 = 0; // 0 = ready to be used, 1 = activated and running
+ break;
+ case SC_SACRIFICE:
+ val2 = 5; //Lasts 5 hits
+ tick = -1;
+ break;
+ case SC_ENCPOISON:
+ val2= 250+50*val1; //Poisoning Chance (2.5+0.5%) in 1/10000 rate
+ case SC_ASPERSIO:
+ case SC_FIREWEAPON:
+ case SC_WATERWEAPON:
+ case SC_WINDWEAPON:
+ case SC_EARTHWEAPON:
+ case SC_SHADOWWEAPON:
+ case SC_GHOSTWEAPON:
+ skill_enchant_elemental_end(bl,type);
+ break;
+ case SC_ELEMENTALCHANGE:
+ // val1 : Element Lvl (if called by skill lvl 1, takes random value between 1 and 4)
+ // val2 : Element (When no element, random one is picked)
+ // val3 : 0 = called by skill 1 = called by script (fixed level)
+ if( !val2 ) val2 = rnd()%ELE_MAX;
+
+ if( val1 == 1 && val3 == 0 )
+ val1 = 1 + rnd()%4;
+ else if( val1 > 4 )
+ val1 = 4; // Max Level
+ val3 = 0; // Not need to keep this info.
+ break;
+ case SC_PROVIDENCE:
+ val2=val1*5; //Race/Ele resist
+ break;
+ case SC_REFLECTSHIELD:
+ val2=10+val1*3; // %Dmg reflected
+ if( !(flag&1) && (bl->type&(BL_PC|BL_MER)) )
+ {
+ struct map_session_data *tsd;
+ if( sd )
+ {
+ int i;
+ for( i = 0; i < 5; i++ )
+ {
+ if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
+ status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
+ }
+ }
+ else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
+ status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
+ }
+ break;
+ case SC_STRIPWEAPON:
+ if (!sd) //Watk reduction
+ val2 = 25;
+ break;
+ case SC_STRIPSHIELD:
+ if (!sd) //Def reduction
+ val2 = 15;
+ break;
+ case SC_STRIPARMOR:
+ if (!sd) //Vit reduction
+ val2 = 40;
+ break;
+ case SC_STRIPHELM:
+ if (!sd) //Int reduction
+ val2 = 40;
+ break;
+ case SC_AUTOSPELL:
+ //Val1 Skill LV of Autospell
+ //Val2 Skill ID to cast
+ //Val3 Max Lv to cast
+ val4 = 5 + val1*2; //Chance of casting
+ break;
+ case SC_VOLCANO:
+ val2 = val1*10; //Watk increase
+#ifndef RENEWAL
+ if (status->def_ele != ELE_FIRE)
+ val2 = 0;
+#endif
+ break;
+ case SC_VIOLENTGALE:
+ val2 = val1*3; //Flee increase
+ #ifndef RENEWAL
+ if (status->def_ele != ELE_WIND)
+ val2 = 0;
+ #endif
+ break;
+ case SC_DELUGE:
+ val2 = deluge_eff[val1-1]; //HP increase
+#ifndef RENEWAL
+ if(status->def_ele != ELE_WATER)
+ val2 = 0;
+#endif
+ break;
+ case SC_SUITON:
+ if (!val2 || (sd && (sd->class_&MAPID_UPPERMASK) == MAPID_NINJA)) {
+ //No penalties.
+ val2 = 0; //Agi penalty
+ val3 = 0; //Walk speed penalty
+ break;
+ }
+ val3 = 50;
+ val2 = 3*((val1+1)/3);
+ if (val1 > 4) val2--;
+ break;
+ case SC_ONEHAND:
+ case SC_TWOHANDQUICKEN:
+ val2 = 300;
+ if (val1 > 10) //For boss casted skills [Skotlex]
+ val2 += 20*(val1-10);
+ break;
+ case SC_MERC_QUICKEN:
+ val2 = 300;
+ break;
+#ifndef RENEWAL
+ case SC_SPEARQUICKEN:
+ val2 = 200+10*val1;
+ break;
+#endif
+ case SC_DANCING:
+ //val1 : Skill ID + LV
+ //val2 : Skill Group of the Dance.
+ //val3 : Brings the skill_lv (merged into val1 here)
+ //val4 : Partner
+ if (val1 == CG_MOONLIT)
+ clif_status_change(bl,SI_MOONLIT,1,tick,0, 0, 0);
+ val1|= (val3<<16);
+ val3 = tick/1000; //Tick duration
+ tick_time = 1000; // [GodLesZ] tick time
+ break;
+ case SC_LONGING:
+ val2 = 500-100*val1; //Aspd penalty.
+ break;
+ case SC_EXPLOSIONSPIRITS:
+ val2 = 75 + 25*val1; //Cri bonus
+ break;
+
+ case SC_ASPDPOTION0:
+ case SC_ASPDPOTION1:
+ case SC_ASPDPOTION2:
+ case SC_ASPDPOTION3:
+ val2 = 50*(2+type-SC_ASPDPOTION0);
+ break;
+
+ case SC_WEDDING:
+ case SC_XMAS:
+ case SC_SUMMER:
+ if (!vd) return 0;
+ //Store previous values as they could be removed.
+ val1 = vd->class_;
+ val2 = vd->weapon;
+ val3 = vd->shield;
+ val4 = vd->cloth_color;
+ unit_stop_attack(bl);
+ clif_changelook(bl,LOOK_WEAPON,0);
+ clif_changelook(bl,LOOK_SHIELD,0);
+ clif_changelook(bl,LOOK_BASE,type==SC_WEDDING?JOB_WEDDING:type==SC_XMAS?JOB_XMAS:JOB_SUMMER);
+ clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color);
+ break;
+ case SC_NOCHAT:
+ // [GodLesZ] FIXME: is this correct? a hardcoded interval of 60sec? what about configuration ?_?
+ tick = 60000;
+ val1 = battle_config.manner_system; //Mute filters.
+ if (sd)
+ {
+ clif_changestatus(sd,SP_MANNER,sd->status.manner);
+ clif_updatestatus(sd,SP_MANNER);
+ }
+ break;
+
+ case SC_STONE:
+ val3 = tick/1000; //Petrified HP-damage iterations.
+ if(val3 < 1) val3 = 1;
+ tick = val4; //Petrifying time.
+ tick = max(tick, 1000); //Min time
+ calc_flag = 0; //Actual status changes take effect on petrified state.
+ break;
+
+ case SC_DPOISON:
+ //Lose 10/15% of your life as long as it doesn't brings life below 25%
+ if (status->hp > status->max_hp>>2) {
+ int diff = status->max_hp*(bl->type==BL_PC?10:15)/100;
+ if (status->hp - diff < status->max_hp>>2)
+ diff = status->hp - (status->max_hp>>2);
+ if( val2 && bl->type == BL_MOB ) {
+ struct block_list* src = map_id2bl(val2);
+ if( src )
+ mob_log_damage((TBL_MOB*)bl,src,diff);
+ }
+ status_zap(bl, diff, 0);
+ }
+ // fall through
+ case SC_POISON:
+ val3 = tick/1000; //Damage iterations
+ if(val3 < 1) val3 = 1;
+ tick_time = 1000; // [GodLesZ] tick time
+ //val4: HP damage
+ if (bl->type == BL_PC)
+ val4 = (type == SC_DPOISON) ? 3 + status->max_hp/50 : 3 + status->max_hp*3/200;
+ else
+ val4 = (type == SC_DPOISON) ? 3 + status->max_hp/100 : 3 + status->max_hp/200;
+
+ break;
+ case SC_CONFUSION:
+ clif_emotion(bl,E_WHAT);
+ break;
+ case SC_BLEEDING:
+ val4 = tick/10000;
+ if (!val4) val4 = 1;
+ tick_time = 10000; // [GodLesZ] tick time
+ break;
+ case SC_S_LIFEPOTION:
+ case SC_L_LIFEPOTION:
+ if( val1 == 0 ) return 0;
+ // val1 = heal percent/amout
+ // val2 = seconds between heals
+ // val4 = total of heals
+ if( val2 < 1 ) val2 = 1;
+ if( (val4 = tick/(val2 * 1000)) < 1 )
+ val4 = 1;
+ tick_time = val2 * 1000; // [GodLesZ] tick time
+ break;
+ case SC_BOSSMAPINFO:
+ if( sd != NULL )
+ {
+ struct mob_data *boss_md = map_getmob_boss(bl->m); // Search for Boss on this Map
+ if( boss_md == NULL || boss_md->bl.prev == NULL )
+ { // No MVP on this map - MVP is dead
+ clif_bossmapinfo(sd->fd, boss_md, 1);
+ return 0; // No need to start SC
+ }
+ val1 = boss_md->bl.id;
+ if( (val4 = tick/1000) < 1 )
+ val4 = 1;
+ tick_time = 1000; // [GodLesZ] tick time
+ }
+ break;
+ case SC_HIDING:
+ val2 = tick/1000;
+ tick_time = 1000; // [GodLesZ] tick time
+ val3 = 0; // unused, previously speed adjustment
+ val4 = val1+3; //Seconds before SP substraction happen.
+ break;
+ case SC_CHASEWALK:
+ val2 = tick>0?tick:10000; //Interval at which SP is drained.
+ val3 = 35 - 5 * val1; //Speed adjustment.
+ if (sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_ROGUE)
+ val3 -= 40;
+ val4 = 10+val1*2; //SP cost.
+ if (map_flag_gvg(bl->m) || map[bl->m].flag.battleground) val4 *= 5;
+ break;
+ case SC_CLOAKING:
+ if (!sd) //Monsters should be able to walk with no penalties. [Skotlex]
+ val1 = 10;
+ tick_time = val2 = tick>0?tick:60000; //SP consumption rate.
+ tick = -1; // duration sent to the client should be infinite
+ val3 = 0; // unused, previously walk speed adjustment
+ //val4&1 signals the presence of a wall.
+ //val4&2 makes cloak not end on normal attacks [Skotlex]
+ //val4&4 makes cloak not end on using skills
+ if (bl->type == BL_PC || (bl->type == BL_MOB && ((TBL_MOB*)bl)->special_state.clone) ) //Standard cloaking.
+ val4 |= battle_config.pc_cloak_check_type&7;
+ else
+ val4 |= battle_config.monster_cloak_check_type&7;
+ break;
+ case SC_SIGHT: /* splash status */
+ case SC_RUWACH:
+ case SC_SIGHTBLASTER:
+ val3 = skill_get_splash(val2, val1); //Val2 should bring the skill-id.
+ val2 = tick/250;
+ tick_time = 10; // [GodLesZ] tick time
+ break;
+
+ //Permanent effects.
+ case SC_AETERNA:
+ case SC_MODECHANGE:
+ case SC_WEIGHT50:
+ case SC_WEIGHT90:
+ case SC_BROKENWEAPON:
+ case SC_BROKENARMOR:
+ case SC_READYSTORM:
+ case SC_READYDOWN:
+ case SC_READYCOUNTER:
+ case SC_READYTURN:
+ case SC_DODGE:
+ case SC_PUSH_CART:
+ tick = -1;
+ break;
+
+ case SC_AUTOGUARD:
+ if( !(flag&1) )
+ {
+ struct map_session_data *tsd;
+ int i,t;
+ for( i = val2 = 0; i < val1; i++)
+ {
+ t = 5-(i>>1);
+ val2 += (t < 0)? 1:t;
+ }
+
+ if( bl->type&(BL_PC|BL_MER) )
+ {
+ if( sd )
+ {
+ for( i = 0; i < 5; i++ )
+ {
+ if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
+ status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
+ }
+ }
+ else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
+ status_change_start(&tsd->bl, type, 10000, val1, val2, 0, 0, tick, 1);
+ }
+ }
+ break;
+
+ case SC_DEFENDER:
+ if (!(flag&1))
+ {
+ val2 = 5 + 15*val1; //Damage reduction
+ val3 = 0; // unused, previously speed adjustment
+ val4 = 250 - 50*val1; //Aspd adjustment
+
+ if (sd)
+ {
+ struct map_session_data *tsd;
+ int i;
+ for (i = 0; i < 5; i++)
+ { //See if there are devoted characters, and pass the status to them. [Skotlex]
+ if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])))
+ status_change_start(&tsd->bl,type,10000,val1,5+val1*5,val3,val4,tick,1);
+ }
+ }
+ }
+ break;
+
+ case SC_TENSIONRELAX:
+ if (sd) {
+ pc_setsit(sd);
+ clif_sitting(&sd->bl);
+ }
+ val2 = 12; //SP cost
+ val4 = 10000; //Decrease at 10secs intervals.
+ val3 = tick/val4;
+ tick = -1; // duration sent to the client should be infinite
+ tick_time = val4; // [GodLesZ] tick time
+ break;
+ case SC_PARRYING:
+ val2 = 20 + val1*3; //Block Chance
+ break;
+
+ case SC_WINDWALK:
+ val2 = (val1+1)/2; // Flee bonus is 1/1/2/2/3/3/4/4/5/5
+ break;
+
+ case SC_JOINTBEAT:
+ if( val2&BREAK_NECK )
+ sc_start(bl,SC_BLEEDING,100,val1,skill_get_time2(status_sc2skill(type),val1));
+ break;
+
+ case SC_BERSERK:
+ if (!sc->data[SC_ENDURE] || !sc->data[SC_ENDURE]->val4)
+ sc_start4(bl, SC_ENDURE, 100,10,0,0,2, tick);
+ case SC__BLOODYLUST:
+ //HP healing is performing after the calc_status call.
+ //Val2 holds HP penalty
+ if (!val4) val4 = skill_get_time2(status_sc2skill(type),val1);
+ if (!val4) val4 = 10000; //Val4 holds damage interval
+ val3 = tick/val4; //val3 holds skill duration
+ tick_time = val4; // [GodLesZ] tick time
+ break;
+
+ case SC_GOSPEL:
+ if(val4 == BCT_SELF) { // self effect
+ val2 = tick/10000;
+ tick_time = 10000; // [GodLesZ] tick time
+ status_change_clear_buffs(bl,3); //Remove buffs/debuffs
+ }
+ break;
+
+ case SC_MARIONETTE:
+ {
+ int stat;
+
+ val3 = 0;
+ val4 = 0;
+ stat = ( sd ? sd->status.str : status_get_base_status(bl)->str ) / 2; val3 |= cap_value(stat,0,0xFF)<<16;
+ stat = ( sd ? sd->status.agi : status_get_base_status(bl)->agi ) / 2; val3 |= cap_value(stat,0,0xFF)<<8;
+ stat = ( sd ? sd->status.vit : status_get_base_status(bl)->vit ) / 2; val3 |= cap_value(stat,0,0xFF);
+ stat = ( sd ? sd->status.int_: status_get_base_status(bl)->int_) / 2; val4 |= cap_value(stat,0,0xFF)<<16;
+ stat = ( sd ? sd->status.dex : status_get_base_status(bl)->dex ) / 2; val4 |= cap_value(stat,0,0xFF)<<8;
+ stat = ( sd ? sd->status.luk : status_get_base_status(bl)->luk ) / 2; val4 |= cap_value(stat,0,0xFF);
+ break;
+ }
+ case SC_MARIONETTE2:
+ {
+ int stat,max_stat;
+ // fetch caster information
+ struct block_list *pbl = map_id2bl(val1);
+ struct status_change *psc = pbl?status_get_sc(pbl):NULL;
+ struct status_change_entry *psce = psc?psc->data[SC_MARIONETTE]:NULL;
+ // fetch target's stats
+ struct status_data* status = status_get_status_data(bl); // battle status
+
+ if (!psce)
+ return 0;
+
+ val3 = 0;
+ val4 = 0;
+ max_stat = battle_config.max_parameter; //Cap to 99 (default)
+ stat = (psce->val3 >>16)&0xFF; stat = min(stat, max_stat - status->str ); val3 |= cap_value(stat,0,0xFF)<<16;
+ stat = (psce->val3 >> 8)&0xFF; stat = min(stat, max_stat - status->agi ); val3 |= cap_value(stat,0,0xFF)<<8;
+ stat = (psce->val3 >> 0)&0xFF; stat = min(stat, max_stat - status->vit ); val3 |= cap_value(stat,0,0xFF);
+ stat = (psce->val4 >>16)&0xFF; stat = min(stat, max_stat - status->int_); val4 |= cap_value(stat,0,0xFF)<<16;
+ stat = (psce->val4 >> 8)&0xFF; stat = min(stat, max_stat - status->dex ); val4 |= cap_value(stat,0,0xFF)<<8;
+ stat = (psce->val4 >> 0)&0xFF; stat = min(stat, max_stat - status->luk ); val4 |= cap_value(stat,0,0xFF);
+ break;
+ }
+ case SC_REJECTSWORD:
+ val2 = 15*val1; //Reflect chance
+ val3 = 3; //Reflections
+ tick = -1;
+ break;
+
+ case SC_MEMORIZE:
+ val2 = 5; //Memorized casts.
+ tick = -1;
+ break;
+
+ case SC_GRAVITATION:
+ val2 = 50*val1; //aspd reduction
+ break;
+
+ case SC_REGENERATION:
+ if (val1 == 1)
+ val2 = 2;
+ else
+ val2 = val1; //HP Regerenation rate: 200% 200% 300%
+ val3 = val1; //SP Regeneration Rate: 100% 200% 300%
+ //if val4 comes set, this blocks regen rather than increase it.
+ break;
+
+ case SC_DEVOTION:
+ {
+ struct block_list *d_bl;
+ struct status_change *d_sc;
+
+ if( (d_bl = map_id2bl(val1)) && (d_sc = status_get_sc(d_bl)) && d_sc->count )
+ { // Inherits Status From Source
+ const enum sc_type types[] = { SC_AUTOGUARD, SC_DEFENDER, SC_REFLECTSHIELD, SC_ENDURE };
+ enum sc_type type2;
+ int i = (map_flag_gvg(bl->m) || map[bl->m].flag.battleground)?2:3;
+ while( i >= 0 )
+ {
+ type2 = types[i];
+ if( d_sc->data[type2] )
+ sc_start(bl, type2, 100, d_sc->data[type2]->val1, skill_get_time(status_sc2skill(type2),d_sc->data[type2]->val1));
+ i--;
+ }
+ }
+ break;
+ }
+
+ case SC_COMA: //Coma. Sends a char to 1HP. If val2, do not zap sp
+ if( val3 && bl->type == BL_MOB ) {
+ struct block_list* src = map_id2bl(val3);
+ if( src )
+ mob_log_damage((TBL_MOB*)bl,src,status->hp - 1);
+ }
+ status_zap(bl, status->hp-1, val2?0:status->sp);
+ return 1;
+ break;
+ case SC_CLOSECONFINE2:
+ {
+ struct block_list *src = val2?map_id2bl(val2):NULL;
+ struct status_change *sc2 = src?status_get_sc(src):NULL;
+ struct status_change_entry *sce2 = sc2?sc2->data[SC_CLOSECONFINE]:NULL;
+ if (src && sc2) {
+ if (!sce2) //Start lock on caster.
+ sc_start4(src,SC_CLOSECONFINE,100,val1,1,0,0,tick+1000);
+ else { //Increase count of locked enemies and refresh time.
+ (sce2->val2)++;
+ delete_timer(sce2->timer, status_change_timer);
+ sce2->timer = add_timer(gettick()+tick+1000, status_change_timer, src->id, SC_CLOSECONFINE);
+ }
+ } else //Status failed.
+ return 0;
+ }
+ break;
+ case SC_KAITE:
+ val2 = 1+val1/5; //Number of bounces: 1 + skill_lv/5
+ break;
+ case SC_KAUPE:
+ switch (val1) {
+ case 3: //33*3 + 1 -> 100%
+ val2++;
+ case 1:
+ case 2: //33, 66%
+ val2 += 33*val1;
+ val3 = 1; //Dodge 1 attack total.
+ break;
+ default: //Custom. For high level mob usage, higher level means more blocks. [Skotlex]
+ val2 = 100;
+ val3 = val1-2;
+ break;
+ }
+ break;
+
+ case SC_COMBO: {
+ //val1: Skill ID
+ //val2: When given, target (for autotargetting skills)
+ //val3: When set, this combo time should NOT delay attack/movement
+ //val3: TK: Last used kick
+ //val4: TK: Combo time
+ struct unit_data *ud = unit_bl2ud(bl);
+ if (ud && !val3) {
+ tick += 300 * battle_config.combo_delay_rate/100;
+ ud->attackabletime = gettick()+tick;
+ unit_set_walkdelay(bl, gettick(), tick, 1);
+ }
+ val3 = 0;
+ val4 = tick;
+ }
+ break;
+ case SC_EARTHSCROLL:
+ val2 = 11-val1; //Chance to consume: 11-skill_lv%
+ break;
+ case SC_RUN:
+ val4 = gettick(); //Store time at which you started running.
+ tick = -1;
+ break;
+ case SC_KAAHI:
+ val2 = 200*val1; //HP heal
+ val3 = 5*val1; //SP cost
+ val4 = INVALID_TIMER; //Kaahi Timer.
+ break;
+ case SC_BLESSING:
+ if ((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC)
+ val2 = val1;
+ else
+ val2 = 0; //0 -> Half stat.
+ break;
+ case SC_TRICKDEAD:
+ if (vd) vd->dead_sit = 1;
+ tick = -1;
+ break;
+ case SC_CONCENTRATE:
+ val2 = 2 + val1;
+ if (sd) { //Store the card-bonus data that should not count in the %
+ val3 = sd->param_bonus[1]; //Agi
+ val4 = sd->param_bonus[4]; //Dex
+ } else {
+ val3 = val4 = 0;
+ }
+ break;
+ case SC_MAXOVERTHRUST:
+ val2 = 20*val1; //Power increase
+ break;
+ case SC_OVERTHRUST:
+ //val2 holds if it was casted on self, or is bonus received from others
+ val3 = 5*val1; //Power increase
+ if(sd && pc_checkskill(sd,BS_HILTBINDING)>0)
+ tick += tick / 10;
+ break;
+ case SC_ADRENALINE2:
+ case SC_ADRENALINE:
+ val3 = (val2) ? 300 : 200; // aspd increase
+ case SC_WEAPONPERFECTION:
+ if(sd && pc_checkskill(sd,BS_HILTBINDING)>0)
+ tick += tick / 10;
+ break;
+ case SC_CONCENTRATION:
+ val2 = 5*val1; //Batk/Watk Increase
+ val3 = 10*val1; //Hit Increase
+ val4 = 5*val1; //Def reduction
+ break;
+ case SC_ANGELUS:
+ val2 = 5*val1; //def increase
+ break;
+ case SC_IMPOSITIO:
+ val2 = 5*val1; //watk increase
+ break;
+ case SC_MELTDOWN:
+ val2 = 100*val1; //Chance to break weapon
+ val3 = 70*val1; //Change to break armor
+ break;
+ case SC_TRUESIGHT:
+ val2 = 10*val1; //Critical increase
+ val3 = 3*val1; //Hit increase
+ break;
+ case SC_SUN_COMFORT:
+ val2 = (status_get_lv(bl) + status->dex + status->luk)/2; //def increase
+ break;
+ case SC_MOON_COMFORT:
+ val2 = (status_get_lv(bl) + status->dex + status->luk)/10; //flee increase
+ break;
+ case SC_STAR_COMFORT:
+ val2 = (status_get_lv(bl) + status->dex + status->luk); //Aspd increase
+ break;
+ case SC_QUAGMIRE:
+ val2 = (sd?5:10)*val1; //Agi/Dex decrease.
+ break;
+
+ // gs_something1 [Vicious]
+ case SC_GATLINGFEVER:
+ val2 = 20*val1; //Aspd increase
+ val3 = 20+10*val1; //Batk increase
+ val4 = 5*val1; //Flee decrease
+ break;
+
+ case SC_FLING:
+ if (bl->type == BL_PC)
+ val2 = 0; //No armor reduction to players.
+ else
+ val2 = 5*val1; //Def reduction
+ val3 = 5*val1; //Def2 reduction
+ break;
+ case SC_PROVOKE:
+ //val2 signals autoprovoke.
+ val3 = 2+3*val1; //Atk increase
+ val4 = 5+5*val1; //Def reduction.
+ break;
+ case SC_AVOID:
+ //val2 = 10*val1; //Speed change rate.
+ break;
+ case SC_DEFENCE:
+ val2 = 2*val1; //Def bonus
+ break;
+ case SC_BLOODLUST:
+ val2 = 20+10*val1; //Atk rate change.
+ val3 = 3*val1; //Leech chance
+ val4 = 20; //Leech percent
+ break;
+ case SC_FLEET:
+ val2 = 30*val1; //Aspd change
+ val3 = 5+5*val1; //bAtk/wAtk rate change
+ break;
+ case SC_MINDBREAKER:
+ val2 = 20*val1; //matk increase.
+ val3 = 12*val1; //mdef2 reduction.
+ break;
+ case SC_SKA:
+ val2 = tick/1000;
+ val3 = rnd()%100; //Def changes randomly every second...
+ tick_time = 1000; // [GodLesZ] tick time
+ break;
+ case SC_JAILED:
+ //Val1 is duration in minutes. Use INT_MAX to specify 'unlimited' time.
+ tick = val1>0?1000:250;
+ if (sd)
+ {
+ if (sd->mapindex != val2)
+ {
+ int pos = (bl->x&0xFFFF)|(bl->y<<16), //Current Coordinates
+ map = sd->mapindex; //Current Map
+ //1. Place in Jail (val2 -> Jail Map, val3 -> x, val4 -> y
+ pc_setpos(sd,(unsigned short)val2,val3,val4, CLR_TELEPORT);
+ //2. Set restore point (val3 -> return map, val4 return coords
+ val3 = map;
+ val4 = pos;
+ } else if (!val3 || val3 == sd->mapindex) { //Use save point.
+ val3 = sd->status.save_point.map;
+ val4 = (sd->status.save_point.x&0xFFFF)
+ |(sd->status.save_point.y<<16);
+ }
+ }
+ break;
+ case SC_UTSUSEMI:
+ val2=(val1+1)/2; // number of hits blocked
+ val3=skill_get_blewcount(NJ_UTSUSEMI, val1); //knockback value.
+ break;
+ case SC_BUNSINJYUTSU:
+ val2=(val1+1)/2; // number of hits blocked
+ break;
+ case SC_CHANGE:
+ val2= 30*val1; //Vit increase
+ val3= 20*val1; //Int increase
+ break;
+ case SC_SWOO:
+ if(status->mode&MD_BOSS)
+ tick /= 5; //TODO: Reduce skill's duration. But for how long?
+ break;
+ case SC_SPIDERWEB:
+ if( bl->type == BL_PC )
+ tick /= 2;
+ break;
+ case SC_ARMOR:
+ //NPC_DEFENDER:
+ val2 = 80; //Damage reduction
+ //Attack requirements to be blocked:
+ val3 = BF_LONG; //Range
+ val4 = BF_WEAPON|BF_MISC; //Type
+ break;
+ case SC_ENCHANTARMS:
+ //end previous enchants
+ skill_enchant_elemental_end(bl,type);
+ //Make sure the received element is valid.
+ if (val2 >= ELE_MAX)
+ val2 = val2%ELE_MAX;
+ else if (val2 < 0)
+ val2 = rnd()%ELE_MAX;
+ break;
+ case SC_CRITICALWOUND:
+ val2 = 20*val1; //Heal effectiveness decrease
+ break;
+ case SC_MAGICMIRROR:
+ case SC_SLOWCAST:
+ val2 = 20*val1; //Magic reflection/cast rate
+ break;
+
+ case SC_ARMORCHANGE:
+ if (val2 == NPC_ANTIMAGIC)
+ { //Boost mdef
+ val2 =-20;
+ val3 = 20;
+ } else { //Boost def
+ val2 = 20;
+ val3 =-20;
+ }
+ val2*=val1; //20% per level
+ val3*=val1;
+ break;
+ case SC_EXPBOOST:
+ case SC_JEXPBOOST:
+ if (val1 < 0)
+ val1 = 0;
+ break;
+ case SC_INCFLEE2:
+ case SC_INCCRI:
+ val2 = val1*10; //Actual boost (since 100% = 1000)
+ break;
+ case SC_SUFFRAGIUM:
+ val2 = 15 * val1; //Speed cast decrease
+ break;
+ case SC_INCHEALRATE:
+ if (val1 < 1)
+ val1 = 1;
+ break;
+ case SC_HALLUCINATION:
+ val2 = 5+val1; //Factor by which displayed damage is increased by
+ break;
+ case SC_DOUBLECAST:
+ val2 = 30+10*val1; //Trigger rate
+ break;
+ case SC_KAIZEL:
+ val2 = 10*val1; //% of life to be revived with
+ break;
+ // case SC_ARMOR_ELEMENT:
+ // case SC_ARMOR_RESIST:
+ // Mod your resistance against elements:
+ // val1 = water | val2 = earth | val3 = fire | val4 = wind
+ // break;
+ //case ????:
+ //Place here SCs that have no SCB_* data, no skill associated, no ICON
+ //associated, and yet are not wrong/unknown. [Skotlex]
+ //break;
+
+ case SC_MERC_FLEEUP:
+ case SC_MERC_ATKUP:
+ case SC_MERC_HITUP:
+ val2 = 15 * val1;
+ break;
+ case SC_MERC_HPUP:
+ case SC_MERC_SPUP:
+ val2 = 5 * val1;
+ break;
+ case SC_REBIRTH:
+ val2 = 20*val1; //% of life to be revived with
+ break;
+
+ case SC_MANU_DEF:
+ case SC_MANU_ATK:
+ case SC_MANU_MATK:
+ val2 = 1; // Manuk group
+ break;
+ case SC_SPL_DEF:
+ case SC_SPL_ATK:
+ case SC_SPL_MATK:
+ val2 = 2; // Splendide group
+ break;
+ /**
+ * General
+ **/
+ case SC_FEAR:
+ val2 = 2;
+ val4 = tick / 1000;
+ tick_time = 1000; // [GodLesZ] tick time
+ break;
+ case SC_BURNING:
+ val4 = tick / 2000; // Total Ticks to Burn!!
+ tick_time = 2000; // [GodLesZ] tick time
+ break;
+ /**
+ * Rune Knight
+ **/
+ case SC_DEATHBOUND:
+ val2 = 500 + 100 * val1;
+ break;
+ case SC_FIGHTINGSPIRIT:
+ val_flag |= 1|2;
+ break;
+ case SC_ABUNDANCE:
+ val4 = tick / 10000;
+ tick_time = 10000; // [GodLesZ] tick time
+ break;
+ case SC_GIANTGROWTH:
+ val2 = 10; // Triple damage success rate.
+ break;
+ /**
+ * Arch Bishop
+ **/
+ case SC_RENOVATIO:
+ val4 = tick / 5000;
+ tick_time = 5000;
+ break;
+ case SC_SECRAMENT:
+ val2 = 10 * val1;
+ break;
+ case SC_VENOMIMPRESS:
+ val2 = 10 * val1;
+ val_flag |= 1|2;
+ break;
+ case SC_POISONINGWEAPON:
+ val_flag |= 1|2|4;
+ break;
+ case SC_WEAPONBLOCKING:
+ val2 = 10 + 2 * val1; // Chance
+ val4 = tick / 3000;
+ tick_time = 3000; // [GodLesZ] tick time
+ val_flag |= 1|2;
+ break;
+ case SC_TOXIN:
+ val4 = tick / 10000;
+ tick_time = 10000; // [GodLesZ] tick time
+ break;
+ case SC_MAGICMUSHROOM:
+ val4 = tick / 4000;
+ tick_time = 4000; // [GodLesZ] tick time
+ break;
+ case SC_PYREXIA:
+ status_change_start(bl,SC_BLIND,10000,val1,0,0,0,30000,11); // Blind status that last for 30 seconds
+ val4 = tick / 3000;
+ tick_time = 3000; // [GodLesZ] tick time
+ break;
+ case SC_LEECHESEND:
+ val4 = tick / 1000;
+ tick_time = 1000; // [GodLesZ] tick time
+ break;
+ case SC_OBLIVIONCURSE:
+ val4 = tick / 3000;
+ tick_time = 3000; // [GodLesZ] tick time
+ break;
+ case SC_ROLLINGCUTTER:
+ val_flag |= 1;
+ break;
+ case SC_CLOAKINGEXCEED:
+ val2 = ( val1 + 1 ) / 2; // Hits
+ val3 = 90 + val1 * 10; // Walk speed
+ val_flag |= 1|2|4;
+ if (bl->type == BL_PC)
+ val4 |= battle_config.pc_cloak_check_type&7;
+ else
+ val4 |= battle_config.monster_cloak_check_type&7;
+ tick_time = 1000; // [GodLesZ] tick time
+ break;
+ case SC_HALLUCINATIONWALK:
+ val2 = 50 * val1; // Evasion rate of physical attacks. Flee
+ val3 = 10 * val1; // Evasion rate of magical attacks.
+ val_flag |= 1|2|4;
+ break;
+ case SC_WHITEIMPRISON:
+ status_change_end(bl, SC_BURNING, INVALID_TIMER);
+ status_change_end(bl, SC_FREEZING, INVALID_TIMER);
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ status_change_end(bl, SC_STONE, INVALID_TIMER);
+ break;
+ case SC_FREEZING:
+ status_change_end(bl, SC_BURNING, INVALID_TIMER);
+ break;
+ case SC_READING_SB:
+ // val2 = sp reduction per second
+ tick_time = 5000; // [GodLesZ] tick time
+ break;
+ case SC_SPHERE_1:
+ case SC_SPHERE_2:
+ case SC_SPHERE_3:
+ case SC_SPHERE_4:
+ case SC_SPHERE_5:
+ if( !sd )
+ return 0; // Should only work on players.
+ val4 = tick / 1000;
+ if( val4 < 1 )
+ val4 = 1;
+ tick_time = 1000; // [GodLesZ] tick time
+ val_flag |= 1;
+ break;
+ case SC_SHAPESHIFT:
+ switch( val1 )
+ {
+ case 1: val2 = ELE_FIRE; break;
+ case 2: val2 = ELE_EARTH; break;
+ case 3: val2 = ELE_WIND; break;
+ case 4: val2 = ELE_WATER; break;
+ }
+ break;
+ case SC_ELECTRICSHOCKER:
+ case SC_CRYSTALIZE:
+ case SC_MEIKYOUSISUI:
+ val4 = tick / 1000;
+ if( val4 < 1 )
+ val4 = 1;
+ tick_time = 1000; // [GodLesZ] tick time
+ break;
+ case SC_CAMOUFLAGE:
+ val4 = tick/1000;
+ tick_time = 1000; // [GodLesZ] tick time
+ break;
+ case SC_WUGDASH:
+ val4 = gettick(); //Store time at which you started running.
+ tick = -1;
+ break;
+ case SC__SHADOWFORM: {
+ struct map_session_data * s_sd = map_id2sd(val2);
+ if( s_sd )
+ s_sd->shadowform_id = bl->id;
+ val4 = tick / 1000;
+ val_flag |= 1|2|4;
+ tick_time = 1000; // [GodLesZ] tick time
+ }
+ break;
+ case SC__STRIPACCESSORY:
+ if (!sd)
+ val2 = 20;
+ break;
+ case SC__INVISIBILITY:
+ val2 = 50 - 10 * val1; // ASPD
+ val3 = 20 * val1; // CRITICAL
+ val4 = tick / 1000;
+ tick_time = 1000; // [GodLesZ] tick time
+ val_flag |= 1|2;
+ break;
+ case SC__ENERVATION:
+ val2 = 20 + 10 * val1; // ATK Reduction
+ val_flag |= 1|2;
+ if( sd ) pc_delspiritball(sd,sd->spiritball,0);
+ break;
+ case SC__GROOMY:
+ val2 = 20 + 10 * val1; //ASPD. Need to confirm if Movement Speed reduction is the same. [Jobbie]
+ val3 = 20 * val1; //HIT
+ val_flag |= 1|2|4;
+ if( sd )
+ { // Removes Animals
+ if( pc_isriding(sd) ) pc_setriding(sd, 0);
+ if( pc_isridingdragon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_DRAGON);
+ if( pc_iswug(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_WUG);
+ if( pc_isridingwug(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_WUGRIDER);
+ if( pc_isfalcon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_FALCON);
+ if( sd->status.pet_id > 0 ) pet_menu(sd, 3);
+ if( merc_is_hom_active(sd->hd) ) merc_hom_vaporize(sd,1);
+ if( sd->md ) merc_delete(sd->md,3);
+ }
+ break;
+ case SC__LAZINESS:
+ val2 = 10 + 10 * val1; // Cast reduction
+ val3 = 10 * val1; // Flee Reduction
+ val_flag |= 1|2|4;
+ break;
+ case SC__UNLUCKY:
+ val2 = 10 * val1; // Crit and Flee2 Reduction
+ val_flag |= 1|2|4;
+ break;
+ case SC__WEAKNESS:
+ val2 = 10 * val1;
+ val_flag |= 1|2;
+ // bypasses coating protection and MADO
+ sc_start(bl,SC_STRIPWEAPON,100,val1,tick);
+ sc_start(bl,SC_STRIPSHIELD,100,val1,tick);
+ break;
+ break;
+ case SC_GN_CARTBOOST:
+ if( val1 < 3 )
+ val2 = 50;
+ else if( val1 < 5 )
+ val2 = 75;
+ else
+ val2 = 100;
+ break;
+ case SC_PROPERTYWALK:
+ val_flag |= 1|2;
+ val3 = 0;
+ break;
+ case SC_WARMER:
+ status_change_end(bl, SC_FREEZE, INVALID_TIMER);
+ status_change_end(bl, SC_FREEZING, INVALID_TIMER);
+ status_change_end(bl, SC_CRYSTALIZE, INVALID_TIMER);
+ break;
+ case SC_STRIKING:
+ val1 = 6 - val1;//spcost = 6 - level (lvl1:5 ... lvl 5: 1)
+ val4 = tick / 1000;
+ tick_time = 1000; // [GodLesZ] tick time
+ break;
+ case SC_BLOODSUCKER:
+ val4 = tick / 1000;
+ tick_time = 1000; // [GodLesZ] tick time
+ break;
+ case SC_VACUUM_EXTREME:
+ tick -= (status->str / 20) * 1000;
+ val4 = val3 = tick / 100;
+ tick_time = 100; // [GodLesZ] tick time
+ break;
+ case SC_SWINGDANCE:
+ val2 = 4 * val1; // Walk speed and aspd reduction.
+ break;
+ case SC_SYMPHONYOFLOVER:
+ case SC_RUSHWINDMILL:
+ case SC_ECHOSONG:
+ val2 = 6 * val1;
+ val2 += val3; //Adding 1% * Lesson Bonus
+ val2 += (int)(val4*2/10); //Adding 0.2% per JobLevel
+ break;
+ case SC_MOONLITSERENADE:
+ val2 = 10 * val1;
+ break;
+ case SC_HARMONIZE:
+ val2 = 5 + 5 * val1;
+ break;
+ case SC_VOICEOFSIREN:
+ val4 = tick / 2000;
+ tick_time = 2000; // [GodLesZ] tick time
+ break;
+ case SC_DEEPSLEEP:
+ val4 = tick / 2000;
+ tick_time = 2000; // [GodLesZ] tick time
+ break;
+ case SC_SIRCLEOFNATURE:
+ val2 = 1 + val1; //SP consume
+ val3 = 40 * val1; //HP recovery
+ val4 = tick / 1000;
+ tick_time = 1000; // [GodLesZ] tick time
+ break;
+ case SC_SONGOFMANA:
+ val3 = 10 + (2 * val2);
+ val4 = tick/3000;
+ tick_time = 3000; // [GodLesZ] tick time
+ break;
+ case SC_SATURDAYNIGHTFEVER:
+ if (!val4) val4 = skill_get_time2(status_sc2skill(type),val1);
+ if (!val4) val4 = 3000;
+ val3 = tick/val4;
+ tick_time = val4; // [GodLesZ] tick time
+ break;
+ case SC_GLOOMYDAY:
+ val2 = 20 + 5 * val1; // Flee reduction.
+ val3 = 15 + 5 * val1; // ASPD reduction.
+ if( sd && rand()%100 < val1 ){ // (Skill Lv) %
+ val4 = 1; // reduce walk speed by half.
+ if( pc_isriding(sd) ) pc_setriding(sd, 0);
+ if( pc_isridingdragon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_DRAGON);
+ }
+ break;
+ case SC_GLOOMYDAY_SK:
+ // Random number between [15 ~ (Voice Lesson Skill Level x 5) + (Skill Level x 10)] %.
+ val2 = 15 + rand()%( (sd?pc_checkskill(sd, WM_LESSON)*5:0) + val1*10 );
+ break;
+ case SC_SITDOWN_FORCE:
+ case SC_BANANA_BOMB_SITDOWN:
+ if( sd && !pc_issit(sd) )
+ {
+ pc_setsit(sd);
+ skill_sit(sd,1);
+ clif_sitting(bl);
+ }
+ break;
+ case SC_DANCEWITHWUG:
+ val3 = (5 * val1) + (1 * val2); //Still need official value.
+ break;
+ case SC_LERADSDEW:
+ val3 = (5 * val1) + (1 * val2);
+ break;
+ case SC_MELODYOFSINK:
+ val3 = (5 * val1) + (1 * val2);
+ break;
+ case SC_BEYONDOFWARCRY:
+ val3 = (5 * val1) + (1 * val2);
+ break;
+ case SC_UNLIMITEDHUMMINGVOICE:
+ {
+ struct unit_data *ud = unit_bl2ud(bl);
+ if( ud == NULL ) return 0;
+ ud->state.skillcastcancel = 0;
+ val3 = 15 - (2 * val2);
+ }
+ break;
+ case SC_REFLECTDAMAGE:
+ val2 = 15 + 5 * val1;
+ val3 = (val1==5)?20:(val1+4)*2; // SP consumption
+ val4 = tick/10000;
+ tick_time = 10000; // [GodLesZ] tick time
+ break;
+ case SC_FORCEOFVANGUARD: // This is not the official way to handle it but I think we should use it. [pakpil]
+ val2 = 20 + 12 * (val1 - 1); // Chance
+ val3 = 5 + (2 * val1); // Max rage counters
+ tick = -1; //endless duration in the client
+ tick_time = 6000; // [GodLesZ] tick time
+ val_flag |= 1|2|4;
+ break;
+ case SC_EXEEDBREAK:
+ val1 *= 150; // 150 * skill_lv
+ if( sd && sd->inventory_data[sd->equip_index[EQI_HAND_R]] ) { // Chars.
+ val1 += (sd->inventory_data[sd->equip_index[EQI_HAND_R]]->weight/10 * sd->inventory_data[sd->equip_index[EQI_HAND_R]]->wlv * status_get_lv(bl) / 100);
+ val1 += 15 * (sd ? sd->status.job_level:50) + 100;
+ }
+ else // Mobs
+ val1 += (400 * status_get_lv(bl) / 100) + (15 * (status_get_lv(bl) / 2)); // About 1138% at mob_lvl 99. Is an aproximation to a standard weapon. [pakpil]
+ break;
+ case SC_PRESTIGE: // Bassed on suggested formula in iRO Wiki and some test, still need more test. [pakpil]
+ val2 = ((status->int_ + status->luk) / 6) + 5; // Chance to evade magic damage.
+ val1 *= 15; // Defence added
+ if( sd )
+ val1 += 10 * pc_checkskill(sd,CR_DEFENDER);
+ val_flag |= 1|2;
+ break;
+ case SC_BANDING:
+ tick_time = 5000; // [GodLesZ] tick time
+ val_flag |= 1;
+ break;
+ case SC_SHIELDSPELL_DEF:
+ case SC_SHIELDSPELL_MDEF:
+ case SC_SHIELDSPELL_REF:
+ val_flag |= 1|2;
+ break;
+ case SC_MAGNETICFIELD:
+ val3 = tick / 1000;
+ tick_time = 1000; // [GodLesZ] tick time
+ break;
+ case SC_INSPIRATION:
+ if( sd )
+ {
+ val2 = (40 * val1) + (3 * sd->status.job_level); // ATK bonus
+ val3 = (sd->status.job_level / 10) * 2 + 12; // All stat bonus
+ }
+ val4 = tick / 1000;
+ tick_time = 1000; // [GodLesZ] tick time
+ status_change_clear_buffs(bl,3); //Remove buffs/debuffs
+ break;
+ case SC_SPELLFIST:
+ case SC_CURSEDCIRCLE_ATKER:
+ val_flag |= 1|2|4;
+ break;
+ case SC_CRESCENTELBOW:
+ val2 = 94 + val1;
+ val_flag |= 1|2;
+ break;
+ case SC_LIGHTNINGWALK: // [(Job Level / 2) + (40 + 5 * Skill Level)] %
+ val1 = (sd?sd->status.job_level:2)/2 + 40 + 5 * val1;
+ val_flag |= 1;
+ break;
+ case SC_RAISINGDRAGON:
+ val3 = tick / 5000;
+ tick_time = 5000; // [GodLesZ] tick time
+ break;
+ case SC_GT_CHANGE:
+ {// take note there is no def increase as skill desc says. [malufett]
+ struct block_list * src;
+ val3 = status->agi * val1 / 60; // ASPD increase: [(Target AGI x Skill Level) / 60] %
+ if( (src = map_id2bl(val2)) )
+ val4 = ( 200/status_get_int(src) ) * val1;// MDEF decrease: MDEF [(200 / Caster INT) x Skill Level]
+ }
+ break;
+ case SC_GT_REVITALIZE:
+ {// take note there is no vit,aspd,speed increase as skill desc says. [malufett]
+ struct block_list * src;
+ val3 = val1 * 30 + 150; // Natural HP recovery increase: [(Skill Level x 30) + 50] %
+ if( (src = map_id2bl(val2)) ) // the stat def is not shown in the status window and it is process differently
+ val4 = ( status_get_vit(src)/4 ) * val1; // STAT DEF increase: [(Caster VIT / 4) x Skill Level]
+ }
+ break;
+ case SC_PYROTECHNIC_OPTION:
+ val_flag |= 1|2|4;
+ break;
+ case SC_HEATER_OPTION:
+ val2 = 120; // Watk. TODO: Renewal (Atk2)
+ val3 = 33; // % Increase effects.
+ val4 = 3; // Change into fire element.
+ val_flag |= 1|2|4;
+ break;
+ case SC_TROPIC_OPTION:
+ val2 = 180; // Watk. TODO: Renewal (Atk2)
+ val3 = MG_FIREBOLT;
+ break;
+ case SC_AQUAPLAY_OPTION:
+ val2 = 40;
+ val_flag |= 1|2|4;
+ break;
+ case SC_COOLER_OPTION:
+ val2 = 80; // % Freezing chance
+ val3 = 33; // % increased damage
+ val4 = 1; // Change into water elemet
+ val_flag |= 1|2|4;
+ break;
+ case SC_CHILLY_AIR_OPTION:
+ val2 = 120; // Matk. TODO: Renewal (Matk1)
+ val3 = MG_COLDBOLT;
+ val_flag |= 1|2;
+ break;
+ case SC_GUST_OPTION:
+ val_flag |= 1|2;
+ break;
+ case SC_WIND_STEP_OPTION:
+ val2 = 50; // % Increase speed and flee.
+ break;
+ case SC_BLAST_OPTION:
+ val2 = 20;
+ val3 = ELE_WIND;
+ val_flag |= 1|2|4;
+ break;
+ case SC_WILD_STORM_OPTION:
+ val2 = MG_LIGHTNINGBOLT;
+ val_flag |= 1|2;
+ break;
+ case SC_PETROLOGY_OPTION:
+ val2 = 5;
+ val3 = 50;
+ val_flag |= 1|2|4;
+ break;
+ case SC_CURSED_SOIL_OPTION:
+ val2 = 10;
+ val3 = 33;
+ val4 = 2;
+ val_flag |= 1|2|4;
+ break;
+ case SC_UPHEAVAL_OPTION:
+ val2 = WZ_EARTHSPIKE;
+ val_flag |= 1|2;
+ break;
+ case SC_CIRCLE_OF_FIRE_OPTION:
+ val2 = 300;
+ val_flag |= 1|2;
+ break;
+ case SC_FIRE_CLOAK_OPTION:
+ case SC_WATER_DROP_OPTION:
+ case SC_WIND_CURTAIN_OPTION:
+ case SC_STONE_SHIELD_OPTION:
+ val2 = 20; // Elemental modifier. Not confirmed.
+ break;
+ case SC_CIRCLE_OF_FIRE:
+ case SC_FIRE_CLOAK:
+ case SC_WATER_DROP:
+ case SC_WATER_SCREEN:
+ case SC_WIND_CURTAIN:
+ case SC_WIND_STEP:
+ case SC_STONE_SHIELD:
+ case SC_SOLID_SKIN:
+ val2 = 10;
+ tick_time = 2000; // [GodLesZ] tick time
+ break;
+ case SC_WATER_BARRIER:
+ val2 = 40; // Increasement. Mdef1 ???
+ val3 = 20; // Reductions. Atk2, Flee1, Matk1 ????
+ val_flag |= 1|2|4;
+ break;
+ case SC_ZEPHYR:
+ val2 = 22; // Flee.
+ break;
+ case SC_TIDAL_WEAPON:
+ val2 = 20; // Increase Elemental's attack.
+ break;
+ case SC_ROCK_CRUSHER:
+ case SC_ROCK_CRUSHER_ATK:
+ case SC_POWER_OF_GAIA:
+ val2 = 33;
+ break;
+ case SC_MELON_BOMB:
+ case SC_BANANA_BOMB:
+ val1 = 15;
+ break;
+ case SC_STOMACHACHE:
+ val2 = 8; // SP consume.
+ val4 = tick / 10000;
+ tick_time = 10000; // [GodLesZ] tick time
+ break;
+ case SC_KYOUGAKU:
+ val2 = 2*val1 + rand()%val1;
+ clif_status_change(bl,SI_ACTIVE_MONSTER_TRANSFORM,1,0,1002,0,0);
+ break;
+ case SC_KAGEMUSYA:
+ val3 = val1 * 2;
+ case SC_IZAYOI:
+ val2 = tick/1000;
+ tick_time = 1000;
+ break;
+ case SC_ZANGETSU:
+ if( (status_get_hp(bl)+status_get_sp(bl)) % 2 == 0)
+ val2 = status_get_lv(bl) / 2 + 50;
+ else
+ val2 -= 50;
+ break;
+ case SC_GENSOU:
+ {
+ int hp = status_get_hp(bl), lv = 5;
+ short per = 100 / (status_get_max_hp(bl) / hp);
+
+ if( per <= 15 )
+ lv = 1;
+ else if( per <= 30 )
+ lv = 2;
+ else if( per <= 50 )
+ lv = 3;
+ else if( per <= 75 )
+ lv = 4;
+ if( hp % 2 == 0)
+ status_heal(bl, hp * (6-lv) * 4 / 100, status_get_sp(bl) * (6-lv) * 3 / 100, 1);
+ else
+ status_zap(bl, hp * (lv*4) / 100, status_get_sp(bl) * (lv*3) / 100);
+ }
+ break;
+ case SC_ANGRIFFS_MODUS:
+ val2 = 50 + 20 * val1; //atk bonus
+ val3 = 40 + 20 * val1; // Flee reduction.
+ val4 = tick/1000; // hp/sp reduction timer
+ tick_time = 1000;
+ break;
+ case SC_GOLDENE_FERSE:
+ val2 = 10 + 10*val1; //max hp bonus
+ val3 = 6 + 4 * val1; // Aspd Bonus
+ val4 = 2 + 2 * val1; // Chance of holy attack
+ break;
+ case SC_OVERED_BOOST:
+ val2 = 300 + 40*val1; //flee bonus
+ val3 = 179 + 2*val1; //aspd bonus
+ break;
+ case SC_GRANITIC_ARMOR:
+ val2 = 2*val1; //dmg reduction
+ val3 = 6*val1; //dmg on status end
+ break;
+ case SC_MAGMA_FLOW:
+ val2 = 3*val1; //activation chance
+ break;
+ case SC_PYROCLASTIC:
+ val2 += 10*val1; //atk bonus
+ break;
+ case SC_PARALYSIS: //[Lighta] need real info
+ val2 = 2*val1; //def reduction
+ val3 = 500*val1; //varcast augmentation
+ break;
+ case SC_PAIN_KILLER: //[Lighta] need real info
+ val2 = 2*val1; //aspd reduction %
+ val3 = 2*val1; //dmg reduction %
+ if(sc->data[SC_PARALYSIS])
+ sc_start(bl, SC_ENDURE, 100, val1, tick); //start endure for same duration
+ break;
+ case SC_STYLE_CHANGE: //[Lighta] need real info
+ tick = -1;
+ if(val2 == MH_MD_FIGHTING) val2 = MH_MD_GRAPPLING;
+ else val2 = MH_MD_FIGHTING;
+ break;
+ default:
+ if( calc_flag == SCB_NONE && StatusSkillChangeTable[type] == 0 && StatusIconChangeTable[type] == 0 )
+ { //Status change with no calc, no icon, and no skill associated...?
+ ShowError("UnknownStatusChange [%d]\n", type);
+ return 0;
+ }
+ }
+ else //Special considerations when loading SC data.
+ switch( type )
+ {
+ case SC_WEDDING:
+ case SC_XMAS:
+ case SC_SUMMER:
+ clif_changelook(bl,LOOK_WEAPON,0);
+ clif_changelook(bl,LOOK_SHIELD,0);
+ clif_changelook(bl,LOOK_BASE,type==SC_WEDDING?JOB_WEDDING:type==SC_XMAS?JOB_XMAS:JOB_SUMMER);
+ clif_changelook(bl,LOOK_CLOTHES_COLOR,val4);
+ break;
+ case SC_KAAHI:
+ val4 = INVALID_TIMER;
+ break;
+ }
+
+ //Those that make you stop attacking/walking....
+ switch (type) {
+ case SC_FREEZE:
+ case SC_STUN:
+ case SC_SLEEP:
+ case SC_STONE:
+ case SC_DEEPSLEEP:
+ if (sd && pc_issit(sd)) //Avoid sprite sync problems.
+ pc_setstand(sd);
+ case SC_TRICKDEAD:
+ status_change_end(bl, SC_DANCING, INVALID_TIMER);
+ // Cancel cast when get status [LuzZza]
+ if (battle_config.sc_castcancel&bl->type)
+ unit_skillcastcancel(bl, 0);
+ case SC_WHITEIMPRISON:
+ unit_stop_attack(bl);
+ case SC_STOP:
+ case SC_CONFUSION:
+ case SC_CLOSECONFINE:
+ case SC_CLOSECONFINE2:
+ case SC_ANKLE:
+ case SC_SPIDERWEB:
+ case SC_ELECTRICSHOCKER:
+ case SC_BITE:
+ case SC_THORNSTRAP:
+ case SC__MANHOLE:
+ case SC_CRYSTALIZE:
+ case SC_CURSEDCIRCLE_ATKER:
+ case SC_CURSEDCIRCLE_TARGET:
+ case SC_FEAR:
+ case SC_NETHERWORLD:
+ case SC_MEIKYOUSISUI:
+ case SC_KYOUGAKU:
+ case SC_PARALYSIS:
+ unit_stop_walking(bl,1);
+ break;
+ case SC_HIDING:
+ case SC_CLOAKING:
+ case SC_CLOAKINGEXCEED:
+ case SC_CHASEWALK:
+ case SC_WEIGHT90:
+ case SC_CAMOUFLAGE:
+ case SC_VOICEOFSIREN:
+ unit_stop_attack(bl);
+ break;
+ case SC_SILENCE:
+ if (battle_config.sc_castcancel&bl->type)
+ unit_skillcastcancel(bl, 0);
+ break;
+ }
+
+ // Set option as needed.
+ opt_flag = 1;
+ switch(type)
+ {
+ //OPT1
+ case SC_STONE: sc->opt1 = OPT1_STONEWAIT; break;
+ case SC_FREEZE: sc->opt1 = OPT1_FREEZE; break;
+ case SC_STUN: sc->opt1 = OPT1_STUN; break;
+ case SC_DEEPSLEEP: opt_flag = 0;
+ case SC_SLEEP: sc->opt1 = OPT1_SLEEP; break;
+ case SC_BURNING: sc->opt1 = OPT1_BURNING; break; // Burning need this to be showed correctly. [pakpil]
+ case SC_WHITEIMPRISON: sc->opt1 = OPT1_IMPRISON; break;
+ case SC_CRYSTALIZE: sc->opt1 = OPT1_CRYSTALIZE; break;
+ //OPT2
+ case SC_POISON: sc->opt2 |= OPT2_POISON; break;
+ case SC_CURSE: sc->opt2 |= OPT2_CURSE; break;
+ case SC_SILENCE: sc->opt2 |= OPT2_SILENCE; break;
+
+ case SC_SIGNUMCRUCIS:
+ sc->opt2 |= OPT2_SIGNUMCRUCIS;
+ break;
+
+ case SC_BLIND: sc->opt2 |= OPT2_BLIND; break;
+ case SC_ANGELUS: sc->opt2 |= OPT2_ANGELUS; break;
+ case SC_BLEEDING: sc->opt2 |= OPT2_BLEEDING; break;
+ case SC_DPOISON: sc->opt2 |= OPT2_DPOISON; break;
+ //OPT3
+ case SC_TWOHANDQUICKEN:
+ case SC_ONEHAND:
+ case SC_SPEARQUICKEN:
+ case SC_CONCENTRATION:
+ case SC_MERC_QUICKEN:
+ sc->opt3 |= OPT3_QUICKEN;
+ opt_flag = 0;
+ break;
+ case SC_MAXOVERTHRUST:
+ case SC_OVERTHRUST:
+ case SC_SWOO: //Why does it shares the same opt as Overthrust? Perhaps we'll never know...
+ sc->opt3 |= OPT3_OVERTHRUST;
+ opt_flag = 0;
+ break;
+ case SC_ENERGYCOAT:
+ case SC_SKE:
+ sc->opt3 |= OPT3_ENERGYCOAT;
+ opt_flag = 0;
+ break;
+ case SC_INCATKRATE:
+ //Simulate Explosion Spirits effect for NPC_POWERUP [Skotlex]
+ if (bl->type != BL_MOB) {
+ opt_flag = 0;
+ break;
+ }
+ case SC_EXPLOSIONSPIRITS:
+ sc->opt3 |= OPT3_EXPLOSIONSPIRITS;
+ opt_flag = 0;
+ break;
+ case SC_STEELBODY:
+ case SC_SKA:
+ sc->opt3 |= OPT3_STEELBODY;
+ opt_flag = 0;
+ break;
+ case SC_BLADESTOP:
+ sc->opt3 |= OPT3_BLADESTOP;
+ opt_flag = 0;
+ break;
+ case SC_AURABLADE:
+ sc->opt3 |= OPT3_AURABLADE;
+ opt_flag = 0;
+ break;
+ case SC_BERSERK:
+ opt_flag = 0;
+// case SC__BLOODYLUST:
+ sc->opt3 |= OPT3_BERSERK;
+ break;
+// case ???: // doesn't seem to do anything
+// sc->opt3 |= OPT3_LIGHTBLADE;
+// opt_flag = 0;
+// break;
+ case SC_DANCING:
+ if ((val1&0xFFFF) == CG_MOONLIT)
+ sc->opt3 |= OPT3_MOONLIT;
+ opt_flag = 0;
+ break;
+ case SC_MARIONETTE:
+ case SC_MARIONETTE2:
+ sc->opt3 |= OPT3_MARIONETTE;
+ opt_flag = 0;
+ break;
+ case SC_ASSUMPTIO:
+ sc->opt3 |= OPT3_ASSUMPTIO;
+ opt_flag = 0;
+ break;
+ case SC_WARM: //SG skills [Komurka]
+ sc->opt3 |= OPT3_WARM;
+ opt_flag = 0;
+ break;
+ case SC_KAITE:
+ sc->opt3 |= OPT3_KAITE;
+ opt_flag = 0;
+ break;
+ case SC_BUNSINJYUTSU:
+ sc->opt3 |= OPT3_BUNSIN;
+ opt_flag = 0;
+ break;
+ case SC_SPIRIT:
+ sc->opt3 |= OPT3_SOULLINK;
+ opt_flag = 0;
+ break;
+ case SC_CHANGEUNDEAD:
+ sc->opt3 |= OPT3_UNDEAD;
+ opt_flag = 0;
+ break;
+// case ???: // from DA_CONTRACT (looks like biolab mobs aura)
+// sc->opt3 |= OPT3_CONTRACT;
+// opt_flag = 0;
+// break;
+ //OPTION
+ case SC_HIDING:
+ sc->option |= OPTION_HIDE;
+ opt_flag = 2;
+ break;
+ case SC_CLOAKING:
+ case SC_CLOAKINGEXCEED:
+ case SC__INVISIBILITY:
+ sc->option |= OPTION_CLOAK;
+ opt_flag = 2;
+ break;
+ case SC_CHASEWALK:
+ sc->option |= OPTION_CHASEWALK|OPTION_CLOAK;
+ opt_flag = 2;
+ break;
+ case SC_SIGHT:
+ sc->option |= OPTION_SIGHT;
+ break;
+ case SC_RUWACH:
+ sc->option |= OPTION_RUWACH;
+ break;
+ case SC_WEDDING:
+ sc->option |= OPTION_WEDDING;
+ break;
+ case SC_XMAS:
+ sc->option |= OPTION_XMAS;
+ break;
+ case SC_SUMMER:
+ sc->option |= OPTION_SUMMER;
+ break;
+ case SC_ORCISH:
+ sc->option |= OPTION_ORCISH;
+ break;
+ case SC_FUSION:
+ sc->option |= OPTION_FLYING;
+ break;
+ default:
+ opt_flag = 0;
+ }
+
+ //On Aegis, when turning on a status change, first goes the option packet, then the sc packet.
+ if(opt_flag)
+ clif_changeoption(bl);
+
+ if (calc_flag&SCB_DYE)
+ { //Reset DYE color
+ if (vd && vd->cloth_color)
+ {
+ val4 = vd->cloth_color;
+ clif_changelook(bl,LOOK_CLOTHES_COLOR,0);
+ }
+ calc_flag&=~SCB_DYE;
+ }
+
+ clif_status_change(bl,StatusIconChangeTable[type],1,tick,(val_flag&1)?val1:1,(val_flag&2)?val2:0,(val_flag&4)?val3:0);
+
+ /**
+ * used as temporary storage for scs with interval ticks, so that the actual duration is sent to the client first.
+ **/
+ if( tick_time )
+ tick = tick_time;
+
+ //Don't trust the previous sce assignment, in case the SC ended somewhere between there and here.
+ if((sce=sc->data[type])) {// reuse old sc
+ if( sce->timer != INVALID_TIMER )
+ delete_timer(sce->timer, status_change_timer);
+ sc_isnew = false;
+ } else {// new sc
+ ++(sc->count);
+ sce = sc->data[type] = ers_alloc(sc_data_ers, struct status_change_entry);
+ }
+ sce->val1 = val1;
+ sce->val2 = val2;
+ sce->val3 = val3;
+ sce->val4 = val4;
+ if (tick >= 0)
+ sce->timer = add_timer(gettick() + tick, status_change_timer, bl->id, type);
+ else
+ sce->timer = INVALID_TIMER; //Infinite duration
+
+ if (calc_flag)
+ status_calc_bl(bl,calc_flag);
+
+ if ( sc_isnew && StatusChangeStateTable[type] ) /* non-zero */
+ status_calc_state(bl,sc,( enum scs_flag ) StatusChangeStateTable[type],true);
+
+
+ if(sd && sd->pd)
+ pet_sc_check(sd, type); //Skotlex: Pet Status Effect Healing
+
+ switch (type) {
+ case SC__BLOODYLUST:
+ case SC_BERSERK:
+ if (!(sce->val2)) { //don't heal if already set
+ status_heal(bl, status->max_hp, 0, 1); //Do not use percent_heal as this healing must override BERSERK's block.
+ status_set_sp(bl, 0, 0); //Damage all SP
+ }
+ sce->val2 = 5 * status->max_hp / 100;
+ break;
+ case SC_CHANGE:
+ status_percent_heal(bl, 100, 100);
+ break;
+ case SC_RUN:
+ {
+ struct unit_data *ud = unit_bl2ud(bl);
+ if( ud )
+ ud->state.running = unit_run(bl);
+ }
+ break;
+ case SC_BOSSMAPINFO:
+ clif_bossmapinfo(sd->fd, map_id2boss(sce->val1), 0); // First Message
+ break;
+ case SC_MERC_HPUP:
+ status_percent_heal(bl, 100, 0); // Recover Full HP
+ break;
+ case SC_MERC_SPUP:
+ status_percent_heal(bl, 0, 100); // Recover Full SP
+ break;
+ /**
+ * Ranger
+ **/
+ case SC_WUGDASH:
+ {
+ struct unit_data *ud = unit_bl2ud(bl);
+ if( ud )
+ ud->state.running = unit_wugdash(bl, sd);
+ }
+ break;
+ case SC_COMBO:
+ switch (sce->val1) {
+ case TK_STORMKICK:
+ clif_skill_nodamage(bl,bl,TK_READYSTORM,1,1);
+ break;
+ case TK_DOWNKICK:
+ clif_skill_nodamage(bl,bl,TK_READYDOWN,1,1);
+ break;
+ case TK_TURNKICK:
+ clif_skill_nodamage(bl,bl,TK_READYTURN,1,1);
+ break;
+ case TK_COUNTER:
+ clif_skill_nodamage(bl,bl,TK_READYCOUNTER,1,1);
+ break;
+ case MO_COMBOFINISH:
+ case CH_TIGERFIST:
+ case CH_CHAINCRUSH:
+ if (sd)
+ clif_skillinfo(sd,MO_EXTREMITYFIST, INF_SELF_SKILL);
+ break;
+ case TK_JUMPKICK:
+ if (sd)
+ clif_skillinfo(sd,TK_JUMPKICK, INF_SELF_SKILL);
+ break;
+ case MO_TRIPLEATTACK:
+ if (sd && pc_checkskill(sd, SR_DRAGONCOMBO) > 0)
+ clif_skillinfo(sd,SR_DRAGONCOMBO, INF_SELF_SKILL);
+ break;
+ case SR_FALLENEMPIRE:
+ if (sd){
+ clif_skillinfo(sd,SR_GATEOFHELL, INF_SELF_SKILL);
+ clif_skillinfo(sd,SR_TIGERCANNON, INF_SELF_SKILL);
+ }
+ break;
+ }
+ break;
+ case SC_RAISINGDRAGON:
+ sce->val2 = status->max_hp / 100;// Officially tested its 1%hp drain. [Jobbie]
+ break;
+ }
+
+ if( opt_flag&2 && sd && sd->touching_id )
+ npc_touchnext_areanpc(sd,false); // run OnTouch_ on next char in range
+
+ return 1;
+}
+
+/*==========================================
+ * Ending all status except those listed.
+ * @TODO maybe usefull for dispel instead reseting a liste there.
+ * type:
+ * 0 - PC killed -> Place here statuses that do not dispel on death.
+ * 1 - If for some reason status_change_end decides to still keep the status when quitting.
+ * 2 - Do clif
+ * 3 - Do not remove some permanent/time-independent effects
+ *------------------------------------------*/
+int status_change_clear(struct block_list* bl, int type)
+{
+ struct status_change* sc;
+ int i;
+
+ sc = status_get_sc(bl);
+
+ if (!sc || !sc->count)
+ return 0;
+
+ for(i = 0; i < SC_MAX; i++)
+ {
+ if(!sc->data[i])
+ continue;
+
+ if(type == 0)
+ switch (i)
+ { //Type 0: PC killed -> Place here statuses that do not dispel on death.
+ case SC_ELEMENTALCHANGE://Only when its Holy or Dark that it doesn't dispell on death
+ if( sc->data[i]->val2 != ELE_HOLY && sc->data[i]->val2 != ELE_DARK )
+ break;
+ case SC_WEIGHT50:
+ case SC_WEIGHT90:
+ case SC_EDP:
+ case SC_MELTDOWN:
+ case SC_XMAS:
+ case SC_SUMMER:
+ case SC_NOCHAT:
+ case SC_FUSION:
+ case SC_EARTHSCROLL:
+ case SC_READYSTORM:
+ case SC_READYDOWN:
+ case SC_READYCOUNTER:
+ case SC_READYTURN:
+ case SC_DODGE:
+ case SC_JAILED:
+ case SC_EXPBOOST:
+ case SC_ITEMBOOST:
+ case SC_HELLPOWER:
+ case SC_JEXPBOOST:
+ case SC_AUTOTRADE:
+ case SC_WHISTLE:
+ case SC_ASSNCROS:
+ case SC_POEMBRAGI:
+ case SC_APPLEIDUN:
+ case SC_HUMMING:
+ case SC_DONTFORGETME:
+ case SC_FORTUNE:
+ case SC_SERVICE4U:
+ case SC_FOOD_STR_CASH:
+ case SC_FOOD_AGI_CASH:
+ case SC_FOOD_VIT_CASH:
+ case SC_FOOD_DEX_CASH:
+ case SC_FOOD_INT_CASH:
+ case SC_FOOD_LUK_CASH:
+ case SC_DEF_RATE:
+ case SC_MDEF_RATE:
+ case SC_INCHEALRATE:
+ case SC_INCFLEE2:
+ case SC_INCHIT:
+ case SC_ATKPOTION:
+ case SC_MATKPOTION:
+ case SC_S_LIFEPOTION:
+ case SC_L_LIFEPOTION:
+ case SC_PUSH_CART:
+ continue;
+
+ }
+
+ if( type == 3 )
+ {
+ switch (i)
+ {// TODO: This list may be incomplete
+ case SC_WEIGHT50:
+ case SC_WEIGHT90:
+ case SC_NOCHAT:
+ case SC_PUSH_CART:
+ continue;
+ }
+ }
+
+ status_change_end(bl, (sc_type)i, INVALID_TIMER);
+
+ if( type == 1 && sc->data[i] )
+ { //If for some reason status_change_end decides to still keep the status when quitting. [Skotlex]
+ (sc->count)--;
+ if (sc->data[i]->timer != INVALID_TIMER)
+ delete_timer(sc->data[i]->timer, status_change_timer);
+ ers_free(sc_data_ers, sc->data[i]);
+ sc->data[i] = NULL;
+ }
+ }
+
+ sc->opt1 = 0;
+ sc->opt2 = 0;
+ sc->opt3 = 0;
+ sc->option &= OPTION_MASK;
+
+ if( type == 0 || type == 2 )
+ clif_changeoption(bl);
+
+ return 1;
+}
+
+/*==========================================
+ * Special condition we want to effectuate, check before ending a status.
+ *------------------------------------------*/
+int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const char* file, int line)
+{
+ struct map_session_data *sd;
+ struct status_change *sc;
+ struct status_change_entry *sce;
+ struct status_data *status;
+ struct view_data *vd;
+ int opt_flag=0, calc_flag;
+
+ nullpo_ret(bl);
+
+ sc = status_get_sc(bl);
+ status = status_get_status_data(bl);
+
+ if(type < 0 || type >= SC_MAX || !sc || !(sce = sc->data[type]))
+ return 0;
+
+ sd = BL_CAST(BL_PC,bl);
+
+ if (sce->timer != tid && tid != INVALID_TIMER)
+ return 0;
+
+ if (tid == INVALID_TIMER) {
+ if (type == SC_ENDURE && sce->val4)
+ //Do not end infinite endure.
+ return 0;
+ if (sce->timer != INVALID_TIMER) //Could be a SC with infinite duration
+ delete_timer(sce->timer,status_change_timer);
+ if (sc->opt1)
+ switch (type) {
+ //"Ugly workaround" [Skotlex]
+ //delays status change ending so that a skill that sets opt1 fails to
+ //trigger when it also removed one
+ case SC_STONE:
+ sce->val3 = 0; //Petrify time counter.
+ case SC_FREEZE:
+ case SC_STUN:
+ case SC_SLEEP:
+ if (sce->val1) {
+ //Removing the 'level' shouldn't affect anything in the code
+ //since these SC are not affected by it, and it lets us know
+ //if we have already delayed this attack or not.
+ sce->val1 = 0;
+ sce->timer = add_timer(gettick()+10, status_change_timer, bl->id, type);
+ return 1;
+ }
+ }
+ }
+
+ (sc->count)--;
+
+ if ( StatusChangeStateTable[type] )
+ status_calc_state(bl,sc,( enum scs_flag ) StatusChangeStateTable[type],false);
+
+ sc->data[type] = NULL;
+
+ vd = status_get_viewdata(bl);
+ calc_flag = StatusChangeFlagTable[type];
+ switch(type){
+ case SC_GRANITIC_ARMOR:{
+ int dammage = status->max_hp*sce->val3/100;
+ if(status->hp < dammage) //to not kill him
+ dammage = status->hp-1;
+ status_damage(NULL, bl, dammage,0,0,1);
+ break;
+ }
+ case SC_PYROCLASTIC:
+ if(bl->type == BL_PC)
+ skill_break_equip(bl,EQP_WEAPON,10000,BCT_SELF);
+ break;
+ case SC_WEDDING:
+ case SC_XMAS:
+ case SC_SUMMER:
+ if (!vd) break;
+ if (sd)
+ { //Load data from sd->status.* as the stored values could have changed.
+ //Must remove OPTION to prevent class being rechanged.
+ sc->option &= type==SC_WEDDING?~OPTION_WEDDING:type==SC_XMAS?~OPTION_XMAS:~OPTION_SUMMER;
+ clif_changeoption(&sd->bl);
+ status_set_viewdata(bl, sd->status.class_);
+ } else {
+ vd->class_ = sce->val1;
+ vd->weapon = sce->val2;
+ vd->shield = sce->val3;
+ vd->cloth_color = sce->val4;
+ }
+ clif_changelook(bl,LOOK_BASE,vd->class_);
+ clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color);
+ clif_changelook(bl,LOOK_WEAPON,vd->weapon);
+ clif_changelook(bl,LOOK_SHIELD,vd->shield);
+ if(sd) clif_skillinfoblock(sd);
+ break;
+ case SC_RUN:
+ {
+ struct unit_data *ud = unit_bl2ud(bl);
+ bool begin_spurt = true;
+ if (ud) {
+ if(!ud->state.running)
+ begin_spurt = false;
+ ud->state.running = 0;
+ if (ud->walktimer != INVALID_TIMER)
+ unit_stop_walking(bl,1);
+ }
+ if (begin_spurt && sce->val1 >= 7 &&
+ DIFF_TICK(gettick(), sce->val4) <= 1000 &&
+ (!sd || (sd->weapontype1 == 0 && sd->weapontype2 == 0))
+ )
+ sc_start(bl,SC_SPURT,100,sce->val1,skill_get_time2(status_sc2skill(type), sce->val1));
+ }
+ break;
+ case SC_AUTOBERSERK:
+ if (sc->data[SC_PROVOKE] && sc->data[SC_PROVOKE]->val2 == 1)
+ status_change_end(bl, SC_PROVOKE, INVALID_TIMER);
+ break;
+
+ case SC_ENDURE:
+ case SC_DEFENDER:
+ case SC_REFLECTSHIELD:
+ case SC_AUTOGUARD:
+ {
+ struct map_session_data *tsd;
+ if( bl->type == BL_PC )
+ { // Clear Status from others
+ int i;
+ for( i = 0; i < 5; i++ )
+ {
+ if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) && tsd->sc.data[type] )
+ status_change_end(&tsd->bl, type, INVALID_TIMER);
+ }
+ }
+ else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag )
+ { // Clear Status from Master
+ tsd = ((TBL_MER*)bl)->master;
+ if( tsd && tsd->sc.data[type] )
+ status_change_end(&tsd->bl, type, INVALID_TIMER);
+ }
+ }
+ break;
+ case SC_DEVOTION:
+ {
+ struct block_list *d_bl = map_id2bl(sce->val1);
+ if( d_bl )
+ {
+ if( d_bl->type == BL_PC )
+ ((TBL_PC*)d_bl)->devotion[sce->val2] = 0;
+ else if( d_bl->type == BL_MER )
+ ((TBL_MER*)d_bl)->devotion_flag = 0;
+ clif_devotion(d_bl, NULL);
+ }
+
+ status_change_end(bl, SC_AUTOGUARD, INVALID_TIMER);
+ status_change_end(bl, SC_DEFENDER, INVALID_TIMER);
+ status_change_end(bl, SC_REFLECTSHIELD, INVALID_TIMER);
+ status_change_end(bl, SC_ENDURE, INVALID_TIMER);
+ }
+ break;
+
+ case SC_BLADESTOP:
+ if(sce->val4)
+ {
+ int tid = sce->val4;
+ struct block_list *tbl = map_id2bl(tid);
+ struct status_change *tsc = status_get_sc(tbl);
+ sce->val4 = 0;
+ if(tbl && tsc && tsc->data[SC_BLADESTOP])
+ {
+ tsc->data[SC_BLADESTOP]->val4 = 0;
+ status_change_end(tbl, SC_BLADESTOP, INVALID_TIMER);
+ }
+ clif_bladestop(bl, tid, 0);
+ }
+ break;
+ case SC_DANCING:
+ {
+ const char* prevfile = "<unknown>";
+ int prevline = 0;
+ struct map_session_data *dsd;
+ struct status_change_entry *dsc;
+ struct skill_unit_group *group;
+
+ if( sd )
+ {
+ if( sd->delunit_prevfile )
+ {// initially this is NULL, when a character logs in
+ prevfile = sd->delunit_prevfile;
+ prevline = sd->delunit_prevline;
+ }
+ else
+ {
+ prevfile = "<none>";
+ }
+ sd->delunit_prevfile = file;
+ sd->delunit_prevline = line;
+ }
+
+ if(sce->val4 && sce->val4 != BCT_SELF && (dsd=map_id2sd(sce->val4)))
+ {// end status on partner as well
+ dsc = dsd->sc.data[SC_DANCING];
+ if(dsc) {
+
+ //This will prevent recursive loops.
+ dsc->val2 = dsc->val4 = 0;
+
+ status_change_end(&dsd->bl, SC_DANCING, INVALID_TIMER);
+ }
+ }
+
+ if(sce->val2)
+ {// erase associated land skill
+ group = skill_id2group(sce->val2);
+
+ if( group == NULL )
+ {
+ ShowDebug("status_change_end: SC_DANCING is missing skill unit group (val1=%d, val2=%d, val3=%d, val4=%d, timer=%d, tid=%d, char_id=%d, map=%s, x=%d, y=%d, prev=%s:%d, from=%s:%d). Please report this! (#3504)\n",
+ sce->val1, sce->val2, sce->val3, sce->val4, sce->timer, tid,
+ sd ? sd->status.char_id : 0,
+ mapindex_id2name(map_id2index(bl->m)), bl->x, bl->y,
+ prevfile, prevline,
+ file, line);
+ }
+
+ sce->val2 = 0;
+ skill_delunitgroup(group);
+ }
+
+ if((sce->val1&0xFFFF) == CG_MOONLIT)
+ clif_status_change(bl,SI_MOONLIT,0,0,0,0,0);
+
+ status_change_end(bl, SC_LONGING, INVALID_TIMER);
+ }
+ break;
+ case SC_NOCHAT:
+ if (sd && sd->status.manner < 0 && tid != INVALID_TIMER)
+ sd->status.manner = 0;
+ if (sd && tid == INVALID_TIMER)
+ {
+ clif_changestatus(sd,SP_MANNER,sd->status.manner);
+ clif_updatestatus(sd,SP_MANNER);
+ }
+ break;
+ case SC_SPLASHER:
+ {
+ struct block_list *src=map_id2bl(sce->val3);
+ if(src && tid != INVALID_TIMER)
+ skill_castend_damage_id(src, bl, sce->val2, sce->val1, gettick(), SD_LEVEL );
+ }
+ break;
+ case SC_CLOSECONFINE2:
+ {
+ struct block_list *src = sce->val2?map_id2bl(sce->val2):NULL;
+ struct status_change *sc2 = src?status_get_sc(src):NULL;
+ if (src && sc2 && sc2->data[SC_CLOSECONFINE]) {
+ //If status was already ended, do nothing.
+ //Decrease count
+ if (--(sc2->data[SC_CLOSECONFINE]->val1) <= 0) //No more holds, free him up.
+ status_change_end(src, SC_CLOSECONFINE, INVALID_TIMER);
+ }
+ }
+ case SC_CLOSECONFINE:
+ if (sce->val2 > 0) {
+ //Caster has been unlocked... nearby chars need to be unlocked.
+ int range = 1
+ +skill_get_range2(bl, status_sc2skill(type), sce->val1)
+ +skill_get_range2(bl, TF_BACKSLIDING, 1); //Since most people use this to escape the hold....
+ map_foreachinarea(status_change_timer_sub,
+ bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,BL_CHAR,bl,sce,type,gettick());
+ }
+ break;
+ case SC_COMBO:
+ if( sd )
+ switch (sce->val1) {
+ case MO_COMBOFINISH:
+ case CH_TIGERFIST:
+ case CH_CHAINCRUSH:
+ clif_skillinfo(sd, MO_EXTREMITYFIST, 0);
+ break;
+ case TK_JUMPKICK:
+ clif_skillinfo(sd, TK_JUMPKICK, 0);
+ break;
+ case MO_TRIPLEATTACK:
+ if (pc_checkskill(sd, SR_DRAGONCOMBO) > 0)
+ clif_skillinfo(sd, SR_DRAGONCOMBO, 0);
+ break;
+ case SR_FALLENEMPIRE:
+ clif_skillinfo(sd, SR_GATEOFHELL, 0);
+ clif_skillinfo(sd, SR_TIGERCANNON, 0);
+ break;
+ }
+ break;
+
+ case SC_MARIONETTE:
+ case SC_MARIONETTE2: /// Marionette target
+ if (sce->val1)
+ { // check for partner and end their marionette status as well
+ enum sc_type type2 = (type == SC_MARIONETTE) ? SC_MARIONETTE2 : SC_MARIONETTE;
+ struct block_list *pbl = map_id2bl(sce->val1);
+ struct status_change* sc2 = pbl?status_get_sc(pbl):NULL;
+
+ if (sc2 && sc2->data[type2])
+ {
+ sc2->data[type2]->val1 = 0;
+ status_change_end(pbl, type2, INVALID_TIMER);
+ }
+ }
+ break;
+
+ case SC_BERSERK:
+ case SC_SATURDAYNIGHTFEVER:
+ //If val2 is removed, no HP penalty (dispelled?) [Skotlex]
+ if (status->hp > 100 && sce->val2)
+ status_set_hp(bl, 100, 0);
+ if(sc->data[SC_ENDURE] && sc->data[SC_ENDURE]->val4 == 2)
+ {
+ sc->data[SC_ENDURE]->val4 = 0;
+ status_change_end(bl, SC_ENDURE, INVALID_TIMER);
+ }
+ case SC__BLOODYLUST:
+ sc_start4(bl, SC_REGENERATION, 100, 10,0,0,(RGN_HP|RGN_SP), skill_get_time(LK_BERSERK, sce->val1));
+ if( type == SC_SATURDAYNIGHTFEVER ) //Sit down force of Saturday Night Fever has the duration of only 3 seconds.
+ sc_start(bl,SC_SITDOWN_FORCE,100,sce->val1,skill_get_time2(WM_SATURDAY_NIGHT_FEVER,sce->val1));
+ break;
+ case SC_GOSPEL:
+ if (sce->val3) { //Clear the group.
+ struct skill_unit_group* group = skill_id2group(sce->val3);
+ sce->val3 = 0;
+ skill_delunitgroup(group);
+ }
+ break;
+ case SC_HERMODE:
+ if(sce->val3 == BCT_SELF)
+ skill_clear_unitgroup(bl);
+ break;
+ case SC_BASILICA: //Clear the skill area. [Skotlex]
+ skill_clear_unitgroup(bl);
+ break;
+ case SC_TRICKDEAD:
+ if (vd) vd->dead_sit = 0;
+ break;
+ case SC_WARM:
+ case SC__MANHOLE:
+ if (sce->val4) { //Clear the group.
+ struct skill_unit_group* group = skill_id2group(sce->val4);
+ sce->val4 = 0;
+ if( group ) /* might have been cleared before status ended, e.g. land protector */
+ skill_delunitgroup(group);
+ }
+ break;
+ case SC_KAAHI:
+ //Delete timer if it exists.
+ if (sce->val4 != INVALID_TIMER)
+ delete_timer(sce->val4,kaahi_heal_timer);
+ break;
+ case SC_JAILED:
+ if(tid == INVALID_TIMER)
+ break;
+ //natural expiration.
+ if(sd && sd->mapindex == sce->val2)
+ pc_setpos(sd,(unsigned short)sce->val3,sce->val4&0xFFFF, sce->val4>>16, CLR_TELEPORT);
+ break; //guess hes not in jail :P
+ case SC_CHANGE:
+ if (tid == INVALID_TIMER)
+ break;
+ // "lose almost all their HP and SP" on natural expiration.
+ status_set_hp(bl, 10, 0);
+ status_set_sp(bl, 10, 0);
+ break;
+ case SC_AUTOTRADE:
+ if (tid == INVALID_TIMER)
+ break;
+ // Note: vending/buying is closed by unit_remove_map, no
+ // need to do it here.
+ map_quit(sd);
+ // Because map_quit calls status_change_end with tid -1
+ // from here it's not neccesary to continue
+ return 1;
+ break;
+ case SC_STOP:
+ if( sce->val2 )
+ {
+ struct block_list* tbl = map_id2bl(sce->val2);
+ sce->val2 = 0;
+ if( tbl && (sc = status_get_sc(tbl)) && sc->data[SC_STOP] && sc->data[SC_STOP]->val2 == bl->id )
+ status_change_end(tbl, SC_STOP, INVALID_TIMER);
+ }
+ break;
+ /**
+ * 3rd Stuff
+ **/
+ case SC_MILLENNIUMSHIELD:
+ clif_millenniumshield(sd,0);
+ break;
+ case SC_HALLUCINATIONWALK:
+ sc_start(bl,SC_HALLUCINATIONWALK_POSTDELAY,100,sce->val1,skill_get_time2(GC_HALLUCINATIONWALK,sce->val1));
+ break;
+ case SC_WHITEIMPRISON:
+ {
+ struct block_list* src = map_id2bl(sce->val2);
+ if( tid == -1 || !src)
+ break; // Terminated by Damage
+ status_fix_damage(src,bl,400*sce->val1,clif_damage(bl,bl,gettick(),0,0,400*sce->val1,0,0,0));
+ }
+ break;
+ case SC_WUGDASH:
+ {
+ struct unit_data *ud = unit_bl2ud(bl);
+ if (ud) {
+ ud->state.running = 0;
+ if (ud->walktimer != -1)
+ unit_stop_walking(bl,1);
+ }
+ }
+ break;
+ case SC_ADORAMUS:
+ status_change_end(bl, SC_BLIND, INVALID_TIMER);
+ break;
+ case SC__SHADOWFORM: {
+ struct map_session_data *s_sd = map_id2sd(sce->val2);
+ if( !s_sd )
+ break;
+ s_sd->shadowform_id = 0;
+ }
+ break;
+ case SC_SITDOWN_FORCE:
+ if( sd && pc_issit(sd) ) {
+ pc_setstand(sd);
+ clif_standing(bl);
+ }
+ break;
+ case SC_NEUTRALBARRIER_MASTER:
+ case SC_STEALTHFIELD_MASTER:
+ if( sce->val2 ) {
+ struct skill_unit_group* group = skill_id2group(sce->val2);
+ sce->val2 = 0;
+ if( group ) /* might have been cleared before status ended, e.g. land protector */
+ skill_delunitgroup(group);
+ }
+ break;
+ case SC_BANDING:
+ if(sce->val4) {
+ struct skill_unit_group *group = skill_id2group(sce->val4);
+ sce->val4 = 0;
+ if( group ) /* might have been cleared before status ended, e.g. land protector */
+ skill_delunitgroup(group);
+ }
+ break;
+ case SC_CURSEDCIRCLE_ATKER:
+ if( sce->val2 ) // used the default area size cause there is a chance the caster could knock back and can't clear the target.
+ map_foreachinrange(status_change_timer_sub, bl, battle_config.area_size,BL_CHAR, bl, sce, SC_CURSEDCIRCLE_TARGET, gettick());
+ break;
+ case SC_RAISINGDRAGON:
+ if( sd && sce->val2 && !pc_isdead(sd) ) {
+ int i;
+ i = min(sd->spiritball,5);
+ pc_delspiritball(sd, sd->spiritball, 0);
+ status_change_end(bl, SC_EXPLOSIONSPIRITS, INVALID_TIMER);
+ while( i > 0 ) {
+ pc_addspiritball(sd, skill_get_time(MO_CALLSPIRITS, pc_checkskill(sd,MO_CALLSPIRITS)), 5);
+ --i;
+ }
+ }
+ break;
+ case SC_CURSEDCIRCLE_TARGET:
+ {
+ struct block_list *src = map_id2bl(sce->val2);
+ struct status_change *sc = status_get_sc(src);
+ if( sc && sc->data[SC_CURSEDCIRCLE_ATKER] && --(sc->data[SC_CURSEDCIRCLE_ATKER]->val2) == 0 ){
+ status_change_end(src, SC_CURSEDCIRCLE_ATKER, INVALID_TIMER);
+ clif_bladestop(bl, sce->val2, 0);
+ }
+ }
+ break;
+ case SC_BLOODSUCKER:
+ if( sce->val2 ){
+ struct block_list *src = map_id2bl(sce->val2);
+ if(src){
+ struct status_change *sc = status_get_sc(src);
+ sc->bs_counter--;
+ }
+ }
+ break;
+ case SC_VACUUM_EXTREME:
+ if(sc && sc->cant.move > 0) sc->cant.move--;
+ break;
+ case SC_KYOUGAKU:
+ clif_status_load(bl, SI_KYOUGAKU, 0); // Avoid client crash
+ clif_status_load(bl, SI_ACTIVE_MONSTER_TRANSFORM, 0);
+ break;
+ case SC_INTRAVISION:
+ calc_flag = SCB_ALL;/* required for overlapping */
+ break;
+ }
+
+ opt_flag = 1;
+ switch(type){
+ case SC_STONE:
+ case SC_FREEZE:
+ case SC_STUN:
+ case SC_SLEEP:
+ case SC_DEEPSLEEP:
+ case SC_BURNING:
+ case SC_WHITEIMPRISON:
+ case SC_CRYSTALIZE:
+ sc->opt1 = 0;
+ break;
+
+ case SC_POISON:
+ case SC_CURSE:
+ case SC_SILENCE:
+ case SC_BLIND:
+ sc->opt2 &= ~(1<<(type-SC_POISON));
+ break;
+ case SC_DPOISON:
+ sc->opt2 &= ~OPT2_DPOISON;
+ break;
+ case SC_SIGNUMCRUCIS:
+ sc->opt2 &= ~OPT2_SIGNUMCRUCIS;
+ break;
+
+ case SC_HIDING:
+ sc->option &= ~OPTION_HIDE;
+ opt_flag|= 2|4; //Check for warp trigger + AoE trigger
+ break;
+ case SC_CLOAKING:
+ case SC_CLOAKINGEXCEED:
+ case SC__INVISIBILITY:
+ sc->option &= ~OPTION_CLOAK;
+ case SC_CAMOUFLAGE:
+ opt_flag|= 2;
+ break;
+ case SC_CHASEWALK:
+ sc->option &= ~(OPTION_CHASEWALK|OPTION_CLOAK);
+ opt_flag|= 2;
+ break;
+ case SC_SIGHT:
+ sc->option &= ~OPTION_SIGHT;
+ break;
+ case SC_WEDDING:
+ sc->option &= ~OPTION_WEDDING;
+ break;
+ case SC_XMAS:
+ sc->option &= ~OPTION_XMAS;
+ break;
+ case SC_SUMMER:
+ sc->option &= ~OPTION_SUMMER;
+ break;
+ case SC_ORCISH:
+ sc->option &= ~OPTION_ORCISH;
+ break;
+ case SC_RUWACH:
+ sc->option &= ~OPTION_RUWACH;
+ break;
+ case SC_FUSION:
+ sc->option &= ~OPTION_FLYING;
+ break;
+ //opt3
+ case SC_TWOHANDQUICKEN:
+ case SC_ONEHAND:
+ case SC_SPEARQUICKEN:
+ case SC_CONCENTRATION:
+ case SC_MERC_QUICKEN:
+ sc->opt3 &= ~OPT3_QUICKEN;
+ opt_flag = 0;
+ break;
+ case SC_OVERTHRUST:
+ case SC_MAXOVERTHRUST:
+ case SC_SWOO:
+ sc->opt3 &= ~OPT3_OVERTHRUST;
+ if( type == SC_SWOO )
+ opt_flag = 8;
+ else
+ opt_flag = 0;
+ break;
+ case SC_ENERGYCOAT:
+ case SC_SKE:
+ sc->opt3 &= ~OPT3_ENERGYCOAT;
+ opt_flag = 0;
+ break;
+ case SC_INCATKRATE: //Simulated Explosion spirits effect.
+ if (bl->type != BL_MOB)
+ {
+ opt_flag = 0;
+ break;
+ }
+ case SC_EXPLOSIONSPIRITS:
+ sc->opt3 &= ~OPT3_EXPLOSIONSPIRITS;
+ opt_flag = 0;
+ break;
+ case SC_STEELBODY:
+ case SC_SKA:
+ sc->opt3 &= ~OPT3_STEELBODY;
+ opt_flag = 0;
+ break;
+ case SC_BLADESTOP:
+ sc->opt3 &= ~OPT3_BLADESTOP;
+ opt_flag = 0;
+ break;
+ case SC_AURABLADE:
+ sc->opt3 &= ~OPT3_AURABLADE;
+ opt_flag = 0;
+ break;
+ case SC_BERSERK:
+ opt_flag = 0;
+// case SC__BLOODYLUST:
+ sc->opt3 &= ~OPT3_BERSERK;
+ break;
+// case ???: // doesn't seem to do anything
+// sc->opt3 &= ~OPT3_LIGHTBLADE;
+// opt_flag = 0;
+// break;
+ case SC_DANCING:
+ if ((sce->val1&0xFFFF) == CG_MOONLIT)
+ sc->opt3 &= ~OPT3_MOONLIT;
+ opt_flag = 0;
+ break;
+ case SC_MARIONETTE:
+ case SC_MARIONETTE2:
+ sc->opt3 &= ~OPT3_MARIONETTE;
+ opt_flag = 0;
+ break;
+ case SC_ASSUMPTIO:
+ sc->opt3 &= ~OPT3_ASSUMPTIO;
+ opt_flag = 0;
+ break;
+ case SC_WARM: //SG skills [Komurka]
+ sc->opt3 &= ~OPT3_WARM;
+ opt_flag = 0;
+ break;
+ case SC_KAITE:
+ sc->opt3 &= ~OPT3_KAITE;
+ opt_flag = 0;
+ break;
+ case SC_BUNSINJYUTSU:
+ sc->opt3 &= ~OPT3_BUNSIN;
+ opt_flag = 0;
+ break;
+ case SC_SPIRIT:
+ sc->opt3 &= ~OPT3_SOULLINK;
+ opt_flag = 0;
+ break;
+ case SC_CHANGEUNDEAD:
+ sc->opt3 &= ~OPT3_UNDEAD;
+ opt_flag = 0;
+ break;
+// case ???: // from DA_CONTRACT (looks like biolab mobs aura)
+// sc->opt3 &= ~OPT3_CONTRACT;
+// opt_flag = 0;
+// break;
+ default:
+ opt_flag = 0;
+ }
+
+ if (calc_flag&SCB_DYE)
+ { //Restore DYE color
+ if (vd && !vd->cloth_color && sce->val4)
+ clif_changelook(bl,LOOK_CLOTHES_COLOR,sce->val4);
+ calc_flag&=~SCB_DYE;
+ }
+
+ //On Aegis, when turning off a status change, first goes the sc packet, then the option packet.
+ clif_status_change(bl,StatusIconChangeTable[type],0,0,0,0,0);
+
+ if( opt_flag&8 ) //bugreport:681
+ clif_changeoption2(bl);
+ else if(opt_flag)
+ clif_changeoption(bl);
+
+ if (calc_flag)
+ status_calc_bl(bl,calc_flag);
+
+ if(opt_flag&4) //Out of hiding, invoke on place.
+ skill_unit_move(bl,gettick(),1);
+
+ if(opt_flag&2 && sd && map_getcell(bl->m,bl->x,bl->y,CELL_CHKNPC))
+ npc_touch_areanpc(sd,bl->m,bl->x,bl->y); //Trigger on-touch event.
+
+ ers_free(sc_data_ers, sce);
+ return 1;
+}
+
+int kaahi_heal_timer(int tid, unsigned int tick, int id, intptr_t data)
+{
+ struct block_list *bl;
+ struct status_change *sc;
+ struct status_change_entry *sce;
+ struct status_data *status;
+ int hp;
+
+ if(!((bl=map_id2bl(id))&&
+ (sc=status_get_sc(bl)) &&
+ (sce = sc->data[SC_KAAHI])))
+ return 0;
+
+ if(sce->val4 != tid) {
+ ShowError("kaahi_heal_timer: Timer mismatch: %d != %d\n", tid, sce->val4);
+ sce->val4 = INVALID_TIMER;
+ return 0;
+ }
+
+ status=status_get_status_data(bl);
+ if(!status_charge(bl, 0, sce->val3)) {
+ sce->val4 = INVALID_TIMER;
+ return 0;
+ }
+
+ hp = status->max_hp - status->hp;
+ if (hp > sce->val2)
+ hp = sce->val2;
+ if (hp)
+ status_heal(bl, hp, 0, 2);
+ sce->val4 = INVALID_TIMER;
+ return 1;
+}
+
+/*==========================================
+ * For recusive status, like for each 5s we drop sp etc.
+ * Reseting the end timer.
+ *------------------------------------------*/
+int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
+{
+ enum sc_type type = (sc_type)data;
+ struct block_list *bl;
+ struct map_session_data *sd;
+ struct status_data *status;
+ struct status_change *sc;
+ struct status_change_entry *sce;
+
+ bl = map_id2bl(id);
+ if(!bl)
+ {
+ ShowDebug("status_change_timer: Null pointer id: %d data: %d\n", id, data);
+ return 0;
+ }
+ sc = status_get_sc(bl);
+ status = status_get_status_data(bl);
+
+ if(!(sc && (sce = sc->data[type])))
+ {
+ ShowDebug("status_change_timer: Null pointer id: %d data: %d bl-type: %d\n", id, data, bl->type);
+ return 0;
+ }
+
+ if( sce->timer != tid )
+ {
+ ShowError("status_change_timer: Mismatch for type %d: %d != %d (bl id %d)\n",type,tid,sce->timer, bl->id);
+ return 0;
+ }
+
+ sd = BL_CAST(BL_PC, bl);
+
+// set the next timer of the sce (don't assume the status still exists)
+#define sc_timer_next(t,f,i,d) \
+ if( (sce=sc->data[type]) ) \
+ sce->timer = add_timer(t,f,i,d); \
+ else \
+ ShowError("status_change_timer: Unexpected NULL status change id: %d data: %d\n", id, data)
+
+ switch(type)
+ {
+ case SC_MAXIMIZEPOWER:
+ case SC_CLOAKING:
+ if(!status_charge(bl, 0, 1))
+ break; //Not enough SP to continue.
+ sc_timer_next(sce->val2+tick, status_change_timer, bl->id, data);
+ return 0;
+
+ case SC_CHASEWALK:
+ if(!status_charge(bl, 0, sce->val4))
+ break; //Not enough SP to continue.
+
+ if (!sc->data[SC_INCSTR]) {
+ sc_start(bl, SC_INCSTR,100,1<<(sce->val1-1),
+ (sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_ROGUE?10:1) //SL bonus -> x10 duration
+ *skill_get_time2(status_sc2skill(type),sce->val1));
+ }
+ sc_timer_next(sce->val2+tick, status_change_timer, bl->id, data);
+ return 0;
+ break;
+
+ case SC_SKA:
+ if(--(sce->val2)>0){
+ sce->val3 = rnd()%100; //Random defense.
+ sc_timer_next(1000+tick, status_change_timer,bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_HIDING:
+ if(--(sce->val2)>0){
+
+ if(sce->val2 % sce->val4 == 0 && !status_charge(bl, 0, 1))
+ break; //Fail if it's time to substract SP and there isn't.
+
+ sc_timer_next(1000+tick, status_change_timer,bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_SIGHT:
+ case SC_RUWACH:
+ case SC_SIGHTBLASTER:
+ if(type == SC_SIGHTBLASTER)
+ map_foreachinrange( status_change_timer_sub, bl, sce->val3, BL_CHAR|BL_SKILL, bl, sce, type, tick);
+ else
+ map_foreachinrange( status_change_timer_sub, bl, sce->val3, BL_CHAR, bl, sce, type, tick);
+
+ if( --(sce->val2)>0 ){
+ sce->val4 += 250; // use for Shadow Form 2 seconds checking.
+ sc_timer_next(250+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_PROVOKE:
+ if(sce->val2) { //Auto-provoke (it is ended in status_heal)
+ sc_timer_next(1000*60+tick,status_change_timer, bl->id, data );
+ return 0;
+ }
+ break;
+
+ case SC_STONE:
+ if(sc->opt1 == OPT1_STONEWAIT && sce->val3) {
+ sce->val4 = 0;
+ unit_stop_walking(bl,1);
+ unit_stop_attack(bl);
+ sc->opt1 = OPT1_STONE;
+ clif_changeoption(bl);
+ sc_timer_next(1000+tick,status_change_timer, bl->id, data );
+ status_calc_bl(bl, StatusChangeFlagTable[type]);
+ return 0;
+ }
+ if(--(sce->val3) > 0) {
+ if(++(sce->val4)%5 == 0 && status->hp > status->max_hp/4)
+ status_percent_damage(NULL, bl, 1, 0, false);
+ sc_timer_next(1000+tick,status_change_timer, bl->id, data );
+ return 0;
+ }
+ break;
+
+ case SC_POISON:
+ if(status->hp <= max(status->max_hp>>2, sce->val4)) //Stop damaging after 25% HP left.
+ break;
+ case SC_DPOISON:
+ if (--(sce->val3) > 0) {
+ if (!sc->data[SC_SLOWPOISON]) {
+ if( sce->val2 && bl->type == BL_MOB ) {
+ struct block_list* src = map_id2bl(sce->val2);
+ if( src )
+ mob_log_damage((TBL_MOB*)bl,src,sce->val4);
+ }
+ map_freeblock_lock();
+ status_zap(bl, sce->val4, 0);
+ if (sc->data[type]) { // Check if the status still last ( can be dead since then ).
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data );
+ }
+ map_freeblock_unlock();
+ }
+ return 0;
+ }
+ break;
+
+ case SC_TENSIONRELAX:
+ if(status->max_hp > status->hp && --(sce->val3) > 0){
+ sc_timer_next(sce->val4+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_KNOWLEDGE:
+ if (!sd) break;
+ if(bl->m == sd->feel_map[0].m ||
+ bl->m == sd->feel_map[1].m ||
+ bl->m == sd->feel_map[2].m)
+ { //Timeout will be handled by pc_setpos
+ sce->timer = INVALID_TIMER;
+ return 0;
+ }
+ break;
+
+ case SC_BLEEDING:
+ if (--(sce->val4) >= 0) {
+ int hp = rnd()%600 + 200;
+ map_freeblock_lock();
+ status_fix_damage(NULL, bl, sd||hp<status->hp?hp:status->hp-1, 1);
+ if( sc->data[type] ) {
+ if( status->hp == 1 ) {
+ map_freeblock_unlock();
+ break;
+ }
+ sc_timer_next(10000 + tick, status_change_timer, bl->id, data);
+ }
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
+
+ case SC_S_LIFEPOTION:
+ case SC_L_LIFEPOTION:
+ if( sd && --(sce->val4) >= 0 )
+ {
+ // val1 < 0 = per max% | val1 > 0 = exact amount
+ int hp = 0;
+ if( status->hp < status->max_hp )
+ hp = (sce->val1 < 0) ? (int)(sd->status.max_hp * -1 * sce->val1 / 100.) : sce->val1 ;
+ status_heal(bl, hp, 0, 2);
+ sc_timer_next((sce->val2 * 1000) + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_BOSSMAPINFO:
+ if( sd && --(sce->val4) >= 0 )
+ {
+ struct mob_data *boss_md = map_id2boss(sce->val1);
+ if( boss_md && sd->bl.m == boss_md->bl.m )
+ {
+ clif_bossmapinfo(sd->fd, boss_md, 1); // Update X - Y on minimap
+ if (boss_md->bl.prev != NULL) {
+ sc_timer_next(5000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ }
+ }
+ break;
+
+ case SC_DANCING: //SP consumption by time of dancing skills
+ {
+ int s = 0;
+ int sp = 1;
+ if (--sce->val3 <= 0)
+ break;
+ switch(sce->val1&0xFFFF){
+ case BD_RICHMANKIM:
+ case BD_DRUMBATTLEFIELD:
+ case BD_RINGNIBELUNGEN:
+ case BD_SIEGFRIED:
+ case BA_DISSONANCE:
+ case BA_ASSASSINCROSS:
+ case DC_UGLYDANCE:
+ s=3;
+ break;
+ case BD_LULLABY:
+ case BD_ETERNALCHAOS:
+ case BD_ROKISWEIL:
+ case DC_FORTUNEKISS:
+ s=4;
+ break;
+ case CG_HERMODE:
+ case BD_INTOABYSS:
+ case BA_WHISTLE:
+ case DC_HUMMING:
+ case BA_POEMBRAGI:
+ case DC_SERVICEFORYOU:
+ s=5;
+ break;
+ case BA_APPLEIDUN:
+ #ifdef RENEWAL
+ s=5;
+ #else
+ s=6;
+ #endif
+ break;
+ case CG_MOONLIT:
+ //Moonlit's cost is 4sp*skill_lv [Skotlex]
+ sp= 4*(sce->val1>>16);
+ //Upkeep is also every 10 secs.
+ case DC_DONTFORGETME:
+ s=10;
+ break;
+ }
+ if( s != 0 && sce->val3 % s == 0 )
+ {
+ if (sc->data[SC_LONGING])
+ sp*= 3;
+ if (!status_charge(bl, 0, sp))
+ break;
+ }
+ sc_timer_next(1000+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+ case SC__BLOODYLUST:
+ case SC_BERSERK:
+ // 5% every 10 seconds [DracoRPG]
+ if( --( sce->val3 ) > 0 && status_charge(bl, sce->val2, 0) && status->hp > 100 )
+ {
+ sc_timer_next(sce->val4+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_NOCHAT:
+ if(sd){
+ sd->status.manner++;
+ clif_changestatus(sd,SP_MANNER,sd->status.manner);
+ clif_updatestatus(sd,SP_MANNER);
+ if (sd->status.manner < 0)
+ { //Every 60 seconds your manner goes up by 1 until it gets back to 0.
+ sc_timer_next(60000+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_SPLASHER:
+ // custom Venom Splasher countdown timer
+ //if (sce->val4 % 1000 == 0) {
+ // char timer[10];
+ // snprintf (timer, 10, "%d", sce->val4/1000);
+ // clif_message(bl, timer);
+ //}
+ if((sce->val4 -= 500) > 0) {
+ sc_timer_next(500 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_MARIONETTE:
+ case SC_MARIONETTE2:
+ {
+ struct block_list *pbl = map_id2bl(sce->val1);
+ if( pbl && check_distance_bl(bl, pbl, 7) )
+ {
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ }
+ break;
+
+ case SC_GOSPEL:
+ if(sce->val4 == BCT_SELF && --(sce->val2) > 0)
+ {
+ int hp, sp;
+ hp = (sce->val1 > 5) ? 45 : 30;
+ sp = (sce->val1 > 5) ? 35 : 20;
+ if(!status_charge(bl, hp, sp))
+ break;
+ sc_timer_next(10000+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_JAILED:
+ if(sce->val1 == INT_MAX || --(sce->val1) > 0)
+ {
+ sc_timer_next(60000+tick, status_change_timer, bl->id,data);
+ return 0;
+ }
+ break;
+
+ case SC_BLIND:
+ if(sc->data[SC_FOGWALL])
+ { //Blind lasts forever while you are standing on the fog.
+ sc_timer_next(5000+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+ case SC_ABUNDANCE:
+ if(--(sce->val4) > 0) {
+ status_heal(bl,0,60,0);
+ sc_timer_next(10000+tick, status_change_timer, bl->id, data);
+ }
+ break;
+
+ case SC_PYREXIA:
+ if( --(sce->val4) >= 0 ) {
+ map_freeblock_lock();
+ clif_damage(bl,bl,tick,status_get_amotion(bl),status_get_dmotion(bl)+500,100,0,0,0);
+ status_fix_damage(NULL,bl,100,0);
+ if( sc->data[type] ) {
+ sc_timer_next(3000+tick,status_change_timer,bl->id,data);
+ }
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
+
+ case SC_LEECHESEND:
+ if( --(sce->val4) >= 0 ) {
+ int damage = status->max_hp/100; // {Target VIT x (New Poison Research Skill Level - 3)} + (Target HP/100)
+ damage += status->vit * (sce->val1 - 3);
+ unit_skillcastcancel(bl,2);
+ map_freeblock_lock();
+ status_damage(bl, bl, damage, 0, clif_damage(bl,bl,tick,status_get_amotion(bl),status_get_dmotion(bl)+500,damage,1,0,0), 1);
+ if( sc->data[type] ) {
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data );
+ }
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
+
+ case SC_MAGICMUSHROOM:
+ if( --(sce->val4) >= 0 ) {
+ bool flag = 0;
+ int damage = status->max_hp * 3 / 100;
+ if( status->hp <= damage )
+ damage = status->hp - 1; // Cannot Kill
+
+ if( damage > 0 ) { // 3% Damage each 4 seconds
+ map_freeblock_lock();
+ status_zap(bl,damage,0);
+ flag = !sc->data[type]; // Killed? Should not
+ map_freeblock_unlock();
+ }
+
+ if( !flag ) { // Random Skill Cast
+ if (sd && !pc_issit(sd)) { //can't cast if sit
+ int mushroom_skill_id = 0, i;
+ unit_stop_attack(bl);
+ unit_skillcastcancel(bl,1);
+ do {
+ i = rnd() % MAX_SKILL_MAGICMUSHROOM_DB;
+ mushroom_skill_id = skill_magicmushroom_db[i].skill_id;
+ }
+ while( mushroom_skill_id == 0 );
+
+ switch( skill_get_casttype(mushroom_skill_id) ) { // Magic Mushroom skills are buffs or area damage
+ case CAST_GROUND:
+ skill_castend_pos2(bl,bl->x,bl->y,mushroom_skill_id,1,tick,0);
+ break;
+ case CAST_NODAMAGE:
+ skill_castend_nodamage_id(bl,bl,mushroom_skill_id,1,tick,0);
+ break;
+ case CAST_DAMAGE:
+ skill_castend_damage_id(bl,bl,mushroom_skill_id,1,tick,0);
+ break;
+ }
+ }
+
+ clif_emotion(bl,E_HEH);
+ sc_timer_next(4000+tick,status_change_timer,bl->id,data);
+ }
+ return 0;
+ }
+ break;
+
+ case SC_TOXIN:
+ if( --(sce->val4) >= 0 )
+ { //Damage is every 10 seconds including 3%sp drain.
+ map_freeblock_lock();
+ clif_damage(bl,bl,tick,status_get_amotion(bl),1,1,0,0,0);
+ status_damage(NULL, bl, 1, status->max_sp * 3 / 100, 0, 0); //cancel dmg only if cancelable
+ if( sc->data[type] ) {
+ sc_timer_next(10000 + tick, status_change_timer, bl->id, data );
+ }
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
+
+ case SC_OBLIVIONCURSE:
+ if( --(sce->val4) >= 0 )
+ {
+ clif_emotion(bl,E_WHAT);
+ sc_timer_next(3000 + tick, status_change_timer, bl->id, data );
+ return 0;
+ }
+ break;
+
+ case SC_WEAPONBLOCKING:
+ if( --(sce->val4) >= 0 )
+ {
+ if( !status_charge(bl,0,3) )
+ break;
+ sc_timer_next(3000+tick,status_change_timer,bl->id,data);
+ return 0;
+ }
+ break;
+
+ case SC_CLOAKINGEXCEED:
+ if(!status_charge(bl,0,10-sce->val1))
+ break;
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+
+ case SC_RENOVATIO:
+ if( --(sce->val4) >= 0 )
+ {
+ int heal = status->max_hp * 3 / 100;
+ if( sc && sc->data[SC_AKAITSUKI] && heal )
+ heal = ~heal + 1;
+ status_heal(bl, heal, 0, 2);
+ sc_timer_next(5000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_BURNING:
+ if( --(sce->val4) >= 0 )
+ {
+ struct block_list *src = map_id2bl(sce->val3);
+ int damage = 1000 + 3 * status_get_max_hp(bl) / 100; // Deals fixed (1000 + 3%*MaxHP)
+
+ map_freeblock_lock();
+ clif_damage(bl,bl,tick,0,0,damage,1,9,0); //damage is like endure effect with no walk delay
+ status_damage(src, bl, damage, 0, 0, 1);
+
+ if( sc->data[type]){ // Target still lives. [LimitLine]
+ sc_timer_next(2000 + tick, status_change_timer, bl->id, data);
+ }
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
+
+ case SC_FEAR:
+ if( --(sce->val4) >= 0 )
+ {
+ if( sce->val2 > 0 )
+ sce->val2--;
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_SPHERE_1:
+ case SC_SPHERE_2:
+ case SC_SPHERE_3:
+ case SC_SPHERE_4:
+ case SC_SPHERE_5:
+ if( --(sce->val4) >= 0 )
+ {
+ if( !status_charge(bl, 0, 1) )
+ break;
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_READING_SB:
+ if( !status_charge(bl, 0, sce->val2) ){
+ int i;
+ for(i = SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++) // Also remove stored spell as well.
+ status_change_end(bl, (sc_type)i, INVALID_TIMER);
+ break;
+ }
+ sc_timer_next(5000 + tick, status_change_timer, bl->id, data);
+ return 0;
+
+ case SC_ELECTRICSHOCKER:
+ if( --(sce->val4) >= 0 )
+ {
+ status_charge(bl, 0, status->max_sp / 100 * sce->val1 );
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_CAMOUFLAGE:
+ if(--(sce->val4) > 0){
+ status_charge(bl,0,7 - sce->val1);
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC__REPRODUCE:
+ if(!status_charge(bl, 0, 1))
+ break;
+ sc_timer_next(1000+tick, status_change_timer, bl->id, data);
+ return 0;
+
+ case SC__SHADOWFORM:
+ if( --(sce->val4) >= 0 )
+ {
+ if( !status_charge(bl, 0, sce->val1 - (sce->val1 - 1)) )
+ break;
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC__INVISIBILITY:
+ if( --(sce->val4) >= 0 )
+ {
+ if( !status_charge(bl, 0, (status->sp * 6 - sce->val1) / 100) )// 6% - skill_lv.
+ break;
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_STRIKING:
+ if( --(sce->val4) >= 0 )
+ {
+ if( !status_charge(bl,0, sce->val1 ) )
+ break;
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+ case SC_VACUUM_EXTREME:
+ if( --(sce->val4) >= 0 ){
+ if( !unit_is_walking(bl) && !sce->val2 ){
+ sc->cant.move++;
+ sce->val2 = 1;
+ }
+ sc_timer_next(100 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+ case SC_BLOODSUCKER:
+ if( --(sce->val4) >= 0 ) {
+ struct block_list *src = map_id2bl(sce->val2);
+ int damage;
+ if( !src || (src && (status_isdead(src) || src->m != bl->m || distance_bl(src, bl) >= 12)) )
+ break;
+ map_freeblock_lock();
+ damage = 200 + 100 * sce->val1 + status_get_int(src);
+ status_damage(src, bl, damage, 0, clif_damage(bl,bl,tick,status->amotion,status->dmotion+200,damage,1,0,0), 1);
+ unit_skillcastcancel(bl,1);
+ if ( sc->data[type] ) {
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ }
+ map_freeblock_unlock();
+ status_heal(src, damage*(5 + 5 * sce->val1)/100, 0, 0); // 5 + 5% per level
+ return 0;
+ }
+ break;
+
+ case SC_VOICEOFSIREN:
+ if( --(sce->val4) >= 0 )
+ {
+ clif_emotion(bl,E_LV);
+ sc_timer_next(2000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_DEEPSLEEP:
+ if( --(sce->val4) >= 0 )
+ { // Recovers 1% HP/SP every 2 seconds.
+ status_heal(bl, status->max_hp / 100, status->max_sp / 100, 2);
+ sc_timer_next(2000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_SIRCLEOFNATURE:
+ if( --(sce->val4) >= 0 )
+ {
+ if( !status_charge(bl,0,sce->val2) )
+ break;
+ status_heal(bl, sce->val3, 0, 1);
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_SONGOFMANA:
+ if( --(sce->val4) >= 0 )
+ {
+ status_heal(bl,0,sce->val3,3);
+ sc_timer_next(3000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+
+ case SC_SATURDAYNIGHTFEVER:
+ // 1% HP/SP drain every val4 seconds [Jobbie]
+ if( --(sce->val3) >= 0 )
+ {
+ int hp = status->hp / 100;
+ int sp = status->sp / 100;
+ if( !status_charge(bl, hp, sp) )
+ break;
+ sc_timer_next(sce->val4+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_CRYSTALIZE:
+ if( --(sce->val4) >= 0 )
+ { // Drains 2% of HP and 1% of SP every seconds.
+ if( bl->type != BL_MOB) // doesn't work on mobs
+ status_charge(bl, status->max_hp * 2 / 100, status->max_sp / 100);
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_FORCEOFVANGUARD:
+ if( !status_charge(bl,0,20) )
+ break;
+ sc_timer_next(6000 + tick, status_change_timer, bl->id, data);
+ return 0;
+
+ case SC_BANDING:
+ if( status_charge(bl, 0, 7 - sce->val1) )
+ {
+ if( sd ) pc_banding(sd, sce->val1);
+ sc_timer_next(5000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_REFLECTDAMAGE:
+ if( --(sce->val4) >= 0 ) {
+ if( !status_charge(bl,0,sce->val3) )
+ break;
+ sc_timer_next(10000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_OVERHEAT_LIMITPOINT:
+ if( --(sce->val1) > 0 ) { // Cooling
+ sc_timer_next(30000 + tick, status_change_timer, bl->id, data);
+ }
+ break;
+
+ case SC_OVERHEAT:
+ {
+ int damage = status->max_hp / 100; // Suggestion 1% each second
+ if( damage >= status->hp ) damage = status->hp - 1; // Do not kill, just keep you with 1 hp minimum
+ map_freeblock_lock();
+ status_fix_damage(NULL,bl,damage,clif_damage(bl,bl,tick,0,0,damage,0,0,0));
+ if( sc->data[type] ) {
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ }
+ map_freeblock_unlock();
+ }
+ break;
+
+ case SC_MAGNETICFIELD:
+ {
+ if( --(sce->val3) <= 0 )
+ break; // Time out
+ if( sce->val2 == bl->id )
+ {
+ if( !status_charge(bl,0,14 + (3 * sce->val1)) )
+ break; // No more SP status should end, and in the next second will end for the other affected players
+ }
+ else
+ {
+ struct block_list *src = map_id2bl(sce->val2);
+ struct status_change *ssc;
+ if( !src || (ssc = status_get_sc(src)) == NULL || !ssc->data[SC_MAGNETICFIELD] )
+ break; // Source no more under Magnetic Field
+ }
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ }
+ break;
+
+ case SC_INSPIRATION:
+ if(--(sce->val4) >= 0)
+ {
+ int hp = status->max_hp * (7-sce->val1) / 100;
+ int sp = status->max_sp * (9-sce->val1) / 100;
+
+ if( !status_charge(bl,hp,sp) ) break;
+
+ sc_timer_next(1000+tick,status_change_timer,bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_RAISINGDRAGON:
+ // 1% every 5 seconds [Jobbie]
+ if( --(sce->val3)>0 && status_charge(bl, sce->val2, 0) )
+ {
+ if( !sc->data[type] ) return 0;
+ sc_timer_next(5000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+
+ case SC_CIRCLE_OF_FIRE:
+ case SC_FIRE_CLOAK:
+ case SC_WATER_DROP:
+ case SC_WATER_SCREEN:
+ case SC_WIND_CURTAIN:
+ case SC_WIND_STEP:
+ case SC_STONE_SHIELD:
+ case SC_SOLID_SKIN:
+ if( !status_charge(bl,0,sce->val2) ){
+ struct block_list *s_bl = battle_get_master(bl);
+ if( s_bl )
+ status_change_end(s_bl,type+1,INVALID_TIMER);
+ status_change_end(bl,type,INVALID_TIMER);
+ break;
+ }
+ sc_timer_next(2000 + tick, status_change_timer, bl->id, data);
+ return 0;
+
+ case SC_STOMACHACHE:
+ if( --(sce->val4) > 0 ){
+ status_charge(bl,0,sce->val2); // Reduce 8 every 10 seconds.
+ if( sd && !pc_issit(sd) ) // Force to sit every 10 seconds.
+ {
+ pc_stop_walking(sd,1|4);
+ pc_stop_attack(sd);
+ pc_setsit(sd);
+ clif_sitting(bl);
+ }
+ sc_timer_next(10000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+ case SC_LEADERSHIP:
+ case SC_GLORYWOUNDS:
+ case SC_SOULCOLD:
+ case SC_HAWKEYES:
+ /* they only end by status_change_end */
+ sc_timer_next(600000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ case SC_MEIKYOUSISUI:
+ if( --(sce->val4) > 0 ){
+ status_heal(bl, status->max_hp * (sce->val1+1) / 100, status->max_sp * sce->val1 / 100, 0);
+ sc_timer_next(1000 + tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+ case SC_IZAYOI:
+ case SC_KAGEMUSYA:
+ if( --(sce->val2) > 0 ){
+ if(!status_charge(bl, 0, 1)) break;
+ sc_timer_next(1000+tick, status_change_timer, bl->id, data);
+ return 0;
+ }
+ break;
+ case SC_ANGRIFFS_MODUS:
+ if(--(sce->val4) >= 0) { //drain hp/sp
+ if( !status_charge(bl,100,20) ) break;
+ sc_timer_next(1000+tick,status_change_timer,bl->id, data);
+ return 0;
+ }
+ break;
+ }
+
+ // default for all non-handled control paths is to end the status
+ return status_change_end( bl,type,tid );
+#undef sc_timer_next
+}
+
+/*==========================================
+ * Foreach iteration of repetitive status
+ *------------------------------------------*/
+int status_change_timer_sub(struct block_list* bl, va_list ap)
+{
+ struct status_change* tsc;
+
+ struct block_list* src = va_arg(ap,struct block_list*);
+ struct status_change_entry* sce = va_arg(ap,struct status_change_entry*);
+ enum sc_type type = (sc_type)va_arg(ap,int); //gcc: enum args get promoted to int
+ unsigned int tick = va_arg(ap,unsigned int);
+
+ if (status_isdead(bl))
+ return 0;
+
+ tsc = status_get_sc(bl);
+
+ switch( type ) {
+ case SC_SIGHT: /* Reveal hidden ennemy on 3*3 range */
+ if( tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 >0 && sce->val4%2000 == 0) && // for every 2 seconds do the checking
+ rnd()%100 < 100-tsc->data[SC__SHADOWFORM]->val1*10 ) // [100 - (Skill Level x 10)] %
+ status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER);
+ case SC_CONCENTRATE:
+ status_change_end(bl, SC_HIDING, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
+ status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER);
+ status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER);
+ break;
+ case SC_RUWACH: /* Reveal hidden target and deal little dammages if ennemy */
+ if (tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING] ||
+ tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_CLOAKINGEXCEED] ||
+ tsc->data[SC__INVISIBILITY])) {
+ status_change_end(bl, SC_HIDING, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
+ status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER);
+ status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
+ status_change_end(bl, SC__INVISIBILITY, INVALID_TIMER);
+ if(battle_check_target( src, bl, BCT_ENEMY ) > 0)
+ skill_attack(BF_MAGIC,src,src,bl,AL_RUWACH,1,tick,0);
+ }
+ if( tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 >0 && sce->val4%2000 == 0) && // for every 2 seconds do the checking
+ rnd()%100 < 100-tsc->data[SC__SHADOWFORM]->val1*10 ) // [100 - (Skill Level x 10)] %
+ status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER);
+ break;
+ case SC_SIGHTBLASTER:
+ if (battle_check_target( src, bl, BCT_ENEMY ) > 0 &&
+ status_check_skilluse(src, bl, WZ_SIGHTBLASTER, 2))
+ {
+ skill_attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,1,tick,0);
+ if (sce && !(bl->type&BL_SKILL)) //The hit is not counted if it's against a trap
+ sce->val2 = 0; //This signals it to end.
+ }
+ break;
+ case SC_CLOSECONFINE:
+ //Lock char has released the hold on everyone...
+ if (tsc && tsc->data[SC_CLOSECONFINE2] && tsc->data[SC_CLOSECONFINE2]->val2 == src->id) {
+ tsc->data[SC_CLOSECONFINE2]->val2 = 0;
+ status_change_end(bl, SC_CLOSECONFINE2, INVALID_TIMER);
+ }
+ break;
+ case SC_CURSEDCIRCLE_TARGET:
+ if( tsc && tsc->data[SC_CURSEDCIRCLE_TARGET] && tsc->data[SC_CURSEDCIRCLE_TARGET]->val2 == src->id ) {
+ clif_bladestop(bl, tsc->data[SC_CURSEDCIRCLE_TARGET]->val2, 0);
+ status_change_end(bl, type, INVALID_TIMER);
+ }
+ break;
+ }
+ return 0;
+}
+
+/*==========================================
+ * Clears buffs/debuffs of a character.
+ * type&1 -> buffs, type&2 -> debuffs
+ * type&4 -> especific debuffs(implemented with refresh)
+ *------------------------------------------*/
+int status_change_clear_buffs (struct block_list* bl, int type)
+{
+ int i;
+ struct status_change *sc= status_get_sc(bl);
+
+ if (!sc || !sc->count)
+ return 0;
+
+ if (type&6) //Debuffs
+ for (i = SC_COMMON_MIN; i <= SC_COMMON_MAX; i++)
+ status_change_end(bl, (sc_type)i, INVALID_TIMER);
+
+ for( i = SC_COMMON_MAX+1; i < SC_MAX; i++ )
+ {
+ if(!sc->data[i])
+ continue;
+
+ switch (i) {
+ //Stuff that cannot be removed
+ case SC_WEIGHT50:
+ case SC_WEIGHT90:
+ case SC_COMBO:
+ case SC_SMA:
+ case SC_DANCING:
+ case SC_LEADERSHIP:
+ case SC_GLORYWOUNDS:
+ case SC_SOULCOLD:
+ case SC_HAWKEYES:
+ case SC_GUILDAURA:
+ case SC_SAFETYWALL:
+ case SC_PNEUMA:
+ case SC_NOCHAT:
+ case SC_JAILED:
+ case SC_ANKLE:
+ case SC_BLADESTOP:
+ case SC_CP_WEAPON:
+ case SC_CP_SHIELD:
+ case SC_CP_ARMOR:
+ case SC_CP_HELM:
+ case SC_STRFOOD:
+ case SC_AGIFOOD:
+ case SC_VITFOOD:
+ case SC_INTFOOD:
+ case SC_DEXFOOD:
+ case SC_LUKFOOD:
+ case SC_HITFOOD:
+ case SC_FLEEFOOD:
+ case SC_BATKFOOD:
+ case SC_WATKFOOD:
+ case SC_MATKFOOD:
+ case SC_FOOD_STR_CASH:
+ case SC_FOOD_AGI_CASH:
+ case SC_FOOD_VIT_CASH:
+ case SC_FOOD_DEX_CASH:
+ case SC_FOOD_INT_CASH:
+ case SC_FOOD_LUK_CASH:
+ case SC_EXPBOOST:
+ case SC_JEXPBOOST:
+ case SC_ITEMBOOST:
+ case SC_ELECTRICSHOCKER:
+ case SC__MANHOLE:
+ case SC_GIANTGROWTH:
+ case SC_MILLENNIUMSHIELD:
+ case SC_REFRESH:
+ case SC_STONEHARDSKIN:
+ case SC_VITALITYACTIVATION:
+ case SC_FIGHTINGSPIRIT:
+ case SC_ABUNDANCE:
+ case SC_CURSEDCIRCLE_ATKER:
+ case SC_CURSEDCIRCLE_TARGET:
+ continue;
+
+ //Debuffs that can be removed.
+ case SC_DEEPSLEEP:
+ case SC_BURNING:
+ case SC_FREEZING:
+ case SC_CRYSTALIZE:
+ case SC_TOXIN:
+ case SC_PARALYSE:
+ case SC_VENOMBLEED:
+ case SC_MAGICMUSHROOM:
+ case SC_DEATHHURT:
+ case SC_PYREXIA:
+ case SC_OBLIVIONCURSE:
+ case SC_LEECHESEND:
+ case SC_MARSHOFABYSS:
+ case SC_MANDRAGORA:
+ if(!(type&4))
+ continue;
+ break;
+ case SC_HALLUCINATION:
+ case SC_QUAGMIRE:
+ case SC_SIGNUMCRUCIS:
+ case SC_DECREASEAGI:
+ case SC_SLOWDOWN:
+ case SC_MINDBREAKER:
+ case SC_WINKCHARM:
+ case SC_STOP:
+ case SC_ORCISH:
+ case SC_STRIPWEAPON:
+ case SC_STRIPSHIELD:
+ case SC_STRIPARMOR:
+ case SC_STRIPHELM:
+ case SC_BITE:
+ case SC_ADORAMUS:
+ case SC_VACUUM_EXTREME:
+ case SC_FEAR:
+ case SC_MAGNETICFIELD:
+ case SC_NETHERWORLD:
+ if (!(type&2))
+ continue;
+ break;
+ //The rest are buffs that can be removed.
+ case SC__BLOODYLUST:
+ case SC_BERSERK:
+ case SC_SATURDAYNIGHTFEVER:
+ if (!(type&1))
+ continue;
+ sc->data[i]->val2 = 0;
+ break;
+ default:
+ if (!(type&1))
+ continue;
+ break;
+ }
+ status_change_end(bl, (sc_type)i, INVALID_TIMER);
+ }
+ return 0;
+}
+
+int status_change_spread( struct block_list *src, struct block_list *bl ) {
+ int i, flag = 0;
+ struct status_change *sc = status_get_sc(src);
+ const struct TimerData *timer;
+ unsigned int tick;
+ struct status_change_data data;
+
+ if( !sc || !sc->count )
+ return 0;
+
+ tick = gettick();
+
+ for( i = SC_COMMON_MIN; i < SC_MAX; i++ ) {
+ if( !sc->data[i] || i == SC_COMMON_MAX )
+ continue;
+
+ switch( i ) {
+ //Debuffs that can be spreaded.
+ // NOTE: We'll add/delte SCs when we are able to confirm it.
+ case SC_CURSE:
+ case SC_SILENCE:
+ case SC_CONFUSION:
+ case SC_BLIND:
+ case SC_NOCHAT:
+ case SC_HALLUCINATION:
+ case SC_SIGNUMCRUCIS:
+ case SC_DECREASEAGI:
+ case SC_SLOWDOWN:
+ case SC_MINDBREAKER:
+ case SC_WINKCHARM:
+ case SC_STOP:
+ case SC_ORCISH:
+ //case SC_STRIPWEAPON://Omg I got infected and had the urge to strip myself physically.
+ //case SC_STRIPSHIELD://No this is stupid and shouldnt be spreadable at all.
+ //case SC_STRIPARMOR:// Disabled until I can confirm if it does or not. [Rytech]
+ //case SC_STRIPHELM:
+ //case SC__STRIPACCESSORY:
+ case SC_BITE:
+ case SC_FREEZING:
+ case SC_VENOMBLEED:
+ case SC_DEATHHURT:
+ case SC_PARALYSE:
+ if( sc->data[i]->timer != INVALID_TIMER ) {
+ timer = get_timer(sc->data[i]->timer);
+ if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0)
+ continue;
+ data.tick = DIFF_TICK(timer->tick,tick);
+ } else
+ data.tick = INVALID_TIMER;
+ break;
+ // Special cases
+ case SC_POISON:
+ case SC_DPOISON:
+ data.tick = sc->data[i]->val3 * 1000;
+ break;
+ case SC_FEAR:
+ case SC_LEECHESEND:
+ data.tick = sc->data[i]->val4 * 1000;
+ break;
+ case SC_BURNING:
+ data.tick = sc->data[i]->val4 * 2000;
+ break;
+ case SC_PYREXIA:
+ case SC_OBLIVIONCURSE:
+ data.tick = sc->data[i]->val4 * 3000;
+ break;
+ case SC_MAGICMUSHROOM:
+ data.tick = sc->data[i]->val4 * 4000;
+ break;
+ case SC_TOXIN:
+ case SC_BLEEDING:
+ data.tick = sc->data[i]->val4 * 10000;
+ break;
+ default:
+ continue;
+ break;
+ }
+ if( i ){
+ data.val1 = sc->data[i]->val1;
+ data.val2 = sc->data[i]->val2;
+ data.val3 = sc->data[i]->val3;
+ data.val4 = sc->data[i]->val4;
+ status_change_start(bl,(sc_type)i,10000,data.val1,data.val2,data.val3,data.val4,data.tick,1|2|8);
+ flag = 1;
+ }
+ }
+
+ return flag;
+}
+
+//Natural regen related stuff.
+static unsigned int natural_heal_prev_tick,natural_heal_diff_tick;
+static int status_natural_heal(struct block_list* bl, va_list args)
+{
+ struct regen_data *regen;
+ struct status_data *status;
+ struct status_change *sc;
+ struct unit_data *ud;
+ struct view_data *vd = NULL;
+ struct regen_data_sub *sregen;
+ struct map_session_data *sd;
+ int val,rate,bonus = 0,flag;
+
+ regen = status_get_regen_data(bl);
+ if (!regen) return 0;
+ status = status_get_status_data(bl);
+ sc = status_get_sc(bl);
+ if (sc && !sc->count)
+ sc = NULL;
+ sd = BL_CAST(BL_PC,bl);
+
+ flag = regen->flag;
+ if (flag&RGN_HP && (status->hp >= status->max_hp || regen->state.block&1))
+ flag&=~(RGN_HP|RGN_SHP);
+ if (flag&RGN_SP && (status->sp >= status->max_sp || regen->state.block&2))
+ flag&=~(RGN_SP|RGN_SSP);
+
+ if (flag && (
+ status_isdead(bl) ||
+ (sc && (sc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || sc->data[SC__INVISIBILITY]))
+ ))
+ flag=0;
+
+ if (sd) {
+ if (sd->hp_loss.value || sd->sp_loss.value)
+ pc_bleeding(sd, natural_heal_diff_tick);
+ if (sd->hp_regen.value || sd->sp_regen.value)
+ pc_regen(sd, natural_heal_diff_tick);
+ }
+
+ if(flag&(RGN_SHP|RGN_SSP) && regen->ssregen &&
+ (vd = status_get_viewdata(bl)) && vd->dead_sit == 2)
+ { //Apply sitting regen bonus.
+ sregen = regen->ssregen;
+ if(flag&(RGN_SHP))
+ { //Sitting HP regen
+ val = natural_heal_diff_tick * sregen->rate.hp;
+ if (regen->state.overweight)
+ val>>=1; //Half as fast when overweight.
+ sregen->tick.hp += val;
+ while(sregen->tick.hp >= (unsigned int)battle_config.natural_heal_skill_interval)
+ {
+ sregen->tick.hp -= battle_config.natural_heal_skill_interval;
+ if(status_heal(bl, sregen->hp, 0, 3) < sregen->hp)
+ { //Full
+ flag&=~(RGN_HP|RGN_SHP);
+ break;
+ }
+ }
+ }
+ if(flag&(RGN_SSP))
+ { //Sitting SP regen
+ val = natural_heal_diff_tick * sregen->rate.sp;
+ if (regen->state.overweight)
+ val>>=1; //Half as fast when overweight.
+ sregen->tick.sp += val;
+ while(sregen->tick.sp >= (unsigned int)battle_config.natural_heal_skill_interval)
+ {
+ sregen->tick.sp -= battle_config.natural_heal_skill_interval;
+ if(status_heal(bl, 0, sregen->sp, 3) < sregen->sp)
+ { //Full
+ flag&=~(RGN_SP|RGN_SSP);
+ break;
+ }
+ }
+ }
+ }
+
+ if (flag && regen->state.overweight)
+ flag=0;
+
+ ud = unit_bl2ud(bl);
+
+ if (flag&(RGN_HP|RGN_SHP|RGN_SSP) && ud && ud->walktimer != INVALID_TIMER)
+ {
+ flag&=~(RGN_SHP|RGN_SSP);
+ if(!regen->state.walk)
+ flag&=~RGN_HP;
+ }
+
+ if (!flag)
+ return 0;
+
+ if (flag&(RGN_HP|RGN_SP))
+ {
+ if(!vd) vd = status_get_viewdata(bl);
+ if(vd && vd->dead_sit == 2)
+ bonus++;
+ if(regen->state.gc)
+ bonus++;
+ }
+
+ //Natural Hp regen
+ if (flag&RGN_HP)
+ {
+ rate = natural_heal_diff_tick*(regen->rate.hp+bonus);
+ if (ud && ud->walktimer != INVALID_TIMER)
+ rate/=2;
+ // Homun HP regen fix (they should regen as if they were sitting (twice as fast)
+ if(bl->type==BL_HOM) rate *=2;
+
+ regen->tick.hp += rate;
+
+ if(regen->tick.hp >= (unsigned int)battle_config.natural_healhp_interval)
+ {
+ val = 0;
+ do {
+ val += regen->hp;
+ regen->tick.hp -= battle_config.natural_healhp_interval;
+ } while(regen->tick.hp >= (unsigned int)battle_config.natural_healhp_interval);
+ if (status_heal(bl, val, 0, 1) < val)
+ flag&=~RGN_SHP; //full.
+ }
+ }
+
+ //Natural SP regen
+ if(flag&RGN_SP)
+ {
+ rate = natural_heal_diff_tick*(regen->rate.sp+bonus);
+ // Homun SP regen fix (they should regen as if they were sitting (twice as fast)
+ if(bl->type==BL_HOM) rate *=2;
+
+ regen->tick.sp += rate;
+
+ if(regen->tick.sp >= (unsigned int)battle_config.natural_healsp_interval)
+ {
+ val = 0;
+ do {
+ val += regen->sp;
+ regen->tick.sp -= battle_config.natural_healsp_interval;
+ } while(regen->tick.sp >= (unsigned int)battle_config.natural_healsp_interval);
+ if (status_heal(bl, 0, val, 1) < val)
+ flag&=~RGN_SSP; //full.
+ }
+ }
+
+ if (!regen->sregen)
+ return flag;
+
+ //Skill regen
+ sregen = regen->sregen;
+
+ if(flag&RGN_SHP)
+ { //Skill HP regen
+ sregen->tick.hp += natural_heal_diff_tick * sregen->rate.hp;
+
+ while(sregen->tick.hp >= (unsigned int)battle_config.natural_heal_skill_interval)
+ {
+ sregen->tick.hp -= battle_config.natural_heal_skill_interval;
+ if(status_heal(bl, sregen->hp, 0, 3) < sregen->hp)
+ break; //Full
+ }
+ }
+ if(flag&RGN_SSP)
+ { //Skill SP regen
+ sregen->tick.sp += natural_heal_diff_tick * sregen->rate.sp;
+ while(sregen->tick.sp >= (unsigned int)battle_config.natural_heal_skill_interval)
+ {
+ val = sregen->sp;
+ if (sd && sd->state.doridori) {
+ val*=2;
+ sd->state.doridori = 0;
+ if ((rate = pc_checkskill(sd,TK_SPTIME)))
+ sc_start(bl,status_skill2sc(TK_SPTIME),
+ 100,rate,skill_get_time(TK_SPTIME, rate));
+ if (
+ (sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR &&
+ rnd()%10000 < battle_config.sg_angel_skill_ratio
+ ) { //Angel of the Sun/Moon/Star
+ clif_feel_hate_reset(sd);
+ pc_resethate(sd);
+ pc_resetfeel(sd);
+ }
+ }
+ sregen->tick.sp -= battle_config.natural_heal_skill_interval;
+ if(status_heal(bl, 0, val, 3) < val)
+ break; //Full
+ }
+ }
+ return flag;
+}
+
+//Natural heal main timer.
+static int status_natural_heal_timer(int tid, unsigned int tick, int id, intptr_t data)
+{
+ natural_heal_diff_tick = DIFF_TICK(tick,natural_heal_prev_tick);
+ map_foreachregen(status_natural_heal);
+ natural_heal_prev_tick = tick;
+ return 0;
+}
+
+/**
+ * Get the chance to upgrade a piece of equipment.
+ * @param wlv The weapon type of the item to refine (see see enum refine_type)
+ * @param refine The target refine level
+ * @return The chance to refine the item, in percent (0~100)
+ **/
+int status_get_refine_chance(enum refine_type wlv, int refine) {
+
+ if ( refine < 0 || refine >= MAX_REFINE)
+ return 0;
+
+ return refine_info[wlv].chance[refine];
+}
+
+
+/*------------------------------------------
+ * DB reading.
+ * job_db1.txt - weight, hp, sp, aspd
+ * job_db2.txt - job level stat bonuses
+ * size_fix.txt - size adjustment table for weapons
+ * refine_db.txt - refining data table
+ *------------------------------------------*/
+static bool status_readdb_job1(char* fields[], int columns, int current)
+{// Job-specific values (weight, HP, SP, ASPD)
+ int idx, class_;
+ unsigned int i;
+
+ class_ = atoi(fields[0]);
+
+ if(!pcdb_checkid(class_))
+ {
+ ShowWarning("status_readdb_job1: Invalid job class %d specified.\n", class_);
+ return false;
+ }
+ idx = pc_class2idx(class_);
+
+ max_weight_base[idx] = atoi(fields[1]);
+ hp_coefficient[idx] = atoi(fields[2]);
+ hp_coefficient2[idx] = atoi(fields[3]);
+ sp_coefficient[idx] = atoi(fields[4]);
+#ifdef RENEWAL_ASPD
+ for(i = 0; i <= MAX_WEAPON_TYPE; i++)
+#else
+ for(i = 0; i < MAX_WEAPON_TYPE; i++)
+#endif
+ {
+ aspd_base[idx][i] = atoi(fields[i+5]);
+ }
+ return true;
+}
+
+static bool status_readdb_job2(char* fields[], int columns, int current)
+{
+ int idx, class_, i;
+
+ class_ = atoi(fields[0]);
+
+ if(!pcdb_checkid(class_))
+ {
+ ShowWarning("status_readdb_job2: Invalid job class %d specified.\n", class_);
+ return false;
+ }
+ idx = pc_class2idx(class_);
+
+ for(i = 1; i < columns; i++)
+ {
+ job_bonus[idx][i-1] = atoi(fields[i]);
+ }
+ return true;
+}
+
+static bool status_readdb_sizefix(char* fields[], int columns, int current)
+{
+ unsigned int i;
+
+ for(i = 0; i < MAX_WEAPON_TYPE; i++)
+ {
+ atkmods[current][i] = atoi(fields[i]);
+ }
+ return true;
+}
+
+static bool status_readdb_refine(char* fields[], int columns, int current)
+{
+ int i, bonus_per_level, random_bonus, random_bonus_start_level;
+
+ current = atoi(fields[0]);
+
+ if (current < 0 || current >= REFINE_TYPE_MAX)
+ return false;
+
+ bonus_per_level = atoi(fields[1]);
+ random_bonus_start_level = atoi(fields[2]);
+ random_bonus = atoi(fields[3]);
+
+ for(i = 0; i < MAX_REFINE; i++)
+ {
+ char* delim;
+
+ if (!(delim = strchr(fields[4+i], ':')))
+ return false;
+
+ *delim = '\0';
+
+ refine_info[current].chance[i] = atoi(fields[4+i]);
+
+ if (i >= random_bonus_start_level - 1)
+ refine_info[current].randombonus_max[i] = random_bonus * (i - random_bonus_start_level + 2);
+
+ refine_info[current].bonus[i] = bonus_per_level + atoi(delim+1);
+ if (i > 0)
+ refine_info[current].bonus[i] += refine_info[current].bonus[i-1];
+ }
+ return true;
+}
+
+/*
+* Read status db
+* job1.txt
+* job2.txt
+* size_fixe.txt
+* refine_db.txt
+*/
+int status_readdb(void)
+{
+ int i, j;
+
+ // initialize databases to default
+ //
+
+ // reset job_db1.txt data
+ memset(max_weight_base, 0, sizeof(max_weight_base));
+ memset(hp_coefficient, 0, sizeof(hp_coefficient));
+ memset(hp_coefficient2, 0, sizeof(hp_coefficient2));
+ memset(sp_coefficient, 0, sizeof(sp_coefficient));
+ memset(aspd_base, 0, sizeof(aspd_base));
+ // reset job_db2.txt data
+ memset(job_bonus,0,sizeof(job_bonus)); // Job-specific stats bonus
+
+ // size_fix.txt
+ for(i=0;i<ARRAYLENGTH(atkmods);i++)
+ for(j=0;j<MAX_WEAPON_TYPE;j++)
+ atkmods[i][j]=100;
+
+ // refine_db.txt
+ for(i=0;i<ARRAYLENGTH(refine_info);i++)
+ {
+ for(j=0;j<MAX_REFINE; j++)
+ {
+ refine_info[i].chance[j] = 100;
+ refine_info[i].bonus[j] = 0;
+ refine_info[i].randombonus_max[j] = 0;
+ }
+ }
+
+ // read databases
+ //
+
+
+#ifdef RENEWAL_ASPD
+ sv_readdb(db_path, "re/job_db1.txt", ',', 6+MAX_WEAPON_TYPE, 6+MAX_WEAPON_TYPE, -1, &status_readdb_job1);
+#else
+ sv_readdb(db_path, "pre-re/job_db1.txt", ',', 5+MAX_WEAPON_TYPE, 5+MAX_WEAPON_TYPE, -1, &status_readdb_job1);
+#endif
+ sv_readdb(db_path, "job_db2.txt", ',', 1, 1+MAX_LEVEL, -1, &status_readdb_job2);
+ sv_readdb(db_path, "size_fix.txt", ',', MAX_WEAPON_TYPE, MAX_WEAPON_TYPE, ARRAYLENGTH(atkmods), &status_readdb_sizefix);
+ sv_readdb(db_path, DBPATH"refine_db.txt", ',', 4+MAX_REFINE, 4+MAX_REFINE, ARRAYLENGTH(refine_info), &status_readdb_refine);
+
+ return 0;
+}
+
+/*==========================================
+ * Status db init and destroy.
+ *------------------------------------------*/
+int do_init_status(void)
+{
+ add_timer_func_list(status_change_timer,"status_change_timer");
+ add_timer_func_list(kaahi_heal_timer,"kaahi_heal_timer");
+ add_timer_func_list(status_natural_heal_timer,"status_natural_heal_timer");
+ initChangeTables();
+ initDummyData();
+ status_readdb();
+ status_calc_sigma();
+ natural_heal_prev_tick = gettick();
+ sc_data_ers = ers_new(sizeof(struct status_change_entry),"status.c::sc_data_ers",ERS_OPT_NONE);
+ add_timer_interval(natural_heal_prev_tick + NATURAL_HEAL_INTERVAL, status_natural_heal_timer, 0, 0, NATURAL_HEAL_INTERVAL);
+ return 0;
+}
+void do_final_status(void)
+{
+ ers_destroy(sc_data_ers);
+}