浏览代码

Merge branch 'cleanup/skill_db-2'

aleos89 10 年之前
父节点
当前提交
20695d629a
共有 70 个文件被更改,包括 1367 次插入1106 次删除
  1. 5 0
      db/const.txt
  2. 12 7
      db/pre-re/skill_db.txt
  3. 12 7
      db/re/skill_db.txt
  4. 7 1
      doc/script_commands.txt
  5. 36 36
      npc/custom/jobmaster.txt
  6. 36 36
      npc/custom/platinum_skills.txt
  7. 1 1
      npc/jobs/2-2a/Creator.txt
  8. 2 2
      npc/jobs/valkyrie.txt
  9. 1 1
      npc/merchants/buying_shops.txt
  10. 2 2
      npc/other/gympass.txt
  11. 3 3
      npc/pre-re/jobs/1-1/acolyte.txt
  12. 3 3
      npc/pre-re/jobs/1-1/archer.txt
  13. 2 2
      npc/pre-re/jobs/1-1/mage.txt
  14. 4 4
      npc/pre-re/jobs/1-1/merchant.txt
  15. 4 4
      npc/pre-re/jobs/1-1/swordman.txt
  16. 5 5
      npc/pre-re/jobs/1-1/thief.txt
  17. 1 1
      npc/pre-re/jobs/novice/novice.txt
  18. 1 1
      npc/quests/skills/acolyte_skills.txt
  19. 1 1
      npc/quests/skills/alchemist_skills.txt
  20. 2 2
      npc/quests/skills/archer_skills.txt
  21. 5 5
      npc/quests/skills/assassin_skills.txt
  22. 3 3
      npc/quests/skills/bard_skills.txt
  23. 4 4
      npc/quests/skills/blacksmith_skills.txt
  24. 2 2
      npc/quests/skills/crusader_skills.txt
  25. 2 2
      npc/quests/skills/dancer_skills.txt
  26. 2 2
      npc/quests/skills/hunter_skills.txt
  27. 2 2
      npc/quests/skills/knight_skills.txt
  28. 1 1
      npc/quests/skills/mage_skills.txt
  29. 5 5
      npc/quests/skills/merchant_skills.txt
  30. 4 4
      npc/quests/skills/monk_skills.txt
  31. 2 2
      npc/quests/skills/novice_skills.txt
  32. 2 2
      npc/quests/skills/priest_skills.txt
  33. 3 3
      npc/quests/skills/rogue_skills.txt
  34. 6 6
      npc/quests/skills/sage_skills.txt
  35. 3 3
      npc/quests/skills/swordman_skills.txt
  36. 4 4
      npc/quests/skills/thief_skills.txt
  37. 2 2
      npc/quests/skills/wizard_skills.txt
  38. 2 2
      npc/re/jobs/1-1/acolyte.txt
  39. 3 3
      npc/re/jobs/1-1/archer.txt
  40. 2 2
      npc/re/jobs/1-1/mage.txt
  41. 4 4
      npc/re/jobs/1-1/merchant.txt
  42. 4 4
      npc/re/jobs/1-1/swordman.txt
  43. 5 5
      npc/re/jobs/1-1/thief.txt
  44. 1 1
      npc/re/jobs/novice/novice.txt
  45. 6 0
      sql-files/upgrades/upgrade_20150211_skillset.sql
  46. 25 26
      src/char/char.c
  47. 10 8
      src/common/mmo.h
  48. 24 19
      src/map/atcommand.c
  49. 7 6
      src/map/battle.c
  50. 13 11
      src/map/chrif.c
  51. 62 45
      src/map/clif.c
  52. 3 3
      src/map/clif.h
  53. 10 8
      src/map/elemental.c
  54. 55 40
      src/map/guild.c
  55. 1 1
      src/map/guild.h
  56. 32 37
      src/map/homunculus.c
  57. 2 0
      src/map/homunculus.h
  58. 4 0
      src/map/itemdb.h
  59. 26 13
      src/map/mercenary.c
  60. 1 0
      src/map/mercenary.h
  61. 6 5
      src/map/mob.c
  62. 11 9
      src/map/npc.c
  63. 352 270
      src/map/pc.c
  64. 20 8
      src/map/pc.h
  65. 6 34
      src/map/script.c
  66. 333 283
      src/map/skill.c
  67. 107 61
      src/map/skill.h
  68. 30 20
      src/map/status.c
  69. 1 0
      src/map/status.h
  70. 7 7
      src/map/unit.c

+ 5 - 0
db/const.txt

@@ -4648,5 +4648,10 @@ BSF_PERMANENT	0x200
 BSF_FORCE_REPLACE	0x400
 BSF_FORCE_DUPLICATE	0x800
 
+SKILL_PERM	0
+SKILL_TEMP	1
+SKILL_TEMPLEVEL	2
+SKILL_PERM_GRANT	3
+
 false	0
 true	1

+ 12 - 7
db/pre-re/skill_db.txt

@@ -504,7 +504,7 @@
 357,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0,0x0,		LK_CONCENTRATION,Concentration
 358,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0,		LK_TENSIONRELAX,Relax
 359,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0,		LK_BERSERK,Frenzy
-//360,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0,	LK_FURY,Fury
+360,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0,	LK_FURY,Fury
 
 //****
 // High Priest
@@ -553,10 +553,10 @@
 //****
 // Whitesmith
 384,0,0,4,0,0x1,0,10,1,yes,0,0,0,weapon,0,0x4000,	WS_MELTDOWN,Shattering Strike
-//385,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0,	WS_CREATECOIN,Create Coins
-//386,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0,	WS_CREATENUGGET,Create Nuggets
+385,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0,	WS_CREATECOIN,Create Coins
+386,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0,	WS_CREATENUGGET,Create Nuggets
 387,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x4000,		WS_CARTBOOST,Cart Boost
-//388,9,6,2,0,0x1,0,5,1,no,0,0,0,none,0,0x0,		WS_SYSTEMCREATE,Auto Attack System
+388,9,6,2,0,0x1,0,5,1,no,0,0,0,none,0,0x0,		WS_SYSTEMCREATE,Auto Attack System
 
 //****
 // Stalker
@@ -768,6 +768,11 @@
 543,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0,0x0,		NJ_NEN,Soul
 544,-5,6,1,0,0x40,0,10,1,no,0,0,0,weapon,0,0x0,	NJ_ISSEN,Final Strike
 
+572,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0,		SL_DEATHKNIGHT,SL_DEATHKNIGHT
+573,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0,		SL_COLLECTOR,SL_COLLECTOR
+574,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0,		SL_NINJA,SL_NINJA
+575,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0,		SL_GUNNER,SL_GUNNER
+
 //****
 // 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,0x0,	NPC_EARTHQUAKE,Earthquake
@@ -818,12 +823,12 @@
 689,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0,0x1000,	CASH_BLESSING,Party Blessing
 690,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0,0x1000,	CASH_INCAGI,Party Increase AGI
 691,0,6,4,0,0x3,-1,5,1,yes,0,0x2,0,magic,0,0x0,	CASH_ASSUMPTIO,Party Assumptio
-//692,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0,		ALL_CATCRY,Cat Cry
+692,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0,		ALL_CATCRY,Cat Cry
 693,0,6,4,0,0x3,-1,1,1,yes,0,0x2,0,magic,0,0x0,	ALL_PARTYFLEE,Party Flee
 //694,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0,		ALL_ANGEL_PROTECT,Angel's Protection
-//695,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0,		ALL_DREAM_SUMMERNIGHT,Summer Night Dream
+695,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0,		ALL_DREAM_SUMMERNIGHT,Summer Night Dream
 //696,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0,		NPC_CHANGEUNDEAD2,Change Undead
-//697,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0,0x0,	ALL_REVERSEORCISH,Reverse Orcish
+697,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0,0x0,	ALL_REVERSEORCISH,Reverse Orcish
 698,0,6,4,0,0x01,0,1,1,no,0,0x2,0,none,0,0x0,		ALL_WEWISH,Christmas Carol
 //699,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0,		ALL_SONKRAN,ALL_SONKRAN
 

+ 12 - 7
db/re/skill_db.txt

@@ -504,7 +504,7 @@
 357,0,6,4,0,0x1,0,5,1,no,0,0,0,weapon,0,0x0,		LK_CONCENTRATION,Concentration
 358,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0,		LK_TENSIONRELAX,Relax
 359,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0,		LK_BERSERK,Frenzy
-//360,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0,	LK_FURY,Fury
+360,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x0,	LK_FURY,Fury
 
 //****
 // High Priest
@@ -553,10 +553,10 @@
 //****
 // Whitesmith
 384,0,0,4,0,0x1,0,10,1,yes,0,0,0,weapon,0,0x4000,	WS_MELTDOWN,Shattering Strike
-//385,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0,	WS_CREATECOIN,Create Coins
-//386,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0,	WS_CREATENUGGET,Create Nuggets
+385,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0,	WS_CREATECOIN,Create Coins
+386,0,0,4,0,0x1,0,1,1,yes,0,0,0,none,0,0x0,	WS_CREATENUGGET,Create Nuggets
 387,0,6,4,0,0x1,0,1,1,no,0,0,0,weapon,0,0x4000,		WS_CARTBOOST,Cart Boost
-//388,9,6,2,0,0x1,0,5,1,no,0,0,0,none,0,0x0,		WS_SYSTEMCREATE,Auto Attack System
+388,9,6,2,0,0x1,0,5,1,no,0,0,0,none,0,0x0,		WS_SYSTEMCREATE,Auto Attack System
 
 //****
 // Stalker
@@ -768,6 +768,11 @@
 543,0,6,4,0,0x1,0,5,1,yes,0,0,0,none,0,0x0,		NJ_NEN,Soul
 544,-5,8,1,0,0x40,0,10,1,no,0,0,0,misc,0,0x0,	NJ_ISSEN,Final Strike
 
+572,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0,		SL_DEATHKNIGHT,SL_DEATHKNIGHT
+573,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0,		SL_COLLECTOR,SL_COLLECTOR
+574,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0,		SL_NINJA,SL_NINJA
+575,0,0,0,0,0,0,5,0,no,0,0,0,none,0,0x0,		SL_GUNNER,SL_GUNNER
+
 //****
 // 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,0x0,	NPC_EARTHQUAKE,Earthquake
@@ -818,10 +823,10 @@
 689,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0,0x0,	CASH_BLESSING,Party Blessing
 690,0,6,4,0,0x3,-1,10,1,yes,0,0x2,0,magic,0,0x0,	CASH_INCAGI,Party Increase AGI
 691,0,6,4,0,0x3,-1,5,1,yes,0,0x2,0,magic,0,0x0,	CASH_ASSUMPTIO,Party Assumptio
-//692,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0,		ALL_CATCRY,Cat Cry
+692,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0,		ALL_CATCRY,Cat Cry
 693,0,6,4,0,0x3,-1,1,1,yes,0,0x2,0,magic,0,0x0,	ALL_PARTYFLEE,Party Flee
-//694,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0,		ALL_ANGEL_PROTECT,Angel's Protection
-//695,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0,		ALL_DREAM_SUMMERNIGHT,Summer Night Dream
+694,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0,		ALL_ANGEL_PROTECT,Angel's Protection
+695,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0,		ALL_DREAM_SUMMERNIGHT,Summer Night Dream
 //696,0,0,0,0,0,0,9,0,no,0,0x2,0,none,0,0x0,		NPC_CHANGEUNDEAD2,Change Undead
 697,9,6,4,0,0x1,0,1,1,yes,0,0x2,0,magic,0,0x0,		ALL_REVERSEORCISH,Reverse Orcish
 698,0,6,4,0,0x01,0,1,1,no,0,0x2,0,none,0,0x0,		ALL_WEWISH,Christmas Carol

+ 7 - 1
doc/script_commands.txt

@@ -5411,9 +5411,15 @@ Flag 2 means that the level parameter is to be interpreted as a stackable
 additional bonus to the skill level. If the character did not have that skill 
 previously, they will now at 0+the level given.
 
-Flag 4 is the same as flag 1 in that it saves to the database.  However, these skills
+Flag 3 is the same as flag 1 in that it saves to the database.  However, these skills
 are ignored when any action is taken that adjusts the skill tree (reset/job change).
 
+Flag contants:
+	0 - SKILL_PERM
+	1 - SKILL_TEMP
+	2 - SKILL_TEMPLEVEL
+	3 - SKILL_PERM_GRANT
+
 // This will permanently give the character Stone Throw (TF_THROWSTONE,152), at 
 // level 1.
     skill 152,1,0;

+ 36 - 36
npc/custom/jobmaster.txt

@@ -206,87 +206,87 @@ function Job_Menu {
 }
 
 Get_Platinum:
-	skill "NV_FIRSTAID",1,0;
+	skill "NV_FIRSTAID",1,SKILL_PERM;
 	switch (BaseClass) {
 	case Job_Novice:
 		if (Class != Job_SuperNovice)
-			skill "NV_TRICKDEAD",1,0;
+			skill "NV_TRICKDEAD",1,SKILL_PERM;
 		break;
 	case Job_Swordman:
-		skill "SM_MOVINGRECOVERY",1,0;
-		skill "SM_FATALBLOW",1,0;
-		skill "SM_AUTOBERSERK",1,0;
+		skill "SM_MOVINGRECOVERY",1,SKILL_PERM;
+		skill "SM_FATALBLOW",1,SKILL_PERM;
+		skill "SM_AUTOBERSERK",1,SKILL_PERM;
 		break;
 	case Job_Mage:
-		skill "MG_ENERGYCOAT",1,0;
+		skill "MG_ENERGYCOAT",1,SKILL_PERM;
 		break;
 	case Job_Archer:
-		skill "AC_MAKINGARROW",1,0;
-		skill "AC_CHARGEARROW",1,0;
+		skill "AC_MAKINGARROW",1,SKILL_PERM;
+		skill "AC_CHARGEARROW",1,SKILL_PERM;
 		break;
 	case Job_Acolyte:
-		skill "AL_HOLYLIGHT",1,0;
+		skill "AL_HOLYLIGHT",1,SKILL_PERM;
 		break;
 	case Job_Merchant:
-		skill "MC_CARTREVOLUTION",1,0;
-		skill "MC_CHANGECART",1,0;
-		skill "MC_LOUD",1,0;
+		skill "MC_CARTREVOLUTION",1,SKILL_PERM;
+		skill "MC_CHANGECART",1,SKILL_PERM;
+		skill "MC_LOUD",1,SKILL_PERM;
 		break;
 	case Job_Thief:
-		skill "TF_SPRINKLESAND",1,0;
-		skill "TF_BACKSLIDING",1,0;
-		skill "TF_PICKSTONE",1,0;
-		skill "TF_THROWSTONE",1,0;
+		skill "TF_SPRINKLESAND",1,SKILL_PERM;
+		skill "TF_BACKSLIDING",1,SKILL_PERM;
+		skill "TF_PICKSTONE",1,SKILL_PERM;
+		skill "TF_THROWSTONE",1,SKILL_PERM;
 		break;
 	default:
 		break;
 	}
 	switch (BaseJob) {
 	case Job_Knight:
-		skill "KN_CHARGEATK",1,0;
+		skill "KN_CHARGEATK",1,SKILL_PERM;
 		break;
 	case Job_Priest:
-		skill "PR_REDEMPTIO",1,0;
+		skill "PR_REDEMPTIO",1,SKILL_PERM;
 		break;
 	case Job_Wizard:
-		skill "WZ_SIGHTBLASTER",1,0;
+		skill "WZ_SIGHTBLASTER",1,SKILL_PERM;
 		break;
 	case Job_Blacksmith:
-		skill "BS_UNFAIRLYTRICK",1,0;
-		skill "BS_GREED",1,0;
+		skill "BS_UNFAIRLYTRICK",1,SKILL_PERM;
+		skill "BS_GREED",1,SKILL_PERM;
 		break;
 	case Job_Hunter:
-		skill "HT_PHANTASMIC",1,0;
+		skill "HT_PHANTASMIC",1,SKILL_PERM;
 		break;
 	case Job_Assassin:
-		skill "AS_SONICACCEL",1,0;
-		skill "AS_VENOMKNIFE",1,0;
+		skill "AS_SONICACCEL",1,SKILL_PERM;
+		skill "AS_VENOMKNIFE",1,SKILL_PERM;
 		break;
 	case Job_Crusader:
-		skill "CR_SHRINK",1,0;
+		skill "CR_SHRINK",1,SKILL_PERM;
 		break;
 	case Job_Monk:
-		skill "MO_KITRANSLATION",1,0;
-		skill "MO_BALKYOUNG",1,0;
+		skill "MO_KITRANSLATION",1,SKILL_PERM;
+		skill "MO_BALKYOUNG",1,SKILL_PERM;
 		break;
 	case Job_Sage:
-		skill "SA_CREATECON",1,0;
-		skill "SA_ELEMENTWATER",1,0;
-		skill "SA_ELEMENTGROUND",1,0;
-		skill "SA_ELEMENTFIRE",1,0;
-		skill "SA_ELEMENTWIND",1,0;
+		skill "SA_CREATECON",1,SKILL_PERM;
+		skill "SA_ELEMENTWATER",1,SKILL_PERM;
+		skill "SA_ELEMENTGROUND",1,SKILL_PERM;
+		skill "SA_ELEMENTFIRE",1,SKILL_PERM;
+		skill "SA_ELEMENTWIND",1,SKILL_PERM;
 		break;
 	case Job_Rogue:
-		skill "RG_CLOSECONFINE",1,0;
+		skill "RG_CLOSECONFINE",1,SKILL_PERM;
 		break;
 	case Job_Alchemist:
-		skill "AM_BIOETHICS",1,0;
+		skill "AM_BIOETHICS",1,SKILL_PERM;
 		break;
 	case Job_Bard:
-		skill "BA_PANGVOICE",1,0;
+		skill "BA_PANGVOICE",1,SKILL_PERM;
 		break;
 	case Job_Dancer:
-		skill "DC_WINKCHARM",1,0;
+		skill "DC_WINKCHARM",1,SKILL_PERM;
 		break;
 	default:
 		break;

+ 36 - 36
npc/custom/platinum_skills.txt

@@ -30,87 +30,87 @@ prontera,128,200,6	script	Platinum Skill NPC	94,{
 		mes "Have a nice day... >.>";
 		close;
 	}
-	skill "NV_FIRSTAID",1,0;
+	skill "NV_FIRSTAID",1,SKILL_PERM;
 	switch (BaseClass) {
 	case Job_Novice:
 		if (Class != Job_SuperNovice)
-			skill "NV_TRICKDEAD",1,0;
+			skill "NV_TRICKDEAD",1,SKILL_PERM;
 		break;
 	case Job_Swordman:
-		skill "SM_MOVINGRECOVERY",1,0;
-		skill "SM_FATALBLOW",1,0;
-		skill "SM_AUTOBERSERK",1,0;
+		skill "SM_MOVINGRECOVERY",1,SKILL_PERM;
+		skill "SM_FATALBLOW",1,SKILL_PERM;
+		skill "SM_AUTOBERSERK",1,SKILL_PERM;
 		break;
 	case Job_Mage:
-		skill "MG_ENERGYCOAT",1,0;
+		skill "MG_ENERGYCOAT",1,SKILL_PERM;
 		break;
 	case Job_Archer:
-		skill "AC_MAKINGARROW",1,0;
-		skill "AC_CHARGEARROW",1,0;
+		skill "AC_MAKINGARROW",1,SKILL_PERM;
+		skill "AC_CHARGEARROW",1,SKILL_PERM;
 		break;
 	case Job_Acolyte:
-		skill "AL_HOLYLIGHT",1,0;
+		skill "AL_HOLYLIGHT",1,SKILL_PERM;
 		break;
 	case Job_Merchant:
-		skill "MC_CARTREVOLUTION",1,0;
-		skill "MC_CHANGECART",1,0;
-		skill "MC_LOUD",1,0;
+		skill "MC_CARTREVOLUTION",1,SKILL_PERM;
+		skill "MC_CHANGECART",1,SKILL_PERM;
+		skill "MC_LOUD",1,SKILL_PERM;
 		break;
 	case Job_Thief:
-		skill "TF_SPRINKLESAND",1,0;
-		skill "TF_BACKSLIDING",1,0;
-		skill "TF_PICKSTONE",1,0;
-		skill "TF_THROWSTONE",1,0;
+		skill "TF_SPRINKLESAND",1,SKILL_PERM;
+		skill "TF_BACKSLIDING",1,SKILL_PERM;
+		skill "TF_PICKSTONE",1,SKILL_PERM;
+		skill "TF_THROWSTONE",1,SKILL_PERM;
 		break;
 	default:
 		break;
 	}
 	switch (BaseJob) {
 	case Job_Knight:
-		skill "KN_CHARGEATK",1,0;
+		skill "KN_CHARGEATK",1,SKILL_PERM;
 		break;
 	case Job_Priest:
-		skill "PR_REDEMPTIO",1,0;
+		skill "PR_REDEMPTIO",1,SKILL_PERM;
 		break;
 	case Job_Wizard:
-		skill "WZ_SIGHTBLASTER",1,0;
+		skill "WZ_SIGHTBLASTER",1,SKILL_PERM;
 		break;
 	case Job_Blacksmith:
-		skill "BS_UNFAIRLYTRICK",1,0;
-		skill "BS_GREED",1,0;
+		skill "BS_UNFAIRLYTRICK",1,SKILL_PERM;
+		skill "BS_GREED",1,SKILL_PERM;
 		break;
 	case Job_Hunter:
-		skill "HT_PHANTASMIC",1,0;
+		skill "HT_PHANTASMIC",1,SKILL_PERM;
 		break;
 	case Job_Assassin:
-		skill "AS_SONICACCEL",1,0;
-		skill "AS_VENOMKNIFE",1,0;
+		skill "AS_SONICACCEL",1,SKILL_PERM;
+		skill "AS_VENOMKNIFE",1,SKILL_PERM;
 		break;
 	case Job_Crusader:
-		skill "CR_SHRINK",1,0;
+		skill "CR_SHRINK",1,SKILL_PERM;
 		break;
 	case Job_Monk:
-		skill "MO_KITRANSLATION",1,0;
-		skill "MO_BALKYOUNG",1,0;
+		skill "MO_KITRANSLATION",1,SKILL_PERM;
+		skill "MO_BALKYOUNG",1,SKILL_PERM;
 		break;
 	case Job_Sage:
-		skill "SA_CREATECON",1,0;
-		skill "SA_ELEMENTWATER",1,0;
-		skill "SA_ELEMENTGROUND",1,0;
-		skill "SA_ELEMENTFIRE",1,0;
-		skill "SA_ELEMENTWIND",1,0;
+		skill "SA_CREATECON",1,SKILL_PERM;
+		skill "SA_ELEMENTWATER",1,SKILL_PERM;
+		skill "SA_ELEMENTGROUND",1,SKILL_PERM;
+		skill "SA_ELEMENTFIRE",1,SKILL_PERM;
+		skill "SA_ELEMENTWIND",1,SKILL_PERM;
 		break;
 	case Job_Rogue:
-		skill "RG_CLOSECONFINE",1,0;
+		skill "RG_CLOSECONFINE",1,SKILL_PERM;
 		break;
 	case Job_Alchemist:
-		skill "AM_BIOETHICS",1,0;
+		skill "AM_BIOETHICS",1,SKILL_PERM;
 		break;
 	case Job_Bard:
-		skill "BA_PANGVOICE",1,0;
+		skill "BA_PANGVOICE",1,SKILL_PERM;
 		break;
 	case Job_Dancer:
-		skill "DC_WINKCHARM",1,0;
+		skill "DC_WINKCHARM",1,SKILL_PERM;
 		break;
 	default:
 		break;

+ 1 - 1
npc/jobs/2-2a/Creator.txt

@@ -101,7 +101,7 @@ valkyrie,53,50,3	script	Biochemist#Valkyrie	122,{
 				mes "responsibility in using these";
 				mes "secrets for the right ends...";
 				next;
-				skill "AM_BIOETHICS",1,0;
+				skill "AM_BIOETHICS",1,SKILL_PERM;
 				mes "[Biochemist]";
 				mes "Open your eyes...";
 				mes "Now that you have";

+ 2 - 2
npc/jobs/valkyrie.txt

@@ -151,8 +151,8 @@ valkyrie,48,86,4	script	Valkyrie#	811,{
 			jobchange Job_Novice_High;
 			resetlvl(1);
 			set MISC_QUEST,MISC_QUEST | 1024; //<-reset Skill Reset Event
-			skill "NV_FIRSTAID",1,0;
-			skill "NV_TRICKDEAD",1,0;
+			skill "NV_FIRSTAID",1,SKILL_PERM;
+			skill "NV_TRICKDEAD",1,SKILL_PERM;
 			completequest 1000;
 			next;
 			mes "[Valkyrie]";

+ 1 - 1
npc/merchants/buying_shops.txt

@@ -225,7 +225,7 @@ alberta_in,58,52,4	script	Purchasing Team#Buying	59,{
 				mes "Okay, you're now approved to open the Bulk Buyer Shop.";
 				set Zeny,Zeny-10000;
 				getitem 6377,5; //Buy_Stall_Permit
-				skill "ALL_BUYING_STORE",1,4;
+				skill "ALL_BUYING_STORE",1,SKILL_PERM_GRANT;
 				next;
 				mes "[Mr. Hugh]";
 				mes "Currently, only normal items ^8C2121EXCEPT^000000 equipment, certain potions, and hand-crafted items can be purchased in bulk, but this can still be very beneficial to you, depending on how you use it.";

+ 2 - 2
npc/other/gympass.txt

@@ -79,7 +79,7 @@ payon,173,141,4	script	Ripped Cabus#GymPass	899,{
 				mes "training together like this.";
 				delitem 7776,1; //Max_Weight_Up_Scroll
 				set gympassmemory,.@add_carry;
-				skill "ALL_INCCARRY",.@add_carry,4;
+				skill "ALL_INCCARRY",.@add_carry,SKILL_PERM_GRANT;
 				close;
 			}
 			else {
@@ -135,7 +135,7 @@ payon,173,141,4	script	Ripped Cabus#GymPass	899,{
 			mes "muscles grew back,";
 			mes "just like that! Try not to";
 			mes "wimp out again, okay?";
-			skill "ALL_INCCARRY",gympassmemory,4;
+			skill "ALL_INCCARRY",gympassmemory,SKILL_PERM_GRANT;
 			close;
 		}
 		else {

+ 3 - 3
npc/pre-re/jobs/1-1/acolyte.txt

@@ -56,9 +56,9 @@ prt_church,184,41,4	script	Cleric#aco	60,{
 				mes "wish you luck on your";
 				mes "new life's journey.";
 				next;
-				skill "NV_TRICKDEAD",0,0;
+				skill "NV_TRICKDEAD",0,SKILL_PERM;
 				jobchange Job_Acolyte_High;
-				skill "AL_HOLYLIGHT",1,0;
+				skill "AL_HOLYLIGHT",1,SKILL_PERM;
 				mes "[Father Mareusis]";
 				mes "Now, venture forth and seek those who need your help. May God light your path.";
 				close;
@@ -181,7 +181,7 @@ prt_church,184,41,4	script	Cleric#aco	60,{
 		mes "[Father Mareusis]";
 		mes "I am proud to say that you are now ready to become an Acolyte!";
 		next;
-		skill "NV_TRICKDEAD",0,0;
+		skill "NV_TRICKDEAD",0,SKILL_PERM;
 		callfunc "Job_Change",Job_Acolyte;
 		callfunc "F_ClearJobVar";
 		if(checkquest(1001) != -1) {

+ 3 - 3
npc/pre-re/jobs/1-1/archer.txt

@@ -51,10 +51,10 @@ payon_in02,64,71,4	script	Archer Guildsman#archer	85,{
 			mes "need to say anything else.";
 			mes "I know you'll make a great Archer...";
 			next;
-			skill "NV_TRICKDEAD",0,0;
+			skill "NV_TRICKDEAD",0,SKILL_PERM;
 			jobchange Job_Archer_high;
-			skill "AC_MAKINGARROW",1,0;
-			skill "AC_CHARGEARROW",1,0;
+			skill "AC_MAKINGARROW",1,SKILL_PERM;
+			skill "AC_CHARGEARROW",1,SKILL_PERM;
 			mes "[Archer Guildsman]";
 			mes "Although there's no special";
 			mes "reward for you this time, I hope you understand. Take care of yourself.";

+ 2 - 2
npc/pre-re/jobs/1-1/mage.txt

@@ -46,9 +46,9 @@ geffen_in,164,124,4	script	Mage Guildsman	123,{
 				mes "[Mage Guildsman]";
 				mes "Well, since you have passed the Mage test once, I will not question your qualification. You want to have your magic skills back immediately, don't you?";
 				next;
-				skill "NV_TRICKDEAD",0,0;
+				skill "NV_TRICKDEAD",0,SKILL_PERM;
 				jobchange Job_Mage_High;
-				skill "MG_ENERGYCOAT",1,0;
+				skill "MG_ENERGYCOAT",1,SKILL_PERM;
 				mes "[Mage Guildsman]";
 				mes "Wow, for some reason, you look way better than you did before. Anyway, I believe you will do a better job being a Mage as well.";
 				close;

+ 4 - 4
npc/pre-re/jobs/1-1/merchant.txt

@@ -56,11 +56,11 @@ alberta_in,53,43,6	script	Merchant#mer	86,{
 			mes "[Chief Mahnsoo]";
 			mes "I guess it's destiny that we meet like this once more. Alright. Once again, let me change you into a Merchant!";
 			next;
-			skill "NV_TRICKDEAD",0,0;
+			skill "NV_TRICKDEAD",0,SKILL_PERM;
 			jobchange Job_Merchant_High;
-			skill "MC_CARTREVOLUTION",1,0;
-			skill "MC_CHANGECART",1,0;
-			skill "MC_LOUD",1,0;
+			skill "MC_CARTREVOLUTION",1,SKILL_PERM;
+			skill "MC_CHANGECART",1,SKILL_PERM;
+			skill "MC_LOUD",1,SKILL_PERM;
 			mes "[Chief Mahnsoo]";
 			mes "Ah~ How nostalgic. Just like old times! Alright, do your best!";
 			close;

+ 4 - 4
npc/pre-re/jobs/1-1/swordman.txt

@@ -59,11 +59,11 @@ izlude_in,74,172,4	script	Swordman#swd_1	119,{
 			mes "[Swordman]";
 			mes "Excellent! Let me promote you to a Swordman right away!";
 			next;
-			skill "NV_TRICKDEAD",0,0;
+			skill "NV_TRICKDEAD",0,SKILL_PERM;
 			jobchange Job_Swordman_High;
-			skill "SM_MOVINGRECOVERY",1,0;
-			skill "SM_FATALBLOW",1,0;
-			skill "SM_AUTOBERSERK",1,0;
+			skill "SM_MOVINGRECOVERY",1,SKILL_PERM;
+			skill "SM_FATALBLOW",1,SKILL_PERM;
+			skill "SM_AUTOBERSERK",1,SKILL_PERM;
 			mes "[Swordman]";
 			mes "Hmm... You look like a well-experienced Swordman. Still, I'm sure that you must train to improve your skills and gain strength!";
 			close;

+ 5 - 5
npc/pre-re/jobs/1-1/thief.txt

@@ -40,12 +40,12 @@ moc_prydb1,39,129,2	script	Thief Guide	69,{
 				mes "[Thief Guide]";
 				mes "Well, I got this feeling like you've been through a lifetime of fighting, so I'm promoting you to a Thief right this minute. I better give you tough guys what you want...";
 				next;
-				skill "NV_TRICKDEAD",0,0;
+				skill "NV_TRICKDEAD",0,SKILL_PERM;
 				jobchange Job_Thief_High;
-				skill "TF_SPRINKLESAND",1,0;
-				skill "TF_BACKSLIDING",1,0;
-				skill "TF_PICKSTONE",1,0;
-				skill "TF_THROWSTONE",1,0;
+				skill "TF_SPRINKLESAND",1,SKILL_PERM;
+				skill "TF_BACKSLIDING",1,SKILL_PERM;
+				skill "TF_PICKSTONE",1,SKILL_PERM;
+				skill "TF_THROWSTONE",1,SKILL_PERM;
 				mes "[Thief Guide]";
 				mes "Since you've become a Thief, live as a Thief. Now, go for it! Next~";
 				close;

+ 1 - 1
npc/pre-re/jobs/novice/novice.txt

@@ -753,7 +753,7 @@ new_1-2,83,111,3	script	Skill Tutor#nv	753,{
 			next;
 			mes "^3355FFYou have learned";
 			mes "the ^4A708BFirst Aid^3355FF skill.^000000";
-			skill "NV_FIRSTAID",1,0;
+			skill "NV_FIRSTAID",1,SKILL_PERM;
 			set NOV_SK,3;
 			set nov_get_item03,11;
 			next;

+ 1 - 1
npc/quests/skills/acolyte_skills.txt

@@ -79,7 +79,7 @@ prt_church,173,23,4	script	Cleric	79,{
 			delitem 727,1; //White_Jewel
 			delitem 991,1; //Crystal_Blue
 			delitem 2608,1; //Rosary
-			skill "AL_HOLYLIGHT",1,0;
+			skill "AL_HOLYLIGHT",1,SKILL_PERM;
 			mes "[Acolyte Klift]";
 			mes "You now know ' Holy Light '";
 			mes "May you use this skill only in the";

+ 1 - 1
npc/quests/skills/alchemist_skills.txt

@@ -1205,7 +1205,7 @@ lhz_in01,224,140,3	script	Kellasus#qsk_al	57,{
 		mes "" + strcharinfo(0) + ".";
 		next;
 		set bioeth,13;
-		skill "AM_BIOETHICS",1,0;
+		skill "AM_BIOETHICS",1,SKILL_PERM;
 		mes "[Kellasus]";
 		mes "Ah...";
 		mes "" + strcharinfo(0) + ".";

+ 2 - 2
npc/quests/skills/archer_skills.txt

@@ -51,7 +51,7 @@ moc_ruins,118,99,5	script	Roberto	88,{
 				delitem 906,41; //Tough_Scalelike_Stem
 				delitem 1019,13; //Wooden_Block
 				delitem 501,1; //Red_Potion
-				skill "AC_MAKINGARROW",1,0;
+				skill "AC_MAKINGARROW",1,SKILL_PERM;
 				mes "[Roberto]";
 				mes "No need to worry about arrows now.";
 				mes "Oh, and did you happen to see";
@@ -224,7 +224,7 @@ payon,103,63,3	script	Jason	88,3,3,{
 					delitem 962,10; //Tentacle
 					delitem 925,10; //Bill_Of_Birds
 					delitem 532,36; //Banana_Juice
-					skill "AC_CHARGEARROW",1,0;
+					skill "AC_CHARGEARROW",1,SKILL_PERM;
 					mes "[Jason]";
 					mes "Oh, works better than I expected!";
 					mes "Won't be needing to modify the bow!";

+ 5 - 5
npc/quests/skills/assassin_skills.txt

@@ -64,7 +64,7 @@ in_moc_16,14,27,5	script	Assassin#realman	884,{
 			mes "skill. Leave me now, and";
 			mes "always fight for the honor";
 			mes "of the Assassin Guild!";
-			skill "AS_VENOMKNIFE",1,0;
+			skill "AS_VENOMKNIFE",1,SKILL_PERM;
 			close;
 		}
 		else {
@@ -234,7 +234,7 @@ in_moc_16,14,27,5	script	Assassin#realman	884,{
 							mes "Knife pretty easily in battle.";
 							set ASSN_SK2,1;
 							set ASSN_SK,1;
-							skill "AS_VENOMKNIFE",1,0;
+							skill "AS_VENOMKNIFE",1,SKILL_PERM;
 							next;
 							mes "[Killtin]";
 							mes "Well, that's all I can";
@@ -358,7 +358,7 @@ in_moc_16,23,27,5	script	Assassin#realgirl	885,{
 			mes "Yes, that's it...!";
 			mes "Very well executed.";
 			mes "Good work, "+ strcharinfo(0) +".";
-			skill "AS_SONICACCEL",1,0;
+			skill "AS_SONICACCEL",1,SKILL_PERM;
 			set ASSN_SK,7;
 			next;
 			mes "[Esmille]";
@@ -455,7 +455,7 @@ in_moc_16,23,27,5	script	Assassin#realgirl	885,{
 		mes "Yes, that's it...!";
 		mes "Very well executed.";
 		mes "Good work, "+ strcharinfo(0) +".";
-		skill "AS_SONICACCEL",1,0;
+		skill "AS_SONICACCEL",1,SKILL_PERM;
 		set ASSN_SK,7;
 		next;
 		mes "[Esmille]";
@@ -538,7 +538,7 @@ in_moc_16,23,27,5	script	Assassin#realgirl	885,{
 			mes "Yes, that's it...!";
 			mes "Very well executed.";
 			mes "Good work, "+ strcharinfo(0) +".";
-			skill "AS_SONICACCEL",1,0;
+			skill "AS_SONICACCEL",1,SKILL_PERM;
 			set ASSN_SK,7;
 			next;
 			mes "[Esmille]";

+ 3 - 3
npc/quests/skills/bard_skills.txt

@@ -436,7 +436,7 @@ morocc_in,169,72,7	script	Spiteful-Looking Bard#bs	741,3,3,{
 				mes "confuse people and disrupt";
 				mes "control of their bodies. It's not a fatal skill, but it is effective";
 				mes "in mentally upsetting your enemy. Make very wise use of this skill.";
-				skill "BA_PANGVOICE",1,0;
+				skill "BA_PANGVOICE",1,SKILL_PERM;
 				close;
 			}
 		}
@@ -489,7 +489,7 @@ morocc_in,169,72,7	script	Spiteful-Looking Bard#bs	741,3,3,{
 				mes "spinning, head spinning...!''";
 				specialeffect2 EF_TALK_FROSTJOKE;
 				delitem 7277,1; //Munak_Doll
-				skill "BA_PANGVOICE",1,0;
+				skill "BA_PANGVOICE",1,SKILL_PERM;
 				set qskill_bard,9;
 				next;
 				mes "[Riott]";
@@ -571,7 +571,7 @@ morocc_in,169,72,7	script	Spiteful-Looking Bard#bs	741,3,3,{
 				mes "spinning, head spinning...!''";
 				specialeffect2 EF_TALK_FROSTJOKE;
 				delitem 574,5; //Egg
-				skill "BA_PANGVOICE",1,0;
+				skill "BA_PANGVOICE",1,SKILL_PERM;
 				set qskill_bard,9;
 				next;
 				mes "[Riott]";

+ 4 - 4
npc/quests/skills/blacksmith_skills.txt

@@ -31,7 +31,7 @@ geffen,178,72,3	script	Akkie#qsk_bs	726,{
 			mes "Dubious Salesmanship? You";
 			mes "should be able to use it now...";
 			set BLACK_SK,8;
-			skill "BS_UNFAIRLYTRICK",1,0;
+			skill "BS_UNFAIRLYTRICK",1,SKILL_PERM;
 			close;
 		}
 		else if (BLACK_SK == 8) {
@@ -371,7 +371,7 @@ geffen,178,72,3	script	Akkie#qsk_bs	726,{
 				mes "this... Isn't it easy? Of course, you can only use this in battle to";
 				mes "reduce Mammonite's zeny cost...";
 				set BLACK_SK,7;
-				skill "BS_UNFAIRLYTRICK",1,0;
+				skill "BS_UNFAIRLYTRICK",1,SKILL_PERM;
 				next;
 				mes "[Akkie]";
 				mes "Also, 90% of the cost of";
@@ -490,7 +490,7 @@ geffen,172,52,1	script	Goodman#qsk_bs	826,{
 			mes "just looking at me should";
 			mes "jog the memories you require";
 			mes "to use this skill once again~";
-			skill "BS_GREED",1,0;
+			skill "BS_GREED",1,SKILL_PERM;
 			set BLACK_SK2,3;
 			close;
 		}
@@ -608,7 +608,7 @@ geffen,172,52,1	script	Goodman#qsk_bs	826,{
 					mes "is different than its name.";
 					mes "Don't forget that, and I hope";
 					mes "you craft true masterpieces.";
-					skill "BS_GREED",1,0;
+					skill "BS_GREED",1,SKILL_PERM;
 					set BLACK_SK2,2;
 					close;
 				}

+ 2 - 2
npc/quests/skills/crusader_skills.txt

@@ -24,7 +24,7 @@ geffen,110,117,3	script	Ford#11	752,{
 			mes "the Shrink skill, so I'll teach";
 			mes "it to you real quick... There!";
 			set CRUS_SK,10;
-			skill "CR_SHRINK",1,0;
+			skill "CR_SHRINK",1,SKILL_PERM;
 			close;
 		}
 
@@ -519,7 +519,7 @@ gef_fild13,297,242,3	script	Soldier#277	751,{
 				mes "and protect the weak. Ah, and";
 				mes "if I learn any new skills, I'll be sure to teach you right away.";
 				set CRUS_SK,8;
-				skill "CR_SHRINK",1,0;
+				skill "CR_SHRINK",1,SKILL_PERM;
 				close;
 			}
 			mes "^3355FFYou carefully pour the";

+ 2 - 2
npc/quests/skills/dancer_skills.txt

@@ -317,7 +317,7 @@ comodo,204,172,5	script	Canell#qsk_dan01	724,{
 				mes "the Charming Wink skill.^000000";
 				set DANCER_SK,9;
 				specialeffect2 EF_ABSORBSPIRITS;
-				skill "DC_WINKCHARM",1,0;
+				skill "DC_WINKCHARM",1,SKILL_PERM;
 				close;
 			}
 		}
@@ -360,7 +360,7 @@ comodo,204,172,5	script	Canell#qsk_dan01	724,{
 			mes "^3355FFYou have learned the";
 			mes "Charming Wink skill.^000000";
 			specialeffect2 EF_ABSORBSPIRITS;
-			skill "DC_WINKCHARM",1,0;
+			skill "DC_WINKCHARM",1,SKILL_PERM;
 			close;
 		}
 	}

+ 2 - 2
npc/quests/skills/hunter_skills.txt

@@ -85,7 +85,7 @@ pay_arche,109,169,3	script	Arpesto	712,{
 				mes "You should be able to use the";
 				mes "Phantasmic Arrow skill now.";
 				mes "Travel safely now, you hear?";
-				skill "HT_PHANTASMIC",1,0;
+				skill "HT_PHANTASMIC",1,SKILL_PERM;
 				close;
 			}
 		}
@@ -213,7 +213,7 @@ pay_arche,109,169,3	script	Arpesto	712,{
 				delitem 7115,5; //Harpy's_Feather
 				delitem 537,30; //Pet_Food
 				set qskill_hunter,100;
-				skill "HT_PHANTASMIC",1,0;
+				skill "HT_PHANTASMIC",1,SKILL_PERM;
 				close;
 			} else {
 				mes "[Arpesto]";

+ 2 - 2
npc/quests/skills/knight_skills.txt

@@ -62,7 +62,7 @@ prt_in,85,99,3	script	Knight#kabuto	734,{
 			mes "is greater risk to yourself.";
 			mes "For now, it would be best to practice this skill on your own.";
 			set KNGT_SK,10;
-			skill "KN_CHARGEATK",1,0;
+			skill "KN_CHARGEATK",1,SKILL_PERM;
 			next;
 			mes "[Essofeit]";
 			mes "Good luck on your";
@@ -112,7 +112,7 @@ prt_in,85,99,3	script	Knight#kabuto	734,{
 			mes "is greater risk to yourself.";
 			mes "For now, it would be best to practice this skill on your own.";
 			set KNGT_SK,9;
-			skill "KN_CHARGEATK",1,0;
+			skill "KN_CHARGEATK",1,SKILL_PERM;
 			next;
 			mes "[Essofeit]";
 			mes "I hope you make good";

+ 1 - 1
npc/quests/skills/mage_skills.txt

@@ -119,7 +119,7 @@ geffen_in,151,119,4	script	Great Wizard	64,{
 			delitem 730,1; //Crystal_Jewel
 			delitem 935,5; //Shell
 			delitem 943,1; //Solid_Shell
-			skill "MG_ENERGYCOAT",1,0;
+			skill "MG_ENERGYCOAT",1,SKILL_PERM;
 			mes "[BLIZZARDRISS]";
 			mes ". . . . .";
 			mes "It is done. . .";

+ 5 - 5
npc/quests/skills/merchant_skills.txt

@@ -79,7 +79,7 @@ alberta,83,96,5	script	Necko	98,7,7,{
 				delitem 722,7; //Scarlet_Jewel
 				delitem 532,1; //Banana_Juice
 				delitem 921,50; //Mushroom_Spore
-				skill "MC_LOUD",1,0;
+				skill "MC_LOUD",1,SKILL_PERM;
 				mes "[Necko]";
 				mes "You have learned Crazy Uproar!!";
 				mes "Shout as much as you wish!";
@@ -223,7 +223,7 @@ alberta,119,221,6	script	Charlron	107,{
 					delitem 1019,50; //Wooden_Block
 					delitem 998,10; //Iron
 					delitem 919,20; //Animal's_Skin
-					skill "MC_CHANGECART",1,0;
+					skill "MC_CHANGECART",1,SKILL_PERM;
 					mes "[Charlron]";
 					mes "Congratulations.";
 					mes "You can choose a cart";
@@ -376,7 +376,7 @@ alberta,119,221,6	script	Charlron	107,{
 						delitem 938,30; //Sticky_Mucus
 						delitem 601,20; //Wing_Of_Fly
 						delitem 962,5; //Tentacle
-						skill "MC_CARTREVOLUTION",1,0;
+						skill "MC_CARTREVOLUTION",1,SKILL_PERM;
 						mes "Now you can use Cart Revolution";
 						mes "I expect you to make merchants";
 						mes "famous by using this amazing";
@@ -448,7 +448,7 @@ alberta,119,221,6	script	Charlron	107,{
 						delitem 938,25; //Sticky_Mucus
 						delitem 601,15; //Wing_Of_Fly
 						delitem 962,5; //Tentacle
-						skill "MC_CARTREVOLUTION",1,0;
+						skill "MC_CARTREVOLUTION",1,SKILL_PERM;
 						mes "Now you can use Cart Revolution";
 						mes "I expect you to make merchants";
 						mes "famous by using its amazing";
@@ -521,7 +521,7 @@ alberta,119,221,6	script	Charlron	107,{
 						delitem 938,32; //Sticky_Mucus
 						delitem 601,23; //Wing_Of_Fly
 						delitem 962,6; //Tentacle
-						skill "MC_CARTREVOLUTION",1,0;
+						skill "MC_CARTREVOLUTION",1,SKILL_PERM;
 						mes "Now you can use Cart Revolution";
 						mes "I expect you to make merchants";
 						mes "famous by using its amazing";

+ 4 - 4
npc/quests/skills/monk_skills.txt

@@ -59,8 +59,8 @@ prt_monk,270,198,3	script	Apprentice Monk#qsk_mo	753,{
 			mes "tap-tap-tap-tap-tap-tap-tap-tap- tap-tap-tap-tap-tap-tap-tap-tap-";
 			mes "tap-tap-tap-tap-tap-tap* *POKE*^000000";
 			set MONK_SK,7;
-			skill "MO_KITRANSLATION",1,0;
-			skill "MO_BALKYOUNG",1,0;
+			skill "MO_KITRANSLATION",1,SKILL_PERM;
+			skill "MO_BALKYOUNG",1,SKILL_PERM;
 			next;
 			mes "[Monk]";
 			mes "It is done. Please";
@@ -147,7 +147,7 @@ prt_monk,270,198,3	script	Apprentice Monk#qsk_mo	753,{
 				mes "times, but it is a means to an";
 				mes "end, and not an end in itself.";
 				set MONK_SK,3;
-				skill "MO_KITRANSLATION",1,0;
+				skill "MO_KITRANSLATION",1,SKILL_PERM;
 				next;
 				mes "[Monk]";
 				mes "Our skills should not be";
@@ -252,7 +252,7 @@ monk_test,316,69,5	script	Monk#qsk_mo	823,{
 			mes "to do it since you've learned";
 			mes "Spiritual Endowment.";
 			set MONK_SK,6;
-			skill "MO_BALKYOUNG",1,0;
+			skill "MO_BALKYOUNG",1,SKILL_PERM;
 			next;
 			mes "[Monk]";
 			mes "Well, we've completed";

+ 2 - 2
npc/quests/skills/novice_skills.txt

@@ -137,7 +137,7 @@ prt_in,234,133,4	script	Nami	66,{
 						mes "and combine them together and. . .";
 						mes "Presto !!";
 						next;
-						skill "NV_FIRSTAID",1,0;
+						skill "NV_FIRSTAID",1,SKILL_PERM;
 						set skill_nov,3;
 						mes "[Nami]";
 						mes "Yes yes, that's right!";
@@ -314,7 +314,7 @@ prt_in,73,87,4	script	Chivalry Member	65,{
 				mes "Okay okay, See you around ! ! !";
 				set skill_nov,6;
 				delitem 7039,1; //Novice_Nametag
-				skill "NV_TRICKDEAD",1,0;
+				skill "NV_TRICKDEAD",1,SKILL_PERM;
 				close;
 			}
 			mes "[Bulma]";

+ 2 - 2
npc/quests/skills/priest_skills.txt

@@ -40,7 +40,7 @@ prt_church,111,112,1	script	Sister Linus	79,{
 				mes "with your light. Give us the";
 				mes "strength to walk the path of";
 				mes "love and sacrifice. Redemptio!^000000";
-				skill "PR_REDEMPTIO",1,0;
+				skill "PR_REDEMPTIO",1,SKILL_PERM;
 				next;
 				mes "[Sister Linus]";
 				mes "There...";
@@ -310,7 +310,7 @@ prt_church,111,112,1	script	Sister Linus	79,{
 						delitem 717,20; //Blue_Gemstone
 						delitem 523,30; //Holy_Water
 						set PRIEST_SK,100;
-						skill "PR_REDEMPTIO",1,0;
+						skill "PR_REDEMPTIO",1,SKILL_PERM;
 						close;
 					}
 					else {

+ 3 - 3
npc/quests/skills/rogue_skills.txt

@@ -140,7 +140,7 @@ in_rogue,355,179,0	script	Haijara Greg#rogueguild	46,{
 			mes "eh? Then I will teach you the";
 			mes "Close Confine skill once again.";
 			specialeffect2 EF_LIGHTSPHERE;
-			skill "RG_CLOSECONFINE",1,0;
+			skill "RG_CLOSECONFINE",1,SKILL_PERM;
 			set ROG_SK,13;
 			close;
 		}
@@ -493,7 +493,7 @@ in_rogue,355,179,0	script	Haijara Greg#rogueguild	46,{
 					mes "more about this skill through";
 					mes "practice, and that you become as great a legend as Chae Takbae.";
 					set ROG_SK,12;
-					skill "RG_CLOSECONFINE",1,0;
+					skill "RG_CLOSECONFINE",1,SKILL_PERM;
 					specialeffect2 EF_LIGHTSPHERE;
 					close;
 				}
@@ -505,7 +505,7 @@ in_rogue,355,179,0	script	Haijara Greg#rogueguild	46,{
 				mes "practice. Good luck, and";
 				mes "thanks again for your help.";
 				set ROG_SK,12;
-				skill "RG_CLOSECONFINE",1,0;
+				skill "RG_CLOSECONFINE",1,SKILL_PERM;
 				specialeffect2 EF_LIGHTSPHERE;
 				close;
 			}

+ 6 - 6
npc/quests/skills/sage_skills.txt

@@ -117,9 +117,9 @@ yuno_in03,176,24,3	script	Mischna	755,{
 					mes "learned the "+.@Skill$[.@i]+" Elemental";
 					mes "Change skill and the Elemental";
 					mes "Converter Creation skill.^000000";
-					skill .@Skill[.@i],1,0;
+					skill .@Skill[.@i],1,SKILL_PERM;
 					if(.@Convert == 0)
-						skill "SA_CREATECON",1,0;
+						skill "SA_CREATECON",1,SKILL_PERM;
 					next;
 					break;
 				}
@@ -146,7 +146,7 @@ yuno_in03,176,24,3	script	Mischna	755,{
 			mes "the Elemental Coverter";
 			mes "Creation skill and are";
 			mes "able to use it again.^000000";
-			skill "SA_CREATECON",1,0;
+			skill "SA_CREATECON",1,SKILL_PERM;
 			next;
 		}
 		mes "[Mishuna]";
@@ -316,7 +316,7 @@ yuno_in03,176,24,3	script	Mischna	755,{
 			delitem 946,10; // Snail's_Shell
 			delitem 7433,4; // Blank_Scroll
 			set SAG_SK,2;
-			skill "SA_CREATECON",1,0;
+			skill "SA_CREATECON",1,SKILL_PERM;
 			next;
 			mes "[Mishuna]";
 			mes "Wow, "+strcharinfo(0)+"!";
@@ -337,7 +337,7 @@ yuno_in03,176,24,3	script	Mischna	755,{
 	}
 	else if(SAG_SK == 2) {
 		if(getskilllv("SA_CREATECON") == 0) {
-			skill "SA_CREATECON",1,0;
+			skill "SA_CREATECON",1,SKILL_PERM;
 			mes "- I recalled ^ff0000Elemental Converter Creation skill^000000 While I talk to Mishuna! -";
 			next;
 		}
@@ -389,7 +389,7 @@ yuno_in03,176,24,3	script	Mischna	755,{
 	}
 	else if(SAG_SK == 10 || SAG_SK == 20 || SAG_SK == 30 || SAG_SK == 40) {
 		if(getskilllv("SA_CREATECON") == 0) {
-			skill "SA_CREATECON",1,0;
+			skill "SA_CREATECON",1,SKILL_PERM;
 			mes "- I recalled ^ff0000Elemental Converter Creation skill^000000 While I talk to Mishuna! -";
 			next;
 		}

+ 3 - 3
npc/quests/skills/swordman_skills.txt

@@ -43,7 +43,7 @@
 				next;
 				delitem 713,200; //Empty_Bottle
 				delitem 1058,1; //Wing_Of_Moth
-				skill "SM_MOVINGRECOVERY",1,0;
+				skill "SM_MOVINGRECOVERY",1,SKILL_PERM;
 				mes "[De Thomas]";
 				mes "There you go!";
 				mes "Try it yourself.";
@@ -176,7 +176,7 @@ prt_in,75,88,5	script	Leon Von Frich	85,3,3,{
 				delitem 532,1; //Banana_Juice
 				delitem 962,30; //Tentacle
 				delitem 526,5; //Royal_Jelly
-				skill "SM_FATALBLOW",1,0;
+				skill "SM_FATALBLOW",1,SKILL_PERM;
 				mes "[Leon]";
 				mes "Success!";
 				mes "Go use your new skill to its full potential.";
@@ -342,7 +342,7 @@ prt_in,94,57,3	script	Juan	85,4,4,{
 				delitem 958,10; //Horrendous_Mouth
 				delitem 957,10; //Decayed_Nail
 				delitem 518,10; //Honey
-				skill "SM_AUTOBERSERK",1,0;
+				skill "SM_AUTOBERSERK",1,SKILL_PERM;
 				mes "[Juan]";
 				mes "You have just become a swordsman";
 				mes "that can use Auto Berserk.";

+ 4 - 4
npc/quests/skills/thief_skills.txt

@@ -148,7 +148,7 @@ moc_prydb1,154,128,4	script	Alcouskou	118,{
 				mes "I hope that this skill will";
 				mes "aid you in the future. -";
 				delitem 7042,1; //Leather_Bag_Of_Infinity
-				skill "TF_SPRINKLESAND",1,0;
+				skill "TF_SPRINKLESAND",1,SKILL_PERM;
 				close;
 			}
 	case 2:
@@ -170,7 +170,7 @@ moc_prydb1,154,128,4	script	Alcouskou	118,{
 			mes "I am sure you can increase";
 			mes "your skill on your own.";
 			delitem 940,20; //Grasshopper's_Leg
-			skill "TF_BACKSLIDING",1,0;
+			skill "TF_BACKSLIDING",1,SKILL_PERM;
 			close;
 		}
 		mes "[Alcouskou]";
@@ -262,7 +262,7 @@ moc_prydb1,154,128,4	script	Alcouskou	118,{
 			delitem 912,1; //Zargon
 			delitem 948,1; //Bear's_Foot
 			delitem 908,5; //Spawn
-			skill "TF_PICKSTONE",1,0;
+			skill "TF_PICKSTONE",1,SKILL_PERM;
 			close;
 		}
 		mes "[Alcouskou]";
@@ -344,7 +344,7 @@ moc_prydb1,154,128,4	script	Alcouskou	118,{
 			mes "I wish you luck!";
 			delitem 910,2; //Garlet
 			delitem 911,2; //Scell
-			skill "TF_THROWSTONE",1,0;
+			skill "TF_THROWSTONE",1,SKILL_PERM;
 			close;
 		}
 		mes "[Alcouskou]";

+ 2 - 2
npc/quests/skills/wizard_skills.txt

@@ -42,7 +42,7 @@ gef_tower,115,36,4	script	Meow#q_wiz	876,{
 				mes "Meow can administer another";
 				mes "vicious beating to you.";
 				specialeffect2 EF_ABSORBSPIRITS;
-				skill "WZ_SIGHTBLASTER",1,0;
+				skill "WZ_SIGHTBLASTER",1,SKILL_PERM;
 				next;
 				mes "[Meow]";
 				mes "Remember now?";
@@ -295,7 +295,7 @@ gef_tower,115,36,4	script	Meow#q_wiz	876,{
 			delitem 990,10; //Boody_Red
 			delitem 992,10; //Wind_Of_Verdure
 			set WIZ_SK,100;
-			skill "WZ_SIGHTBLASTER",1,0;
+			skill "WZ_SIGHTBLASTER",1,SKILL_PERM;
 			next;
 			mes "[Meow]";
 			mes "I'm so tired from all of";

+ 2 - 2
npc/re/jobs/1-1/acolyte.txt

@@ -39,9 +39,9 @@ prt_church,184,41,4	script	Cleric#aco	60,{
 				mes "wish you luck on your";
 				mes "new life's journey.";
 				next;
-				skill "NV_TRICKDEAD",0,0;
+				skill "NV_TRICKDEAD",0,SKILL_PERM;
 				jobchange Job_Acolyte_High;
-				skill "AL_HOLYLIGHT",1,0;
+				skill "AL_HOLYLIGHT",1,SKILL_PERM;
 				mes "[Father Mareusis]";
 				mes "Now, venture forth and seek those who need your help. May God light your path.";
 				close;

+ 3 - 3
npc/re/jobs/1-1/archer.txt

@@ -39,10 +39,10 @@ payon_in02,64,71,4	script	Archer Guildsman#archer	85,{
 			mes "need to say anything else.";
 			mes "I know you'll make a great Archer...";
 			next;
-			skill "NV_TRICKDEAD",0,0;
+			skill "NV_TRICKDEAD",0,SKILL_PERM;
 			jobchange Job_Archer_high;
-			skill "AC_MAKINGARROW",1,0;
-			skill "AC_CHARGEARROW",1,0;
+			skill "AC_MAKINGARROW",1,SKILL_PERM;
+			skill "AC_CHARGEARROW",1,SKILL_PERM;
 			mes "[Archer Guildsman]";
 			mes "Although there's no special";
 			mes "reward for you this time, I hope you understand. Take care of yourself.";

+ 2 - 2
npc/re/jobs/1-1/mage.txt

@@ -33,9 +33,9 @@ geffen_in,164,124,4	script	Mage Guildsman#mage	123,{
 				mes "[Mage Guildsman]";
 				mes "Well, since you have passed the Mage test once, I will not question your qualification. You want to have your magic skills back immediately, don't you?";
 				next;
-				skill "NV_TRICKDEAD",0,0;
+				skill "NV_TRICKDEAD",0,SKILL_PERM;
 				jobchange Job_Mage_High;
-				skill "MG_ENERGYCOAT",1,0;
+				skill "MG_ENERGYCOAT",1,SKILL_PERM;
 				mes "[Mage Guildsman]";
 				mes "Wow, for some reason, you look way better than you did before. Anyway, I believe you will do a better job being a Mage as well.";
 				close;

+ 4 - 4
npc/re/jobs/1-1/merchant.txt

@@ -37,11 +37,11 @@ alberta_in,53,43,6	script	Merchant#mer	86,{
 			mes "[Chief Mahnsoo]";
 			mes "I guess it's destiny that we meet like this once more. Alright. Once again, let me change you into a Merchant!";
 			next;
-			skill "NV_TRICKDEAD",0,0;
+			skill "NV_TRICKDEAD",0,SKILL_PERM;
 			jobchange Job_Merchant_High;
-			skill "MC_CARTREVOLUTION",1,0;
-			skill "MC_CHANGECART",1,0;
-			skill "MC_LOUD",1,0;
+			skill "MC_CARTREVOLUTION",1,SKILL_PERM;
+			skill "MC_CHANGECART",1,SKILL_PERM;
+			skill "MC_LOUD",1,SKILL_PERM;
 			mes "[Chief Mahnsoo]";
 			mes "Ah~ How nostalgic. Just like old times! Alright, do your best!";
 			close;

+ 4 - 4
npc/re/jobs/1-1/swordman.txt

@@ -34,11 +34,11 @@ izlude_in,74,172,4	script	Swordman#swd	119,{
 			mes "[Swordman]";
 			mes "Excellent! Let me promote you to a Swordman right away!";
 			next;
-			skill "NV_TRICKDEAD",0,0;
+			skill "NV_TRICKDEAD",0,SKILL_PERM;
 			jobchange Job_Swordman_High;
-			skill "SM_MOVINGRECOVERY",1,0;
-			skill "SM_FATALBLOW",1,0;
-			skill "SM_AUTOBERSERK",1,0;
+			skill "SM_MOVINGRECOVERY",1,SKILL_PERM;
+			skill "SM_FATALBLOW",1,SKILL_PERM;
+			skill "SM_AUTOBERSERK",1,SKILL_PERM;
 			mes "[Swordman]";
 			mes "Hmm... You look like a well-experienced Swordman. Still, I'm sure that you must train to improve your skills and gain strength!";
 			close;

+ 5 - 5
npc/re/jobs/1-1/thief.txt

@@ -30,12 +30,12 @@ moc_prydb1,39,129,2	script	Thief Guide#thief	69,{
 				mes "[Thief Guide]";
 				mes "Well, I got this feeling like you've been through a lifetime of fighting, so I'm promoting you to a Thief right this minute. I better give you tough guys what you want...";
 				next;
-				skill "NV_TRICKDEAD",0,0;
+				skill "NV_TRICKDEAD",0,SKILL_PERM;
 				jobchange Job_Thief_High;
-				skill "TF_SPRINKLESAND",1,0;
-				skill "TF_BACKSLIDING",1,0;
-				skill "TF_PICKSTONE",1,0;
-				skill "TF_THROWSTONE",1,0;
+				skill "TF_SPRINKLESAND",1,SKILL_PERM;
+				skill "TF_BACKSLIDING",1,SKILL_PERM;
+				skill "TF_PICKSTONE",1,SKILL_PERM;
+				skill "TF_THROWSTONE",1,SKILL_PERM;
 				mes "[Thief Guide]";
 				mes "Since you've become a Thief, live as a Thief. Now, go for it! Next~";
 				close;

+ 1 - 1
npc/re/jobs/novice/novice.txt

@@ -539,7 +539,7 @@ new_5-2,100,105,3	duplicate(NvBradeA)	Brade#nv5a	733
 			mes "You can open the Skill Window by pressing the ^4d4dffskill^000000 button in the Basic Window.";
 			mes "Hot Key is ^4d4dffALT + S^000000.";
 			mes "Why don't you try the skill?";
-			skill "NV_FIRSTAID",1,0;
+			skill "NV_FIRSTAID",1,SKILL_PERM;
 			set skill_nov,3; // Has future uses - do not remove! [Euphy]
 			set job_novice_q,6;
 			next;

+ 6 - 0
sql-files/upgrades/upgrade_20150211_skillset.sql

@@ -0,0 +1,6 @@
+-- Resetting all `lv` of skills that has `flag` >= 3 (the skill that its `learned_lv` has been changed by script or special case by `learned_lv` + SKILL_FLAG_REPLACED_LV_0)
+-- If there's ALL_INCCARRY and ALL_BUYING_STORE, set the `flag` to SKILL_FLAG_PERM_GRANTED (new value is 3), those are exclusive skills given in our official scripts!
+
+UPDATE `skill` SET `lv` = `flag` - 3 WHERE `flag` >= 3;
+UPDATE `skill` SET `flag` = 0 WHERE `flag` >= 3;
+UPDATE `skill` SET `flag` = 3 WHERE `id` = 681 OR `id` = 2535;

+ 25 - 26
src/char/char.c

@@ -434,12 +434,6 @@ int char_mmo_char_tosql(uint32 char_id, struct mmo_charstatus* p){
 		strcat(save_status, " memo");
 	}
 
-	//FIXME: is this neccessary? [ultramage]
-	for(i=0;i<MAX_SKILL;i++)
-		if ((p->skill[i].lv != 0) && (p->skill[i].id == 0))
-			p->skill[i].id = i; // Fix skill tree
-
-
 	//skills
 	if( memcmp(p->skill, cp->skill, sizeof(p->skill)) )
 	{
@@ -944,7 +938,6 @@ int char_mmo_chars_fromsql(struct char_session_data* sd, uint8* buf) {
 //=====================================================================================================
 int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_everything) {
 	int i,j;
-	char t_msg[128] = "";
 	struct mmo_charstatus* cp;
 	StringBuf buf;
 	SqlStmt* stmt;
@@ -954,11 +947,13 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
 	struct point tmp_point;
 	struct item tmp_item;
 	struct s_skill tmp_skill;
+	uint16 skill_count = 0;
 	struct s_friend tmp_friend;
 #ifdef HOTKEY_SAVING
 	struct hotkey tmp_hotkey;
 	int hotkey_num;
 #endif
+	StringBuf msg_buf;
 
 	memset(p, 0, sizeof(struct mmo_charstatus));
 
@@ -1065,11 +1060,13 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
 		p->save_point.y = charserv_config.default_map_y;
 	}
 
-	strcat(t_msg, " status");
+	StringBuf_Init(&msg_buf);
+	StringBuf_AppendStr(&msg_buf, " status");
 
 	if (!load_everything) // For quick selection of data when displaying the char menu
 	{
 		SqlStmt_Free(stmt);
+		StringBuf_Destroy(&msg_buf);
 		return 1;
 	}
 
@@ -1088,7 +1085,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
 		tmp_point.map = mapindex_name2id(point_map);
 		memcpy(&p->memo_point[i], &tmp_point, sizeof(tmp_point));
 	}
-	strcat(t_msg, " memo");
+	StringBuf_AppendStr(&msg_buf, " memo");
 
 	//read inventory
 	//`inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, `expire_time`, `favorite`, `unique_id`)
@@ -1120,7 +1117,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
 	for( i = 0; i < MAX_INVENTORY && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i )
 		memcpy(&p->inventory[i], &tmp_item, sizeof(tmp_item));
 
-	strcat(t_msg, " inventory");
+	StringBuf_AppendStr(&msg_buf, " inventory");
 
 	//read cart
 	//`cart_inventory` (`id`,`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`, `card0`, `card1`, `card2`, `card3`, expire_time`, `unique_id`)
@@ -1150,33 +1147,34 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
 
 	for( i = 0; i < MAX_CART && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i )
 		memcpy(&p->cart[i], &tmp_item, sizeof(tmp_item));
-	strcat(t_msg, " cart");
+	StringBuf_AppendStr(&msg_buf, " cart");
 
 	//read storage
 	storage_fromsql(p->account_id, &p->storage);
-	strcat(t_msg, " storage");
+	StringBuf_AppendStr(&msg_buf, " storage");
 
 	//read skill
 	//`skill` (`char_id`, `id`, `lv`)
 	if( SQL_ERROR == SqlStmt_Prepare(stmt, "SELECT `id`, `lv`,`flag` FROM `%s` WHERE `char_id`=? LIMIT %d", schema_config.skill_db, MAX_SKILL)
-	||	SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
-	||	SQL_ERROR == SqlStmt_Execute(stmt)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_USHORT, &tmp_skill.id  , 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_UCHAR , &tmp_skill.lv  , 0, NULL, NULL)
-	||	SQL_ERROR == SqlStmt_BindColumn(stmt, 2, SQLDT_UCHAR , &tmp_skill.flag, 0, NULL, NULL) )
+		||	SQL_ERROR == SqlStmt_BindParam(stmt, 0, SQLDT_INT, &char_id, 0)
+		||	SQL_ERROR == SqlStmt_Execute(stmt)
+		||	SQL_ERROR == SqlStmt_BindColumn(stmt, 0, SQLDT_UINT16, &tmp_skill.id  , 0, NULL, NULL)
+		||	SQL_ERROR == SqlStmt_BindColumn(stmt, 1, SQLDT_UINT8 , &tmp_skill.lv  , 0, NULL, NULL)
+		||	SQL_ERROR == SqlStmt_BindColumn(stmt, 2, SQLDT_UINT8 , &tmp_skill.flag, 0, NULL, NULL) )
 		SqlStmt_ShowDebug(stmt);
 
 	if( tmp_skill.flag != SKILL_FLAG_PERM_GRANTED )
 		tmp_skill.flag = SKILL_FLAG_PERMANENT;
 
-	for( i = 0; i < MAX_SKILL && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i )
-	{
-		if( tmp_skill.id < ARRAYLENGTH(p->skill) )
-			memcpy(&p->skill[tmp_skill.id], &tmp_skill, sizeof(tmp_skill));
+	for( i = 0; skill_count < MAX_SKILL && SQL_SUCCESS == SqlStmt_NextRow(stmt); i++ ) {
+		if( tmp_skill.id > 0 && tmp_skill.id < MAX_SKILL_ID ) {
+			memcpy(&p->skill[i], &tmp_skill, sizeof(tmp_skill));
+			skill_count++;
+		}
 		else
 			ShowWarning("mmo_char_fromsql: ignoring invalid skill (id=%u,lv=%u) of character %s (AID=%d,CID=%d)\n", tmp_skill.id, tmp_skill.lv, p->name, p->account_id, p->char_id);
 	}
-	strcat(t_msg, " skills");
+	StringBuf_Printf(&msg_buf, " %d skills", skill_count);
 
 	//read friends
 	//`friends` (`char_id`, `friend_account`, `friend_id`)
@@ -1190,7 +1188,7 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
 
 	for( i = 0; i < MAX_FRIENDS && SQL_SUCCESS == SqlStmt_NextRow(stmt); ++i )
 		memcpy(&p->friends[i], &tmp_friend, sizeof(tmp_friend));
-	strcat(t_msg, " friends");
+	StringBuf_AppendStr(&msg_buf, " friends");
 
 #ifdef HOTKEY_SAVING
 	//read hotkeys
@@ -1211,20 +1209,21 @@ int char_mmo_char_fromsql(uint32 char_id, struct mmo_charstatus* p, bool load_ev
 		else
 			ShowWarning("mmo_char_fromsql: ignoring invalid hotkey (hotkey=%d,type=%u,id=%u,lv=%u) of character %s (AID=%d,CID=%d)\n", hotkey_num, tmp_hotkey.type, tmp_hotkey.id, tmp_hotkey.lv, p->name, p->account_id, p->char_id);
 	}
-	strcat(t_msg, " hotkeys");
+	StringBuf_AppendStr(&msg_buf, " hotkeys");
 #endif
 
 	/* Mercenary Owner DataBase */
 	mercenary_owner_fromsql(char_id, p);
-	strcat(t_msg, " mercenary");
+	StringBuf_AppendStr(&msg_buf, " mercenary");
 
 
-	if (charserv_config.save_log) ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, t_msg);	//ok. all data load successfuly!
+	if (charserv_config.save_log) ShowInfo("Loaded char (%d - %s): %s\n", char_id, p->name, StringBuf_Value(&msg_buf));	//ok. all data load successfuly!
 	SqlStmt_Free(stmt);
 	StringBuf_Destroy(&buf);
 
 	cp = idb_ensure(char_db_, char_id, char_create_charstatus);
 	memcpy(cp, p, sizeof(struct mmo_charstatus));
+	StringBuf_Destroy(&msg_buf);
 	return 1;
 }
 

+ 10 - 8
src/common/mmo.h

@@ -55,7 +55,7 @@
 #define MAX_BANK_ZENY SINT32_MAX ///Max zeny in Bank
 #define MAX_FAME 1000000000 ///Max fame points
 #define MAX_CART 100 ///Maximum item in cart
-#define MAX_SKILL 5020 ///Maximum skill data
+#define MAX_SKILL 1200 ///Maximum skill can be hold by Player, Homunculus, & Mercenary (skill list) AND skill_db limit
 #define GLOBAL_REG_NUM 256 ///Max permanent character variables per char
 #define ACCOUNT_REG_NUM 64 ///Max permanent local account variables per account
 #define ACCOUNT_REG2_NUM 16 ///Max permanent global account variables per account
@@ -224,10 +224,11 @@ enum e_skill_flag
 	SKILL_FLAG_PERMANENT,
 	SKILL_FLAG_TEMPORARY,
 	SKILL_FLAG_PLAGIARIZED,
-	SKILL_FLAG_REPLACED_LV_0, // temporary skill overshadowing permanent skill of level 'N - SKILL_FLAG_REPLACED_LV_0',
-	SKILL_FLAG_PERM_GRANTED, // permanent, granted through someway e.g. script
-	SKILL_FLAG_TMP_COMBO, //@FIXME for homon combo atm
-	//...
+	SKILL_FLAG_PERM_GRANTED, // Permanent, granted through someway e.g. script
+	SKILL_FLAG_TMP_COMBO, //@FIXME for homunculus combo atm
+
+	//! NOTE: This flag be the last flag, and don't change the value if not needed!
+	SKILL_FLAG_REPLACED_LV_0 = 10, // temporary skill overshadowing permanent skill of level 'N - SKILL_FLAG_REPLACED_LV_0',
 };
 
 enum e_mmo_charstatus_opt {
@@ -237,9 +238,9 @@ enum e_mmo_charstatus_opt {
 };
 
 struct s_skill {
-	unsigned short id;
-	unsigned char lv;
-	unsigned char flag; // see enum e_skill_flag
+	uint16 id;
+	uint8 lv;
+	uint8 flag; // see enum e_skill_flag
 };
 
 struct global_reg {
@@ -633,6 +634,7 @@ enum e_guild_skill {
 	GD_MAX,
 };
 
+#define MAX_SKILL_ID GD_MAX
 
 //These mark the ID of the jobs, as expected by the client. [Skotlex]
 enum e_job {

+ 24 - 19
src/map/atcommand.c

@@ -32,6 +32,7 @@
 #include "trade.h"
 #include "mapreg.h"
 #include "quest.h"
+#include "pc.h"
 
 #include <stdlib.h>
 #include <math.h>
@@ -3250,7 +3251,7 @@ ACMD_FUNC(questskill)
 
 		return -1;
 	}
-	if (skill_id >= MAX_SKILL_DB) {
+	if (skill_id >= MAX_SKILL_ID) {
 		clif_displaymessage(fd, msg_txt(sd,198)); // This skill number doesn't exist.
 		return -1;
 	}
@@ -3263,7 +3264,7 @@ ACMD_FUNC(questskill)
 		return -1;
 	}
 
-	pc_skill(sd, skill_id, 1, 0);
+	pc_skill(sd, skill_id, 1, ADDSKILL_PERMANENT);
 	clif_displaymessage(fd, msg_txt(sd,70)); // You have learned the skill.
 
 	return 0;
@@ -3274,7 +3275,7 @@ ACMD_FUNC(questskill)
  *------------------------------------------*/
 ACMD_FUNC(lostskill)
 {
-	uint16 skill_id;
+	uint16 skill_id = 0, sk_idx = 0;
 	nullpo_retr(-1, sd);
 
 	if (!message || !*message || (skill_id = atoi(message)) <= 0)
@@ -3294,7 +3295,7 @@ ACMD_FUNC(lostskill)
 
 		return -1;
 	}
-	if (skill_id >= MAX_SKILL) {
+	if (!(sk_idx = skill_get_index(skill_id))) {
 		clif_displaymessage(fd, msg_txt(sd,198)); // This skill number doesn't exist.
 		return -1;
 	}
@@ -3307,8 +3308,8 @@ ACMD_FUNC(lostskill)
 		return -1;
 	}
 
-	sd->status.skill[skill_id].lv = 0;
-	sd->status.skill[skill_id].flag = SKILL_FLAG_PERMANENT;
+	sd->status.skill[sk_idx].lv = 0;
+	sd->status.skill[sk_idx].flag = SKILL_FLAG_PERMANENT;
 	clif_deleteskill(sd,skill_id);
 	clif_displaymessage(fd, msg_txt(sd,71)); // You have forgotten the skill.
 
@@ -5507,11 +5508,11 @@ ACMD_FUNC(skillid) {
 
 	for( data = iter->first(iter,&key); iter->exists(iter); data = iter->next(iter,&key) ) {
 		int idx = skill_get_index(db_data2i(data));
-		if (strnicmp(key.str, message, skillen) == 0 || strnicmp(skill_db[idx].desc, message, skillen) == 0) {
-			sprintf(atcmd_output, msg_txt(sd,1164), db_data2i(data), skill_db[idx].desc, key.str); // skill %d: %s (%s)
+		if (strnicmp(key.str, message, skillen) == 0 || strnicmp(skill_db[idx]->desc, message, skillen) == 0) {
+			sprintf(atcmd_output, msg_txt(sd,1164), db_data2i(data), skill_db[idx]->desc, key.str); // skill %d: %s (%s)
 			clif_displaymessage(fd, atcmd_output);
-		} else if ( found < MAX_SKILLID_PARTIAL_RESULTS && ( stristr(key.str,message) || stristr(skill_db[idx].desc,message) ) ) {
-			snprintf(partials[found++], MAX_SKILLID_PARTIAL_RESULTS_LEN, msg_txt(sd,1164), db_data2i(data), skill_db[idx].desc, key.str);
+		} else if ( found < MAX_SKILLID_PARTIAL_RESULTS && ( stristr(key.str,message) || stristr(skill_db[idx]->desc,message) ) ) {
+			snprintf(partials[found++], MAX_SKILLID_PARTIAL_RESULTS_LEN, msg_txt(sd,1164), db_data2i(data), skill_db[idx]->desc, key.str);
 		}
 	}
 
@@ -5559,7 +5560,7 @@ ACMD_FUNC(useskill)
 		return -1;
 	}
 
-	if (skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE+MAX_HOMUNSKILL
+	if (SKILL_CHK_HOMUN(skill_id)
 		&& sd->hd && hom_is_active(sd->hd)) // (If used with @useskill, put the homunc as dest)
 		bl = &sd->hd->bl;
 	else
@@ -5643,7 +5644,7 @@ ACMD_FUNC(skilltree)
 	{
 		if( ent->need[j].id && pc_checkskill(sd,ent->need[j].id) < ent->need[j].lv)
 		{
-			sprintf(atcmd_output, msg_txt(sd,1170), ent->need[j].lv, skill_db[ent->need[j].id].desc); // Player requires level %d of skill %s.
+			sprintf(atcmd_output, msg_txt(sd,1170), ent->need[j].lv, skill_db[skill_get_index(ent->need[j].id)]->desc); // Player requires level %d of skill %s.
 			clif_displaymessage(fd, atcmd_output);
 			meets = 0;
 		}
@@ -9118,13 +9119,14 @@ ACMD_FUNC(unloadnpcfile) {
 	return 0;
 }
 ACMD_FUNC(cart) {
-#define MC_CART_MDFY(x) \
-	sd->status.skill[MC_PUSHCART].id = x?MC_PUSHCART:0; \
-	sd->status.skill[MC_PUSHCART].lv = x?1:0; \
-	sd->status.skill[MC_PUSHCART].flag = x?SKILL_FLAG_TEMPORARY:SKILL_FLAG_PERMANENT;
+#define MC_CART_MDFY(idx, x) \
+	sd->status.skill[(idx)].id = x?MC_PUSHCART:0; \
+	sd->status.skill[(idx)].lv = x?1:0; \
+	sd->status.skill[(idx)].flag = x?SKILL_FLAG_TEMPORARY:SKILL_FLAG_PERMANENT;
 
 	int val = atoi(message);
 	bool need_skill = (pc_checkskill(sd, MC_PUSHCART) == 0);
+	uint16 sk_idx = 0;
 
 	if( !message || !*message || val < 0 || val > MAX_CARTS ) {
 		sprintf(atcmd_output, msg_txt(sd,1390),command,MAX_CARTS); // Unknown Cart (usage: %s <0-%d>).
@@ -9137,19 +9139,22 @@ ACMD_FUNC(cart) {
 		return -1;
 	}
 
+	if (!(sk_idx = skill_get_index(MC_PUSHCART)))
+		return -1;
+
 	if( need_skill ) {
-		MC_CART_MDFY(1);
+		MC_CART_MDFY(sk_idx,1);
 	}
 
 	if( !pc_setcart(sd, val) ) {
 		if( need_skill ) {
-			MC_CART_MDFY(0);
+			MC_CART_MDFY(sk_idx,0);
 		}
 		return -1;/* @cart failed */
 	}
 
 	if( need_skill ) {
-		MC_CART_MDFY(0);
+		MC_CART_MDFY(sk_idx,0);
 	}
 
 	clif_displaymessage(fd, msg_txt(sd,1392)); // Cart Added

+ 7 - 6
src/map/battle.c

@@ -1883,10 +1883,10 @@ static int battle_skill_damage_skill(struct block_list *src, struct block_list *
 	struct s_skill_damage *damage = NULL;
 	struct map_data *mapd = &map[m];
 
-	if (!idx || !skill_db[idx].damage.map)
+	if (!idx || !skill_db[idx]->damage.map)
 		return 0;
 
-	damage = &skill_db[idx].damage;
+	damage = &skill_db[idx]->damage;
 
 	//check the adjustment works for specified type
 	if (!battle_skill_damage_iscaster(damage->caster, src->type))
@@ -6957,11 +6957,12 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
 		}
 	}
 	if (sd) {
+		uint16 r_skill = 0, sk_idx = 0;
 		if( wd.flag&BF_SHORT && sc && sc->data[SC__AUTOSHADOWSPELL] && rnd()%100 < sc->data[SC__AUTOSHADOWSPELL]->val3 &&
-			sd->status.skill[sc->data[SC__AUTOSHADOWSPELL]->val1].id != 0 && sd->status.skill[sc->data[SC__AUTOSHADOWSPELL]->val1].flag == SKILL_FLAG_PLAGIARIZED )
+			(r_skill = (uint16)sc->data[SC__AUTOSHADOWSPELL]->val1) && (sk_idx = skill_get_index(r_skill)) &&
+			sd->status.skill[sk_idx].id != 0 && sd->status.skill[sk_idx].flag == SKILL_FLAG_PLAGIARIZED )
 		{
-			int r_skill = sd->status.skill[sc->data[SC__AUTOSHADOWSPELL]->val1].id,
-				r_lv = sc->data[SC__AUTOSHADOWSPELL]->val2;
+			int r_lv = sc->data[SC__AUTOSHADOWSPELL]->val2;
 
 			if (r_skill != AL_HOLYLIGHT && r_skill != PR_MAGNUS) {
 				int type;
@@ -7801,7 +7802,7 @@ static const struct _battle_data {
 	{ "show_hp_sp_gain",                    &battle_config.show_hp_sp_gain,                 1,      0,      1,              },
 	{ "mob_npc_event_type",                 &battle_config.mob_npc_event_type,              1,      0,      1,              },
 	{ "character_size",                     &battle_config.character_size,                  1|2,    0,      1|2,            },
-	{ "mob_max_skilllvl",                   &battle_config.mob_max_skilllvl,                MAX_SKILL_LEVEL, 1, MAX_SKILL_LEVEL, },
+	{ "mob_max_skilllvl",                   &battle_config.mob_max_skilllvl,                MAX_MOBSKILL_LEVEL, 1, MAX_MOBSKILL_LEVEL, },
 	{ "retaliate_to_master",                &battle_config.retaliate_to_master,             1,      0,      1,              },
 	{ "rare_drop_announce",                 &battle_config.rare_drop_announce,              0,      0,      10000,          },
 	{ "duel_allow_pvp",                     &battle_config.duel_allow_pvp,                  0,      0,      1,              },

+ 13 - 11
src/map/chrif.c

@@ -965,19 +965,21 @@ int chrif_changedsex(int fd) {
 		if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER) {
 			int i;
 			// remove specifical skills of Bard classes
-			for(i = 315; i <= 322; i++) {
-				if (sd->status.skill[i].id > 0 && sd->status.skill[i].flag == SKILL_FLAG_PERMANENT) {
-					sd->status.skill_point += sd->status.skill[i].lv;
-					sd->status.skill[i].id = 0;
-					sd->status.skill[i].lv = 0;
+			for(i = BA_MUSICALLESSON; i <= BA_APPLEIDUN; i++) {
+				uint16 sk_idx = skill_get_index(i);
+				if (sd->status.skill[sk_idx].id > 0 && sd->status.skill[sk_idx].flag == SKILL_FLAG_PERMANENT) {
+					sd->status.skill_point += sd->status.skill[sk_idx].lv;
+					sd->status.skill[sk_idx].id = 0;
+					sd->status.skill[sk_idx].lv = 0;
 				}
 			}
 			// remove specifical skills of Dancer classes
-			for(i = 323; i <= 330; i++) {
-				if (sd->status.skill[i].id > 0 && sd->status.skill[i].flag == SKILL_FLAG_PERMANENT) {
-					sd->status.skill_point += sd->status.skill[i].lv;
-					sd->status.skill[i].id = 0;
-					sd->status.skill[i].lv = 0;
+			for(i = DC_DANCINGLESSON; i <= DC_SERVICEFORYOU; i++) {
+				uint16 sk_idx = skill_get_index(i);
+				if (sd->status.skill[sk_idx].id > 0 && sd->status.skill[sk_idx].flag == SKILL_FLAG_PERMANENT) {
+					sd->status.skill_point += sd->status.skill[sk_idx].lv;
+					sd->status.skill[sk_idx].id = 0;
+					sd->status.skill[sk_idx].lv = 0;
 				}
 			}
 			clif_updatestatus(sd, SP_SKILLPOINT);
@@ -1044,7 +1046,7 @@ int chrif_divorceack(uint32 char_id, int partner_id) {
  *------------------------------------------*/
 int chrif_deadopt(int father_id, int mother_id, int child_id) {
 	struct map_session_data* sd;
-	int idx = skill_get_index(WE_CALLBABY);
+	uint16 idx = skill_get_index(WE_CALLBABY);
 
 	if( father_id && ( sd = map_charid2sd(father_id) ) != NULL && sd->status.child == child_id ) {
 		sd->status.child = 0;

+ 62 - 45
src/map/clif.c

@@ -1495,7 +1495,7 @@ int clif_homskillinfoblock(struct map_session_data *sd)
 {	//[orn]
 	struct homun_data *hd;
 	int fd = sd->fd;
-	int i,j,len=4;
+	int i, len=4;
 	WFIFOHEAD(fd, 4+37*MAX_HOMUNSKILL);
 
 	hd = sd->hd;
@@ -1507,15 +1507,17 @@ int clif_homskillinfoblock(struct map_session_data *sd)
 		int id = hd->homunculus.hskill[i].id;
 		if( id != 0 ){
 			int combo = (hd->homunculus.hskill[i].flag)&SKILL_FLAG_TMP_COMBO;
-			j = id - HM_SKILLBASE;
+			short idx = hom_skill_get_index(id);
+			if (idx == -1)
+				continue;
 			WFIFOW(fd,len  ) = id;
 			WFIFOW(fd,len+2) = ((combo)?INF_SELF_SKILL: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);
+			WFIFOW(fd,len+6) = hd->homunculus.hskill[idx].lv;
+			WFIFOW(fd,len+8) = skill_get_sp(id,hd->homunculus.hskill[idx].lv);
+			WFIFOW(fd,len+10)= skill_get_range2(&sd->hd->bl, id,hd->homunculus.hskill[idx].lv);
 			safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
-			WFIFOB(fd,len+36) = (hd->homunculus.level < hom_skill_get_min_level(hd->homunculus.class_, id) || hd->homunculus.hskill[j].lv >= hom_skill_tree_get_max(id, hd->homunculus.class_)) ? 0 : 1;
+			WFIFOB(fd,len+36) = (hd->homunculus.level < hom_skill_get_min_level(hd->homunculus.class_, id) || hd->homunculus.hskill[idx].lv >= hom_skill_tree_get_max(id, hd->homunculus.class_)) ? 0 : 1;
 			len+=37;
 		}
 	}
@@ -1528,12 +1530,15 @@ int clif_homskillinfoblock(struct map_session_data *sd)
 void clif_homskillup(struct map_session_data *sd, uint16 skill_id)
 {	//[orn]
 	struct homun_data *hd;
-	int fd, idx;
+	int fd;
+	short idx = -1;
 	nullpo_retv(sd);
-	idx = skill_id - HM_SKILLBASE;
 
-	fd=sd->fd;
-	hd=sd->hd;
+	if ((idx = hom_skill_get_index(skill_id) == -1))
+		return;
+
+	fd = sd->fd;
+	hd = sd->hd;
 
 	WFIFOHEAD(fd, packet_len(0x239));
 	WFIFOW(fd,0) = 0x239;
@@ -4825,8 +4830,9 @@ void clif_skillinfoblock(struct map_session_data *sd)
 
 	nullpo_retv(sd);
 
-	fd=sd->fd;
-	if (!fd) return;
+	fd = sd->fd;
+	if (!fd)
+		return;
 
 	WFIFOHEAD(fd, MAX_SKILL * 37 + 4);
 	WFIFOW(fd,0) = 0x10f;
@@ -4870,28 +4876,30 @@ void clif_skillinfoblock(struct map_session_data *sd)
 
 /// 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)
+void clif_addskill(struct map_session_data *sd, int skill_id)
 {
 	int fd;
+	uint16 idx = 0;
 
 	nullpo_retv(sd);
 
 	fd = sd->fd;
-	if (!fd) return;
+	if (!fd || !(idx = skill_get_index(skill_id)))
+		return;
 
-	if( sd->status.skill[id].id <= 0 )
+	if( sd->status.skill[idx].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;
+	WFIFOW(fd,2) = skill_id;
+	WFIFOL(fd,4) = skill_get_inf(skill_id);
+	WFIFOW(fd,8) = sd->status.skill[idx].lv;
+	WFIFOW(fd,10) = skill_get_sp(skill_id,sd->status.skill[idx].lv);
+	WFIFOW(fd,12)= skill_get_range2(&sd->bl, skill_id,sd->status.skill[idx].lv);
+	safestrncpy((char*)WFIFOP(fd,14), skill_get_name(skill_id), NAME_LENGTH);
+	if( sd->status.skill[idx].flag == SKILL_FLAG_PERMANENT )
+		WFIFOB(fd,38) = (sd->status.skill[idx].lv < skill_tree_get_max(skill_id, sd->status.class_))? 1:0;
 	else
 		WFIFOB(fd,38) = 0;
 	WFIFOSET(fd,packet_len(0x111));
@@ -4900,18 +4908,21 @@ void clif_addskill(struct map_session_data *sd, int id)
 
 /// Deletes a skill from the skill tree (ZC_SKILLINFO_DELETE).
 /// 0441 <skill id>.W
-void clif_deleteskill(struct map_session_data *sd, int id)
+void clif_deleteskill(struct map_session_data *sd, int skill_id)
 {
 #if PACKETVER >= 20081217
 	int fd;
+	uint16 idx = 0;
 
 	nullpo_retv(sd);
+
 	fd = sd->fd;
-	if( !fd ) return;
+	if (!fd || !(idx = skill_get_index(skill_id)))
+		return;
 
 	WFIFOHEAD(fd,packet_len(0x441));
 	WFIFOW(fd,0) = 0x441;
-	WFIFOW(fd,2) = id;
+	WFIFOW(fd,2) = skill_id;
 	WFIFOSET(fd,packet_len(0x441));
 #endif
 	clif_skillinfoblock(sd);
@@ -4920,7 +4931,7 @@ void clif_deleteskill(struct map_session_data *sd, int id)
 /// 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 lv, int range, int upgradable) {
-    int fd;
+	int fd;
 
 	nullpo_retv(sd);
 
@@ -4938,19 +4949,22 @@ void clif_skillup(struct map_session_data *sd, uint16 skill_id, int lv, int rang
 
 /// 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)
+void clif_skillinfo(struct map_session_data *sd,int skill_id, int inf)
 {
 	const int fd = sd->fd;
+	uint16 idx = skill_get_index(skill_id);
+	if (!idx)
+		return;
 
 	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;
+	WFIFOW(fd,2) = skill_id;
+	WFIFOL(fd,4) = inf?inf:skill_get_inf(skill_id);
+	WFIFOW(fd,8) = sd->status.skill[idx].lv;
+	WFIFOW(fd,10) = skill_get_sp(skill_id,sd->status.skill[idx].lv);
+	WFIFOW(fd,12) = skill_get_range2(&sd->bl,skill_id,sd->status.skill[idx].lv);
+	if( sd->status.skill[idx].flag == SKILL_FLAG_PERMANENT )
+		WFIFOB(fd,14) = (sd->status.skill[idx].lv < skill_tree_get_max(skill_id, sd->status.class_))? 1:0;
 	else
 		WFIFOB(fd,14) = 0;
 	WFIFOSET(fd,packet_len(0x7e1));
@@ -11384,12 +11398,12 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
 	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 ) {
+	if( SKILL_CHK_HOMUN(skill_id) ) {
 		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 ) {
+	if( SKILL_CHK_MERC(skill_id) ) {
 		clif_parse_UseSkillToId_mercenary(sd->md, sd, tick, skill_id, skill_lv, target_id);
 		return;
 	}
@@ -11458,7 +11472,7 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
 
 	sd->skillitem = sd->skillitemlv = 0;
 
-	if( skill_id >= GD_SKILLBASE ) {
+	if( SKILL_CHK_GUILD(skill_id) ) {
 		if( sd->state.gmaster_flag )
 			skill_lv = guild_checkskill(sd->guild, skill_id);
 		else
@@ -11485,12 +11499,12 @@ static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, uin
 	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 ) {
+	if( SKILL_CHK_HOMUN(skill_id) ) {
 		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 ) {
+	if( SKILL_CHK_MERC(skill_id) ) {
 		clif_parse_UseSkillToPos_mercenary(sd->md, sd, tick, skill_id, skill_lv, x, y, skillmoreinfo);
 		return;
 	}
@@ -15699,15 +15713,18 @@ void clif_mercenary_skillblock(struct map_session_data *sd)
 	WFIFOW(fd,0) = 0x29d;
 	for( i = 0; i < MAX_MERCSKILL; i++ )
 	{
-		int id, j;
+		uint16 id;
+		short idx = -1;
 		if( (id = md->db->skill[i].id) == 0 )
 			continue;
-		j = id - MC_SKILLBASE;
+		if ((idx = mercenary_skill_get_index(id)) == -1)
+			continue;
+
 		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);
+		WFIFOW(fd,len+6) = md->db->skill[idx].lv;
+		WFIFOW(fd,len+8) = skill_get_sp(id, md->db->skill[idx].lv);
+		WFIFOW(fd,len+10) = skill_get_range2(&md->bl, id, md->db->skill[idx].lv);
 		safestrncpy((char*)WFIFOP(fd,len+12), skill_get_name(id), NAME_LENGTH);
 		WFIFOB(fd,len+36) = 0; // Skillable for Mercenary?
 		len += 37;

+ 3 - 3
src/map/clif.h

@@ -532,9 +532,9 @@ void clif_class_change(struct block_list *bl,int class_,int type);
 
 void clif_skillinfoblock(struct map_session_data *sd);
 void clif_skillup(struct map_session_data *sd, uint16 skill_id, int lv, int range, int upgradable);
-void clif_skillinfo(struct map_session_data *sd,int skill, int inf);
-void clif_addskill(struct map_session_data *sd, int id);
-void clif_deleteskill(struct map_session_data *sd, int id);
+void clif_skillinfo(struct map_session_data *sd,int skill_id, int inf);
+void clif_addskill(struct map_session_data *sd, int skill_id);
+void clif_deleteskill(struct map_session_data *sd, int skill_id);
 
 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);
 void clif_skillcastcancel(struct block_list* bl);

+ 10 - 8
src/map/elemental.c

@@ -9,6 +9,7 @@
 #include "../common/showmsg.h"
 #include "../common/random.h"
 #include "../common/strlib.h"
+#include "../common/utils.h"
 
 #include "log.h"
 #include "clif.h"
@@ -558,13 +559,12 @@ struct skill_condition elemental_skill_get_requirements(uint16 skill_id, uint16
 	memset(&req,0,sizeof(req));
 
 	if( idx == 0 ) // invalid skill id
-  		return req;
-
-	if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL )
 		return req;
 
-	req.hp = skill_db[idx].require.hp[skill_lv-1];
-	req.sp = skill_db[idx].require.sp[skill_lv-1];
+	skill_lv = cap_value(skill_lv, 1, MAX_SKILL_LEVEL);
+
+	req.hp = skill_db[idx]->require.hp[skill_lv-1];
+	req.sp = skill_db[idx]->require.sp[skill_lv-1];
 
 	return req;
 }
@@ -815,6 +815,7 @@ void read_elementaldb(void) {
 	uint8 i;
 
 	elemental_count = 0;
+	memset(elemental_db, 0, sizeof(elemental_db));
 	for(i = 0; i<ARRAYLENGTH(filename); i++){
 		sv_readdb(db_path, filename[i], ',', 26, 26, -1, &read_elementaldb_sub, i);
 	}
@@ -825,7 +826,8 @@ void read_elementaldb(void) {
 * ElementalID,SkillID,SkillLevel,ReqMode
 */
 static bool read_elemental_skilldb_sub(char* str[], int columns, int current) {
-	uint16 class_ = atoi(str[0]), i, skill_id, skill_lv, skillmode;
+	uint16 class_ = atoi(str[0]), skill_id, skill_lv, skillmode;
+	uint8 i;
 	struct s_elemental_db *db;
 
 	ARR_FIND(0, MAX_ELEMENTAL_CLASS, i, class_ == elemental_db[i].class_);
@@ -835,8 +837,8 @@ static bool read_elemental_skilldb_sub(char* str[], int columns, int current) {
 	}
 
 	skill_id = atoi(str[1]);
-	if( skill_id < EL_SKILLBASE || skill_id >= EL_SKILLBASE + MAX_ELEMENTALSKILL ) {
-		ShowError("read_elemental_skilldb_sub: Skill out of range, line %d.\n", current);
+	if( !SKILL_CHK_ELEM(skill_id) ) {
+		ShowError("read_elemental_skilldb_sub: Invalid Elemental skill '%d'.\n", skill_id);
 		return false;
 	}
 

+ 55 - 40
src/map/guild.c

@@ -64,6 +64,20 @@ static int guild_send_xy_timer(int tid, unsigned int tick, int id, intptr_t data
 struct npc_data **guild_flags;
 unsigned short guild_flags_count;
 
+/**
+ * Get guild skill index in guild_skill_tree
+ * @param skill_id
+ * @return Index in skill_tree or -1
+ **/
+static short guild_skill_get_index(uint16 skill_id) {
+	if (!SKILL_CHK_GUILD(skill_id))
+		return -1;
+	skill_id -= GD_SKILLBASE;
+	if (skill_id >= MAX_GUILDSKILL)
+		return -1;
+	return skill_id;
+}
+
 /*==========================================
  * Retrieves and validates the sd pointer for this guild member [Skotlex]
  *------------------------------------------*/
@@ -84,44 +98,41 @@ static TBL_PC* guild_sd_check(int guild_id, uint32 account_id, uint32 char_id) {
 
  // Modified [Komurka]
 int guild_skill_get_max (int id) {
-	if (id < GD_SKILLBASE || id >= GD_SKILLBASE+MAX_GUILDSKILL)
+	if ((id = guild_skill_get_index(id)) < 0)
 		return 0;
-	return guild_skill_tree[id-GD_SKILLBASE].max;
+	return guild_skill_tree[id].max;
 }
 
 // Retrive skill_lv learned by guild
 
 int guild_checkskill(struct guild *g, int id) {
-	int idx = id - GD_SKILLBASE;
-	if (idx < 0 || idx >= MAX_GUILDSKILL)
+	if ((id = guild_skill_get_index(id)) < 0)
 		return 0;
-	return g->skill[idx].lv;
+	return g->skill[id].lv;
 }
 
 /*==========================================
  * guild_skill_tree.txt reading - from jA [Komurka]
  *------------------------------------------*/
 static bool guild_read_guildskill_tree_db(char* split[], int columns, int current) {// <skill id>,<max lv>,<req id1>,<req lv1>,<req id2>,<req lv2>,<req id3>,<req lv3>,<req id4>,<req lv4>,<req id5>,<req lv5>
-	int k, id, skill_id;
+	int k, skill_id = atoi(split[0]);
+	short idx = -1;
 
-	skill_id = atoi(split[0]);
-	id = skill_id - GD_SKILLBASE;
-
-	if( id < 0 || id >= MAX_GUILDSKILL ) 	{
-		ShowWarning("guild_read_guildskill_tree_db: Invalid skill id %d.\n", skill_id);
+	if ((idx = guild_skill_get_index(skill_id)) < 0) {
+		ShowError("guild_read_guildskill_tree_db: Invalid Guild skill '%s'.\n", split[1]);
 		return false;
 	}
 
-	guild_skill_tree[id].id = skill_id;
-	guild_skill_tree[id].max = atoi(split[1]);
+	guild_skill_tree[idx].id = skill_id;
+	guild_skill_tree[idx].max = atoi(split[1]);
 
-	if( guild_skill_tree[id].id == GD_GLORYGUILD && battle_config.require_glory_guild && guild_skill_tree[id].max == 0 ) 	{// enable guild's glory when required for emblems
-		guild_skill_tree[id].max = 1;
+	if( guild_skill_tree[idx].id == GD_GLORYGUILD && battle_config.require_glory_guild && guild_skill_tree[idx].max == 0 ) 	{// enable guild's glory when required for emblems
+		guild_skill_tree[idx].max = 1;
 	}
 
 	for( k = 0; k < MAX_GUILD_SKILL_REQUIRE; k++ ) 	{
-		guild_skill_tree[id].need[k].id = atoi(split[k*2+2]);
-		guild_skill_tree[id].need[k].lv = atoi(split[k*2+3]);
+		guild_skill_tree[idx].need[k].id = atoi(split[k*2+2]);
+		guild_skill_tree[idx].need[k].lv = atoi(split[k*2+3]);
 	}
 
 	return true;
@@ -131,13 +142,13 @@ static bool guild_read_guildskill_tree_db(char* split[], int columns, int curren
  * Guild skill check - from jA [Komurka]
  *------------------------------------------*/
 int guild_check_skill_require(struct guild *g,int id) {
-	int i;
-	int idx = id-GD_SKILLBASE;
+	uint8 i;
+	short idx = -1;
 
 	if(g == NULL)
 		return 0;
 
-	if (idx < 0 || idx >= MAX_GUILDSKILL)
+	if ((idx = guild_skill_get_index(id)) < 0)
 		return 0;
 
 	for(i=0;i<MAX_GUILD_SKILL_REQUIRE;i++)
@@ -1257,37 +1268,41 @@ int guild_getexp(struct map_session_data *sd,int exp) {
 /*====================================================
  * Ask to increase guildskill skill_id
  *---------------------------------------------------*/
-int guild_skillup(TBL_PC* sd, uint16 skill_id) {
+void guild_skillup(TBL_PC* sd, uint16 skill_id) {
 	struct guild* g;
-	int idx = skill_id - GD_SKILLBASE;
-	int max = guild_skill_get_max(skill_id);
+	short idx = guild_skill_get_index(skill_id);
+	short max = 0;
 
-	nullpo_ret(sd);
+	nullpo_retv(sd);
 
-	if( idx < 0 || idx >= MAX_GUILDSKILL || // not a guild skill
-			sd->status.guild_id == 0 || (g=sd->guild) == NULL || // no guild
-			strcmp(sd->status.name, g->master) ) // not the guild master
-		return 0;
+	if (idx == -1)
+		return;
+
+	if( sd->status.guild_id == 0 || (g=sd->guild) == NULL || // no guild
+		strcmp(sd->status.name, g->master) ) // not the guild master
+		return;
+
+	max = guild_skill_get_max(skill_id);
 
 	if( g->skill_point > 0 &&
-			g->skill[idx].id != 0 &&
-			g->skill[idx].lv < max )
+		g->skill[idx].id != 0 &&
+		g->skill[idx].lv < max )
 		intif_guild_skillup(g->guild_id, skill_id, sd->status.account_id, max);
-
-	return 0;
 }
 
 /*====================================================
  * Notification of guildskill skill_id increase request
  *---------------------------------------------------*/
 int guild_skillupack(int guild_id,uint16 skill_id,uint32 account_id) {
-	struct map_session_data *sd=map_id2sd(account_id);
-	struct guild *g=guild_search(guild_id);
+	struct map_session_data *sd = map_id2sd(account_id);
+	struct guild *g = guild_search(guild_id);
 	int i;
-	if(g==NULL)
+	short idx = guild_skill_get_index(skill_id);
+
+	if (g == NULL || idx == -1)
 		return 0;
-	if( sd != NULL ) {
-		int lv = g->skill[skill_id-GD_SKILLBASE].lv;
+	if (sd != NULL) {
+		int lv = g->skill[idx].lv;
 		int range = skill_get_range(skill_id, lv);
 		clif_skillup(sd,skill_id,lv,range,1);
 
@@ -1297,14 +1312,14 @@ int guild_skillupack(int guild_id,uint16 skill_id,uint32 account_id) {
 			case GD_GLORYWOUNDS:
 			case GD_SOULCOLD:
 			case GD_HAWKEYES:
-					guild_guildaura_refresh(sd,skill_id,g->skill[skill_id-GD_SKILLBASE].lv);
+					guild_guildaura_refresh(sd,skill_id,g->skill[idx].lv);
 				break;
 		}
 	}
 
 	// Inform all members
-	for(i=0;i<g->max_member;i++)
-		if((sd=g->member[i].sd)!=NULL)
+	for (i = 0; i < g->max_member; i++)
+		if ((sd = g->member[i].sd) != NULL)
 			clif_guild_skillinfo(sd);
 
 	return 0;

+ 1 - 1
src/map/guild.h

@@ -60,7 +60,7 @@ int guild_member_withdraw(int guild_id,uint32 account_id,uint32 char_id,int flag
 	const char *name,const char *mes);
 int guild_expulsion(struct map_session_data *sd,int guild_id,
 	uint32 account_id,uint32 char_id,const char *mes);
-int guild_skillup(struct map_session_data* sd, uint16 skill_id);
+void guild_skillup(struct map_session_data* sd, uint16 skill_id);
 void guild_block_skill(struct map_session_data *sd, int time);
 int guild_reqalliance(struct map_session_data *sd,struct map_session_data *tsd);
 int guild_reply_reqalliance(struct map_session_data *sd,uint32 account_id,int flag);

+ 32 - 37
src/map/homunculus.c

@@ -54,10 +54,11 @@ static struct s_homun_intimacy_grade intimacy_grades[] = {
 * @param skill_id
 * @return -1 if invalid skill or skill index for homunculus skill in s_homunculus::hskill
 */
-static short hom_skill_get_index(int skill_id) {
-	if (!skill_get_index(skill_id))
+short hom_skill_get_index(uint16 skill_id) {
+	if (!SKILL_CHK_HOMUN(skill_id))
 		return -1;
-	if ((skill_id -= HM_SKILLBASE) < 0 || skill_id >= MAX_HOMUNSKILL)
+	skill_id -= HM_SKILLBASE;
+	if (skill_id >= MAX_HOMUNSKILL)
 		return -1;
 	return skill_id;
 }
@@ -140,8 +141,8 @@ int hom_class2mapid(int hom_class)
 void hom_addspiritball(TBL_HOM *hd, int max) {
 	nullpo_retv(hd);
 
-	if (max > MAX_SKILL_LEVEL)
-		max = MAX_SKILL_LEVEL;
+	if (max > MAX_SPIRITBALL)
+		max = MAX_SPIRITBALL;
 	if (hd->homunculus.spiritball < 0)
 		hd->homunculus.spiritball = 0;
 
@@ -168,8 +169,8 @@ void hom_delspiritball(TBL_HOM *hd, int count, int type) {
 	}
 	if (count <= 0)
 		return;
-	if (count > MAX_SKILL_LEVEL)
-		count = MAX_SKILL_LEVEL;
+	if (count > MAX_SPIRITBALL)
+		count = MAX_SPIRITBALL;
 	if (count > hd->homunculus.spiritball)
 		count = hd->homunculus.spiritball;
 
@@ -275,17 +276,17 @@ int hom_delete(struct homun_data *hd, int emote)
 */
 void hom_calc_skilltree(struct homun_data *hd, bool flag_evolve) {
 	uint8 i;
-	uint16 skill_id = 0;
 	short c = 0;
 
 	nullpo_retv(hd);
 
 	/* load previous homunculus form skills first. */
 	if (hd->homunculus.prev_class != 0 && (c = hom_class2index(hd->homunculus.prev_class)) >= 0) {
-		for (i = 0; i < MAX_HOM_SKILL_TREE && (skill_id = hskill_tree[c][i].id) > 0; i++) {
+		for (i = 0; i < MAX_SKILL_TREE; i++) {
+			uint16 skill_id;
+			short idx = -1;
 			bool fail = false;
-			short idx = hom_skill_get_index(skill_id);
-			if (idx < 0)
+			if (!(skill_id = hskill_tree[c][i].id) || (idx = hom_skill_get_index(skill_id)) == -1)
 				continue;
 			if (hd->homunculus.hskill[idx].id)
 				continue; //Skill already known.
@@ -311,11 +312,12 @@ void hom_calc_skilltree(struct homun_data *hd, bool flag_evolve) {
 	if ((c = hom_class2index(hd->homunculus.class_)) < 0)
 		return;
 
-	for (i = 0; i < MAX_HOM_SKILL_TREE && (skill_id = hskill_tree[c][i].id) > 0; i++) {
-		bool fail = false;
+	for (i = 0; i < MAX_SKILL_TREE; i++) {
 		unsigned int intimacy = 0;
-		short idx = hom_skill_get_index(skill_id);
-		if (idx < 0)
+		uint16 skill_id;
+		short idx = -1;
+		bool fail = false;
+		if (!(skill_id = hskill_tree[c][i].id) || (idx = hom_skill_get_index(skill_id)) == -1)
 			continue;
 		if (hd->homunculus.hskill[idx].id)
 			continue; //Skill already known.
@@ -351,7 +353,7 @@ void hom_calc_skilltree(struct homun_data *hd, bool flag_evolve) {
 */
 short hom_checkskill(struct homun_data *hd,uint16 skill_id)
 {
-	int idx = hom_skill_get_index(skill_id);
+	short idx = hom_skill_get_index(skill_id);
 	if (idx < 0) // Invalid skill
 		return 0;
 
@@ -390,6 +392,7 @@ int hom_skill_tree_get_max(int skill_id, int b_class){
 uint8 hom_skill_get_min_level(int class_, uint16 skill_id) {
 	short class_idx = hom_class2index(class_), skill_idx = -1;
 	uint8 i;
+
 	if (class_idx == -1 || (skill_idx = hom_skill_get_index(skill_id)) == -1)
 		return 0;
 	ARR_FIND(0, MAX_HOM_SKILL_REQUIRE, i, hskill_tree[class_idx][i].id == skill_id);
@@ -1511,40 +1514,32 @@ void read_homunculusdb(void) {
 * <hom class>,<skill id>,<max level>,<need level>,<req id1>,<req lv1>,<req id2>,<req lv2>,<req id3>,<req lv3>,<req id4>,<req lv4>,<req id5>,<req lv5>,<intimacy lv req>
 */
 static bool read_homunculus_skilldb_sub(char* split[], int columns, int current) {
-	int skill_id, class_idx;
-	int8 i, j;
+	uint16 skill_id;
+	int8 i;
+	short class_idx, idx = -1;
 
 	// check for bounds [celest]
 	if ((class_idx = hom_class2index(atoi(split[0]))) == -1) {
-		ShowWarning("read_homunculus_skilldb: Invalud homunculus class %d.\n", atoi(split[0]));
+		ShowWarning("read_homunculus_skilldb: Invalid homunculus class %d.\n", atoi(split[0]));
 		return false;
 	}
 
-	skill_id = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex]
-	// Search an empty line or a line with the same skill_id (stored in j)
-	ARR_FIND( 0, MAX_HOM_SKILL_TREE, j, !hskill_tree[class_idx][j].id || hskill_tree[class_idx][j].id == skill_id );
-	if (j == MAX_HOM_SKILL_TREE) {
-		ShowWarning("Unable to load skill %d into homunculus %d's tree. Maximum number of skills per class has been reached.\n", skill_id, atoi(split[0]));
+	skill_id = atoi(split[1]);
+	if ((idx = hom_skill_get_index(skill_id)) == -1) {
+		ShowError("read_homunculus_skilldb: Invalid Homunculus skill '%s'.\n", split[1]);
 		return false;
 	}
 
-	hskill_tree[class_idx][j].id = skill_id;
-	hskill_tree[class_idx][j].max = atoi(split[2]);
-	hskill_tree[class_idx][j].need_level = atoi(split[3]);
+	hskill_tree[class_idx][idx].id = skill_id;
+	hskill_tree[class_idx][idx].max = atoi(split[2]);
+	hskill_tree[class_idx][idx].need_level = atoi(split[3]);
 
 	for (i = 0; i < MAX_HOM_SKILL_REQUIRE; i++) {
-		uint16 id = atoi(split[4+i*2]), idx = 0;
-		if (!id)
-			continue;
-		if (!(idx = skill_get_index(id))) {
-			ShowWarning("read_homunculus_skilldb_sub: Invalid skill %d as requirement for skill %hu class %d. Skipping\n", id, skill_id, atoi(split[0]));
-			continue;
-		}
-		hskill_tree[class_idx][j].need[i].id = id;
-		hskill_tree[class_idx][j].need[i].lv = atoi(split[4+i*2+1]);
+		hskill_tree[class_idx][idx].need[i].id = atoi(split[4+i*2]);
+		hskill_tree[class_idx][idx].need[i].lv = atoi(split[4+i*2+1]);
 	}
 
-	hskill_tree[class_idx][j].intimacy = atoi(split[14]);
+	hskill_tree[class_idx][idx].intimacy = atoi(split[14]);
 	return true;
 }
 

+ 2 - 0
src/map/homunculus.h

@@ -186,6 +186,8 @@ uint8 hom_get_intimacy_grade(struct homun_data *hd);
 uint32 hom_intimacy_grade2intimacy(enum e_homun_grade grade);
 enum e_homun_grade hom_intimacy_intimacy2grade(uint32 intimacy);
 
+short hom_skill_get_index(uint16 skill_id);
+
 void do_final_homunculus(void);
 void do_init_homunculus(void);
 

+ 4 - 0
src/map/itemdb.h

@@ -325,8 +325,12 @@ enum e_item_ammo
 	AMMO_KUNAI,
 	AMMO_CANNONBALL,
 	AMMO_THROWABLE_ITEM, ///Sling items
+
+	MAX_AMMO_TYPE,
 };
 
+#define AMMO_TYPE_ALL ((1<<MAX_AMMO_TYPE)-1)
+
 ///Item combo struct
 struct item_combo
 {

+ 26 - 13
src/map/mercenary.c

@@ -59,6 +59,20 @@ struct view_data * mercenary_get_viewdata(int class_){
 	return &mercenary_db[i].vd;
 }
 
+/**
+ * Get mercenary skill index for mercenary skill tree
+ * @param skill_id
+ * @return Index in skill_tree or -1
+ **/
+short mercenary_skill_get_index(uint16 skill_id) {
+	if (!SKILL_CHK_MERC(skill_id))
+		return -1;
+	skill_id -= MC_SKILLBASE;
+	if (skill_id >= MAX_MERCSKILL)
+		return -1;
+	return skill_id;
+}
+
 /**
 * Create a new Mercenary for Player
 * @param sd The Player
@@ -445,14 +459,11 @@ void mercenary_kills(struct mercenary_data *md){
 * @return Skill Level or 0 if Mercenary doesn't have the skill
 **/
 int mercenary_checkskill(struct mercenary_data *md, uint16 skill_id) {
-	int i = skill_id - MC_SKILLBASE;
+	short idx = mercenary_skill_get_index(skill_id);
 
-	if( !md || !md->db )
+	if( !md || !md->db || idx == -1)
 		return 0;
-	if( md->db->skill[i].id == skill_id )
-		return md->db->skill[i].lv;
-
-	return 0;
+	return md->db->skill[idx].lv;
 }
 
 /**
@@ -529,6 +540,7 @@ static bool mercenary_readdb_sub(char* str[], int columns, int current)
 void mercenary_readdb(void) {
 	const char *filename[]={ "mercenary_db.txt",DBIMPORT"/mercenary_db.txt"};
 	uint8 i;
+
 	mercenary_count = 0; //Reset the counter
 	memset(mercenary_db,0,sizeof(mercenary_db));
 	for(i = 0; i<ARRAYLENGTH(filename); i++){
@@ -542,7 +554,9 @@ void mercenary_readdb(void) {
 static bool mercenary_read_skilldb_sub(char* str[], int columns, int current)
 {// <merc id>,<skill id>,<skill level>
 	struct s_mercenary_db *db;
-	uint16 i, class_, skill_id, skill_lv;
+	uint16 class_, skill_id, skill_lv;
+	uint8 i = 0;
+	short idx = -1;
 
 	class_ = atoi(str[0]);
 	ARR_FIND(0, MAX_MERCENARY_CLASS, i, class_ == mercenary_db[i].class_);
@@ -553,18 +567,16 @@ static bool mercenary_read_skilldb_sub(char* str[], int columns, int current)
 	}
 
 	skill_id = atoi(str[1]);
-	if( skill_id < MC_SKILLBASE || skill_id >= MC_SKILLBASE + MAX_MERCSKILL )
-	{
-		ShowError("read_mercenary_skilldb : Skill %d out of range.\n", skill_id);
+	if( (idx = mercenary_skill_get_index(skill_id)) == -1 ) {
+		ShowError("read_mercenary_skilldb: Invalid Mercenary skill '%s'.\n", str[1]);
 		return false;
 	}
 
 	db = &mercenary_db[i];
 	skill_lv = atoi(str[2]);
 
-	i = skill_id - MC_SKILLBASE;
-	db->skill[i].id = skill_id;
-	db->skill[i].lv = skill_lv;
+	db->skill[idx].id = skill_id;
+	db->skill[idx].lv = skill_lv;
 
 	return true;
 }
@@ -575,6 +587,7 @@ static bool mercenary_read_skilldb_sub(char* str[], int columns, int current)
 void mercenary_read_skilldb(void){
 	const char *filename[]={ "mercenary_skill_db.txt",DBIMPORT"/mercenary_skill_db.txt"};
 	uint8 i;
+
 	for(i = 0; i<ARRAYLENGTH(filename); i++){
 		sv_readdb(db_path, filename[i], ',', 3, 3, -1, &mercenary_read_skilldb_sub, i);
 	}

+ 1 - 0
src/map/mercenary.h

@@ -71,6 +71,7 @@ void mercenary_set_calls(struct mercenary_data *md, int value);
 void mercenary_kills(struct mercenary_data *md);
 
 int mercenary_checkskill(struct mercenary_data *md, uint16 skill_id);
+short mercenary_skill_get_index(uint16 skill_id);
 
 /**
  * atcommand.c required

+ 6 - 5
src/map/mob.c

@@ -3492,8 +3492,9 @@ int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, cons
 
 	//Go Backwards to give better priority to advanced skills.
 	for (i=0,j = MAX_SKILL_TREE-1;j>=0 && i< MAX_MOBSKILL ;j--) {
-		int skill_id = skill_tree[pc_class2idx(sd->status.class_)][j].id;
-		if (!skill_id || sd->status.skill[skill_id].lv < 1 ||
+		uint16 skill_id = skill_tree[pc_class2idx(sd->status.class_)][j].id;
+		uint16 sk_idx = 0;
+		if (!skill_id || !(sk_idx = skill_get_index(skill_id)) || sd->status.skill[sk_idx].lv < 1 ||
 			(skill_get_inf2(skill_id)&(INF2_WEDDING_SKILL|INF2_GUILD_SKILL)) ||
 			skill_get_nocast(skill_id)&16
 		)
@@ -3508,12 +3509,12 @@ int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, cons
 		/**
 		 * The clone should be able to cast the skill (e.g. have the required weapon) bugreport:5299)
 		 **/
-		if( !skill_check_condition_castbegin(sd,skill_id,sd->status.skill[skill_id].lv) )
+		if( !skill_check_condition_castbegin(sd,skill_id,sd->status.skill[sk_idx].lv) )
 			continue;
 
 		memset (&ms[i], 0, sizeof(struct mob_skill));
 		ms[i].skill_id = skill_id;
-		ms[i].skill_lv = sd->status.skill[skill_id].lv;
+		ms[i].skill_lv = sd->status.skill[sk_idx].lv;
 		ms[i].state = MSS_ANY;
 		ms[i].permillage = 500*battle_config.mob_skill_rate/100; //Default chance of all skills: 5%
 		ms[i].emotion = -1;
@@ -4289,7 +4290,7 @@ static bool mob_parse_row_mobskilldb(char** str, int columns, int current)
 
 	//Skill ID
 	j = atoi(str[3]);
-	if (j <= 0 || j > MAX_SKILL_DB) //fixed Lupus
+	if (j <= 0 || j > MAX_SKILL_ID || !skill_get_index(j)) //fixed Lupus
 	{
 		if (mob_id < 0)
 			ShowError("mob_parse_row_mobskilldb: Invalid Skill ID (%d) for all mobs\n", j);

+ 11 - 9
src/map/npc.c

@@ -112,12 +112,12 @@ int npc_isnear_sub(struct block_list* bl, va_list args) {
 	if (skill_id > 0) { //If skill_id > 0 that means is used for INF2_NO_NEARNPC [Cydh]
 		uint16 idx = skill_get_index(skill_id);
 
-		if (idx > 0 && skill_db[idx].unit_nonearnpc_type) {
+		if (idx > 0 && skill_db[idx]->unit_nonearnpc_type) {
 			while (1) {
-				if (skill_db[idx].unit_nonearnpc_type&1 && nd->subtype == NPCTYPE_WARP) break;
-				if (skill_db[idx].unit_nonearnpc_type&2 && nd->subtype == NPCTYPE_SHOP) break;
-				if (skill_db[idx].unit_nonearnpc_type&4 && nd->subtype == NPCTYPE_SCRIPT) break;
-				if (skill_db[idx].unit_nonearnpc_type&8 && nd->subtype == NPCTYPE_TOMB) break;
+				if (skill_db[idx]->unit_nonearnpc_type&1 && nd->subtype == NPCTYPE_WARP) break;
+				if (skill_db[idx]->unit_nonearnpc_type&2 && nd->subtype == NPCTYPE_SHOP) break;
+				if (skill_db[idx]->unit_nonearnpc_type&4 && nd->subtype == NPCTYPE_SCRIPT) break;
+				if (skill_db[idx]->unit_nonearnpc_type&8 && nd->subtype == NPCTYPE_TOMB) break;
 					return 0;
 			}
 		}
@@ -1687,8 +1687,9 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list)
 	// custom merchant shop exp bonus
 	if( battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_DISCOUNT)) > 0 )
 	{
-		if( sd->status.skill[MC_DISCOUNT].flag >= SKILL_FLAG_REPLACED_LV_0 )
-			skill = sd->status.skill[MC_DISCOUNT].flag - SKILL_FLAG_REPLACED_LV_0;
+		uint16 sk_idx = skill_get_index(MC_DISCOUNT);
+		if( sd->status.skill[sk_idx].flag >= SKILL_FLAG_REPLACED_LV_0 )
+			skill = sd->status.skill[sk_idx].flag - SKILL_FLAG_REPLACED_LV_0;
 
 		if( skill > 0 )
 		{
@@ -1850,8 +1851,9 @@ int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list)
 	// custom merchant shop exp bonus
 	if( battle_config.shop_exp > 0 && z > 0 && ( skill = pc_checkskill(sd,MC_OVERCHARGE) ) > 0)
 	{
-		if( sd->status.skill[MC_OVERCHARGE].flag >= SKILL_FLAG_REPLACED_LV_0 )
-			skill = sd->status.skill[MC_OVERCHARGE].flag - SKILL_FLAG_REPLACED_LV_0;
+		uint16 sk_idx = skill_get_index(MC_OVERCHARGE);
+		if( sd->status.skill[sk_idx].flag >= SKILL_FLAG_REPLACED_LV_0 )
+			skill = sd->status.skill[sk_idx].flag - SKILL_FLAG_REPLACED_LV_0;
 
 		if( skill > 0 )
 		{

+ 352 - 270
src/map/pc.c

@@ -934,12 +934,12 @@ bool pc_adoption(struct map_session_data *p1_sd, struct map_session_data *p2_sd,
 		clif_updatestatus(b_sd, SP_JOBEXP);
 
 		// Baby Skills
-		pc_skill(b_sd, WE_BABY, 1, 0);
-		pc_skill(b_sd, WE_CALLPARENT, 1, 0);
+		pc_skill(b_sd, WE_BABY, 1, ADDSKILL_PERMANENT);
+		pc_skill(b_sd, WE_CALLPARENT, 1, ADDSKILL_PERMANENT);
 
 		// Parents Skills
-		pc_skill(p1_sd, WE_CALLBABY, 1, 0);
-		pc_skill(p2_sd, WE_CALLBABY, 1, 0);
+		pc_skill(p1_sd, WE_CALLBABY, 1, ADDSKILL_PERMANENT);
+		pc_skill(p2_sd, WE_CALLBABY, 1, ADDSKILL_PERMANENT);
 
 		return true;
 	}
@@ -1246,6 +1246,7 @@ bool pc_authok(struct map_session_data *sd, uint32 login_id2, time_t expiration_
 	 * Check if player have any item cooldowns on
 	 **/
 	pc_itemcd_do(sd,true);
+	pc_validate_skill(sd);
 
 #ifdef BOUND_ITEMS
 	// Party bound item check
@@ -1356,7 +1357,7 @@ void pc_reg_received(struct map_session_data *sd)
 
 	if ((i = pc_checkskill(sd,RG_PLAGIARISM)) > 0) {
 		sd->cloneskill_idx = skill_get_index(pc_readglobalreg(sd,SKILL_VAR_PLAGIARISM));
-		if (sd->cloneskill_idx >= 0) {
+		if (sd->cloneskill_idx > 0) {
 			sd->status.skill[sd->cloneskill_idx].id = pc_readglobalreg(sd,SKILL_VAR_PLAGIARISM);
 			sd->status.skill[sd->cloneskill_idx].lv = pc_readglobalreg(sd,SKILL_VAR_PLAGIARISM_LV);
 			if (sd->status.skill[sd->cloneskill_idx].lv > i)
@@ -1366,7 +1367,7 @@ void pc_reg_received(struct map_session_data *sd)
 	}
 	if ((i = pc_checkskill(sd,SC_REPRODUCE)) > 0) {
 		sd->reproduceskill_idx = skill_get_index(pc_readglobalreg(sd,SKILL_VAR_REPRODUCE));
-		if (sd->reproduceskill_idx >= 0) {
+		if (sd->reproduceskill_idx > 0) {
 			sd->status.skill[sd->reproduceskill_idx].id = pc_readglobalreg(sd,SKILL_VAR_REPRODUCE);
 			sd->status.skill[sd->reproduceskill_idx].lv = pc_readglobalreg(sd,SKILL_VAR_REPRODUCE_LV);
 			if (i < sd->status.skill[sd->reproduceskill_idx].lv)
@@ -1449,21 +1450,20 @@ void pc_reg_received(struct map_session_data *sd)
 
 static int pc_calc_skillpoint(struct map_session_data* sd)
 {
-	uint16 i, skill_point=0;
+	uint16 i, skill_point = 0;
 
 	nullpo_ret(sd);
 
-	for(i=1;i<MAX_SKILL;i++){
-		uint8 skill_lv;
-		if( (skill_lv = pc_checkskill(sd,i)) > 0) {
-			uint16 inf2 = skill_get_inf2(i);
-			if((!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) &&
+	for(i = 1; i < MAX_SKILL; i++) {
+		if( sd->status.skill[i].id && sd->status.skill[i].lv > 0) {
+			uint16 inf2 = skill_get_inf2(sd->status.skill[i].id);
+			if ((!(inf2&INF2_QUEST_SKILL) || battle_config.quest_skill_learn) &&
 				!(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL)) //Do not count wedding/link skills. [Skotlex]
-				) {
+				)
+			{
 				if(sd->status.skill[i].flag == SKILL_FLAG_PERMANENT)
-					skill_point += skill_lv;
-				else
-				if(sd->status.skill[i].flag == SKILL_FLAG_REPLACED_LV_0)
+					skill_point += sd->status.skill[i].lv;
+				else if(sd->status.skill[i].flag >= SKILL_FLAG_REPLACED_LV_0)
 					skill_point += (sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0);
 			}
 		}
@@ -1472,6 +1472,57 @@ static int pc_calc_skillpoint(struct map_session_data* sd)
 	return skill_point;
 }
 
+static bool pc_grant_allskills(struct map_session_data *sd, bool addlv) {
+	uint16 i = 0;
+
+	if (!sd || !pc_has_permission(sd, PC_PERM_ALL_SKILL) || !SKILL_MAX_DB())
+		return false;
+
+	/**
+	* Dummy skills must NOT be added here otherwise they'll be displayed in the,
+	* skill tree and since they have no icons they'll give resource errors
+	* Get ALL skills except npc/guild ones. [Skotlex]
+	* Don't add SG_DEVIL [Komurka] and MO_TRIPLEATTACK and RG_SNATCHER [ultramage]
+	**/
+	for( i = 0; i < MAX_SKILL; i++ ) {
+		uint16 skill_id = skill_idx2id(i);
+		if (!skill_id || (skill_get_inf2(skill_id)&(INF2_NPC_SKILL|INF2_GUILD_SKILL)))
+			continue;
+		switch (skill_id) {
+			case SM_SELFPROVOKE:
+			case AB_DUPLELIGHT_MELEE:
+			case AB_DUPLELIGHT_MAGIC:
+			case WL_CHAINLIGHTNING_ATK:
+			case WL_TETRAVORTEX_FIRE:
+			case WL_TETRAVORTEX_WATER:
+			case WL_TETRAVORTEX_WIND:
+			case WL_TETRAVORTEX_GROUND:
+			case WL_SUMMON_ATK_FIRE:
+			case WL_SUMMON_ATK_WIND:
+			case WL_SUMMON_ATK_WATER:
+			case WL_SUMMON_ATK_GROUND:
+			case LG_OVERBRAND_BRANDISH:
+			case LG_OVERBRAND_PLUSATK:
+			case WM_SEVERE_RAINSTORM_MELEE:
+			case RL_R_TRIP_PLUSATK:
+			case SG_DEVIL:
+			case MO_TRIPLEATTACK:
+			case RG_SNATCHER:
+				continue;
+			default:
+				{
+					uint8 lv = (uint8)skill_get_max(skill_id);
+					if (lv > 0) {
+						sd->status.skill[i].id = skill_id;
+						if (addlv)
+							sd->status.skill[i].lv = lv;
+					}
+				}
+				break;
+		}
+	}
+	return true;
+}
 
 /*==========================================
  * Calculation of skill level.
@@ -1479,8 +1530,8 @@ static int pc_calc_skillpoint(struct map_session_data* sd)
  *------------------------------------------*/
 void pc_calc_skilltree(struct map_session_data *sd)
 {
-	int i,flag;
-	int c=0;
+	int i, flag;
+	int c = 0;
 
 	nullpo_retv(sd);
 	i = pc_calc_skilltree_normalize_job(sd);
@@ -1494,44 +1545,57 @@ void pc_calc_skilltree(struct map_session_data *sd)
 
 	for( i = 0; i < MAX_SKILL; i++ ) {
 		if( sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED ) //Don't touch these
+		{
 			sd->status.skill[i].id = 0; //First clear skills.
+		}
 		/* permanent skills that must be re-checked */
 		if( sd->status.skill[i].flag == SKILL_FLAG_PERM_GRANTED ) {
-			switch( i ) {
+			uint16 sk_id = skill_idx2id(i);
+			if (!sk_id) {
+				sd->status.skill[i].id = 0;
+				sd->status.skill[i].lv = 0;
+				sd->status.skill[i].flag = SKILL_FLAG_PERMANENT;
+				continue;
+			}
+			switch (sk_id) {
 				case NV_TRICKDEAD:
 					if( (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE ) {
-							sd->status.skill[i].id = 0;
-							sd->status.skill[i].lv = 0;
-							sd->status.skill[i].flag = SKILL_FLAG_PERMANENT;
+						sd->status.skill[i].id = 0;
+						sd->status.skill[i].lv = 0;
+						sd->status.skill[i].flag = SKILL_FLAG_PERMANENT;
 					}
 					break;
 			}
 		}
 	}
 
-	for( i = 0; i < MAX_SKILL; i++ )
-	{
-		if( sd->status.skill[i].flag != SKILL_FLAG_PERMANENT && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED && sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED )
-		{ // Restore original level of skills after deleting earned skills.
+	for( i = 0; i < MAX_SKILL; i++ ) {
+		uint16 skill_id = 0;
+
+		// Restore original level of skills after deleting earned skills.
+		if( sd->status.skill[i].flag != SKILL_FLAG_PERMANENT && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED && sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED ) {
 			sd->status.skill[i].lv = (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY) ? 0 : sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0;
 			sd->status.skill[i].flag = SKILL_FLAG_PERMANENT;
 		}
 
-		if( sd->sc.count && sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_BARDDANCER && i >= DC_HUMMING && i<= DC_SERVICEFORYOU )
-		{ //Enable Bard/Dancer spirit linked skills.
-			if( sd->status.sex )
-			{ //Link dancer skills to bard.
+		//Enable Bard/Dancer spirit linked skills.
+		if (!(skill_id = skill_idx2id(i)) || skill_id < DC_HUMMING || skill_id > DC_SERVICEFORYOU)
+			continue;
+
+		if( &sd->sc && sd->sc.count && sd->sc.data[SC_SPIRIT] && sd->sc.data[SC_SPIRIT]->val2 == SL_BARDDANCER ) {
+			//Link Dancer skills to bard.
+			if( sd->status.sex ) {
 				if( sd->status.skill[i-8].lv < 10 )
 					continue;
-				sd->status.skill[i].id = i;
+				sd->status.skill[i].id = skill_id;
 				sd->status.skill[i].lv = sd->status.skill[i-8].lv; // Set the level to the same as the linking skill
 				sd->status.skill[i].flag = SKILL_FLAG_TEMPORARY; // Tag it as a non-savable, non-uppable, bonus skill
 			}
-			else
-			{ //Link bard skills to dancer.
+			//Link Bard skills to dancer.
+			else {
 				if( sd->status.skill[i].lv < 10 )
 					continue;
-				sd->status.skill[i-8].id = i - 8;
+				sd->status.skill[i-8].id = skill_id - 8;
 				sd->status.skill[i-8].lv = sd->status.skill[i].lv; // Set the level to the same as the linking skill
 				sd->status.skill[i-8].flag = SKILL_FLAG_TEMPORARY; // Tag it as a non-savable, non-uppable, bonus skill
 			}
@@ -1541,105 +1605,86 @@ void pc_calc_skilltree(struct map_session_data *sd)
 	// Removes Taekwon Ranker skill bonus
 	if ((sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON) {
 		uint16 c_ = pc_class2idx(JOB_TAEKWON);
+
 		for (i = 0; i < MAX_SKILL_TREE; i++) {
-			uint16 x = skill_get_index(skill_tree[c_][i].id), skid = sd->status.skill[x].id;
-			if (skid && x > 0 && sd->status.skill[x].flag != SKILL_FLAG_PLAGIARIZED && sd->status.skill[x].flag != SKILL_FLAG_PERM_GRANTED) {
-				if (skid == NV_BASIC || skid == NV_FIRSTAID || skid == WE_CALLBABY)
-					continue;
-				sd->status.skill[x].id = 0;
-			}
-		}
-	}
+			uint16 sk_id = skill_tree[c_][i].id;
+			uint16 sk_idx = 0;
 
-	if( pc_has_permission(sd, PC_PERM_ALL_SKILL) ) {
-		for( i = 0; i < MAX_SKILL; i++ ) {
-			switch(i) {
-				/**
-				 * Dummy skills must be added here otherwise they'll be displayed in the,
-				 * skill tree and since they have no icons they'll give resource errors
-				 **/
-				case SM_SELFPROVOKE:
-				case AB_DUPLELIGHT_MELEE:
-				case AB_DUPLELIGHT_MAGIC:
-				case WL_CHAINLIGHTNING_ATK:
-				case WL_TETRAVORTEX_FIRE:
-				case WL_TETRAVORTEX_WATER:
-				case WL_TETRAVORTEX_WIND:
-				case WL_TETRAVORTEX_GROUND:
-				case WL_SUMMON_ATK_FIRE:
-				case WL_SUMMON_ATK_WIND:
-				case WL_SUMMON_ATK_WATER:
-				case WL_SUMMON_ATK_GROUND:
-				case LG_OVERBRAND_BRANDISH:
-				case LG_OVERBRAND_PLUSATK:
-				case WM_SEVERE_RAINSTORM_MELEE:
-				case RL_R_TRIP_PLUSATK:
+			if (!sk_id || !(sk_idx = skill_get_index(skill_tree[c_][i].id)))
+				continue;
+
+			if (sd->status.skill[sk_idx].flag != SKILL_FLAG_PLAGIARIZED && sd->status.skill[sk_idx].flag != SKILL_FLAG_PERM_GRANTED) {
+				if (sk_id == NV_BASIC || sk_id == NV_FIRSTAID || sk_id == WE_CALLBABY)
 					continue;
-				default:
-					break;
+				sd->status.skill[sk_idx].id = 0;
+				sd->status.skill[sk_idx].lv = 0;
+				sd->status.skill[sk_idx].flag = SKILL_FLAG_PERMANENT;
 			}
-			if( skill_get_inf2(i)&(INF2_NPC_SKILL|INF2_GUILD_SKILL) )
-				continue; //Only skills you can't have are npc/guild ones
-			if( skill_get_max(i) > 0 )
-				sd->status.skill[i].id = i;
 		}
-		return;
 	}
 
+	// Grant all skills
+	pc_grant_allskills(sd, false);
+
 	do {
-		short skid=0;
+		uint16 skid = 0;
+
 		flag = 0;
-		for( i = 0; i < MAX_SKILL_TREE && (skid = skill_tree[c][i].id) > 0; i++ )
-		{
-			int f;
-			if( sd->status.skill[skid].id )
+		for (i = 0; i < MAX_SKILL_TREE && (skid = skill_tree[c][i].id) > 0; i++) {
+			bool fail = false;
+			uint16 sk_idx = skill_get_index(skid);
+
+			if (sd->status.skill[sk_idx].id)
 				continue; //Skill already known.
 
-			f = 1;
-			if(!battle_config.skillfree) {
-				int j;
+			if (!battle_config.skillfree) {
+				uint8 j;
+
+				// Checking required skills
 				for(j = 0; j < MAX_PC_SKILL_REQUIRE; j++) {
-					int k;
-					if((k=skill_tree[c][i].need[j].id))
-					{
-						if (sd->status.skill[k].id == 0 || sd->status.skill[k].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[k].flag == SKILL_FLAG_PLAGIARIZED)
-							k = 0; //Not learned.
-						else
-						if (sd->status.skill[k].flag >= SKILL_FLAG_REPLACED_LV_0) //Real learned level
-							k = sd->status.skill[skill_tree[c][i].need[j].id].flag - SKILL_FLAG_REPLACED_LV_0;
+					uint16 sk_need_id = skill_tree[c][i].need[j].id;
+					uint16 sk_need_idx = 0;
+
+					if (sk_need_id && (sk_need_idx = skill_get_index(sk_need_id))) {
+						short sk_need = sk_need_id;
+
+						if (sd->status.skill[sk_need_idx].id == 0 || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_PLAGIARIZED)
+							sk_need = 0; //Not learned.
+						else if (sd->status.skill[sk_need_idx].flag >= SKILL_FLAG_REPLACED_LV_0) //Real learned level
+							sk_need = sd->status.skill[sk_need_idx].flag - SKILL_FLAG_REPLACED_LV_0;
 						else
-							k = pc_checkskill(sd,k);
-						if (k < skill_tree[c][i].need[j].lv)
-						{
-							f = 0;
+							sk_need = pc_checkskill(sd,sk_need_id);
+
+						if (sk_need < skill_tree[c][i].need[j].lv) {
+							fail = true;
 							break;
 						}
 					}
 				}
-				if( sd->status.job_level < skill_tree[c][i].joblv ) { //We need to get the actual class in this case
+
+				if (sd->status.job_level < skill_tree[c][i].joblv) { //We need to get the actual class in this case
 					int class_ = pc_mapid2jobid(sd->class_, sd->status.sex);
 					class_ = pc_class2idx(class_);
 					if (class_ == c || (class_ != c && sd->status.job_level < skill_tree[class_][i].joblv))
-						f = 0; // job level requirement wasn't satisfied
+						fail = true; // job level requirement wasn't satisfied
 				}
 			}
 
-			if( f ) {
-				int inf2;
-				inf2 = skill_get_inf2(skid);
+			if (!fail) {
+				int inf2 = skill_get_inf2(skid);
 
-				if(!sd->status.skill[skid].lv && (
+				if (!sd->status.skill[sk_idx].lv && (
 					(inf2&INF2_QUEST_SKILL && !battle_config.quest_skill_learn) ||
 					inf2&INF2_WEDDING_SKILL ||
 					(inf2&INF2_SPIRIT_SKILL && !sd->sc.data[SC_SPIRIT])
 				))
 					continue; //Cannot be learned via normal means. Note this check DOES allows raising already known skills.
 
-				sd->status.skill[skid].id = skid;
+				sd->status.skill[sk_idx].id = skid;
 
 				if(inf2&INF2_SPIRIT_SKILL) { //Spirit skills cannot be learned, they will only show up on your tree when you get buffed.
-					sd->status.skill[skid].lv = 1; // need to manually specify a skill level
-					sd->status.skill[skid].flag = SKILL_FLAG_TEMPORARY; //So it is not saved, and tagged as a "bonus" skill.
+					sd->status.skill[sk_idx].lv = 1; // need to manually specify a skill level
+					sd->status.skill[sk_idx].flag = SKILL_FLAG_TEMPORARY; //So it is not saved, and tagged as a "bonus" skill.
 				}
 				flag = 1; // skill list has changed, perform another pass
 			}
@@ -1647,7 +1692,7 @@ void pc_calc_skilltree(struct map_session_data *sd)
 	} while(flag);
 
 	if( c > 0 && sd->status.skill_point == 0 && pc_is_taekwon_ranker(sd) ) {
-		short skid=0;
+		unsigned short skid = 0;
 		/* Taekwon Ranker Bonus Skill Tree
 		============================================
 		- Grant All Taekwon Tree, but only as Bonus Skills in case they drop from ranking.
@@ -1655,25 +1700,28 @@ void pc_calc_skilltree(struct map_session_data *sd)
 		- (sd->status.skill_point == 0) to wait until all skill points are assigned to avoid problems with Job Change quest. */
 
 		for( i = 0; i < MAX_SKILL_TREE && (skid = skill_tree[c][i].id) > 0; i++ ) {
+			uint16 sk_idx = 0;
+			if (!(sk_idx = skill_get_index(skid)))
+				continue;
 			if( (skill_get_inf2(skid)&(INF2_QUEST_SKILL|INF2_WEDDING_SKILL)) )
 				continue; //Do not include Quest/Wedding skills.
-			if( sd->status.skill[skid].id == 0 ) { //do we really want skid as index ? //Lighta
-				sd->status.skill[skid].id = skid;
-				sd->status.skill[skid].flag = SKILL_FLAG_TEMPORARY; // So it is not saved, and tagged as a "bonus" skill.
+			if( sd->status.skill[sk_idx].id == 0 ) { //do we really want skid as index ? //Lighta
+				sd->status.skill[sk_idx].id = skid;
+				sd->status.skill[sk_idx].flag = SKILL_FLAG_TEMPORARY; // So it is not saved, and tagged as a "bonus" skill.
 			} else if( skid != NV_BASIC )
-				sd->status.skill[skid].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[skid].lv; // Remember original level
-			sd->status.skill[skid].lv = skill_tree_get_max(skid, sd->status.class_);
+				sd->status.skill[sk_idx].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[sk_idx].lv; // Remember original level
+			sd->status.skill[sk_idx].lv = skill_tree_get_max(skid, sd->status.class_);
 		}
 	}
 }
 
 //Checks if you can learn a new skill after having leveled up a skill.
-static void pc_check_skilltree(struct map_session_data *sd, int skill)
+static void pc_check_skilltree(struct map_session_data *sd)
 {
-	int i,id=0,flag;
-	int c=0;
+	int i, flag = 0;
+	int c = 0;
 
-	if(battle_config.skillfree)
+	if (battle_config.skillfree)
 		return; //Function serves no purpose if this is set
 
 	i = pc_calc_skilltree_normalize_job(sd);
@@ -1683,44 +1731,55 @@ static void pc_check_skilltree(struct map_session_data *sd, int skill)
 		return;
 	}
 	c = pc_class2idx(c);
+
 	do {
+		uint16 skid = 0;
+
 		flag = 0;
-		for( i = 0; i < MAX_SKILL_TREE && (id=skill_tree[c][i].id)>0; i++ )
-		{
-			int j, f = 1;
-			if( sd->status.skill[id].id ) //Already learned
+		for (i = 0; i < MAX_SKILL_TREE && (skid = skill_tree[c][i].id) > 0; i++ ) {
+			uint16 sk_idx = skill_get_index(skid);
+			bool fail = false;
+			uint8 j = 0;
+
+			if (sd->status.skill[sk_idx].id) //Already learned
 				continue;
-			for( j = 0; j < MAX_PC_SKILL_REQUIRE; j++ ){
-				int k = skill_tree[c][i].need[j].id;
-				if( k != 0 ){
-					if( sd->status.skill[k].id == 0 || sd->status.skill[k].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[k].flag == SKILL_FLAG_PLAGIARIZED )
-						k = 0; //Not learned.
-					else
-					if( sd->status.skill[k].flag >= SKILL_FLAG_REPLACED_LV_0) //Real lerned level
-						k = sd->status.skill[skill_tree[c][i].need[j].id].flag - SKILL_FLAG_REPLACED_LV_0;
+
+			// Checking required skills
+			for (j = 0; j < MAX_PC_SKILL_REQUIRE; j++) {
+				uint16 sk_need_id = skill_tree[c][i].need[j].id;
+				uint16 sk_need_idx = 0;
+
+				if (sk_need_id && (sk_need_idx = skill_get_index(sk_need_id))) {
+					short sk_need = sk_need_id;
+
+					if (sd->status.skill[sk_need_idx].id == 0 || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[sk_need_idx].flag == SKILL_FLAG_PLAGIARIZED)
+						sk_need = 0; //Not learned.
+					else if (sd->status.skill[sk_need_idx].flag >= SKILL_FLAG_REPLACED_LV_0) //Real lerned level
+						sk_need = sd->status.skill[sk_need_idx].flag - SKILL_FLAG_REPLACED_LV_0;
 					else
-						k = pc_checkskill(sd,k);
-					if( k < skill_tree[c][i].need[j].lv )
-					{
-						f = 0;
+						sk_need = pc_checkskill(sd,sk_need_id);
+
+					if (sk_need < skill_tree[c][i].need[j].lv) {
+						fail = true;
 						break;
 					}
 				}
 			}
-			if( !f )
+
+			if( fail )
 				continue;
 			if( sd->status.job_level < skill_tree[c][i].joblv )
 				continue;
 
-			j = skill_get_inf2(id);
-			if( !sd->status.skill[id].lv && (
+			j = skill_get_inf2(skid);
+			if( !sd->status.skill[sk_idx].lv && (
 				(j&INF2_QUEST_SKILL && !battle_config.quest_skill_learn) ||
 				j&INF2_WEDDING_SKILL ||
 				(j&INF2_SPIRIT_SKILL && !sd->sc.data[SC_SPIRIT])
 			) )
 				continue; //Cannot be learned via normal means.
 
-			sd->status.skill[id].id = id;
+			sd->status.skill[sk_idx].id = skid;
 			flag = 1;
 		}
 	} while(flag);
@@ -1732,14 +1791,12 @@ void pc_clean_skilltree(struct map_session_data *sd)
 {
 	uint16 i;
 	for (i = 0; i < MAX_SKILL; i++){
-		if (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[i].flag == SKILL_FLAG_PLAGIARIZED)
-		{
+		if (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY || sd->status.skill[i].flag == SKILL_FLAG_PLAGIARIZED) {
 			sd->status.skill[i].id = 0;
 			sd->status.skill[i].lv = 0;
 			sd->status.skill[i].flag = SKILL_FLAG_PERMANENT;
 		}
-		else
-		if (sd->status.skill[i].flag == SKILL_FLAG_REPLACED_LV_0){
+		else if (sd->status.skill[i].flag >= SKILL_FLAG_REPLACED_LV_0){
 			sd->status.skill[i].lv = sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0;
 			sd->status.skill[i].flag = SKILL_FLAG_PERMANENT;
 		}
@@ -3833,74 +3890,78 @@ void pc_bonus5(struct map_session_data *sd,int type,int type2,int type3,int type
  *	2 - Like 1, except the level granted can stack with previously learned level.
  *	4 - Like 0, except the skill will ignore skill tree (saves through job changes and resets).
  *------------------------------------------*/
-int pc_skill(TBL_PC* sd, int id, int level, int flag)
-{
+bool pc_skill(TBL_PC* sd, uint16 skill_id, int level, enum e_addskill_type type) {
+	uint16 idx = 0;
 	nullpo_ret(sd);
 
-	if( id <= 0 || id >= MAX_SKILL || skill_db[id].name == NULL) {
-		ShowError("pc_skill: Skill with id %d does not exist in the skill database\n", id);
-		return 0;
+	if (!skill_id || !(idx = skill_get_index(skill_id))) {
+		ShowError("pc_skill: Skill with id %d does not exist in the skill database\n", skill_id);
+		return false;
 	}
-	if( level > MAX_SKILL_LEVEL ) {
+	if (level > MAX_SKILL_LEVEL) {
 		ShowError("pc_skill: Skill level %d too high. Max lv supported is %d\n", level, MAX_SKILL_LEVEL);
-		return 0;
+		return false;
 	}
-	if( flag == 2 && sd->status.skill[id].lv + level > MAX_SKILL_LEVEL ) {
-		ShowError("pc_skill: Skill level bonus %d too high. Max lv supported is %d. Curr lv is %d\n", level, MAX_SKILL_LEVEL, sd->status.skill[id].lv);
-		return 0;
+	if (type == ADDSKILL_TEMP_ADDLEVEL && sd->status.skill[idx].lv + level > MAX_SKILL_LEVEL) {
+		ShowWarning("pc_skill: Skill level bonus %d too high. Max lv supported is %d. Curr lv is %d. Set to max level.\n", level, MAX_SKILL_LEVEL, sd->status.skill[idx].lv);
+		level = MAX_SKILL_LEVEL - sd->status.skill[idx].lv;
 	}
 
-	switch( flag ){
-		case 0: //Set skill data overwriting whatever was there before.
-			sd->status.skill[id].id   = id;
-			sd->status.skill[id].lv   = level;
-			sd->status.skill[id].flag = SKILL_FLAG_PERMANENT;
-			if( level == 0 ) { //Remove skill.
-				sd->status.skill[id].id = 0;
-				clif_deleteskill(sd,id);
+	switch (type) {
+		case ADDSKILL_PERMANENT: //Set skill data overwriting whatever was there before.
+			sd->status.skill[idx].id   = skill_id;
+			sd->status.skill[idx].lv   = level;
+			sd->status.skill[idx].flag = SKILL_FLAG_PERMANENT;
+			if (level == 0) { //Remove skill.
+				sd->status.skill[idx].id = 0;
+				clif_deleteskill(sd,skill_id);
 			} else
-				clif_addskill(sd,id);
-			if( !skill_get_inf(id) ) //Only recalculate for passive skills.
+				clif_addskill(sd,skill_id);
+			if (!skill_get_inf(skill_id)) //Only recalculate for passive skills.
 				status_calc_pc(sd, SCO_NONE);
 			break;
-		case 1: //Item bonus skill.
-			if( sd->status.skill[id].id == id ) {
-				if( sd->status.skill[id].lv >= level )
-					return 0;
-				if( sd->status.skill[id].flag == SKILL_FLAG_PERMANENT ) //Non-granted skill, store it's level.
-					sd->status.skill[id].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[id].lv;
+
+		case ADDSKILL_TEMP: //Item bonus skill.
+			if (sd->status.skill[idx].id != 0) {
+				if (sd->status.skill[idx].lv >= level)
+					return true;
+				if (sd->status.skill[idx].flag == SKILL_FLAG_PERMANENT) //Non-granted skill, store it's level.
+					sd->status.skill[idx].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[idx].lv;
 			} else {
-				sd->status.skill[id].id   = id;
-				sd->status.skill[id].flag = SKILL_FLAG_TEMPORARY;
+				sd->status.skill[idx].id   = skill_id;
+				sd->status.skill[idx].flag = SKILL_FLAG_TEMPORARY;
 			}
-			sd->status.skill[id].lv = level;
+			sd->status.skill[idx].lv = level;
 			break;
-		case 2: //Add skill bonus on top of what you had.
-			if( sd->status.skill[id].id == id ) {
-				if( sd->status.skill[id].flag == SKILL_FLAG_PERMANENT )
-					sd->status.skill[id].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[id].lv; // Store previous level.
+
+		case ADDSKILL_TEMP_ADDLEVEL: //Add skill bonus on top of what you had.
+			if (sd->status.skill[idx].id != 0) {
+				if (sd->status.skill[idx].flag == SKILL_FLAG_PERMANENT)
+					sd->status.skill[idx].flag = SKILL_FLAG_REPLACED_LV_0 + sd->status.skill[idx].lv; // Store previous level.
 			} else {
-				sd->status.skill[id].id   = id;
-				sd->status.skill[id].flag = SKILL_FLAG_TEMPORARY; //Set that this is a bonus skill.
+				sd->status.skill[idx].id   = skill_id;
+				sd->status.skill[idx].flag = SKILL_FLAG_TEMPORARY; //Set that this is a bonus skill.
 			}
-			sd->status.skill[id].lv += level;
-			break;
-		case 4: //Permanent granted skills ignore the skill tree
-			sd->status.skill[id].id   = id;
-			sd->status.skill[id].lv   = level;
-			sd->status.skill[id].flag = SKILL_FLAG_PERM_GRANTED;
-			if( level == 0 ) { //Remove skill.
-				sd->status.skill[id].id = 0;
-				clif_deleteskill(sd,id);
+			sd->status.skill[idx].lv += level;
+			break;
+
+		case ADDSKILL_PERMANENT_GRANTED: //Permanent granted skills ignore the skill tree
+			sd->status.skill[idx].id   = skill_id;
+			sd->status.skill[idx].lv   = level;
+			sd->status.skill[idx].flag = SKILL_FLAG_PERM_GRANTED;
+			if (level == 0) { //Remove skill.
+				sd->status.skill[idx].id = 0;
+				clif_deleteskill(sd,skill_id);
 			} else
-				clif_addskill(sd,id);
-			if( !skill_get_inf(id) ) //Only recalculate for passive skills.
+				clif_addskill(sd,skill_id);
+			if (!skill_get_inf(skill_id)) //Only recalculate for passive skills.
 				status_calc_pc(sd, SCO_NONE);
 			break;
-		default: //Unknown flag?
-			return 0;
+
+		default:
+			return false;
 	}
-	return 1;
+	return true;
 }
 /*==========================================
  * Append a card to an item ?
@@ -5446,8 +5507,8 @@ int pc_get_skillcooldown(struct map_session_data *sd, uint16 skill_id, uint16 sk
 	int cooldown = 0, cooldownlen = ARRAYLENGTH(sd->skillcooldown);
 	
 	if (!idx) return 0;
-	if (skill_db[idx].cooldown[skill_lv - 1])
-		cooldown = skill_db[idx].cooldown[skill_lv - 1];
+	if (skill_db[idx]->cooldown[skill_lv - 1])
+		cooldown = skill_db[idx]->cooldown[skill_lv - 1];
 
 	ARR_FIND(0, cooldownlen, i, sd->skillcooldown[i].id == skill_id);
 	if (i < cooldownlen) {
@@ -5460,24 +5521,23 @@ int pc_get_skillcooldown(struct map_session_data *sd, uint16 skill_id, uint16 sk
 /*==========================================
  * Return player sd skill_lv learned for given skill
  *------------------------------------------*/
-uint8 pc_checkskill(struct map_session_data *sd,uint16 skill_id)
+uint8 pc_checkskill(struct map_session_data *sd, uint16 skill_id)
 {
-	if(sd == NULL) return 0;
-	if( skill_id >= GD_SKILLBASE && skill_id < GD_MAX ) {
+	uint16 i = 0, idx = 0;
+	if (sd == NULL)
+		return 0;
+	if ((idx = skill_get_index(skill_id)) == 0) {
+		ShowError("pc_checkskill: Invalid skill id %d (char_id=%d).\n", skill_id, sd->status.char_id);
+		return 0;
+	}
+	if (SKILL_CHK_GUILD(skill_id) ) {
 		struct guild *g;
 
 		if( sd->status.guild_id>0 && (g=sd->guild)!=NULL)
 			return guild_checkskill(g,skill_id);
 		return 0;
-	} else if(skill_id >= ARRAYLENGTH(sd->status.skill) ) {
-		ShowError("pc_checkskill: Invalid skill id %d (char_id=%d).\n", skill_id, sd->status.char_id);
-		return 0;
 	}
-
-	if(sd->status.skill[skill_id].id == skill_id)
-		return (sd->status.skill[skill_id].lv);
-
-	return 0;
+	return sd->status.skill[idx].lv;
 }
 
 /**
@@ -6649,52 +6709,57 @@ int pc_statusup2(struct map_session_data* sd, int type, int val)
  * Update skill_lv for player sd
  * Skill point allocation
  *------------------------------------------*/
-int pc_skillup(struct map_session_data *sd,uint16 skill_id)
+void pc_skillup(struct map_session_data *sd,uint16 skill_id)
 {
-	nullpo_ret(sd);
+	uint16 idx = skill_get_index(skill_id);
 
-	if( skill_id >= GD_SKILLBASE && skill_id < GD_SKILLBASE+MAX_GUILDSKILL )
-	{
-		guild_skillup(sd, skill_id);
-		return 0;
+	nullpo_retv(sd);
+
+	if (!idx) {
+		if (skill_id)
+			ShowError("pc_skillup: Player attempts to level up invalid skill '%d'\n", skill_id);
+		return;
 	}
 
-	if( skill_id >= HM_SKILLBASE && skill_id < HM_SKILLBASE+MAX_HOMUNSKILL && sd->hd )
-	{
+	// Level up guild skill
+	if (SKILL_CHK_GUILD(skill_id)) {
+		guild_skillup(sd, skill_id);
+		return;
+	}
+	// Level up homunculus skill
+	else if (sd->hd && SKILL_CHK_HOMUN(skill_id)) {
 		hom_skillup(sd->hd, skill_id);
-		return 0;
+		return;
 	}
-
-	if(skill_id >= MAX_SKILL )
-		return 0;
-
-	if( sd->status.skill_point > 0 &&
-		sd->status.skill[skill_id].id &&
-		sd->status.skill[skill_id].flag == SKILL_FLAG_PERMANENT && //Don't allow raising while you have granted skills. [Skotlex]
-		sd->status.skill[skill_id].lv < skill_tree_get_max(skill_id, sd->status.class_) )
-	{
-		int lv,range, upgradable;
-		sd->status.skill[skill_id].lv++;
-		sd->status.skill_point--;
-		if( !skill_get_inf(skill_id) )
-			status_calc_pc(sd,SCO_NONE); // Only recalculate for passive skills.
-		else if( sd->status.skill_point == 0 && pc_is_taekwon_ranker(sd) )
-			pc_calc_skilltree(sd); // Required to grant all TK Ranker skills.
+	else {
+		if( sd->status.skill_point > 0 &&
+			sd->status.skill[idx].id &&
+			sd->status.skill[idx].flag == SKILL_FLAG_PERMANENT && //Don't allow raising while you have granted skills. [Skotlex]
+			sd->status.skill[idx].lv < skill_tree_get_max(skill_id, sd->status.class_) )
+		{
+			int lv, range, upgradable;
+			sd->status.skill[idx].lv++;
+			sd->status.skill_point--;
+			if( !skill_get_inf(skill_id) )
+				status_calc_pc(sd,SCO_NONE); // Only recalculate for passive skills.
+			else if( sd->status.skill_point == 0 && pc_is_taekwon_ranker(sd) )
+				pc_calc_skilltree(sd); // Required to grant all TK Ranker skills.
+			else
+				pc_check_skilltree(sd); // Check if a new skill can Lvlup
+
+			lv = sd->status.skill[idx].lv;
+			range = skill_get_range2(&sd->bl, skill_id, lv);
+			upgradable = (lv < skill_tree_get_max(sd->status.skill[idx].id, sd->status.class_)) ? 1 : 0;
+			clif_skillup(sd,skill_id,lv,range,upgradable);
+			clif_updatestatus(sd,SP_SKILLPOINT);
+			if( skill_id == GN_REMODELING_CART ) /* cart weight info was updated by status_calc_pc */
+				clif_updatestatus(sd,SP_CARTINFO);
+			if (!pc_has_permission(sd, PC_PERM_ALL_SKILL)) // may skill everything at any time anyways, and this would cause a huge slowdown
+				clif_skillinfoblock(sd);
+		}
 		else
-			pc_check_skilltree(sd, skill_id); // Check if a new skill can Lvlup
-
-		lv = sd->status.skill[skill_id].lv;
-		range = skill_get_range2(&sd->bl, skill_id, lv);
-		upgradable = (lv < skill_tree_get_max(sd->status.skill[skill_id].id, sd->status.class_)) ? 1 : 0;
-		clif_skillup(sd,skill_id,lv,range,upgradable);
-		clif_updatestatus(sd,SP_SKILLPOINT);
-		if( skill_id == GN_REMODELING_CART ) /* cart weight info was updated by status_calc_pc */
-			clif_updatestatus(sd,SP_CARTINFO);
-		if (!pc_has_permission(sd, PC_PERM_ALL_SKILL)) // may skill everything at any time anyways, and this would cause a huge slowdown
-			clif_skillinfoblock(sd);
+			ShowDebug("Skill Level up failed. ID:%d idx:%d (CID=%d. AID=%d)\n", skill_id, idx, sd->status.char_id, sd->status.account_id);
 	}
-
-	return 0;
 }
 
 /*==========================================
@@ -6706,7 +6771,7 @@ int pc_allskillup(struct map_session_data *sd)
 
 	nullpo_ret(sd);
 
-	for(i=0;i<MAX_SKILL;i++){
+	for (i = 0; i < MAX_SKILL; i++) {
 		if (sd->status.skill[i].flag != SKILL_FLAG_PERMANENT && sd->status.skill[i].flag != SKILL_FLAG_PERM_GRANTED && sd->status.skill[i].flag != SKILL_FLAG_PLAGIARIZED) {
 			sd->status.skill[i].lv = (sd->status.skill[i].flag == SKILL_FLAG_TEMPORARY) ? 0 : sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0;
 			sd->status.skill[i].flag = SKILL_FLAG_PERMANENT;
@@ -6715,34 +6780,23 @@ int pc_allskillup(struct map_session_data *sd)
 		}
 	}
 
-	if (pc_has_permission(sd, PC_PERM_ALL_SKILL))
-	{	//Get ALL skills except npc/guild ones. [Skotlex]
-		//and except SG_DEVIL [Komurka] and MO_TRIPLEATTACK and RG_SNATCHER [ultramage]
-		for(i=0;i<MAX_SKILL;i++){
-			switch( i ) {
-				case SG_DEVIL:
-				case MO_TRIPLEATTACK:
-				case RG_SNATCHER:
-					continue;
-				default:
-					if( !(skill_get_inf2(i)&(INF2_NPC_SKILL|INF2_GUILD_SKILL)) )
-						if ( ( sd->status.skill[i].lv = skill_get_max(i) ) )//Nonexistant skills should return a max of 0 anyway.
-							sd->status.skill[i].id = i;
-			}
-		}
-	} else {
-		int id;
-		for(i=0;i < MAX_SKILL_TREE && (id=skill_tree[pc_class2idx(sd->status.class_)][i].id)>0;i++){
-			int inf2 = skill_get_inf2(id);
+	if (!pc_grant_allskills(sd, true)) {
+		uint16 sk_id;
+		for (i = 0; i < MAX_SKILL_TREE && (sk_id = skill_tree[pc_class2idx(sd->status.class_)][i].id) > 0;i++){
+			int inf2 = 0;
+			uint16 sk_idx = 0;
+			if (!sk_id || !(sk_idx = skill_get_index(sk_id)))
+				continue;
+			inf2 = skill_get_inf2(sk_id);
 			if (
 				(inf2&INF2_QUEST_SKILL && !battle_config.quest_skill_learn) ||
 				(inf2&(INF2_WEDDING_SKILL|INF2_SPIRIT_SKILL)) ||
-				id==SG_DEVIL
+				sk_id == SG_DEVIL
 			)
 				continue; //Cannot be learned normally.
 
-			sd->status.skill[id].id = id;
-			sd->status.skill[id].lv = skill_tree_get_max(id, sd->status.class_);	// celest
+			sd->status.skill[sk_idx].id = sk_id;
+			sd->status.skill[sk_idx].lv = skill_tree_get_max(sk_id, sd->status.class_);	// celest
 		}
 	}
 	status_calc_pc(sd,SCO_NONE);
@@ -6782,8 +6836,8 @@ int pc_resetlvl(struct map_session_data* sd,int type)
 		if(sd->status.class_ == JOB_NOVICE_HIGH) {
 			sd->status.status_point=100;	// not 88 [celest]
 			// give platinum skills upon changing
-			pc_skill(sd,142,1,0);
-			pc_skill(sd,143,1,0);
+			pc_skill(sd,NV_FIRSTAID,1,ADDSKILL_PERMANENT);
+			pc_skill(sd,NV_TRICKDEAD,1,ADDSKILL_PERMANENT);
 		}
 	}
 
@@ -6957,9 +7011,11 @@ int pc_resetskill(struct map_session_data* sd, int flag)
 
 	for( i = 1; i < MAX_SKILL; i++ )
 	{
-		int lv = sd->status.skill[i].lv;
+		uint8 lv = sd->status.skill[i].lv;
 		int inf2;
-		if (lv < 1) continue;
+		uint16 skill_id = skill_idx2id(i);
+		if (lv == 0 || skill_id == 0)
+			continue;
 
 		inf2 = skill_get_inf2(i);
 
@@ -6967,7 +7023,7 @@ int pc_resetskill(struct map_session_data* sd, int flag)
 			continue;
 
 		// Don't reset trick dead if not a novice/baby
-		if( i == NV_TRICKDEAD && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE )
+		if( skill_id == NV_TRICKDEAD && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE )
 		{
 			sd->status.skill[i].lv = 0;
 			sd->status.skill[i].flag = SKILL_FLAG_PERMANENT;
@@ -6975,13 +7031,13 @@ int pc_resetskill(struct map_session_data* sd, int flag)
 		}
 
 		// do not reset basic skill
-		if( i == NV_BASIC && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE )
+		if( skill_id == NV_BASIC && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE )
 			continue;
 
 		if( sd->status.skill[i].flag == SKILL_FLAG_PERM_GRANTED )
 			continue;
 
-		if( flag&4 && !skill_ischangesex(i) )
+		if( flag&4 && !skill_ischangesex(skill_id) )
 			continue;
 
 		if( inf2&INF2_QUEST_SKILL && !battle_config.quest_skill_learn )
@@ -6996,7 +7052,7 @@ int pc_resetskill(struct map_session_data* sd, int flag)
 		if( sd->status.skill[i].flag == SKILL_FLAG_PERMANENT )
 			skill_point += lv;
 		else
-		if( sd->status.skill[i].flag == SKILL_FLAG_REPLACED_LV_0 )
+		if( sd->status.skill[i].flag >= SKILL_FLAG_REPLACED_LV_0 )
 			skill_point += (sd->status.skill[i].flag - SKILL_FLAG_REPLACED_LV_0);
 
 		if( !(flag&2) )
@@ -8030,26 +8086,26 @@ bool pc_jobchange(struct map_session_data *sd,int job, char upper)
 		pc_setglobalreg (sd, "jobchange_level_3rd", sd->change_level_3rd);
 	}
 
-	if(sd->cloneskill_idx >= 0) {
+	if(sd->cloneskill_idx > 0) {
 		if( sd->status.skill[sd->cloneskill_idx].flag == SKILL_FLAG_PLAGIARIZED ) {
 			sd->status.skill[sd->cloneskill_idx].id = 0;
 			sd->status.skill[sd->cloneskill_idx].lv = 0;
 			sd->status.skill[sd->cloneskill_idx].flag = SKILL_FLAG_PERMANENT;
 			clif_deleteskill(sd,pc_readglobalreg(sd,SKILL_VAR_PLAGIARISM));
 		}
-		sd->cloneskill_idx = -1;
+		sd->cloneskill_idx = 0;
 		pc_setglobalreg(sd,SKILL_VAR_PLAGIARISM, 0);
 		pc_setglobalreg(sd,SKILL_VAR_PLAGIARISM_LV, 0);
 	}
 
-	if(sd->reproduceskill_idx >= 0) {
+	if(sd->reproduceskill_idx > 0) {
 		if( sd->status.skill[sd->reproduceskill_idx].flag == SKILL_FLAG_PLAGIARIZED ) {
 			sd->status.skill[sd->reproduceskill_idx].id = 0;
 			sd->status.skill[sd->reproduceskill_idx].lv = 0;
 			sd->status.skill[sd->reproduceskill_idx].flag = SKILL_FLAG_PERMANENT;
 			clif_deleteskill(sd,pc_readglobalreg(sd,SKILL_VAR_REPRODUCE));
 		}
-		sd->reproduceskill_idx = -1;
+		sd->reproduceskill_idx = 0;
 		pc_setglobalreg(sd,SKILL_VAR_REPRODUCE,0);
 		pc_setglobalreg(sd,SKILL_VAR_REPRODUCE_LV,0);
 	}
@@ -11441,6 +11497,32 @@ uint64 pc_generate_unique_id(struct map_session_data *sd) {
 	return ((uint64)sd->status.char_id << 32) | sd->status.uniqueitem_counter++;
 }
 
+/**
+ * Validating skill from player after logged on
+ * @param sd
+ **/
+void pc_validate_skill(struct map_session_data *sd) {
+	if (sd) {
+		uint16 i = 0, count = 0;
+		struct s_skill tmp_skills[MAX_SKILL] = {{ 0 }};
+
+		memcpy(tmp_skills, sd->status.skill, sizeof(sd->status.skill));
+		memset(sd->status.skill, 0, sizeof(sd->status.skill));
+
+		for (i = 0; i < MAX_SKILL; i++) {
+			uint16 idx = 0;
+			if (tmp_skills[i].id == 0 || tmp_skills[i].lv == 0)
+				continue;
+			if ((idx = skill_get_index(tmp_skills[i].id))) {
+				memcpy(&sd->status.skill[idx], &tmp_skills[i], sizeof(tmp_skills[i]));
+				count++;
+			}
+			else
+				ShowWarning("pc_validate_skill: Removing invalid skill '%d' from player (AID=%d CID=%d).\n", tmp_skills[i].id, sd->status.account_id, sd->status.char_id);
+		}
+	}
+}
+
 /*==========================================
  * pc Init/Terminate
  *------------------------------------------*/

+ 20 - 8
src/map/pc.h

@@ -444,7 +444,7 @@ struct map_session_data {
 
 	short catch_target_class; // pet catching, stores a pet class to catch (short now) [zzo]
 
-	short spiritball, spiritball_old;
+	int8 spiritball, spiritball_old;
 	int spirit_timer[MAX_SPIRITBALL];
 	short talisman[ELE_POISON+1]; // There are actually 5 talisman Fire, Ice, Wind, Earth & Poison maybe because its color violet.
 	int talisman_timer[ELE_POISON+1][10];
@@ -669,6 +669,8 @@ enum weapon_type {
 	W_DOUBLE_SA, // sword + axe
 };
 
+#define WEAPON_TYPE_ALL ((1<<MAX_WEAPON_TYPE)-1)
+
 enum ammo_type {
 	A_ARROW = 1,
 	A_DAGGER,   //2
@@ -926,7 +928,15 @@ void pc_bonus2(struct map_session_data *sd, int type, int type2, int val);
 void pc_bonus3(struct map_session_data *sd, int type, int type2, int type3, int val);
 void pc_bonus4(struct map_session_data *sd, int type, int type2, int type3, int type4, int val);
 void pc_bonus5(struct map_session_data *sd, int type, int type2, int type3, int type4, int type5, int val);
-int pc_skill(struct map_session_data *sd, int id, int level, int flag);
+
+enum e_addskill_type {
+	ADDSKILL_PERMANENT			= 0,	///< Permanent skill. Remove the skill if level is 0
+	ADDSKILL_TEMP				= 1,	///< Temporary skill. If player learned the skill and the given level is higher, level will be replaced and learned level will be palced in skill flag. `flag = learned + SKILL_FLAG_REPLACED_LV_0; learned_level = level;`
+	ADDSKILL_TEMP_ADDLEVEL		= 2,	///< Like PCSKILL_TEMP, except the level will be stacked. `learned_level += level`. The flag is used to store original learned level
+	ADDSKILL_PERMANENT_GRANTED	= 3,	///< Grant permanent skill, ignore skill tree and learned level
+};
+
+bool pc_skill(struct map_session_data *sd, uint16 skill_id, int level, enum e_addskill_type type);
 
 int pc_insert_card(struct map_session_data *sd,int idx_card,int idx_equip);
 
@@ -953,7 +963,7 @@ int pc_need_status_point(struct map_session_data *,int,int);
 int pc_maxparameterincrease(struct map_session_data*,int);
 bool pc_statusup(struct map_session_data*,int,int);
 int pc_statusup2(struct map_session_data*,int,int);
-int pc_skillup(struct map_session_data*,uint16 skill_id);
+void pc_skillup(struct map_session_data*,uint16 skill_id);
 int pc_allskillup(struct map_session_data*);
 int pc_resetlvl(struct map_session_data*,int type);
 int pc_resetstate(struct map_session_data*);
@@ -1038,12 +1048,12 @@ int pc_mapid2jobid(unsigned short class_, int sex);	// Skotlex
 const char * job_name(int class_);
 
 struct skill_tree_entry {
-	short id;
-	unsigned char max;
-	unsigned char joblv;
+	uint16 id;
+	uint8 max;
+	uint8 joblv;
 	struct {
-		short id;
-		unsigned char lv;
+		uint16 id;
+		uint8 lv;
 	} need[MAX_PC_SKILL_REQUIRE];
 }; // Celest
 extern struct skill_tree_entry skill_tree[CLASS_COUNT][MAX_SKILL_TREE];
@@ -1146,6 +1156,8 @@ bool pc_is_same_equip_index(enum equip_index eqi, short *equip_index, short inde
 
 int pc_autotrade_timer(int tid, unsigned int tick, int id, intptr_t data);
 
+void pc_validate_skill(struct map_session_data *sd);
+
 #if defined(RENEWAL_DROP) || defined(RENEWAL_EXP)
 int pc_level_penalty_mod(struct map_session_data *sd, int mob_level, uint32 mob_class, int type);
 #endif

+ 6 - 34
src/map/script.c

@@ -8598,45 +8598,17 @@ BUILDIN_FUNC(skill)
 {
 	int id;
 	int level;
-	int flag = 1;
+	int flag = ADDSKILL_TEMP;
 	TBL_PC* sd;
 	struct script_data *data;
+	const char* command = script_getfuncname(st);
 
 	sd = script_rid2sd(st);
 	if( sd == NULL )
 		return 0;// no player attached, report source
 
-	data = script_getdata(st, 2);
-	get_val(st, data); // Convert into value in case of a variable
-	id = ( data_isstring(data) ? 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 SCRIPT_CMD_SUCCESS;
-}
-
-/// 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;
-	struct script_data *data;
-
-	sd = script_rid2sd(st);
-	if( sd == NULL )
-		return 0;// no player attached, report source
+	if (strcmpi(command, "addtoskill") == 0)
+		flag = ADDSKILL_TEMP_ADDLEVEL;
 
 	data = script_getdata(st, 2);
 	get_val(st, data); // Convert into value in case of a variable
@@ -8644,7 +8616,7 @@ BUILDIN_FUNC(addtoskill)
 	level = script_getnum(st,3);
 	if( script_hasdata(st,4) )
 		flag = script_getnum(st,4);
-	pc_skill(sd, id, level, flag);
+	pc_skill(sd, id, level, (enum e_addskill_type)flag);
 
 	return SCRIPT_CMD_SUCCESS;
 }
@@ -19356,7 +19328,7 @@ struct script_function buildin_func[] = {
 	BUILDIN_DEF(autobonus2,"sii??"),
 	BUILDIN_DEF(autobonus3,"siiv?"),
 	BUILDIN_DEF(skill,"vi?"),
-	BUILDIN_DEF(addtoskill,"vi?"), // [Valaris]
+	BUILDIN_DEF2(skill,"addtoskill","vi?"), // [Valaris]
 	BUILDIN_DEF(guildskill,"vi"),
 	BUILDIN_DEF(getskilllv,"v"),
 	BUILDIN_DEF(getgdskilllv,"iv"),

文件差异内容过多而无法显示
+ 333 - 283
src/map/skill.c


+ 107 - 61
src/map/skill.h

@@ -13,14 +13,14 @@ struct skill_unit;
 struct skill_unit_group;
 struct status_change_entry;
 
-#define MAX_SKILL_DB			MAX_SKILL /// Max Skill DB
 #define MAX_SKILL_PRODUCE_DB	270 /// Max Produce DB
 #define MAX_PRODUCE_RESOURCE	12 /// Max Produce requirements
 #define MAX_SKILL_ARROW_DB		150 /// Max Arrow Creation DB
 #define MAX_ARROW_RESULT		5 /// Max Arrow results/created
 #define MAX_SKILL_ABRA_DB		160 /// Max Skill list of Abracadabra DB
 #define MAX_SKILL_IMPROVISE_DB 30 /// Max Skill for Improvise
-#define MAX_SKILL_LEVEL 100 /// Max Skill Level
+#define MAX_SKILL_LEVEL 10 /// Max Skill Level (for skill_db storage)
+#define MAX_MOBSKILL_LEVEL 100	/// Max monster skill level (on skill usage)
 #define MAX_SKILL_CRIMSON_MARKER 3 /// Max Crimson Marker targets (RL_C_MARKER)
 #define SKILL_NAME_LENGTH 31 /// Max Skill Name length
 #define SKILL_DESC_LENGTH 31 /// Max Skill Desc length
@@ -111,80 +111,116 @@ enum e_skill_display {
 #define MAX_SKILL_ITEM_REQUIRE	10 /// Maximum required items
 #define MAX_SKILL_STATUS_REQUIRE 3 /// Maximum required statuses
 #define MAX_SKILL_EQUIP_REQUIRE 10 /// Maximum required equipped item
+
+/// Single skill requirement. !TODO: Cleanup the variable types
 struct skill_condition {
-	int hp, /// HP cost
-		mhp, /// Max HP to trigger
-		sp, /// SP cost
-		hp_rate, /// HP cost (%)
-		sp_rate, /// SP cost (%)
-		ammo, /// Ammo type
-		ammo_qty, /// Amount of ammo
-		weapon, /// Weapon type
-		zeny, /// Zeny cost
-		state, /// State/condition
-		spiritball, /// Spiritball cost
-		itemid[MAX_SKILL_ITEM_REQUIRE], /// Required item
-		amount[MAX_SKILL_ITEM_REQUIRE]; /// Amount of item
-	uint16 *eqItem; /// List of equipped item
-	enum sc_type *status; /// List of Status required (SC)
-	uint8 status_count, /// Count of SC
-		eqItem_count; /// Count of equipped item
+	int16 hp;								 ///< HP cost
+	int16 mhp;								 ///< Max HP to trigger
+	int16 sp;								 /// SP cost
+	int16 hp_rate;							 /// HP cost (%)
+	int16 sp_rate;							 /// SP cost (%)
+	uint32 zeny;							 /// Zeny cost
+	uint32 weapon;							 /// Weapon type. Combined bitmask of enum weapon_type (1<<weapon)
+	uint16 ammo;							 /// Ammo type. Combine bitmask of enum ammo_type (1<<ammo)
+	int8 ammo_qty;							 /// Amount of ammo
+	uint8 state;							 /// State/condition. @see enum e_require_state
+	int8 spiritball;						 /// Spiritball cost
+	uint16 itemid[MAX_SKILL_ITEM_REQUIRE];	 /// Required item
+	uint16 amount[MAX_SKILL_ITEM_REQUIRE];	 /// Amount of item
+	uint16 *eqItem;							 /// List of equipped item
+	enum sc_type *status;					 /// List of Status required (SC)
+	uint8 status_count,						 /// Count of SC
+		eqItem_count;						 /// Count of equipped item
 };
 
+/// Skill requirement structure. !TODO: Cleanup the variable types that use array [MAX_SKILL_LEVEL]
 struct s_skill_require {
-	int hp[MAX_SKILL_LEVEL], /// HP cost
-		mhp[MAX_SKILL_LEVEL], /// Max HP to trigger
-		sp[MAX_SKILL_LEVEL], /// SP cost
-		hp_rate[MAX_SKILL_LEVEL], /// HP cost (%)
-		sp_rate[MAX_SKILL_LEVEL], /// SP cost (%)
-		zeny[MAX_SKILL_LEVEL], /// Zeny cost
-		weapon, /// Weapon type
-		ammo, /// Ammo type
-		ammo_qty[MAX_SKILL_LEVEL], /// Amount of ammo
-		state, /// State/condition
-		spiritball[MAX_SKILL_LEVEL], /// Spiritball cost
-		itemid[MAX_SKILL_ITEM_REQUIRE], /// Required item
-		amount[MAX_SKILL_ITEM_REQUIRE]; /// Amount of item
-	uint16 *eqItem; /// List of equipped item
-	enum sc_type *status; /// List of Status required (SC)
-	uint8 status_count, /// Count of SC
-		eqItem_count; /// Count of equipped item
+	int hp[MAX_SKILL_LEVEL];			 ///< HP cost
+	int mhp[MAX_SKILL_LEVEL];			 ///< Max HP to trigger
+	int sp[MAX_SKILL_LEVEL];			 /// SP cost
+	int hp_rate[MAX_SKILL_LEVEL];		 /// HP cost (%)
+	int sp_rate[MAX_SKILL_LEVEL];		 /// SP cost (%)
+	int zeny[MAX_SKILL_LEVEL];			 /// Zeny cost
+	uint32 weapon;						 /// Weapon type. Combined bitmask of enum weapon_type (1<<weapon)
+	uint16 ammo;						 /// Ammo type. Combine bitmask of enum ammo_type (1<<ammo)
+	int ammo_qty[MAX_SKILL_LEVEL];		 /// Amount of ammo
+	uint8 state;						 /// State/condition. @see enum e_require_state
+	int spiritball[MAX_SKILL_LEVEL];	 /// Spiritball cost
+	int itemid[MAX_SKILL_ITEM_REQUIRE];	 /// Required item
+	int amount[MAX_SKILL_ITEM_REQUIRE];	 /// Amount of item
+	uint16 *eqItem;						 /// List of equipped item
+	enum sc_type *status;				 /// List of Status required (SC)
+	uint8 status_count,					 /// Count of SC
+		eqItem_count;					 /// Count of equipped item
 };
 
-/// Database skills
+/// Database skills. !TODO: Cleanup the variable types that use array [MAX_SKILL_LEVEL]
 struct s_skill_db {
-	char name[SKILL_NAME_LENGTH];
-	char desc[SKILL_DESC_LENGTH];
-	int range[MAX_SKILL_LEVEL],hit,inf,element[MAX_SKILL_LEVEL],nk,splash[MAX_SKILL_LEVEL],max;
-	int num[MAX_SKILL_LEVEL];
-	int cast[MAX_SKILL_LEVEL],walkdelay[MAX_SKILL_LEVEL],delay[MAX_SKILL_LEVEL];
+	// skill_db.txt
+	uint16 nameid;								 ///< Skill ID
+	char name[SKILL_NAME_LENGTH];				 ///< AEGIS_Name
+	char desc[SKILL_DESC_LENGTH];				 ///< English Name
+	int range[MAX_SKILL_LEVEL];					 ///< Range
+	int8 hit;									 ///< Hit type
+	uint8 inf;									 ///< Inf: 0- passive, 1- enemy, 2- place, 4- self, 16- friend, 32- trap
+	int element[MAX_SKILL_LEVEL];				 ///< Element
+	uint8 nk;									 ///< Damage properties
+	int splash[MAX_SKILL_LEVEL];				 ///< Splash effect
+	uint8 max;									 ///< Max level
+	int num[MAX_SKILL_LEVEL];					 ///< Number of hit
+	bool castcancel;							 ///< Cancel cast when being hit
+	int16 cast_def_rate;						 ///< Def rate during cast a skill
+	uint16 skill_type;							 ///< Skill type
+	int blewcount[MAX_SKILL_LEVEL];				 ///< Blew count
+	uint32 inf2;								 ///<
+	uint32 inf3;								 ///<
+	int maxcount[MAX_SKILL_LEVEL];				 ///< Max number skill can be casted in same map
+
+	// skill_castnodex_db.txt
+	uint8 castnodex;							 ///< 1 - Not affected by dex, 2 - Not affected by SC, 4 - Not affected by item
+	uint8 delaynodex;							 ///< 1 - Not affected by dex, 2 - Not affected by SC, 4 - Not affected by item
+
+	// skill_nocast_db.txt
+	uint32 nocast;								 ///< Skill cannot be casted at this zone
+
+	// skill_unit_db.txt
+	uint16 unit_id[2];							 ///< Unit ID. @see enum s_skill_unit_id
+	int unit_layout_type[MAX_SKILL_LEVEL];		 ///< Layout type. -1 is special layout, others are square with lenght*width: (val*2+1)^2
+	int unit_range[MAX_SKILL_LEVEL];			 ///< Unit cell effect range
+	int16 unit_interval;						 ///< Interval
+	uint32 unit_target;							 ///< Unit target. @see enum e_battle_check_target
+	uint32 unit_flag;							 ///< Unit flags. @see enum e_skill_unit_flag
+
+	// skill_cast_db.txt
+	int cast[MAX_SKILL_LEVEL];				 ///< Variable casttime
 #ifdef RENEWAL_CAST
-	int fixed_cast[MAX_SKILL_LEVEL];
+	int fixed_cast[MAX_SKILL_LEVEL];			 ///< If -1 means 20% than 'cast'
 #endif
-	int upkeep_time[MAX_SKILL_LEVEL],upkeep_time2[MAX_SKILL_LEVEL],cooldown[MAX_SKILL_LEVEL];
-	int castcancel,cast_def_rate;
-	int inf2,maxcount[MAX_SKILL_LEVEL],skill_type,inf3;
-	int blewcount[MAX_SKILL_LEVEL];
-	struct s_skill_require require;
-	int castnodex[MAX_SKILL_LEVEL], delaynodex[MAX_SKILL_LEVEL];
-	int32 nocast;
-	int unit_id[2];
-	int unit_layout_type[MAX_SKILL_LEVEL];
-	int unit_range[MAX_SKILL_LEVEL];
-	int unit_interval;
-	int unit_target;
-	int unit_flag;
+	int walkdelay[MAX_SKILL_LEVEL];			 ///< Delay to walk after casting
+	int delay[MAX_SKILL_LEVEL];				 ///< Global delay (delay before reusing all skills)
+	int cooldown[MAX_SKILL_LEVEL];			 ///< Cooldown (delay before reusing same skill)
+	int upkeep_time[MAX_SKILL_LEVEL];		 ///< Duration
+	int upkeep_time2[MAX_SKILL_LEVEL];		 ///< Duration2
+
+	// skill_require_db.txt
+	struct s_skill_require require;				 ///< Skill requirement
+
+	// skill_nonearnpc_db.txt
 	uint8 unit_nonearnpc_range;	//additional range for UF_NONEARNPC or INF2_NO_NEARNPC [Cydh]
 	uint8 unit_nonearnpc_type;	//type of NPC [Cydh]
+
+	// skill_damage_db.txt
 #ifdef ADJUST_SKILL_DAMAGE
 	struct s_skill_damage damage;
 #endif
+
+	// skill_copyable_db.txt
 	struct s_copyable { // [Cydh]
 		uint8 option;
 		uint16 joballowed, req_opt;
 	} copyable;
 };
-extern struct s_skill_db skill_db[MAX_SKILL_DB];
+extern struct s_skill_db **skill_db;
 
 #define MAX_SKILL_UNIT_LAYOUT	52
 #define MAX_SKILL_UNIT_LAYOUT2	17
@@ -261,7 +297,7 @@ struct skill_unit_group_tickset {
 };
 
 
-enum {
+enum e_skill_unit_flag {
 	UF_DEFNOTENEMY      = 0x00001,	// If 'defunit_not_enemy' is set, the target is changed to 'friend'
 	UF_NOREITERATION    = 0x00002,	// Spell cannot be stacked
 	UF_NOFOOTSET        = 0x00004,	// Spell cannot be cast near/on targets
@@ -321,7 +357,9 @@ const char*	skill_get_desc( uint16 skill_id ); 	// [Skotlex]
 int skill_tree_get_max( uint16 skill_id, int b_class );	// Celest
 
 // Accessor to the skills database
-int skill_get_index( uint16 skill_id );
+int skill_get_index_( uint16 skill_id, bool silent, const char *func, const char *file, int line );
+#define skill_get_index(skill_id)  skill_get_index_((skill_id), false, __FUNCTION__, __FILE__, __LINE__) /// Get skill index from skill_id (common usage on source)
+#define skill_get_index2(skill_id) skill_get_index_((skill_id), true, __FUNCTION__, __FILE__, __LINE__)  /// Get skill index from skill_id (used when reading skill_db files)
 int skill_get_type( uint16 skill_id );
 int skill_get_hit( uint16 skill_id );
 int skill_get_inf( uint16 skill_id );
@@ -337,7 +375,7 @@ int skill_get_delay( uint16 skill_id ,uint16 skill_lv );
 int skill_get_walkdelay( uint16 skill_id ,uint16 skill_lv );
 int skill_get_time( uint16 skill_id ,uint16 skill_lv );
 int skill_get_time2( uint16 skill_id ,uint16 skill_lv );
-int skill_get_castnodex( uint16 skill_id ,uint16 skill_lv );
+int skill_get_castnodex( uint16 skill_id );
 int skill_get_castdef( uint16 skill_id );
 int skill_get_nocast( uint16 skill_id );
 int skill_get_unit_id(uint16 skill_id,int flag);
@@ -367,6 +405,9 @@ int skill_get_itemid( uint16 skill_id, int idx );
 int skill_get_itemqty( uint16 skill_id, int idx );
 
 int skill_name2id(const char* name);
+uint16 skill_idx2id(uint16 idx);
+
+uint16 SKILL_MAX_DB(void);
 
 int skill_isammotype(struct map_session_data *sd, unsigned short skill_id);
 int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data);
@@ -1815,7 +1856,7 @@ enum e_skill {
 };
 
 /// The client view ids for land skills.
-enum {
+enum s_skill_unit_id {
 	UNT_SAFETYWALL = 0x7e,
 	UNT_FIREWALL,
 	UNT_WARP_WAITING,
@@ -2055,4 +2096,9 @@ enum e_skill_damage_caster {
 /// Variable name of copied skill level by Reproduce
 #define SKILL_VAR_REPRODUCE_LV "REPRODUCE_SKILL_LV"
 
+#define SKILL_CHK_HOMUN(skill_id) ( (skill_id) >= HM_SKILLBASE && (skill_id) < HM_SKILLBASE+MAX_HOMUNSKILL )
+#define SKILL_CHK_MERC(skill_id)  ( (skill_id) >= MC_SKILLBASE && (skill_id) < MC_SKILLBASE+MAX_MERCSKILL )
+#define SKILL_CHK_ELEM(skill_id)  ( (skill_id) >= EL_SKILLBASE && (skill_id) < EL_SKILLBASE+MAX_ELEMENTALSKILL )
+#define SKILL_CHK_GUILD(skill_id) ( (skill_id) >= GD_SKILLBASE && (skill_id) < GD_SKILLBASE+MAX_GUILDSKILL )
+
 #endif /* _SKILL_H_ */

+ 30 - 20
src/map/status.c

@@ -154,11 +154,11 @@ 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);
+		ShowError("set_sc: Unsupported skill id %d (SC: %d. Icon: %d)\n", skill_id, sc, icon);
 		return;
 	}
 	if( sc < 0 || sc >= SC_MAX ) {
-		ShowError("set_sc: Unsupported status change id %d\n", sc);
+		ShowError("set_sc: Unsupported status change id %d (Skill: %d. Icon: %d)\n", sc, skill_id, icon);
 		return;
 	}
 
@@ -172,6 +172,16 @@ static void set_sc(uint16 skill_id, sc_type sc, int icon, unsigned int flag)
 		SkillStatusChangeTable[idx] = sc;
 }
 
+static void set_sc_with_vfx_noskill(sc_type sc, int icon, unsigned flag) {
+	if (sc > SC_NONE && sc < SC_MAX) {
+		if (StatusIconChangeTable[sc] == SI_BLANK)
+			StatusIconChangeTable[sc] = icon;
+		StatusChangeFlagTable[sc] |= flag;
+	}
+	if (icon > SI_BLANK && icon < SI_MAX)
+		StatusRelevantBLTypes[icon] |= BL_SCEFFECT;
+}
+
 void initChangeTables(void)
 {
 	int i;
@@ -791,8 +801,6 @@ void initChangeTables(void)
 	set_sc( OB_OBOROGENSOU			, SC_GENSOU		, SI_GENSOU		, SCB_NONE );
 
 	set_sc( ALL_FULL_THROTTLE		, SC_FULL_THROTTLE	, SI_FULL_THROTTLE	, SCB_SPEED|SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK );
-	set_sc_with_vfx( SC_MOONSTAR		, SC_MOONSTAR		, SI_MOONSTAR		, SCB_NONE );
-	set_sc_with_vfx( SC_SUPER_STAR		, SC_SUPER_STAR		, SI_SUPER_STAR		, SCB_NONE );
 
 	/* Rebellion */
 	add_sc( RL_MASS_SPIRAL		, SC_BLEEDING );
@@ -806,24 +814,26 @@ void initChangeTables(void)
 	set_sc_with_vfx( RL_C_MARKER	, SC_C_MARKER		, SI_C_MARKER		, SCB_FLEE );
 	set_sc_with_vfx( RL_AM_BLAST	, SC_ANTI_M_BLAST	, SI_ANTI_M_BLAST	, SCB_NONE );
 
-	set_sc_with_vfx( SC_ALL_RIDING		, SC_ALL_RIDING		, SI_ALL_RIDING		, SCB_SPEED );
+	set_sc_with_vfx_noskill( SC_MOONSTAR	, SI_MOONSTAR	, SCB_NONE );
+	set_sc_with_vfx_noskill( SC_SUPER_STAR	, SI_SUPER_STAR	, SCB_NONE );
+	set_sc_with_vfx_noskill( SC_ALL_RIDING	, SI_ALL_RIDING	, SCB_SPEED );
 
 	/* 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,
+	SkillStatusChangeTable[skill_get_index(SL_ALCHEMIST)]	= (sc_type)MAPID_ALCHEMIST,
+	SkillStatusChangeTable[skill_get_index(SL_MONK)]		= (sc_type)MAPID_MONK,
+	SkillStatusChangeTable[skill_get_index(SL_STAR)]		= (sc_type)MAPID_STAR_GLADIATOR,
+	SkillStatusChangeTable[skill_get_index(SL_SAGE)]		= (sc_type)MAPID_SAGE,
+	SkillStatusChangeTable[skill_get_index(SL_CRUSADER)]	= (sc_type)MAPID_CRUSADER,
+	SkillStatusChangeTable[skill_get_index(SL_SUPERNOVICE)]	= (sc_type)MAPID_SUPER_NOVICE,
+	SkillStatusChangeTable[skill_get_index(SL_KNIGHT)]	= (sc_type)MAPID_KNIGHT,
+	SkillStatusChangeTable[skill_get_index(SL_WIZARD)]	= (sc_type)MAPID_WIZARD,
+	SkillStatusChangeTable[skill_get_index(SL_PRIEST)]	= (sc_type)MAPID_PRIEST,
+	SkillStatusChangeTable[skill_get_index(SL_BARDDANCER)]	= (sc_type)MAPID_BARDDANCER,
+	SkillStatusChangeTable[skill_get_index(SL_ROGUE)]	= (sc_type)MAPID_ROGUE,
+	SkillStatusChangeTable[skill_get_index(SL_ASSASIN)]	= (sc_type)MAPID_ASSASSIN,
+	SkillStatusChangeTable[skill_get_index(SL_BLACKSMITH)]	= (sc_type)MAPID_BLACKSMITH,
+	SkillStatusChangeTable[skill_get_index(SL_HUNTER)]	= (sc_type)MAPID_HUNTER,
+	SkillStatusChangeTable[skill_get_index(SL_SOULLINKER)]	= (sc_type)MAPID_SOUL_LINKER,
 
 	/* Status that don't have a skill associated */
 	StatusIconChangeTable[SC_WEIGHT50] = SI_WEIGHT50;

+ 1 - 0
src/map/status.h

@@ -2067,6 +2067,7 @@ int status_change_spread( struct block_list *src, struct block_list *bl );
 
 unsigned short status_base_atk(const struct block_list *bl, const struct status_data *status);
 
+void initChangeTables(void);
 int status_readdb(void);
 int do_init_status(void);
 void do_final_status(void);

+ 7 - 7
src/map/unit.c

@@ -1796,7 +1796,7 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
 
 	// Moved here to prevent Suffragium from ending if skill fails
 #ifndef RENEWAL_CAST
-	if (!(skill_get_castnodex(skill_id, skill_lv)&2))
+	if (!(skill_get_castnodex(skill_id)&2))
 		casttime = skill_castfix_sc(src, casttime);
 #else
 	casttime = skill_vfcastfix(src, casttime, skill_id, skill_lv);
@@ -2013,7 +2013,7 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
 
 	// Moved here to prevent Suffragium from ending if skill fails
 #ifndef RENEWAL_CAST
-	if (!(skill_get_castnodex(skill_id, skill_lv)&2))
+	if (!(skill_get_castnodex(skill_id)&2))
 		casttime = skill_castfix_sc(src, casttime);
 #else
 	casttime = skill_vfcastfix(src, casttime, skill_id, skill_lv );
@@ -2035,11 +2035,11 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
 // 		}
 // 	}
 
-	ud->skill_id      = skill_id;
-	ud->skill_lv      = skill_lv;
-	ud->skillx       = skill_x;
-	ud->skilly       = skill_y;
-	ud->skilltarget  = 0;
+	ud->skill_id    = skill_id;
+	ud->skill_lv    = skill_lv;
+	ud->skillx      = skill_x;
+	ud->skilly      = skill_y;
+	ud->skilltarget = 0;
 
 	if( sc ) {
 		// These 3 status do not stack, so it's efficient to use if-else

部分文件因为文件数量过多而无法显示