Browse Source

Updated Summoner class (#1965)

* Fixes #1865.
* Updated and added new Summoner class skills.
* Information based on kRO patch notes.
- http://ro.gnjoy.com/news/devnote/View.asp?category=1&seq=1987966&curpage=1
* Updated previously implemented skills to better mimic official behavior.
Thanks to @RagnarokNova, Fyrus, and Jet for their hard work on gathering the information!
Thanks to @Grimfiend, @Atemo, @feltenc, @Lemongrass3110, @Felleonel, and @sanny1128 for the rapid test and reports!
Aleos 8 năm trước cách đây
mục cha
commit
5f7a143d3e

+ 18 - 0
db/pre-re/skill_db.txt

@@ -1379,6 +1379,24 @@
 5040,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_BUNCHOFSHRIMP,Bunch of Shrimp
 5041,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_FRESHSHRIMP,Fresh Shrimp
 
+// Unknown Unconfirmed Summoner Skills - Animations Show On These
+//5042,0,0,0,0,0,0,5,0,yes,0,0,0,none,0,0x0,		SU_CN_METEOR_SEC,
+//5043,0,0,0,0,0,0,5,0,yes,0,0,0,none,0,0x0,		SU_LUNATICCARROTBEAT_SEC,
+
+5044,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_SOULATTACK,Soul Attack
+5045,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_POWEROFFLOCK,Power of Flock
+5046,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_SVG_SPIRIT,Spirit of Savage
+5047,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_HISS,Hiss
+5048,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_NYANGGRASS,Nyang Grass
+5049,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_GROOMING,Grooming
+5050,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_PURRING,Purring
+5051,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_SHRIMPARTY,Tasty Shrimp Party
+5052,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_SPIRITOFLIFE,Spirit of Life
+5053,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_MEOWMEOW,Meow Meow
+5054,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_SPIRITOFLAND,Spirit of Land
+5055,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_CHATTERING,Chattering
+5056,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,	SU_SPIRITOFSEA,Spirit of Sea
+
 //****
 // Homunculus S
 8001,9,6,4,0,0x1,0,5,1,no,0,0,0,magic,0,0x0,	HLIF_HEAL,Healing Touch

+ 33 - 15
db/re/skill_cast_db.txt

@@ -1770,7 +1770,7 @@
 
 //===== Summoner ===========================
 //-- SU_BITE
-5019,1000,1000,0,0,0,0,0
+5019,1000,1000,0,0,0,0,-1
 //-- SU_HIDE
 5020,0,1000,0,-1,0,15000,0
 //-- SU_SCRATCH
@@ -1778,35 +1778,53 @@
 //-- SU_STOOP
 5022,0,1000,0,6000,0,15000,0
 //-- SU_LOPE
-5023,500,1000,0,0,0,2000:4000:6000,0
+5023,500,1000,0,0,0,2000:4000:6000,-1
 //-- SU_SV_STEMSPEAR
-5026,2500,1000,0,0,120000,0,0
+5026,2000,1000,0,0,120000,0,-1
 //-- SU_CN_POWDERING
-5027,1500,1000,0,3000:4000:5000:6000:7000,0,0,0
+5027,2000,1000,0,3000:4000:5000:6000:7000,0,10000,-1
 //-- SU_CN_METEOR
-5028,7500,1000,0,1500:2000:2500:3000:3500,20000,5000,-1
+5028,3000,1000,0,1500:2000:2500:3000:3500,20000,5000,3000
 //-- SU_SV_ROOTTWIST
-5029,0,1000,0,7000:9000:11000:13000:15000,0,3000,0
-//-- SU_SV_ROOTTWIST_ATK
-5030,0,1000,0,0,0,0
+5029,0,1000,0,7000:9000:11000:13000:15000,0,3000:2500:2000:1500:1000,0
 //-- SU_SCAROFTAROU
-5032,500,1000,0,9000,1000,0,0
+5032,500,1000,0,9000,1000,10000,-1
 //-- SU_PICKYPECK
-5033,2500,1000,0,0,0,0,0
+5033,1000,1000,0,0,0,0,-1
 //-- SU_PICKYPECK_DOUBLE_ATK
 5034,0,1000,0,0,0,0,0
 //-- SU_ARCLOUSEDASH
-5035,2500,1000,0,60000:70000:80000:90000:100000,0,10000,0
+5035,1000,1000,0,60000:70000:80000:90000:100000,0,10000,0
 //-- SU_LUNATICCARROTBEAT
-5036,1500,1000,0,0,5000,6000,0
+5036,1000,1000,0,0,5000,6000,-1
 //-- SU_TUNABELLY
-5038,2000,1000,0,0,0,8000:10000:12000:14000:16000,0
+5038,1000,1000,0,5000,0,2000:5000:8000:11000:14000,-1
 //-- SU_TUNAPARTY
 5039,0,1000,0,30000,0,20000,0
 //-- SU_BUNCHOFSHRIMP
-5040,0,1000,0,60000:90000:120000:150000:180000,0,10000,0
+5040,0,1000,0,60000:90000:120000:150000:180000,120000,10000,0
 //-- SU_FRESHSHRIMP
-5041,0,1000,0,120000,0,7000,0
+5041,0,1000,0,120000,0,6000:5000:4000:3000:2000,0
+//-- SU_POWEROFFLOCK
+5045,5000:4000:3000:2000:1000,1000,0,15000,10000,100000,-1
+//-- SU_SVG_SPIRIT
+5046,3000:3000:2500:2000:0,1000,0,0,0,22000,-1
+//-- SU_HISS
+5047,1000,1000,0,3000,3000:3000:4000:4000:5000,60000,-1
+//-- SU_NYANGGRASS
+5048,3000:2500:2000:1500:1000,1000,0,6000:7000:8000:9000:10000,0,60000,-1
+//-- SU_GROOMING
+5049,1000,1000,0,3000:4000:5000:6000:7000,0,60000:50000:40000:30000:20000,0
+//-- SU_PURRING
+5050,1000,1000,0,3000:4000:5000:6000:7000,0,65000:60000:55000:50000:45000,-1
+//-- SU_SHRIMPARTY
+5051,3500:3000:2500:2000:1500,1000,0,12000:14000:16000:18000:20000,0,65000:60000:55000:50000:45000,-1
+//-- SU_MEOWMEOW
+5053,0,1000,0,8000:8000:10000:10000:12000,0,180000:160000:140000:120000:100000,0
+//-- SU_SPIRITOFLAND
+5054,0,0,0,3000,0,0,0
+//-- SU_CHATTERING
+5055,0,1000,0,5000,10000,140000:120000:100000:80000:60000,0
 //==========================================
 
 //===== Homunculus Skills ==================

+ 16 - 2
db/re/skill_db.txt

@@ -1365,11 +1365,11 @@
 5023,6:10:14,6,2,0,0x1,0,3,1,yes,0,0,0,none,0,0x0,	SU_LOPE,Lope
 5024,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,		SU_SPRITEMABLE,Spirit Marble
 5025,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,		SU_POWEROFLAND,Power of Land
-5026,9,6,1,2:3:1:4:8,0x0,0,5,1,yes,0,0,0,magic,0,0x0,		SU_SV_STEMSPEAR,Silvervine Stem Spear
+5026,9,6,1,2:3:1:4:8,0,0,5,1,yes,0,0,0,magic,0,0x0,		SU_SV_STEMSPEAR,Silvervine Stem Spear
 5027,9,6,1,0,0x3,0,5,1,yes,0,0,1,none,0,0x0,		SU_CN_POWDERING,Catnip Powdering
 5028,9,8,2,0,0,1:1:2:2:3,5,-5,yes,0,0,0,magic,0,0x0,		SU_CN_METEOR,Catnip Meteor
 5029,9,6,1,0,0x1,0,5,1,yes,0,0,1,none,0,0x0,		SU_SV_ROOTTWIST,Silvervine Root Twist
-5030,0,6,1,5,0x30,0,5,1,no,0,0,1,magic,0,0x0,	SU_SV_ROOTTWIST_ATK,Silver Vine Root Twist Attack
+5030,0,6,1,5,0x70,0,5,1,no,0,0,1,misc,0,0x0,	SU_SV_ROOTTWIST_ATK,Silver Vine Root Twist Attack
 5031,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,		SU_POWEROFLIFE,Power of Life
 5032,9,6,1,-1,0,0,5,1,yes,0,0,0,weapon,0,0x0,	SU_SCAROFTAROU,Scar of Tarou
 5033,9,8,1,-1,0,0,5,-5,yes,0,0,0,weapon,0,0x0,	SU_PICKYPECK,Picky Peck
@@ -1386,6 +1386,20 @@
 //5042,0,0,0,0,0,0,5,0,yes,0,0,0,none,0,0x0,		SU_CN_METEOR_SEC,
 //5043,0,0,0,0,0,0,5,0,yes,0,0,0,none,0,0x0,		SU_LUNATICCARROTBEAT_SEC,
 
+5044,14,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,		SU_SOULATTACK,Soul Attack
+5045,0,6,4,0,0x3,3:4:5:6:-1,5,0,yes,0,0,0,magic,0,0x0,	SU_POWEROFFLOCK,Power of Flock
+5046,9,8,1,-1,0,1,5,1,yes,0,0,14,weapon,0,0x0,	SU_SVG_SPIRIT,Spirit of Savage
+5047,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0,0x0,		SU_HISS,Hiss
+5048,9,6,2,0,0x1,0,5,1,yes,0,0,1,none,0,0x0,		SU_NYANGGRASS,Nyang Grass
+5049,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0,0x0,		SU_GROOMING,Grooming
+5050,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0,0x0,		SU_PURRING,Purring
+5051,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0,0x0,		SU_SHRIMPARTY,Tasty Shrimp Party
+5052,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,		SU_SPIRITOFLIFE,Spirit of Life
+5053,0,6,4,0,0x3,-1,5,1,yes,0,0,0,magic,0,0x0,		SU_MEOWMEOW,Meow Meow
+5054,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,		SU_SPIRITOFLAND,Spirit of Land
+5055,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0,0x0,		SU_CHATTERING,Chattering
+5056,0,0,0,0,0,0,1,0,no,0,0,0,none,0,0x0,		SU_SPIRITOFSEA,Spirit of Sea
+
 //****
 // Homunculus S
 8001,9,6,4,0,0x1,0,5,1,no,0,0,0,magic,0,0x0,	HLIF_HEAL,Healing Touch

+ 0 - 2
db/re/skill_nocast_db.txt

@@ -93,7 +93,6 @@
 691,8	//CASH_ASSUMPITO
 2284,8	//SC_FATALMENACE
 2300,8	//SC_DIMENSIONDOOR
-5023,8	//SU_LOPE
 
 //----------------------------------------------------------------------------
 // Mixed
@@ -177,7 +176,6 @@
 //----------------------------------------------------------------------------
 426,256 //TK_HIGHJUMP
 290,256	//SA_ABRACADABRA
-5023,256	//SU_LOPE
 
 //----------------------------------------------------------------------------
 // Zone 5 - Sealed Shrine

+ 11 - 1
db/re/skill_require_db.txt

@@ -973,7 +973,7 @@
 
 // Summoner
 5019,0,0,10,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0									//SU_BITE
-5020,0,0,30,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0									//SU_HIDE
+5020,0,0,10,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0									//SU_HIDE
 5021,0,0,20:25:30,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0							//SU_SCRATCH
 5022,0,0,10,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0									//SU_STOOP
 5023,0,0,30,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0									//SU_LOPE
@@ -996,6 +996,16 @@
 //5042,0,0,1,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0								//
 //5043,0,0,1,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0								//
 
+5045,0,0,50,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0									//SU_POWEROFFLOCK
+5046,0,0,60,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0									//SU_SVG_SPIRIT
+5047,0,0,50:46:42:38:34,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0						//SU_HISS
+5048,0,0,50:48:46:44:42,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0						//SU_NYANGGRASS
+5049,0,0,15,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0									//SU_GROOMING
+5050,0,0,70:65:60:55:50,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0						//SU_PURRING
+5051,0,0,100:90:80:70:60,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0					//SU_SHRIMPARTY
+5053,0,0,100:90:80:70:60,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0					//SU_MEOWMEOW
+5055,0,0,50:45:40:35:30,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0						//SU_CHATTERING
+
 8001,0,0,13:16:19:22:25,0,0,0,99,0,0,none,0,0,545,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0				//HLIF_HEAL
 8002,0,0,20:25:30:35:40,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0					//HLIF_AVOID
 8004,0,0,100,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0							//HLIF_CHANGE

+ 13 - 0
db/re/skill_tree.txt

@@ -5691,6 +5691,19 @@
 4218,5039,5,5038,3,0,0,0,0,0,0,0,0 //SU_TUNAPARTY##
 4218,5040,5,5041,3,0,0,0,0,0,0,0,0 //SU_BUNCHOFSHRIMP##
 4218,5041,5,5024,1,0,0,0,0,0,0,0,0 //SU_FRESHSHRIMP##
+4218,5044,1,5024,1,0,0,0,0,0,0,0,0 //SU_SOULATTACK##
+4218,5045,5,100,0,5047,5,0,0,0,0,0,0,0,0 //SU_POWEROFFLOCK##
+4218,5046,5,100,0,5045,5,0,0,0,0,0,0,0,0 //SU_SVG_SPIRIT##
+4218,5047,5,100,0,5031,1,0,0,0,0,0,0,0,0 //SU_HISS##
+4218,5048,5,100,0,5053,5,0,0,0,0,0,0,0,0 //SU_NYANGGRASS##
+4218,5049,5,100,0,5037,1,0,0,0,0,0,0,0,0 //SU_GROOMING##
+4218,5050,5,100,0,5049,5,0,0,0,0,0,0,0,0 //SU_PURRING##
+4218,5051,5,100,0,5050,5,0,0,0,0,0,0,0,0 //SU_SHRIMPARTY##
+4218,5052,1,100,0,5046,5,0,0,0,0,0,0,0,0 //SU_SPIRITOFLIFE##
+4218,5053,5,100,0,5025,1,0,0,0,0,0,0,0,0 //SU_MEOWMEOW##
+4218,5054,1,100,0,5048,5,0,0,0,0,0,0,0,0 //SU_SPIRITOFLAND##
+4218,5055,5,100,0,5025,1,0,0,0,0,0,0,0,0 //SU_CHATTERING##
+4218,5056,1,100,0,5051,5,0,0,0,0,0,0,0,0 //SU_SPIRITOFSEA##
 4218,681,1,0,0,0,0,0,0,0,0,0,0 //ALL_INCCARRY#Enlarge Weight Limit R#
 
 //Baby Summoner

+ 1 - 1
db/re/skill_unit_db.txt

@@ -179,7 +179,7 @@
 
 5027,0x106,   ,  1:1:2:2:3, 0,  -1,enemy, 0x2010 // SU_CN_POWDERING
 5028,0x86,    ,  0, 3, 500,enemy, 0x10 // SU_CN_METEOR
-5029,0x107,   ,  0, 0,1000,enemy, 0x10 // SU_SV_ROOTTWIST
+5048,0x107,   ,  2:2:3:3:4, 0, -1, enemy, 0x2010 // SU_NYANGGRASS
 
 8020,0xf5,    ,  3, 0,2300:2100:1900:1700:1500,enemy,   0x018	//MH_POISON_MIST
 8033,0x7e,    ,  0, 0,  -1,all,   0x003	//MH_STEINWAND

+ 88 - 0
doc/status_change.txt

@@ -2530,6 +2530,94 @@ SC_CHASEWALK2	(SI_CHASEWALK2)
 	desc: 2nd effect of Chasewalk
 	val1: +STR
 
+SC_SUHIDE	(SI_SUHIDE)
+	desc: Hide caster. Can be seen by insect, demon, and boss. Cannot move or pickup items.
+	val1: Skill Lv
+
+SC_SU_STOOP	(SI_SU_STOOP)
+	desc: Places a temporary buff on the user that decreases all damage taken by 90%.
+	val1: Skill Lv
+
+SC_SPRITEMABLE	(SI_SPRITEMABLE)
+	desc: Increase 1000 HP and 100 SP.
+	val1:
+
+SC_CATNIPPOWDER	(SI_CATNIPPOWDER)
+	desc: Reduces ATK and MATK by 50% to targets in a 3x3~7x7 area. HP and SP recovery rate increase.
+	val1: Skill Lv
+	val2: WATK% / MATK%
+	val3: Movement speed reduction
+
+SC_SV_ROOTTWIST	(SI_SV_ROOTTWIST)
+	desc: Prevents the target from moving and receives 100 Poison damage every second. Cannot be used on Boss monsters.
+	val1: Skill Lv
+
+SC_BITESCAR	(SI_BITESCAR)
+	desc: Drains a portion of the target's Max HP each second.
+	val1: Skill Lv
+	val2: Max HP% damage
+	val4: Tick
+
+SC_ARCLOUSEDASH	(SI_ARCLOUSEDASH)
+	desc: Increases Agi and movement speed.
+	val1: AGI
+	val2: Movement speed increase
+	val4: Ranged ATK increase for Doram
+
+SC_TUNAPARTY	(SI_TUNAPARTY)
+	desc: Protects from damage, the amount is based on Max HP.
+	val1: Max HP% to absorb
+	val2: Double the shield life with Spirit of Sea
+
+SC_SHRIMP	(SI_SHRIMP)
+	desc: Gives all party members on screen +10% ATK and MATK.
+	val1: BATK% / MATK%
+
+SC_FRESHSHRIMP	(SI_FRESHSHRIMP)
+	desc: Recovers a small amount of HP. Each level reduces the time between each HP recovery tick.
+	val1: Skill Lv
+	val2: Heal amount
+	val4: Tick
+
+SC_HISS	(SI_HISS)
+	desc: Increases movement speed and perfect dodge of the user and his party.
+	val1: Skill Lv
+	val2: Perfect Dodge
+
+SC_NYANGGRASS	(SI_NYANGGRASS)
+	desc: Reduces monster's DEF and MDEF by 50%. Reduces other player's equipment DEF and MDEF to 0.
+	val1: Skill Lv
+
+SC_GROOMING	(SI_GROOMING)
+	desc: FLEE + 100. Cures Poison, Frozen, Stun, Sleep, Bleeding, Silence, Crystallization, Deep Sleep, Fear, and Mandragora Howling.
+	val1: Skill Lv
+	val2: FLEE
+
+SC_SHRIMPBLESSING	(SI_PROTECTIONOFSHRIMP)
+	desc: Increases caster's SP recovery by 150%.
+	val1: Skill Lv
+
+SC_CHATTERING	(SI_CHATTERING)
+	desc: Increases the player's ATK and MATK by 100. Increases the player's movespeed.
+	val1: Skill Lv
+	val2: ATK / MATK
+
+SC_DORAM_WALKSPEED	()
+	desc: Adjusts player's walk speed.
+	val1: Movement speed adjustment
+
+SC_DORAM_MATK	()
+	desc: Statically increases MATK for Spirit of Land.
+	val1: MATK
+
+SC_DORAM_FLEE2	()
+	desc: Statically increase FLEE2 for Spirit of Land.
+	val1: FLEE2
+
+SC_DORAM_SVSP	()
+	desc: Casts Silvervine Stem Spear when receiving Magic or Ranged damage after using Catnip Meteor for Spirit of Land.
+	val1: Value to know it's active
+
 SC_GVG_GIANT	(SI_GVG_GIANT)
 	desc: Instantly consumes HP/SP, increases Physical/Magic damage on player enemies by n%.
 	val1: Amount of HP that are instantly consumed

+ 20 - 9
src/map/battle.c

@@ -1408,13 +1408,12 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam
 		}
 
 		if ((sce = sc->data[SC_TUNAPARTY]) && damage > 0) {
-			clif_specialeffect(bl, 336, AREA);
 			sce->val2 -= (int)cap_value(damage, INT_MIN, INT_MAX);
 			if (sce->val2 >= 0)
 				damage = 0;
 			else
 			  	damage = -sce->val2;
-			if (/*(--sce->val3) <= 0 ||*/ (sce->val2 <= 0))
+			if (sce->val2 <= 0)
 				status_change_end(bl, SC_TUNAPARTY, INVALID_TIMER);
 		}
 
@@ -4276,15 +4275,26 @@ static int battle_calc_attack_skill_ratio(struct Damage wd, struct block_list *s
 			break;
 		case SU_SCAROFTAROU:
 			skillratio += -100 + 100 * skill_lv;
+			if (sd && pc_checkskill(sd, SU_SPIRITOFLIFE))
+				skillratio += skillratio * status_get_hp(src) / status_get_max_hp(src);
 			break;
 		case SU_PICKYPECK:
 		case SU_PICKYPECK_DOUBLE_ATK:
 			skillratio += 100 + 100 * skill_lv;
-			if (status_get_max_hp(target) / 100 <= 50)
+			if (status_get_hp(target) < status_get_max_hp(target) >> 1)
 				skillratio *= 2;
+			if (sd && pc_checkskill(sd, SU_SPIRITOFLIFE))
+				skillratio += skillratio * status_get_hp(src) / status_get_max_hp(src);
 			break;
 		case SU_LUNATICCARROTBEAT:
 			skillratio += 100 + 100 * skill_lv;
+			if (sd && pc_checkskill(sd, SU_SPIRITOFLIFE))
+				skillratio += skillratio * status_get_hp(src) / status_get_max_hp(src);
+			break;
+		case SU_SVG_SPIRIT:
+			skillratio += 150 + 150 * skill_lv;
+			if (sd && pc_checkskill(sd, SU_SPIRITOFLIFE))
+				skillratio += skillratio * status_get_hp(src) / status_get_max_hp(src);
 			break;
 	}
 	return skillratio;
@@ -4524,9 +4534,10 @@ struct Damage battle_attack_sc_bonus(struct Damage wd, struct block_list *src, s
 
 	if ((wd.flag&(BF_LONG|BF_MAGIC)) == BF_LONG) {
 		if (sd && pc_checkskill(sd, SU_POWEROFLIFE) > 0) {
-			if (pc_checkskill(sd, SU_SCAROFTAROU) == 5 && pc_checkskill(sd, SU_PICKYPECK) == 5 && pc_checkskill(sd, SU_ARCLOUSEDASH) == 5 && pc_checkskill(sd, SU_LUNATICCARROTBEAT) == 5) {
-				ATK_ADDRATE(wd.damage, wd.damage2, 20);
-				RE_ALLATK_ADDRATE(wd, 20);
+			if ((pc_checkskill(sd, SU_SCAROFTAROU) + pc_checkskill(sd, SU_PICKYPECK) + pc_checkskill(sd, SU_ARCLOUSEDASH) + pc_checkskill(sd, SU_LUNATICCARROTBEAT) +
+				pc_checkskill(sd, SU_HISS) + pc_checkskill(sd, SU_POWEROFFLOCK) + pc_checkskill(sd, SU_SVG_SPIRIT)) > 19) {
+					ATK_ADDRATE(wd.damage, wd.damage2, 20);
+					RE_ALLATK_ADDRATE(wd, 20);
 			}
 		}
 	}
@@ -5730,9 +5741,6 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list
 					ad.damage >>= 1;
 #endif
 				break;
-			case SU_SV_ROOTTWIST_ATK:
-				ad.damage = 100;
-				break;
 			default: {
 				if (sstatus->matk_max > sstatus->matk_min) {
 					MATK_ADD(sstatus->matk_min+rnd()%(sstatus->matk_max-sstatus->matk_min));
@@ -6586,6 +6594,9 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list *
 			else
 				md.damage = 0;
 			break;
+		case SU_SV_ROOTTWIST_ATK:
+			md.damage = 100;
+			break;
 	}
 
 	if (nk&NK_SPLASHSPLIT) { // Divide ATK among targets

+ 1 - 0
src/map/map.cpp

@@ -2000,6 +2000,7 @@ int map_quit(struct map_session_data *sd) {
 		status_change_end(&sd->bl, SC_CBC, INVALID_TIMER);
 		status_change_end(&sd->bl, SC_EQC, INVALID_TIMER);
 		status_change_end(&sd->bl, SC_SPRITEMABLE, INVALID_TIMER);
+		status_change_end(&sd->bl, SC_SV_ROOTTWIST, INVALID_TIMER);
 		// Remove visuals effect from headgear
 		status_change_end(&sd->bl, SC_MOONSTAR, INVALID_TIMER); 
 		status_change_end(&sd->bl, SC_SUPER_STAR, INVALID_TIMER); 

+ 5 - 3
src/map/pc.c

@@ -1038,7 +1038,7 @@ uint8 pc_isequip(struct map_session_data *sd,int n)
 			return ITEM_EQUIP_ACK_FAIL;
 		if(item->equip & EQP_ACC && sd->sc.data[SC__STRIPACCESSORY])
 			return ITEM_EQUIP_ACK_FAIL;
-		if(item->equip && sd->sc.data[SC_KYOUGAKU])
+		if(item->equip && (sd->sc.data[SC_KYOUGAKU] || sd->sc.data[SC_SUHIDE]))
 			return ITEM_EQUIP_ACK_FAIL;
 
 		if (sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_SUPERNOVICE) {
@@ -5666,6 +5666,8 @@ int pc_get_skillcooldown(struct map_session_data *sd, uint16 skill_id, uint16 sk
 	if (!idx) return 0;
 	if (skill_db[idx]->cooldown[skill_lv - 1])
 		cooldown = skill_db[idx]->cooldown[skill_lv - 1];
+	if (skill_id == SU_TUNABELLY && pc_checkskill(sd, SU_SPIRITOFSEA))
+		cooldown -= skill_get_time(SU_TUNABELLY, skill_lv);
 
 	ARR_FIND(0, cooldownlen, i, sd->skillcooldown[i].id == skill_id);
 	if (i < cooldownlen) {
@@ -8257,8 +8259,8 @@ bool pc_setparam(struct map_session_data *sd,int type,int val)
  *------------------------------------------*/
 void pc_heal(struct map_session_data *sd,unsigned int hp,unsigned int sp, int type)
 {
-	if (type) {
-		if (hp)
+	if (type&2) {
+		if (hp || type&4)
 			clif_heal(sd->fd,SP_HP,hp);
 		if (sp)
 			clif_heal(sd->fd,SP_SP,sp);

+ 9 - 0
src/map/script_constants.h

@@ -1469,6 +1469,15 @@
 	export_constant(SC_DAILYSENDMAILCNT);
 	export_constant(SC_DORAM_BUF_01);
 	export_constant(SC_DORAM_BUF_02);
+	export_constant(SC_HISS);
+	export_constant(SC_NYANGGRASS);
+	export_constant(SC_GROOMING);
+	export_constant(SC_SHRIMPBLESSING);
+	export_constant(SC_CHATTERING);
+	export_constant(SC_DORAM_WALKSPEED);
+	export_constant(SC_DORAM_MATK);
+	export_constant(SC_DORAM_FLEE2);
+	export_constant(SC_DORAM_SVSP);
 #ifdef RENEWAL
 	export_constant(SC_EXTREMITYFIST2);
 #endif

+ 147 - 60
src/map/skill.c

@@ -421,6 +421,8 @@ unsigned short skill_dummy2skill_id(unsigned short skill_id) {
 			return RL_R_TRIP;
 		case NPC_MAXPAIN_ATK:
 			return NPC_MAXPAIN;
+		case SU_SV_ROOTTWIST_ATK:
+			return SU_SV_ROOTTWIST;
 	}
 	return skill_id;
 }
@@ -483,7 +485,8 @@ int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 sk
 				hp *= 2;
 			if (sd && ((skill = pc_checkskill(sd, SU_POWEROFSEA)) > 0)) {
 				hp += hp * 10 / 100;
-				if (pc_checkskill(sd, SU_TUNABELLY) == 5 && pc_checkskill(sd, SU_TUNAPARTY) == 5 && pc_checkskill(sd, SU_BUNCHOFSHRIMP) == 5 && pc_checkskill(sd, SU_FRESHSHRIMP) == 5)
+				if ((pc_checkskill(sd, SU_TUNABELLY) + pc_checkskill(sd, SU_TUNAPARTY) + pc_checkskill(sd, SU_BUNCHOFSHRIMP) + pc_checkskill(sd, SU_FRESHSHRIMP) +
+					pc_checkskill(sd, SU_GROOMING) + pc_checkskill(sd, SU_PURRING) + pc_checkskill(sd, SU_SHRIMPARTY)) > 19)
 					hp += hp * 20 / 100;
 			}
 			break;
@@ -1930,7 +1933,7 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1
 		status_change_end(bl, SC_C_MARKER, INVALID_TIMER);
 		break;
 	case SU_SCRATCH:
-		sc_start2(src, bl, SC_BLEEDING, (skill_lv * 3), skill_lv, src->id, skill_get_time2(skill_id, skill_lv)); // TODO: What's the chance/time?
+		sc_start2(src, bl, SC_BLEEDING, skill_lv * 10 + 70, skill_lv, src->id, skill_get_time(skill_id, skill_lv));
 		break;
 	case SU_SV_STEMSPEAR:
 		sc_start2(src, bl, SC_BLEEDING, 10, skill_lv, src->id, skill_get_time2(skill_id, skill_lv));
@@ -1939,9 +1942,9 @@ int skill_additional_effect(struct block_list* src, struct block_list *bl, uint1
 		if (skill_area_temp[3] == 1)
 			sc_start(src, bl, SC_CURSE, 20, skill_lv, skill_get_time2(skill_id, skill_lv));
 		break;
-	//case SU_SCAROFTAROU:
-	//	sc_start(src, bl, SC_STUN, 10, skill_lv, skill_get_time2(skill_id, skill_lv)); // TODO: What's the chance/time?
-	//	break;
+	case SU_SCAROFTAROU:
+		sc_start(src, bl, SC_STUN, 10, skill_lv, skill_get_time2(skill_id, skill_lv)); //! TODO: What's the chance/time?
+		break;
 	case SU_LUNATICCARROTBEAT:
 		if (skill_area_temp[3] == 1)
 			sc_start(src, bl, SC_STUN, 20, skill_lv, skill_get_time2(skill_id, skill_lv));
@@ -2386,6 +2389,13 @@ int skill_counter_additional_effect (struct block_list* src, struct block_list *
 		}
 	}
 
+	if (dstsd && !status_isdead(bl) && !(skill_id && skill_get_nk(skill_id)&NK_NO_DAMAGE)) {
+		struct status_change *sc = status_get_sc(bl);
+
+		if (sc && sc->data[SC_DORAM_SVSP] && attack_type&(BF_MAGIC|BF_LONG))
+			skill_castend_damage_id(bl, src, SU_SV_STEMSPEAR, (pc_checkskill(dstsd, SU_SV_STEMSPEAR) ? pc_checkskill(dstsd, SU_SV_STEMSPEAR) : 1), tick, 0);
+	}
+
 	// 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))
@@ -2676,8 +2686,12 @@ short skill_blown(struct block_list* src, struct block_list* target, char count,
 		dy = -diry[dir];
 	}
 
-	if (tsc && tsc->data[SC_SU_STOOP]) // Any knockback will cancel it.
-		status_change_end(target, SC_SU_STOOP, INVALID_TIMER);
+	if (tsc) {
+		if (tsc->data[SC_SU_STOOP]) // Any knockback will cancel it.
+			status_change_end(target, SC_SU_STOOP, INVALID_TIMER);
+		if (tsc->data[SC_SV_ROOTTWIST]) // Shouldn't move.
+			return 0;
+	}
 
 	return unit_blown(target, dx, dy, count, flag);	// Send over the proper flag
 }
@@ -3386,6 +3400,7 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list *
 		case KO_BAKURETSU:
 		case GN_CRAZYWEED_ATK:
 		case NC_MAGMA_ERUPTION:
+		case SU_SV_ROOTTWIST_ATK:
 			dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,DMG_SPLASH);
 			break;
 		case GN_FIRE_EXPANSION_ACID:
@@ -4179,6 +4194,16 @@ static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data)
 					if (target->type == BL_PC)
 						sc_start(src, target, SC_SITDOWN_FORCE, 100, skl->skill_lv, skill_get_time(skl->skill_id, skl->skill_lv));
 					break;
+				case SU_SV_ROOTTWIST_ATK: {
+						struct status_change *tsc = status_get_sc(target);
+
+						if (tsc && tsc->data[SC_SV_ROOTTWIST]) {
+							if (check_distance_bl(src, target, 32)) // Only damage if caster is within 32x32 area
+								skill_attack(skl->type, src, target, target, skl->skill_id, skl->skill_lv, tick, skl->flag);
+							skill_addtimerskill(src, tick + 1000, target->id, 0, 0, skl->skill_id, skl->skill_lv, skl->type, skl->flag);
+						}
+					}
+					break;
 				default:
 					skill_attack(skl->type,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag);
 					break;
@@ -4857,9 +4882,15 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint
 		clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
 	case SU_BITE:
 		skill_attack(BF_WEAPON, src, src, bl, skill_id, skill_lv, tick, flag);
-		if (status_get_lv(src) >= 30 && (rnd() % 100 < (int)(status_get_lv(src) / 30) + 10)) // TODO: Need activation chance.
+		if (status_get_lv(src) > 29 && (rnd() % 100 < (int)((status_get_lv(src) / 30) * 10 + 10)))
 			skill_addtimerskill(src, tick + skill_get_delay(skill_id, skill_lv), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag);
 		break;
+	case SU_SVG_SPIRIT:
+		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;
 
 	//Splash attack skills.
 	case AS_GRIMTOOTH:
@@ -4941,7 +4972,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint
 				clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1);
 				status_heal(src,heal,0,0);
 			}
-			if (skill_id == SU_SCRATCH && status_get_lv(src) >= 30 && (rnd() % 100 < (int)(status_get_lv(src) / 30) + 10)) // TODO: Need activation chance.
+			if (skill_id == SU_SCRATCH && status_get_lv(src) > 29 && (rnd() % 100 < (int)((status_get_lv(src) / 30) * 10 + 10)))
 				skill_addtimerskill(src, tick + skill_get_delay(skill_id, skill_lv), bl->id, 0, 0, skill_id, skill_lv, BF_WEAPON, flag);
 		} else {
 			int starget = BL_CHAR|BL_SKILL;
@@ -5971,11 +6002,16 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, uint
 		break;
 
 	case SU_SCAROFTAROU:
-		sc_start(src, bl, status_skill2sc(skill_id), 10, skill_lv, skill_get_time(skill_id, skill_lv)); // TODO: What's the activation chance for the Bite effect?
 	case SU_SV_STEMSPEAR:
+		if (skill_id == SU_SCAROFTAROU)
+			sc_start(src, bl, status_skill2sc(skill_id), 10, skill_lv, skill_get_time(skill_id, skill_lv)); //! TODO: What's the activation chance for the Bite effect?
+		else {
+			if (sd && pc_checkskill(sd, SU_SPIRITOFLAND))
+				sc_start(src, src, SC_DORAM_WALKSPEED, 100, 50, skill_get_time(SU_SPIRITOFLAND, 1));
+		}
 		skill_attack(skill_get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, flag);
-		if (status_get_lv(src) >= 30 && (rnd() % 100 < (int)(status_get_lv(src) / 30) + 10)) // TODO: Need activation chance.
-			skill_addtimerskill(src, tick + skill_get_delay(skill_id, skill_lv), bl->id, 0, 0, skill_id, skill_lv, (skill_id == SU_SV_STEMSPEAR) ? BF_MAGIC : BF_WEAPON, flag);
+		if (status_get_lv(src) > 29 && (rnd() % 100 < (int)((status_get_lv(src) / 30) * 10 + 10)))
+			skill_addtimerskill(src, tick + skill_get_delay(skill_id, skill_lv), bl->id, 0, 0, skill_id, skill_lv, skill_get_type(skill_id), flag);
 		break;
 
 	case 0:/* no skill - basic/normal attack */
@@ -6559,6 +6595,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 	case PR_KYRIE:
 	case MER_KYRIE:
 	case SU_TUNAPARTY:
+	case SU_GROOMING:
+	case SU_CHATTERING:
 		clif_skill_nodamage(bl,bl,skill_id,skill_lv,
 			sc_start(src,bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv)));
 		break;
@@ -6682,11 +6720,6 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 			sc_start(src, bl, type, skill_lv*20, skill_lv, skill_get_time2(skill_id, skill_lv)));
 		break;
 
-	case SU_STOOP:
-		clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
-		sc_start(src, bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
- 		break;
-
 	case KN_AUTOCOUNTER:
 		sc_start(src,bl,type,100,skill_lv,skill_get_time(skill_id,skill_lv));
 		skill_addtimerskill(src,tick + 100,bl->id,0,0,skill_id,skill_lv,BF_WEAPON,flag);
@@ -10806,26 +10839,70 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 		sc_start(src, bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
  		break;
 
+	case SU_STOOP:
+		clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+		sc_start(src, bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+ 		break;
+
+	case SU_SV_ROOTTWIST:
+		clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+		if (sd && status_get_class_(bl) == CLASS_BOSS) {
+			clif_skill_fail(sd, skill_id, USESKILL_FAIL_TOTARGET, 0);
+			break;
+		}
+		if (tsc->count && tsc->data[type]) // Refresh the status only if it's already active.
+			sc_start(src, bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+		else {
+			sc_start(src, bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+			if (sd && pc_checkskill(sd, SU_SPIRITOFLAND))
+				sc_start(src, src, SC_DORAM_MATK, 100, sd->status.base_level, skill_get_time(SU_SPIRITOFLAND, 1));
+			skill_addtimerskill(src, tick + 1000, bl->id, 0, 0, SU_SV_ROOTTWIST_ATK, skill_lv, skill_get_type(SU_SV_ROOTTWIST_ATK), flag);
+		}
+		break;
+
 	case SU_TUNABELLY:
 	{
-		unsigned int heal;
+		unsigned int heal = 0;
 
 		if (dstmd && (dstmd->mob_id == MOBID_EMPERIUM || status_get_class_(bl) == CLASS_BATTLEFIELD))
 			heal = 0;
-		else {
+		else if (status_get_hp(bl) != status_get_max_hp(bl))
 			heal = ((2 * skill_lv - 1) * 10) * status_get_max_hp(bl) / 100;
-			status_heal(bl, heal, 0, 0);
-		}
+		status_heal(bl, heal, 0, 1|2|4);
 
-		clif_skill_nodamage(src, bl, skill_id, heal, 1);
+		clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
 	}
 		break;
 
 	case SU_BUNCHOFSHRIMP:
-		if (sd == NULL || sd->status.party_id == 0 || flag&1)
-			clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(src, bl, type, 100, skill_lv, skill_get_time(skill_id, skill_lv)));
-		else if (sd)
+	case SU_HISS:
+	case SU_PURRING:
+	case SU_SHRIMPARTY:
+	case SU_MEOWMEOW:
+		if (sd == NULL || sd->status.party_id == 0 || flag&1) {
+			int duration = skill_get_time(skill_id, skill_lv);
+
+			if (skill_id == SU_BUNCHOFSHRIMP && pc_checkskill(sd, SU_SPIRITOFSEA))
+				duration += skill_get_time2(SU_BUNCHOFSHRIMP, skill_lv);
+			clif_skill_nodamage(bl, bl, skill_id, skill_lv, sc_start(src, bl, type, 100, skill_lv, duration));
+		} else if (sd) {
+			if (skill_id == SU_SHRIMPARTY)
+				sc_start(src, bl, SC_SHRIMPBLESSING, 100, skill_lv, skill_get_time2(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);
+		}
+		break;
+
+	case SU_POWEROFFLOCK:
+		if (flag&1) {
+			sc_start(src, bl, SC_FEAR, 100, skill_lv, skill_get_time(skill_id, skill_lv));
+			sc_start(src, bl, SC_FREEZE, 100, skill_lv, skill_get_time2(skill_id, skill_lv)); //! TODO: What's the duration?
+		} else {
+			clif_skill_nodamage(src, bl, skill_id, skill_lv, 1);
+			if (battle_config.skill_wall_check)
+				map_foreachinshootrange(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);
+			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;
 
 	default:
@@ -11091,7 +11168,6 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data)
 			case GN_WALLOFTHORN:
 			case SC_ESCAPE:
 			case SU_CN_POWDERING:
-			case SU_SV_ROOTTWIST:
 				ud->skillx = target->x;
 				ud->skilly = target->y;
 				ud->skilltimer = tid;
@@ -11647,8 +11723,6 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui
 	case MH_XENO_SLASHER:
 	case LG_KINGS_GRACE:
 	case RL_B_TRAP:
-	case SU_CN_POWDERING:
-	case SU_SV_ROOTTWIST:
 		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.
 	case GN_WALLOFTHORN:
@@ -11695,16 +11769,31 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui
 		skill_unitsetting(src,skill_id,skill_lv,x,y,0);
 		break;
 
+	case SU_CN_POWDERING:
+	case SU_NYANGGRASS:
+		if (sd && pc_checkskill(sd, SU_SPIRITOFLAND)) {
+			if (skill_id == SU_CN_POWDERING)
+				sc_start(src, src, SC_DORAM_FLEE2, 100, sd->status.base_level / 12, skill_get_time(SU_SPIRITOFLAND, 1));
+			else
+				sc_start(src, src, SC_DORAM_MATK, 100, sd->status.base_level, skill_get_time(SU_SPIRITOFLAND, 1));
+		}
+		flag |= 1;
+		skill_unitsetting(src, skill_id, skill_lv, x, y, 0);
+		break;
+
 	case WZ_METEOR:
 	case SU_CN_METEOR: {
 			int area = skill_get_splash(skill_id, skill_lv);
 			short tmpx = 0, tmpy = 0;
 			if (sd && skill_id == SU_CN_METEOR) {
 				short item_idx = pc_search_inventory(sd, ITEMID_CATNIP_FRUIT);
+
 				if (item_idx >= 0) {
 					pc_delitem(sd, item_idx, 1, 0, 1, LOG_TYPE_CONSUME);
 					flag |= 1;
 				}
+				if (pc_checkskill(sd, SU_SPIRITOFLAND))
+					sc_start(src, src, SC_DORAM_SVSP, 100, 100, skill_get_time(SU_SPIRITOFLAND, 1));
 			}
 			for (i = 1; i <= skill_get_time(skill_id, skill_lv)/skill_get_unit_interval(skill_id); i++) {
 				// Creates a random Cell in the Splash Area
@@ -12230,16 +12319,25 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui
 			skill_addtimerskill(src,gettick()+500,0,x,y,skill_id,skill_lv,BF_MISC,flag);
 		}
 		break;
-
 	case SU_LOPE:
-		if (map[src->m].flag.noteleport && !(map[src->m].flag.battleground || map_flag_gvg2(src->m))) {
-			x = src->x;
-			y = src->y;
-		}
-		clif_skill_nodamage(src, src, SU_LOPE, skill_lv, 1);
-		if (!map_count_oncell(src->m, x, y, BL_PC|BL_NPC|BL_MOB, 0) && path_search(NULL, src->m, src->x, src->y, x, y, 1, CELL_CHKNOREACH)) {
-			clif_slide(src, x, y);
-			unit_movepos(src, x, y, 1, 0);
+		{
+			uint8 dir = map_calc_dir(src, x, y);
+
+			// Fails on noteleport maps, except for GvG and BG maps
+			if (map[src->m].flag.noteleport && !(map[src->m].flag.battleground || map_flag_gvg2(src->m))) {
+				x = src->x;
+				y = src->y;
+			} else if (dir%2) { // Diagonal
+				x += dirx[dir] * (skill_lv * 4) / 3;
+				y += diry[dir] * (skill_lv * 4) / 3;
+			} else {
+				x += dirx[dir] * skill_lv * 2;
+				y += diry[dir] * skill_lv * 2;
+			}
+
+			clif_skill_nodamage(src, src, skill_id, skill_lv, 1);
+			if (!map_count_oncell(src->m, x, y, BL_PC|BL_NPC|BL_MOB, 0) && map_getcell(src->m, x, y, CELL_CHKREACH) && unit_movepos(src, x, y, 1, 0))
+				clif_blown(src);
 		}
 		break;
 
@@ -13383,6 +13481,11 @@ static int skill_unit_onplace(struct skill_unit *unit, struct block_list *bl, un
 				sc_start(ss, bl, type, 100, sg->skill_lv, skill_get_time(sg->skill_id, sg->skill_lv));
 			break;
 
+		case UNT_NYANGGRASS:
+			if (!sce)
+				sc_start(ss, bl, type, 100, sg->skill_lv, skill_get_time(sg->skill_id, sg->skill_lv));
+			break;
+
 		case UNT_GD_LEADERSHIP:
 		case UNT_GD_GLORYWOUNDS:
 		case UNT_GD_SOULCOLD:
@@ -14124,28 +14227,6 @@ int skill_unit_onplace_timer(struct skill_unit *unit, struct block_list *bl, uns
 				skill_attack(skill_get_type(sg->skill_id),ss,&unit->bl,bl,sg->skill_id,sg->skill_lv,tick,SD_ANIMATION|SD_SPLASH),
 				1,sg->skill_id,sg->skill_lv,DMG_SKILL);
 			break;
-
-		case UNT_SV_ROOTTWIST:
-			if (status_bl_has_mode(bl,MD_STATUS_IMMUNE))
-				break;
-			if (tsc) {
-				if (!sg->val2) {
-					int sec = skill_get_time(sg->skill_id, sg->skill_lv);
-
-					if (sc_start2(ss, bl, type, 100, sg->skill_lv, sg->group_id, sec)) {
-						const struct TimerData* td = tsc->data[type]?get_timer(tsc->data[type]->timer):NULL;
-
-						if(td)
-							sec = DIFF_TICK(td->tick, tick);
-						clif_fixpos(bl);
-						sg->val2 = bl->id;
-					} else // Couldn't trap it?
-						sec = 7000;
-					sg->limit = DIFF_TICK(tick, sg->tick) + sec;
-				} else if (tsc->data[type] && bl->id == sg->val2)
-					skill_attack(skill_get_type(SU_SV_ROOTTWIST_ATK), ss, &unit->bl, bl, SU_SV_ROOTTWIST_ATK, sg->skill_lv, tick, SD_LEVEL|SD_ANIMATION);
-			}
-			break;
 	}
 
 	if (bl->type == BL_MOB && ss != bl)
@@ -14293,6 +14374,7 @@ int skill_unit_onleft(uint16 skill_id, struct block_list *bl, unsigned int tick)
 		case LG_KINGS_GRACE:
 		case NC_STEALTHFIELD:
 		case NC_NEUTRALBARRIER:
+		case SU_NYANGGRASS:
 			if (sce)
 				status_change_end(bl, type, INVALID_TIMER);
 			break;
@@ -20563,7 +20645,6 @@ int skill_disable_check(struct status_change *sc, uint16 skill_id)
 		case KO_YAMIKUMO:
 		case RA_WUGDASH:
 		case RA_CAMOUFLAGE:
-		case SU_HIDE:
 			if( sc->data[status_skill2sc(skill_id)] )
 				return 1;
 			break;
@@ -20603,6 +20684,8 @@ int skill_get_elemental_type( uint16 skill_id , uint16 skill_lv ) {
  * @return True:If unit can be moved, False:If check on flags are met or unit cannot be moved.
  **/
 static bool skill_check_unit_movepos(uint8 check_flag, struct block_list *bl, short dst_x, short dst_y, int easy, bool checkpath) {
+	struct status_change *sc;
+
 	nullpo_retr(false, bl);
 
 	if (check_flag&1 && map[bl->m].flag.battleground)
@@ -20612,6 +20695,10 @@ static bool skill_check_unit_movepos(uint8 check_flag, struct block_list *bl, sh
 	if (check_flag&4 && map_flag_gvg2(bl->m))
 		return false;
 
+	sc = status_get_sc(bl);
+	if (sc && sc->data[SC_SV_ROOTTWIST])
+		return false;
+
 	return unit_movepos(bl, dst_x, dst_y, easy, checkpath);
 }
 

+ 16 - 1
src/map/skill.h

@@ -1789,6 +1789,21 @@ enum e_skill {
 	SU_TUNAPARTY,
 	SU_BUNCHOFSHRIMP,
 	SU_FRESHSHRIMP,
+	SU_CN_METEOR2,
+	SU_LUNATICCARROTBEAT2,
+	SU_SOULATTACK,
+	SU_POWEROFFLOCK,
+	SU_SVG_SPIRIT,
+	SU_HISS,
+	SU_NYANGGRASS,
+	SU_GROOMING,
+	SU_PURRING,
+	SU_SHRIMPARTY,
+	SU_SPIRITOFLIFE,
+	SU_MEOWMEOW,
+	SU_SPIRITOFLAND,
+	SU_CHATTERING,
+	SU_SPIRITOFSEA,
 
 	HLIF_HEAL = 8001,
 	HLIF_AVOID,
@@ -2061,7 +2076,7 @@ enum s_skill_unit_id {
 	UNT_FIRE_RAIN,
 
 	UNT_CATNIPPOWDER,
-	UNT_SV_ROOTTWIST,
+	UNT_NYANGGRASS,
 
 	/**
 	 * Guild Auras

+ 146 - 41
src/map/status.c

@@ -852,16 +852,22 @@ void initChangeTables(void)
 	add_sc( SU_SCRATCH				, SC_BLEEDING );
 	set_sc( SU_STOOP				, SC_SU_STOOP		, SI_SU_STOOP		, SCB_NONE );
 	add_sc( SU_SV_STEMSPEAR			, SC_BLEEDING );
-	set_sc( SU_CN_POWDERING			, SC_CATNIPPOWDER	, SI_CATNIPPOWDER	, SCB_WATK|SCB_SPEED|SCB_REGEN );
+	set_sc( SU_CN_POWDERING			, SC_CATNIPPOWDER	, SI_CATNIPPOWDER	, SCB_WATK|SCB_MATK|SCB_SPEED|SCB_REGEN );
 	add_sc( SU_CN_METEOR			, SC_CURSE );
-	set_sc_with_vfx( SU_SV_ROOTTWIST			, SC_SV_ROOTTWIST	, SI_SV_ROOTTWIST	, SCB_NONE );
-	//add_sc( SU_SCAROFTAROU			, SC_STUN );
+	set_sc_with_vfx( SU_SV_ROOTTWIST, SC_SV_ROOTTWIST	, SI_SV_ROOTTWIST	, SCB_NONE );
 	set_sc( SU_SCAROFTAROU			, SC_BITESCAR		, SI_BITESCAR		, SCB_NONE );
 	set_sc( SU_ARCLOUSEDASH			, SC_ARCLOUSEDASH	, SI_ARCLOUSEDASH	, SCB_AGI|SCB_SPEED );
 	add_sc( SU_LUNATICCARROTBEAT	, SC_STUN );
 	set_sc( SU_TUNAPARTY			, SC_TUNAPARTY		, SI_TUNAPARTY		, SCB_NONE );
 	set_sc( SU_BUNCHOFSHRIMP		, SC_SHRIMP			, SI_SHRIMP			, SCB_BATK|SCB_MATK );
 	set_sc( SU_FRESHSHRIMP			, SC_FRESHSHRIMP	, SI_FRESHSHRIMP	, SCB_NONE );
+	set_sc( SU_HISS					, SC_HISS			, SI_HISS			, SCB_FLEE2 );
+	set_sc( SU_NYANGGRASS			, SC_NYANGGRASS		, SI_NYANGGRASS		, SCB_DEF|SCB_MDEF );
+	set_sc( SU_GROOMING				, SC_GROOMING		, SI_GROOMING		, SCB_FLEE );
+	add_sc( SU_PURRING				, SC_GROOMING );
+	add_sc( SU_SHRIMPARTY			, SC_FRESHSHRIMP );
+	add_sc( SU_MEOWMEOW				, SC_CHATTERING );
+	set_sc( SU_CHATTERING			, SC_CHATTERING		, SI_CHATTERING		, SCB_WATK|SCB_MATK );
 
 	/* Storing the target job rather than simply SC_SPIRIT simplifies code later on */
 	SkillStatusChangeTable[skill_get_index(SL_ALCHEMIST)]	= (sc_type)MAPID_ALCHEMIST,
@@ -1083,6 +1089,7 @@ void initChangeTables(void)
 
 	/* Summoners status icons */
 	StatusIconChangeTable[SC_SPRITEMABLE] = SI_SPRITEMABLE;
+	StatusIconChangeTable[SC_SHRIMPBLESSING] = SI_PROTECTIONOFSHRIMP;
 	StatusIconChangeTable[SC_DORAM_BUF_01] = SI_DORAM_BUF_01;
 	StatusIconChangeTable[SC_DORAM_BUF_02] = SI_DORAM_BUF_02;
 
@@ -1268,7 +1275,11 @@ void initChangeTables(void)
 	// RODEX
 	StatusChangeFlagTable[SC_DAILYSENDMAILCNT] |= SCB_NONE;
 
-	// Doram Buffs
+	// Summoner
+	StatusChangeFlagTable[SC_SHRIMPBLESSING] |= SCB_REGEN;
+	StatusChangeFlagTable[SC_DORAM_WALKSPEED] |= SCB_SPEED;
+	StatusChangeFlagTable[SC_DORAM_MATK] |= SCB_MATK;
+	StatusChangeFlagTable[SC_DORAM_FLEE2] |= SCB_FLEE2;
 	StatusChangeFlagTable[SC_DORAM_BUF_01] |= SCB_REGEN;
 	StatusChangeFlagTable[SC_DORAM_BUF_02] |= SCB_REGEN;
 
@@ -1315,6 +1326,7 @@ void initChangeTables(void)
 	StatusDisplayType[SC_C_MARKER]		  = BL_PC;
 	StatusDisplayType[SC_ANTI_M_BLAST]	  = BL_PC;
 	StatusDisplayType[SC_SPRITEMABLE]     = BL_PC;
+	StatusDisplayType[SC_SV_ROOTTWIST]    = BL_PC;
 
 	// Costumes
 	StatusDisplayType[SC_MOONSTAR] = BL_PC;
@@ -1817,7 +1829,8 @@ int status_damage(struct block_list *src,struct block_list *target,int64 dhp, in
  * @param bl: Object to heal [PC|MOB|HOM|MER|ELEM]
  * @param hhp: How much HP to heal
  * @param hsp: How much SP to heal
- * @param flag:	Whether it's Forced(&1) or gives HP/SP(&2) heal effect \n
+ * @param flag:	Whether it's Forced(&1), gives HP/SP(&2) heal effect,
+ *      or gives HP(&4) heal effect with 0 heal
  *		Forced healing overrides heal impedement statuses (Berserk)
  * @return hp+sp
  */
@@ -1868,7 +1881,7 @@ int status_heal(struct block_list *bl,int64 hhp,int64 hsp, int flag)
 			sp = status->max_sp - status->sp;
 	}
 
-	if(!sp && !hp)
+	if(!sp && !hp && !(flag&4))
 		return 0;
 
 	status->hp += hp;
@@ -1884,7 +1897,7 @@ int status_heal(struct block_list *bl,int64 hhp,int64 hsp, int flag)
 
 	// 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_PC:  pc_heal((TBL_PC*)bl,hp,sp,flag); break;
 		case BL_MOB: mob_heal((TBL_MOB*)bl,hp); break;
 		case BL_HOM: hom_heal((TBL_HOM*)bl); break;
 		case BL_MER: mercenary_heal((TBL_MER*)bl,hp,sp); break;
@@ -2087,7 +2100,7 @@ bool status_check_skilluse(struct block_list *src, struct block_list *target, ui
 		if (flag == 1 && sc->data[SC_CURSEDCIRCLE_TARGET] && skill_id == MO_ABSORBSPIRITS) // Absorb Spirits fails to go through
 			return false;
 
-		if (skill_id != RK_REFRESH && sc->opt1 && sc->opt1 != OPT1_BURNING && skill_id != SR_GENTLETOUCH_CURE) { // Stuned/Frozen/etc
+		if (skill_id != RK_REFRESH && skill_id != SU_GROOMING && sc->opt1 && sc->opt1 != OPT1_BURNING && skill_id != SR_GENTLETOUCH_CURE) { // Stuned/Frozen/etc
 			if (flag != 1) // Can't cast, casted stuff can't damage.
 				return false;
 			if (!(skill_get_inf(skill_id)&INF_GROUND_SKILL)) 
@@ -2291,7 +2304,7 @@ int status_check_visibility(struct block_list *src, struct block_list *target)
 				}
 				break;
 			default:
-				if (((tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK)) || tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_STEALTHFIELD]) && !is_boss && !is_detector)
+				if (((tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK)) || tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_STEALTHFIELD] || tsc->data[SC_SUHIDE]) && !is_boss && !is_detector)
 					return 0;
 		}
 	}
@@ -2420,7 +2433,7 @@ unsigned int status_weapon_atk(struct weapon_atk wa, struct map_session_data *sd
 	float str = sd->base_status.str;
 	int weapon_atk_bonus = 0;
 
-	if (wa.range > 3)
+	if (wa.range > 3 && !pc_checkskill(sd, SU_SOULATTACK))
 		str = sd->base_status.dex;
 	if (sd->bonus.weapon_atk_rate)
 		weapon_atk_bonus = wa.atk * sd->bonus.weapon_atk_rate / 100;
@@ -2911,8 +2924,9 @@ static int status_get_hpbonus(struct block_list *bl, enum e_status_bonus type) {
 				bonus += 1000;
 			if (pc_checkskill(sd, SU_POWEROFSEA) > 0) {
 				bonus += 1000;
-				if (pc_checkskill(sd, SU_TUNABELLY) == 5 && pc_checkskill(sd, SU_TUNAPARTY) == 5 && pc_checkskill(sd, SU_BUNCHOFSHRIMP) == 5 && pc_checkskill(sd, SU_FRESHSHRIMP) == 5)
-					bonus += 2000;
+				if ((pc_checkskill(sd, SU_TUNABELLY) + pc_checkskill(sd, SU_TUNAPARTY) + pc_checkskill(sd, SU_BUNCHOFSHRIMP) + pc_checkskill(sd, SU_FRESHSHRIMP) +
+					pc_checkskill(sd, SU_GROOMING) + pc_checkskill(sd, SU_PURRING) + pc_checkskill(sd, SU_SHRIMPARTY)) > 19)
+						bonus += 2000;
 			}
 #ifndef HP_SP_TABLES
 			if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.base_level >= 99)
@@ -3047,8 +3061,9 @@ static int status_get_spbonus(struct block_list *bl, enum e_status_bonus type) {
 				bonus += 100;
 			if (pc_checkskill(sd, SU_POWEROFSEA) > 0) {
 				bonus += 100;
-				if (pc_checkskill(sd, SU_TUNABELLY) == 5 && pc_checkskill(sd, SU_TUNAPARTY) == 5 && pc_checkskill(sd, SU_BUNCHOFSHRIMP) == 5 && pc_checkskill(sd, SU_FRESHSHRIMP) == 5)
-					bonus += 200;
+				if ((pc_checkskill(sd, SU_TUNABELLY) + pc_checkskill(sd, SU_TUNAPARTY) + pc_checkskill(sd, SU_BUNCHOFSHRIMP) + pc_checkskill(sd, SU_FRESHSHRIMP) +
+					pc_checkskill(sd, SU_GROOMING) + pc_checkskill(sd, SU_PURRING) + pc_checkskill(sd, SU_SHRIMPARTY)) > 19)
+						bonus += 200;
 			}
 		}
 
@@ -3880,6 +3895,9 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt)
 	if (pc_checkskill(sd, SU_POWEROFLIFE) > 0)
 		base_status->hit += 20;
 
+	if ((skill = pc_checkskill(sd, SU_SOULATTACK)) > 0)
+		base_status->rhw.range += skill_get_range2(&sd->bl, SU_SOULATTACK, skill, true);
+
 // ----- FLEE CALCULATION -----
 
 	// Absolute modifiers from passive skills
@@ -4143,7 +4161,7 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt)
 		clif_skillinfoblock(sd);
 
 	// If the skill is learned, the status is infinite.
-	if( (skill = pc_checkskill(sd,SU_SPRITEMABLE)) > 0 )
+	if( (skill = pc_checkskill(sd,SU_SPRITEMABLE)) > 0 && !sd->sc.data[SC_SPRITEMABLE] )
 		sc_start(&sd->bl, &sd->bl, SC_SPRITEMABLE, 100, 1, -1);
 
 	calculating = 0;
@@ -4377,12 +4395,15 @@ int status_calc_npc_(struct npc_data *nd, enum e_status_calc_opt opt)
 void status_calc_regen(struct block_list *bl, struct status_data *status, struct regen_data *regen)
 {
 	struct map_session_data *sd;
+	struct status_change *sc;
 	int val, skill, reg_flag;
 
 	if( !(bl->type&BL_REGEN) || !regen )
 		return;
 
 	sd = BL_CAST(BL_PC,bl);
+	sc = status_get_sc(bl);
+
 	val = 1 + (status->vit/5) + (status->max_hp/200);
 
 	if( sd && sd->hprecov_rate != 100 )
@@ -4423,6 +4444,9 @@ void status_calc_regen(struct block_list *bl, struct status_data *status, struct
 		if( (skill=pc_checkskill(sd,WM_LESSON)) > 0 )
 			val += 3 + 3 * skill;
 
+		if (sc && sc->count && sc->data[SC_SHRIMPBLESSING])
+			val += 150 / 100;
+
 		sregen->sp = cap_value(val, 0, SHRT_MAX);
 
 		// Skill-related recovery (only when sit)
@@ -4963,7 +4987,8 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
 					if (sd->bonus.ematk > 0)
 						status->matk_min += sd->bonus.ematk;
 					if (pc_checkskill(sd, SU_POWEROFLAND) > 0) {
-						if (pc_checkskill(sd, SU_SV_STEMSPEAR) == 5 && pc_checkskill(sd, SU_CN_POWDERING) == 5 && pc_checkskill(sd, SU_CN_METEOR) == 5 && pc_checkskill(sd, SU_SV_ROOTTWIST) == 5)
+						if ((pc_checkskill(sd, SU_SV_STEMSPEAR) + pc_checkskill(sd, SU_CN_POWDERING) + pc_checkskill(sd, SU_CN_METEOR) + pc_checkskill(sd, SU_SV_ROOTTWIST) +
+						pc_checkskill(sd, SU_CHATTERING) + pc_checkskill(sd, SU_MEOWMEOW) + pc_checkskill(sd, SU_NYANGGRASS)) > 19)
 							status->matk_min += status->matk_min * 20 / 100;
 					}
 				}
@@ -4996,9 +5021,6 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
 					variance += status->lhw.matk * status->lhw.wlv / 10;
 				}
 
-				if (sc && sc->data[SC_CATNIPPOWDER])
-					wMatk -= wMatk * sc->data[SC_CATNIPPOWDER]->val2 / 100;
-
 				status->matk_min += wMatk - variance;
 				status->matk_max += wMatk + variance;
 				}
@@ -5903,6 +5925,8 @@ static unsigned short status_calc_watk(struct block_list *bl, struct status_chan
 		watk += sc->data[SC_FLASHCOMBO]->val2;
 	if (sc->data[SC_CATNIPPOWDER])
 		watk -= watk * sc->data[SC_CATNIPPOWDER]->val2 / 100;
+	if (sc->data[SC_CHATTERING])
+		watk += sc->data[SC_CHATTERING]->val2;
 
 	return (unsigned short)cap_value(watk,0,USHRT_MAX);
 }
@@ -5952,6 +5976,12 @@ static unsigned short status_calc_ematk(struct block_list *bl, struct status_cha
 		matk += sc->data[SC_MTF_MATK2]->val1;
 	if(sc->data[SC_2011RWC_SCROLL])
 		matk += 30;
+	if (sc->data[SC_CATNIPPOWDER])
+		matk -= matk * sc->data[SC_CATNIPPOWDER]->val2 / 100;
+	if (sc->data[SC_CHATTERING])
+		matk += sc->data[SC_CHATTERING]->val2;
+	if (sc->data[SC_DORAM_MATK])
+		matk += sc->data[SC_DORAM_MATK]->val1;
 
 	return (unsigned short)cap_value(matk,0,USHRT_MAX);
 }
@@ -6211,6 +6241,8 @@ static signed short status_calc_flee(struct block_list *bl, struct status_change
 	//	flee -= (flee * sc->data[SC_C_MARKER]->val3) / 100;
 	if(sc->data[SC_HEAT_BARREL])
 		flee -= sc->data[SC_HEAT_BARREL]->val4;
+	if (sc->data[SC_GROOMING])
+		flee += sc->data[SC_GROOMING]->val2;
 
 	return (short)cap_value(flee,1,SHRT_MAX);
 }
@@ -6233,6 +6265,10 @@ static signed short status_calc_flee2(struct block_list *bl, struct status_chang
 		flee2 += sc->data[SC_WHISTLE]->val3*10;
 	if(sc->data[SC__UNLUCKY])
 		flee2 -= flee2 * sc->data[SC__UNLUCKY]->val2 / 100;
+	if (sc->data[SC_HISS])
+		flee2 += sc->data[SC_HISS]->val2;
+	if (sc->data[SC_DORAM_FLEE2])
+		flee2 += sc->data[SC_DORAM_FLEE2]->val1;
 
 	return (short)cap_value(flee2,10,SHRT_MAX);
 }
@@ -6259,6 +6295,12 @@ static defType status_calc_def(struct block_list *bl, struct status_change *sc,
 	if(sc->data[SC_STEELBODY])
 		return 90;
 #endif
+	if (sc->data[SC_NYANGGRASS]) {
+		if (bl->type == BL_PC)
+			return 0;
+		else
+			return def >>= 1;
+	}
 	if(sc->data[SC_DEFSET])
 		return sc->data[SC_DEFSET]->val1;
 
@@ -6421,6 +6463,12 @@ static defType status_calc_mdef(struct block_list *bl, struct status_change *sc,
 	if(sc->data[SC_STEELBODY])
 		return 90;
 #endif
+	if (sc->data[SC_NYANGGRASS]) {
+		if (bl->type == BL_PC)
+			return 0;
+		else
+			return mdef >>= 1;
+	}
 	if(sc->data[SC_MDEFSET])
 		return sc->data[SC_MDEFSET]->val1;
 
@@ -6642,6 +6690,8 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha
 			val = max( val, 25 );
 		if (sc->data[SC_ARCLOUSEDASH])
 			val = max(val, sc->data[SC_ARCLOUSEDASH]->val3);
+		if( sc->data[SC_DORAM_WALKSPEED] )
+			val = max(val, sc->data[SC_DORAM_WALKSPEED]->val1);
 
 		// !FIXME: official items use a single bonus for this [ultramage]
 		if( sc->data[SC_SPEEDUP0] ) // Temporary item-based speedup
@@ -8677,6 +8727,10 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 		if (sc->data[SC_DEVOTION] || sc->data[SC_WHITEIMPRISON])
 			return 0;
 		break;
+	case SC_GROOMING:
+	case SC_CHATTERING:
+		if (sc->data[type])
+			return 0;
 
 	case SC_WEDDING:
 	case SC_XMAS:
@@ -9038,23 +9092,25 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 		status_change_end(bl, SC_BLIND, INVALID_TIMER);
 		break;
 	case SC_KINGS_GRACE:
-		status_change_end(bl,SC_POISON,INVALID_TIMER);
 		status_change_end(bl,SC_BLIND,INVALID_TIMER);
-		status_change_end(bl,SC_FREEZE,INVALID_TIMER);
-		status_change_end(bl,SC_STONE,INVALID_TIMER);
-		status_change_end(bl,SC_STUN,INVALID_TIMER);
-		status_change_end(bl,SC_SLEEP,INVALID_TIMER);
-		status_change_end(bl,SC_BLEEDING,INVALID_TIMER);
 		status_change_end(bl,SC_CURSE,INVALID_TIMER);
 		status_change_end(bl,SC_CONFUSION,INVALID_TIMER);
 		status_change_end(bl,SC_HALLUCINATION,INVALID_TIMER);
-		status_change_end(bl,SC_SILENCE,INVALID_TIMER);
 		status_change_end(bl,SC_BURNING,INVALID_TIMER);
-		status_change_end(bl,SC_CRYSTALIZE,INVALID_TIMER);
-		status_change_end(bl,SC_FREEZING,INVALID_TIMER);
 		status_change_end(bl,SC_DEEPSLEEP,INVALID_TIMER);
+		// Fall through
+	case SC_GROOMING:
+		status_change_end(bl,SC_STUN,INVALID_TIMER);
+		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_SILENCE,INVALID_TIMER);
+		status_change_end(bl,SC_BLEEDING,INVALID_TIMER);
+		status_change_end(bl,SC_POISON,INVALID_TIMER);
 		status_change_end(bl,SC_FEAR,INVALID_TIMER);
 		status_change_end(bl,SC_MANDRAGORA,INVALID_TIMER);
+		status_change_end(bl,SC_CRYSTALIZE,INVALID_TIMER);
+		status_change_end(bl,SC_FREEZING,INVALID_TIMER);
 		break;
 	case SC_2011RWC_SCROLL:
 		status_change_end(bl,SC_FOOD_STR_CASH,INVALID_TIMER);
@@ -10847,11 +10903,16 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 		case SC_CATNIPPOWDER:
 			val2 = 50; // WATK%, MATK%
 			val3 = 25 * val1; // Move speed reduction
+			if (bl->type == BL_PC && pc_checkskill(sd, SU_SPIRITOFLAND))
+				val4 = status_get_lv(src) / 12;
 			break;
-		case SC_BITESCAR:
-			val2 = 2 * val1; // MHP% damage
-			val4 = tick / 1000;
-			tick_time = 1000;
+		case SC_BITESCAR: {
+				const struct status_data *b_status = status_get_base_status(src); // Base Status
+
+				val2 = (status_get_max_hp(bl) * (val1 + (b_status->dex / 25))) / status_get_max_hp(bl); // MHP% damage
+				tick_time = 1000;
+				val4 = tick / tick_time;
+			}
 			break;
 		case SC_ARCLOUSEDASH:
 			val2 = 15 + 5 * val1; // AGI
@@ -10862,15 +10923,59 @@ int status_change_start(struct block_list* src, struct block_list* bl,enum sc_ty
 		case SC_SHRIMP:
 			val2 = 10; // BATK%, MATK%
 			break;
-		case SC_FRESHSHRIMP:
-			val4 = tick / (10000 - ((val1 - 1) * 1000));
-			tick_time = 10000 - ((val1 - 1) * 1000);
-			if (val4 <= 0) // Prevents a negeative value from happening
-				val4 = 0;
+		case SC_FRESHSHRIMP: {
+				int min = 0, max = 0;
+
+#ifdef RENEWAL
+				min = max = status_base_matk(src, status, status_get_lv(src));
+				if (status->rhw.matk > 0) {
+					int wMatk, variance;
+
+					wMatk = status->rhw.matk;
+					variance = wMatk * status->rhw.wlv / 10;
+					min += wMatk - variance;
+					max += wMatk + variance;
+				}
+#endif
+
+				if (sd && sd->right_weapon.overrefine > 0) {
+					min++;
+					max += sd->right_weapon.overrefine - 1;
+				}
+
+				val2 += min + 178; // Heal
+				if (max > min)
+					val2 += rnd() % (max - min); // Heal
+
+				if (sd) {
+					if (pc_checkskill(sd, SU_POWEROFSEA)) {
+						val2 += val2 * 10 / 100;
+						if ((pc_checkskill(sd, SU_TUNABELLY) + pc_checkskill(sd, SU_TUNAPARTY) + pc_checkskill(sd, SU_BUNCHOFSHRIMP) + pc_checkskill(sd, SU_FRESHSHRIMP) +
+							pc_checkskill(sd, SU_GROOMING) + pc_checkskill(sd, SU_PURRING) + pc_checkskill(sd, SU_SHRIMPARTY)) > 19)
+								val2 += val2 * 20 / 100;
+					}
+					if (pc_checkskill(sd, SU_SPIRITOFSEA))
+						val2 *= 2; // Doubles HP
+				}
+				tick_time = 10000 - ((val1 - 1) * 1000);
+				val4 = tick / tick_time;
+			}
 			break;
 		case SC_TUNAPARTY:
-			val2 = (status->max_hp * (val1 * 10) / 100); // %Max HP to absorb
-			//val3 = 6 + val1; // Hits !TODO: Does it have hits too?
+			val2 = (status->max_hp * (val1 * 10) / 100); // Max HP% to absorb
+			if (sd && pc_checkskill(sd, SU_SPIRITOFSEA))
+				val2 <<= 1; // Double the shield life
+			break;
+		case SC_HISS:
+			val2 = 50; // Perfect Dodge
+			sc_start(src, bl, SC_DORAM_WALKSPEED, 100, 50, skill_get_time2(SU_HISS, val1));
+			break;
+		case SC_GROOMING:
+			val2 = 100; // Flee
+			break;
+		case SC_CHATTERING:
+			val2 = 100; // eATK, eMATK
+			sc_start(src, bl, SC_DORAM_WALKSPEED, 100, 50, skill_get_time2(SU_CHATTERING, val1));
 			break;
 		case SC_SWORDCLAN:
 		case SC_ARCWANDCLAN:
@@ -12078,7 +12183,6 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
 			break;
 		case SC_NEUTRALBARRIER_MASTER:
 		case SC_STEALTHFIELD_MASTER:
-		case SC_SV_ROOTTWIST:
 			if( sce->val2 ) {
 				struct skill_unit_group* group = skill_id2group(sce->val2);
 				sce->val2 = 0;
@@ -13319,8 +13423,9 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
 		break;
 	case SC_FRESHSHRIMP:
 		if (--(sce->val4) >= 0) {
-			status_heal(bl, status->max_hp * 4 / 100, 0, 2);
+			status_heal(bl, sce->val2, 0, 3);
 			sc_timer_next((10000 - ((sce->val1 - 1) * 1000)) + tick, status_change_timer, bl->id, data);
+			return 0;
 		}
 		break;
 	case SC_DORAM_BUF_01:

+ 14 - 1
src/map/status.h

@@ -804,9 +804,22 @@ typedef enum sc_type {
 
 	SC_DAILYSENDMAILCNT,
 
-  SC_DORAM_BUF_01,
+	SC_DORAM_BUF_01,
 	SC_DORAM_BUF_02,
 
+	/**
+	 * Summoner - Extended
+	 */
+	SC_HISS,
+	SC_NYANGGRASS,
+	SC_GROOMING,
+	SC_SHRIMPBLESSING,
+	SC_CHATTERING,
+	SC_DORAM_WALKSPEED,
+	SC_DORAM_MATK,
+	SC_DORAM_FLEE2,
+	SC_DORAM_SVSP,
+
 #ifdef RENEWAL
 	SC_EXTREMITYFIST2, //! NOTE: This SC should be right before SC_MAX, so it doesn't disturb if RENEWAL is disabled
 #endif