From 9d3d34205fdb424e4a9281c3e6a951a1f0cdbdc1 Mon Sep 17 00:00:00 2001 From: Cydh Ramdh Date: Thu, 12 Feb 2015 15:50:50 +0700 Subject: [PATCH] Skill DB clean ups * Changed skill_db array to better memory allocation. * Reduced MAX_SKILL_LEVEL from 100 to 10 (part of Hercules https://github.com/HerculesWS/Hercules/commit/0f4a50d13538c3e5d3ca4d3822f92217c8da0479), also has changes on skill_get checks for skill level that more than 10. * Reduced MAX_SKILL from 5020 to 1200 (actually only 1109 skills are used). * Added macros for checking Homunculus, Guild, Mercenary, & Elemental skill ranges. * Added skill check & index validation when player logged in and when @reloadskilldb. * Corrected `enum e_skill_flag` order for SKILL_FLAG_REPLACED_LV_0's sake. * Merged 'addtoskill' script command just as alias of 'skill' script command. * Fixed #277 * Changed 'skill' script command flag to constant value Signed-off-by: Cydh Ramdh NOTE: * Decreased memory usage by map-server reduced up to 50 MB. * Decreased memory for each player because of mmo_charstatus::skill[] only has 1200 array, before is 5020. * Please use skill_get_index() for accessing sd->status.skill[] or skill_db[], don't reckless use skill_id as array index. * Please import upgrade_20150211_skillset.sql Signed-off-by: Cydh Ramdh --- db/const.txt | 5 + db/pre-re/skill_db.txt | 19 +- db/re/skill_db.txt | 19 +- doc/script_commands.txt | 8 +- npc/custom/jobmaster.txt | 72 +- npc/custom/platinum_skills.txt | 72 +- npc/jobs/2-2a/Creator.txt | 2 +- npc/jobs/valkyrie.txt | 4 +- npc/merchants/buying_shops.txt | 2 +- npc/other/gympass.txt | 4 +- npc/pre-re/jobs/1-1/acolyte.txt | 6 +- npc/pre-re/jobs/1-1/archer.txt | 6 +- npc/pre-re/jobs/1-1/mage.txt | 4 +- npc/pre-re/jobs/1-1/merchant.txt | 8 +- npc/pre-re/jobs/1-1/swordman.txt | 8 +- npc/pre-re/jobs/1-1/thief.txt | 10 +- npc/pre-re/jobs/novice/novice.txt | 2 +- npc/quests/skills/acolyte_skills.txt | 2 +- npc/quests/skills/alchemist_skills.txt | 2 +- npc/quests/skills/archer_skills.txt | 4 +- npc/quests/skills/assassin_skills.txt | 10 +- npc/quests/skills/bard_skills.txt | 6 +- npc/quests/skills/blacksmith_skills.txt | 8 +- npc/quests/skills/crusader_skills.txt | 4 +- npc/quests/skills/dancer_skills.txt | 4 +- npc/quests/skills/hunter_skills.txt | 4 +- npc/quests/skills/knight_skills.txt | 4 +- npc/quests/skills/mage_skills.txt | 2 +- npc/quests/skills/merchant_skills.txt | 10 +- npc/quests/skills/monk_skills.txt | 8 +- npc/quests/skills/novice_skills.txt | 4 +- npc/quests/skills/priest_skills.txt | 4 +- npc/quests/skills/rogue_skills.txt | 6 +- npc/quests/skills/sage_skills.txt | 12 +- npc/quests/skills/swordman_skills.txt | 6 +- npc/quests/skills/thief_skills.txt | 8 +- npc/quests/skills/wizard_skills.txt | 4 +- npc/re/jobs/1-1/acolyte.txt | 4 +- npc/re/jobs/1-1/archer.txt | 6 +- npc/re/jobs/1-1/mage.txt | 4 +- npc/re/jobs/1-1/merchant.txt | 8 +- npc/re/jobs/1-1/swordman.txt | 8 +- npc/re/jobs/1-1/thief.txt | 10 +- npc/re/jobs/novice/novice.txt | 2 +- .../upgrades/upgrade_20150211_skillset.sql | 6 + src/char/char.c | 51 +- src/common/mmo.h | 18 +- src/map/atcommand.c | 43 +- src/map/battle.c | 13 +- src/map/chrif.c | 24 +- src/map/clif.c | 107 ++- src/map/clif.h | 6 +- src/map/elemental.c | 18 +- src/map/guild.c | 95 +- src/map/guild.h | 2 +- src/map/homunculus.c | 48 +- src/map/homunculus.h | 2 + src/map/itemdb.h | 4 + src/map/mercenary.c | 39 +- src/map/mercenary.h | 1 + src/map/mob.c | 9 +- src/map/npc.c | 20 +- src/map/pc.c | 622 +++++++------ src/map/pc.h | 28 +- src/map/script.c | 40 +- src/map/skill.c | 851 ++++++++++-------- src/map/skill.h | 168 ++-- src/map/status.c | 50 +- src/map/status.h | 1 + src/map/unit.c | 14 +- 70 files changed, 1489 insertions(+), 1196 deletions(-) create mode 100644 sql-files/upgrades/upgrade_20150211_skillset.sql diff --git a/db/const.txt b/db/const.txt index fe3f82d58d9..c8a3d6d375b 100644 --- a/db/const.txt +++ b/db/const.txt @@ -4644,5 +4644,10 @@ BSF_REM_ON_MADOGEAR 0x080 BSF_REM_ON_DAMAGED 0x100 BSF_PERMANENT 0x200 +SKILL_PERM 0 +SKILL_TEMP 1 +SKILL_TEMPLEVEL 2 +SKILL_PERM_GRANT 3 + false 0 true 1 diff --git a/db/pre-re/skill_db.txt b/db/pre-re/skill_db.txt index ce122606e42..82f4da4e3d4 100644 --- a/db/pre-re/skill_db.txt +++ b/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 diff --git a/db/re/skill_db.txt b/db/re/skill_db.txt index a33ba5dd59d..89269628bbf 100644 --- a/db/re/skill_db.txt +++ b/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 diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 552755f3257..07a2f719657 100644 --- a/doc/script_commands.txt +++ b/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; diff --git a/npc/custom/jobmaster.txt b/npc/custom/jobmaster.txt index 80fc73bf2e2..a7e095c1529 100644 --- a/npc/custom/jobmaster.txt +++ b/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; diff --git a/npc/custom/platinum_skills.txt b/npc/custom/platinum_skills.txt index 0a45bb3850d..1d85a4ee782 100644 --- a/npc/custom/platinum_skills.txt +++ b/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; diff --git a/npc/jobs/2-2a/Creator.txt b/npc/jobs/2-2a/Creator.txt index 42a6f14ab55..cfc919ffad7 100644 --- a/npc/jobs/2-2a/Creator.txt +++ b/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"; diff --git a/npc/jobs/valkyrie.txt b/npc/jobs/valkyrie.txt index f4a1a7fc5b9..10ed63fda00 100644 --- a/npc/jobs/valkyrie.txt +++ b/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]"; diff --git a/npc/merchants/buying_shops.txt b/npc/merchants/buying_shops.txt index 3da03e54cc5..802be147c73 100644 --- a/npc/merchants/buying_shops.txt +++ b/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."; diff --git a/npc/other/gympass.txt b/npc/other/gympass.txt index 078e5bc71d2..b522012ecda 100644 --- a/npc/other/gympass.txt +++ b/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 { diff --git a/npc/pre-re/jobs/1-1/acolyte.txt b/npc/pre-re/jobs/1-1/acolyte.txt index 4aac026d6aa..e3e3b2e3d84 100644 --- a/npc/pre-re/jobs/1-1/acolyte.txt +++ b/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) { diff --git a/npc/pre-re/jobs/1-1/archer.txt b/npc/pre-re/jobs/1-1/archer.txt index a496450bc88..783cb6bbed6 100644 --- a/npc/pre-re/jobs/1-1/archer.txt +++ b/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."; diff --git a/npc/pre-re/jobs/1-1/mage.txt b/npc/pre-re/jobs/1-1/mage.txt index 2acbac2c2fc..4f230161e21 100644 --- a/npc/pre-re/jobs/1-1/mage.txt +++ b/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; diff --git a/npc/pre-re/jobs/1-1/merchant.txt b/npc/pre-re/jobs/1-1/merchant.txt index 1940bb6d55f..5f71f516079 100644 --- a/npc/pre-re/jobs/1-1/merchant.txt +++ b/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; diff --git a/npc/pre-re/jobs/1-1/swordman.txt b/npc/pre-re/jobs/1-1/swordman.txt index 7187272c350..56b31bf88c6 100644 --- a/npc/pre-re/jobs/1-1/swordman.txt +++ b/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; diff --git a/npc/pre-re/jobs/1-1/thief.txt b/npc/pre-re/jobs/1-1/thief.txt index a9587cce133..71ea0fb368c 100644 --- a/npc/pre-re/jobs/1-1/thief.txt +++ b/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; diff --git a/npc/pre-re/jobs/novice/novice.txt b/npc/pre-re/jobs/novice/novice.txt index c2543115dd0..7cffb40f504 100644 --- a/npc/pre-re/jobs/novice/novice.txt +++ b/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; diff --git a/npc/quests/skills/acolyte_skills.txt b/npc/quests/skills/acolyte_skills.txt index e0f38e0d339..3d7d8285661 100644 --- a/npc/quests/skills/acolyte_skills.txt +++ b/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"; diff --git a/npc/quests/skills/alchemist_skills.txt b/npc/quests/skills/alchemist_skills.txt index 598bdb21ded..10ea476b271 100644 --- a/npc/quests/skills/alchemist_skills.txt +++ b/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) + "."; diff --git a/npc/quests/skills/archer_skills.txt b/npc/quests/skills/archer_skills.txt index fa9b1073807..47fff734725 100644 --- a/npc/quests/skills/archer_skills.txt +++ b/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!"; diff --git a/npc/quests/skills/assassin_skills.txt b/npc/quests/skills/assassin_skills.txt index c96e8652e2f..dab592374cb 100644 --- a/npc/quests/skills/assassin_skills.txt +++ b/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]"; diff --git a/npc/quests/skills/bard_skills.txt b/npc/quests/skills/bard_skills.txt index ba05e010c63..f24c31c357a 100644 --- a/npc/quests/skills/bard_skills.txt +++ b/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]"; diff --git a/npc/quests/skills/blacksmith_skills.txt b/npc/quests/skills/blacksmith_skills.txt index d8d210b7eb4..1676d1a851a 100644 --- a/npc/quests/skills/blacksmith_skills.txt +++ b/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; } diff --git a/npc/quests/skills/crusader_skills.txt b/npc/quests/skills/crusader_skills.txt index f78ecdb4e5d..207acfc9b87 100644 --- a/npc/quests/skills/crusader_skills.txt +++ b/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"; diff --git a/npc/quests/skills/dancer_skills.txt b/npc/quests/skills/dancer_skills.txt index 1b4f84ddd6c..45583b500ea 100644 --- a/npc/quests/skills/dancer_skills.txt +++ b/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; } } diff --git a/npc/quests/skills/hunter_skills.txt b/npc/quests/skills/hunter_skills.txt index a8fe9749d64..b238667975a 100644 --- a/npc/quests/skills/hunter_skills.txt +++ b/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]"; diff --git a/npc/quests/skills/knight_skills.txt b/npc/quests/skills/knight_skills.txt index 7b89c43aff6..3a7961b25ee 100644 --- a/npc/quests/skills/knight_skills.txt +++ b/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"; diff --git a/npc/quests/skills/mage_skills.txt b/npc/quests/skills/mage_skills.txt index c26db4ad5aa..96a5e48709b 100644 --- a/npc/quests/skills/mage_skills.txt +++ b/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. . ."; diff --git a/npc/quests/skills/merchant_skills.txt b/npc/quests/skills/merchant_skills.txt index 3e2b9d7fc82..c7e3e5687e7 100644 --- a/npc/quests/skills/merchant_skills.txt +++ b/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"; diff --git a/npc/quests/skills/monk_skills.txt b/npc/quests/skills/monk_skills.txt index 33f2da1005c..08108208a99 100644 --- a/npc/quests/skills/monk_skills.txt +++ b/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"; diff --git a/npc/quests/skills/novice_skills.txt b/npc/quests/skills/novice_skills.txt index 7724455a117..f1f2c9eb219 100644 --- a/npc/quests/skills/novice_skills.txt +++ b/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]"; diff --git a/npc/quests/skills/priest_skills.txt b/npc/quests/skills/priest_skills.txt index e30e2f30176..bb126f72c57 100644 --- a/npc/quests/skills/priest_skills.txt +++ b/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 { diff --git a/npc/quests/skills/rogue_skills.txt b/npc/quests/skills/rogue_skills.txt index 90a19d0cf54..6e775043a84 100644 --- a/npc/quests/skills/rogue_skills.txt +++ b/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; } diff --git a/npc/quests/skills/sage_skills.txt b/npc/quests/skills/sage_skills.txt index bbb59f6fc00..669d7ada737 100644 --- a/npc/quests/skills/sage_skills.txt +++ b/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; } diff --git a/npc/quests/skills/swordman_skills.txt b/npc/quests/skills/swordman_skills.txt index 0bf9dd46461..79c972f9590 100644 --- a/npc/quests/skills/swordman_skills.txt +++ b/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."; diff --git a/npc/quests/skills/thief_skills.txt b/npc/quests/skills/thief_skills.txt index 2f0a8974378..dde77745569 100644 --- a/npc/quests/skills/thief_skills.txt +++ b/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]"; diff --git a/npc/quests/skills/wizard_skills.txt b/npc/quests/skills/wizard_skills.txt index 1fbeb5b26cd..edfe1d4079d 100644 --- a/npc/quests/skills/wizard_skills.txt +++ b/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"; diff --git a/npc/re/jobs/1-1/acolyte.txt b/npc/re/jobs/1-1/acolyte.txt index 2792a17a39f..4f202eca875 100644 --- a/npc/re/jobs/1-1/acolyte.txt +++ b/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; diff --git a/npc/re/jobs/1-1/archer.txt b/npc/re/jobs/1-1/archer.txt index 1e10c098b97..3323ddbd7ce 100644 --- a/npc/re/jobs/1-1/archer.txt +++ b/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."; diff --git a/npc/re/jobs/1-1/mage.txt b/npc/re/jobs/1-1/mage.txt index 4726aef50f1..4eb18f94cc4 100644 --- a/npc/re/jobs/1-1/mage.txt +++ b/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; diff --git a/npc/re/jobs/1-1/merchant.txt b/npc/re/jobs/1-1/merchant.txt index 8b55d13718a..e3a567232e7 100644 --- a/npc/re/jobs/1-1/merchant.txt +++ b/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; diff --git a/npc/re/jobs/1-1/swordman.txt b/npc/re/jobs/1-1/swordman.txt index 0dfaaabae17..56ec1561123 100644 --- a/npc/re/jobs/1-1/swordman.txt +++ b/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; diff --git a/npc/re/jobs/1-1/thief.txt b/npc/re/jobs/1-1/thief.txt index 41e88964dee..45809cb4af9 100644 --- a/npc/re/jobs/1-1/thief.txt +++ b/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; diff --git a/npc/re/jobs/novice/novice.txt b/npc/re/jobs/novice/novice.txt index 97f9c0f68b0..4ae426df029 100644 --- a/npc/re/jobs/novice/novice.txt +++ b/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; diff --git a/sql-files/upgrades/upgrade_20150211_skillset.sql b/sql-files/upgrades/upgrade_20150211_skillset.sql new file mode 100644 index 00000000000..e60ea411aa4 --- /dev/null +++ b/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; diff --git a/src/char/char.c b/src/char/char.c index 3b04ffc990d..1ad603ff65a 100644 --- a/src/char/char.c +++ b/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;iskill[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 = MAP_DEFAULT_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; } diff --git a/src/common/mmo.h b/src/common/mmo.h index b76de1a899c..8f20e983fd2 100644 --- a/src/common/mmo.h +++ b/src/common/mmo.h @@ -54,7 +54,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 @@ -223,10 +223,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 { @@ -236,9 +237,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 { @@ -631,6 +632,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 { diff --git a/src/map/atcommand.c b/src/map/atcommand.c index bd87d013d3a..31eab3d5e42 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -32,6 +32,7 @@ #include "trade.h" #include "mapreg.h" #include "quest.h" +#include "pc.h" #include #include @@ -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 diff --git a/src/map/battle.c b/src/map/battle.c index 084d9608ef1..af3ab24d649 100644 --- a/src/map/battle.c +++ b/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)) @@ -6945,11 +6945,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; @@ -7789,7 +7790,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, }, diff --git a/src/map/chrif.c b/src/map/chrif.c index 4810b7f5249..e60dbfb729e 100644 --- a/src/map/chrif.c +++ b/src/map/chrif.c @@ -951,19 +951,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); @@ -1030,7 +1032,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; diff --git a/src/map/clif.c b/src/map/clif.c index 9f16c8bc73f..d81218569d4 100644 --- a/src/map/clif.c +++ b/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.hskill[j].lv < hom_skill_tree_get_max(id, hd->homunculus.class_))?1:0; + WFIFOB(fd,len+36) = (hd->homunculus.hskill[idx].lv < hom_skill_tree_get_max(id, hd->homunculus.class_))?1:0; 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 .W .L .W .W .W .24B .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 .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 .W .W .W .W .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 .W .L .W .W .W .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; diff --git a/src/map/clif.h b/src/map/clif.h index 6907b6d11cb..4a578b13fb5 100644 --- a/src/map/clif.h +++ b/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); diff --git a/src/map/elemental.c b/src/map/elemental.c index 5d7017ec5d7..643dd456aa4 100644 --- a/src/map/elemental.c +++ b/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= 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; } diff --git a/src/map/guild.c b/src/map/guild.c index 5e0b8c8cab8..4ac3a0df1cc 100644 --- a/src/map/guild.c +++ b/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) {// ,,,,,,,,,,, - 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_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;imax_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; diff --git a/src/map/guild.h b/src/map/guild.h index 36d8b93084d..94b842551d4 100644 --- a/src/map/guild.h +++ b/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); diff --git a/src/map/homunculus.c b/src/map/homunculus.c index 1556b66f325..be41b87d4cf 100644 --- a/src/map/homunculus.c +++ b/src/map/homunculus.c @@ -38,10 +38,11 @@ static struct view_data hom_viewdb[MAX_HOMUNCULUS_CLASS]; * @param skill_id * @return -1 if invalid skill or skill index for homunculus skill_tree */ -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; } @@ -124,8 +125,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; @@ -152,8 +153,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; @@ -268,7 +269,7 @@ void hom_calc_skilltree(struct homun_data *hd, int flag_evolve) /* 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_SKILL_TREE && (skill_id = hskill_tree[c][i].id) > 0; i++) { - int idx = hom_skill_get_index(skill_id); + short idx = hom_skill_get_index(skill_id); if (idx < 0) continue; if (hd->homunculus.hskill[idx].id) @@ -296,7 +297,7 @@ void hom_calc_skilltree(struct homun_data *hd, int flag_evolve) for (i = 0; i < MAX_SKILL_TREE && (skill_id = hskill_tree[c][i].id) > 0; i++) { int intimacy; - int idx = hom_skill_get_index(skill_id); + short idx = hom_skill_get_index(skill_id); if (idx < 0) continue; if (hd->homunculus.hskill[idx].id) @@ -331,7 +332,7 @@ void hom_calc_skilltree(struct homun_data *hd, int 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; @@ -1462,8 +1463,9 @@ void read_homunculusdb(void) { */ static bool read_homunculus_skilldb_sub(char* split[], int columns, int current) {// ,,[,],,,,,,,,,,, - int skill_id, class_idx; - int i, j; + uint16 skill_id; + uint8 i; + short class_idx, idx = -1; int minJobLevelPresent = 0; if (columns == 14) @@ -1471,29 +1473,27 @@ static bool read_homunculus_skilldb_sub(char* split[], int columns, int current) // 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_SKILL_TREE, j, !hskill_tree[class_idx][j].id || hskill_tree[class_idx][j].id == skill_id ); - if (j == MAX_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][idx].id = skill_id; + hskill_tree[class_idx][idx].max = atoi(split[2]); if (minJobLevelPresent) - hskill_tree[class_idx][j].joblv = atoi(split[3]); + hskill_tree[class_idx][idx].joblv = atoi(split[3]); for (i = 0; i < MAX_HOM_SKILL_REQUIRE; i++) { - hskill_tree[class_idx][j].need[i].id = atoi(split[3+i*2+minJobLevelPresent]); - hskill_tree[class_idx][j].need[i].lv = atoi(split[3+i*2+minJobLevelPresent+1]); + hskill_tree[class_idx][idx].need[i].id = atoi(split[3+i*2+minJobLevelPresent]); + hskill_tree[class_idx][idx].need[i].lv = atoi(split[3+i*2+minJobLevelPresent+1]); } - hskill_tree[class_idx][j].intimacylv = atoi(split[13+minJobLevelPresent]); + hskill_tree[class_idx][idx].intimacylv = atoi(split[13+minJobLevelPresent]); return true; } diff --git a/src/map/homunculus.h b/src/map/homunculus.h index 79fddc13fc8..ffb612dd801 100644 --- a/src/map/homunculus.h +++ b/src/map/homunculus.h @@ -170,6 +170,8 @@ void hom_delspiritball(TBL_HOM *hd, int count, int type); uint8 hom_get_intimacy_grade(struct homun_data *hd); +short hom_skill_get_index(uint16 skill_id); + void do_final_homunculus(void); void do_init_homunculus(void); diff --git a/src/map/itemdb.h b/src/map/itemdb.h index 4956786b605..882c97ee714 100644 --- a/src/map/itemdb.h +++ b/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_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,, 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=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 ) @@ -3513,7 +3514,7 @@ int mob_clone_spawn(struct map_session_data *sd, int16 m, int16 x, int16 y, cons 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); diff --git a/src/map/npc.c b/src/map/npc.c index 1a3c894c2e0..b577a31f910 100644 --- a/src/map/npc.c +++ b/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 ) { diff --git a/src/map/pc.c b/src/map/pc.c index f7fea6464b1..c77cbf967d6 100755 --- a/src/map/pc.c +++ b/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; } @@ -1248,6 +1248,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 @@ -1355,7 +1356,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) @@ -1365,7 +1366,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) @@ -1448,21 +1449,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 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); } } @@ -1471,6 +1471,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. @@ -1478,8 +1529,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); @@ -1493,44 +1544,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 } @@ -1540,105 +1604,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[skill_tree[c][i].need[j].id].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 } @@ -1646,7 +1691,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. @@ -1654,25 +1699,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); @@ -1682,44 +1730,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); @@ -1731,14 +1790,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; } @@ -3816,74 +3873,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 ? @@ -5429,8 +5490,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) { @@ -5443,24 +5504,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; } /** @@ -6632,52 +6692,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; } /*========================================== @@ -6689,7 +6754,7 @@ int pc_allskillup(struct map_session_data *sd) nullpo_ret(sd); - for(i=0;istatus.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; @@ -6698,34 +6763,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;istatus.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); @@ -6765,8 +6819,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); } } @@ -6940,9 +6994,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); @@ -6950,7 +7006,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; @@ -6958,13 +7014,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 ) @@ -6979,7 +7035,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) ) @@ -8013,26 +8069,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); } @@ -11283,6 +11339,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 *------------------------------------------*/ diff --git a/src/map/pc.h b/src/map/pc.h index 908a1d6a753..8d47400840d 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -437,7 +437,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]; @@ -657,6 +657,8 @@ enum weapon_type { W_DOUBLE_SA, // sword + axe }; +#define WEAPON_TYPE_ALL ((1< defaults to 2 -/// -/// addtoskill ,, -/// addtoskill , -/// addtoskill "",, -/// addtoskill "", -/// -/// @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 @@ -8650,7 +8622,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; } @@ -19376,7 +19348,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"), diff --git a/src/map/skill.c b/src/map/skill.c index 147178efd46..548d4defcf5 100755 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -53,10 +53,17 @@ #define EL_SKILLRANGEMAX EL_SKILLRANGEMIN + MAX_ELEMENTALSKILL #define GD_SKILLRANGEMIN EL_SKILLRANGEMAX + 1 #define GD_SKILLRANGEMAX GD_SKILLRANGEMIN + MAX_GUILDSKILL - #if GD_SKILLRANGEMAX > 999 #error GD_SKILLRANGEMAX is greater than 999 #endif + + +static DBMap *skilldb_id2idx; /// Skill ID to Index lookup: skill_index = skill_get_index(skill_id) +struct s_skill_db **skill_db; /// Skill DB +static uint16 skill_num; /// Skill count, also as last index +#define skill_next_idx() ( skill_num++ ) /// Macro to get&increase last skill number/index +static uint16 skill_db_create(uint16 skill_id); + static struct eri *skill_unit_ers = NULL; //For handling skill_unit's [Skotlex] static struct eri *skill_timer_ers = NULL; //For handling skill_timerskills [Skotlex] static DBMap* bowling_db = NULL; // int mob_id -> struct mob_data* @@ -71,8 +78,6 @@ struct skill_usave { uint16 skill_id, skill_lv; }; -struct s_skill_db skill_db[MAX_SKILL_DB]; - struct s_skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB]; static unsigned short skill_produce_count; @@ -137,7 +142,15 @@ static inline int splash_target(struct block_list* bl) { #endif } -/// Returns the id of the skill, or 0 if not found. +uint16 SKILL_MAX_DB(void) { + return skill_num; +} + +/** + * Get skill id from name + * @param name + * @return Skill ID of the skill, or 0 if not found. + **/ int skill_name2id(const char* name) { if( name == NULL ) return 0; @@ -145,39 +158,60 @@ int skill_name2id(const char* name) { return strdb_iget(skilldb_name2id, name); } -/// Maps skill ids to skill db offsets. -/// Returns the skill's array index, or 0 (Unknown Skill). -int skill_get_index( uint16 skill_id ) { - // avoid ranges reserved for mapping guild/homun/mercenary skills - if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX) - || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX) - || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX) - || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) ) - return 0; +/** + * Get Skill ID from Skill Index + * @param idx + * @return Skill ID or 0 if not found + **/ +uint16 skill_idx2id(uint16 idx) { + if (idx < SKILL_MAX_DB() && skill_db[idx]) + return skill_db[idx]->nameid; + return 0; +} - // map skill id to skill db index - if( skill_id >= GD_SKILLBASE ) - skill_id = GD_SKILLRANGEMIN + skill_id - GD_SKILLBASE; - else if( skill_id >= EL_SKILLBASE ) - skill_id = EL_SKILLRANGEMIN + skill_id - EL_SKILLBASE; - else if( skill_id >= MC_SKILLBASE ) - skill_id = MC_SKILLRANGEMIN + skill_id - MC_SKILLBASE; - else if( skill_id >= HM_SKILLBASE ) - skill_id = HM_SKILLRANGEMIN + skill_id - HM_SKILLBASE; - - // validate result - if( !skill_id || skill_id >= MAX_SKILL_DB ) - return 0; +/** + * Get skill index from skill_db array. The index is also being used for skill lookup in mmo_charstatus::skill[] + * @param skill_id + * @param silent If Skill is undefined, show error message! + * @return Skill Index or 0 if not found/unset + **/ +int skill_get_index_( uint16 skill_id, bool silent, const char *func, const char *file, int line ) { + uint16 idx = (uint16)uidb_iget(skilldb_id2idx, skill_id); + if (!idx && skill_id != 0 && !silent) + ShowError("Skill '%d' is undefined! %s:%d::%s\n", skill_id, file, line, func); + return idx; +} - return skill_id; +/** + * Check if skill is set yet. If not, create new one (for skill_db files reading purpose) + * @param skill_id + * @return Skill index + **/ +static uint16 skill_db_isset(uint16 skill_id, const char *func) { + uint16 idx = skill_get_index2(skill_id); + if (idx || idx == skill_id) + return idx; + ShowWarning("%s: Skill '%d' isn't created in 'skill_db.txt' yet. Creating dummy value...\n", func, skill_id); + idx = skill_db_create(skill_id); + return idx; } +/** + * Get Skill name + * @param skill_id + * @return AEGIS Skill name + **/ const char* skill_get_name( uint16 skill_id ) { - return skill_db[skill_get_index(skill_id)].name; + return skill_db[skill_get_index(skill_id)]->name; } +/** + * Get Skill name + * @param skill_id + * @return English Skill name + **/ const char* skill_get_desc( uint16 skill_id ) { - return skill_db[skill_get_index(skill_id)].desc; + return skill_db[skill_get_index(skill_id)]->desc; } /// out of bounds error checking [celest] @@ -186,70 +220,87 @@ static void skill_chk(uint16 *skill_id) { } // checks/adjusts level static void skill_chk2(uint16 *skill_lv) { - *skill_lv = (*skill_lv < 1) ? 1 : (*skill_lv > MAX_SKILL_LEVEL) ? MAX_SKILL_LEVEL : *skill_lv; + *skill_lv = cap_value(*skill_lv, 1, MAX_SKILL_LEVEL); } // checks/adjusts index. make sure we don't use negative index static void skill_chk3(int *idx) { if (*idx < 0) *idx = 0; } -#define skill_get(var,id) { skill_chk(&id); if(!id) return 0; return var; } -#define skill_get2(var,id,lv) { skill_chk(&id); if (!id) return 0; skill_chk2(&lv); return var; } -#define skill_get3(var,id,x) { skill_chk(&id); if (!id) return 0; skill_chk3(&x); return var; } +#define skill_get(id,var) { skill_chk(&id); if (!id) return 0; return var; } +#define skill_get2(id, lv, arrvar) do {\ + int idx;\ + skill_chk(&(id));\ + if (!(id))\ + return 0;\ + idx = min((lv), MAX_SKILL_LEVEL) - 1;\ + if ((lv) > MAX_SKILL_LEVEL && (arrvar)[idx] > 1) {\ + int lv__ = (lv);\ + (lv) = skill_db[(id)]->max-1;\ + return ((arrvar)[(lv)] + ((lv__-(lv))/2));\ + }\ + return ((arrvar)[idx]);\ +} while(0) +#define skill_get3(id,x,var) { skill_chk(&id); if (!id) return 0; skill_chk3(&x); return var; } // Skill DB -int skill_get_hit( uint16 skill_id ) { skill_get (skill_db[skill_id].hit, skill_id); } -int skill_get_inf( uint16 skill_id ) { skill_get (skill_db[skill_id].inf, skill_id); } -int skill_get_ele( uint16 skill_id , uint16 skill_lv ) { skill_get2 (skill_db[skill_id].element[skill_lv-1], skill_id, skill_lv); } -int skill_get_nk( uint16 skill_id ) { skill_get (skill_db[skill_id].nk, skill_id); } -int skill_get_max( uint16 skill_id ) { skill_get (skill_db[skill_id].max, skill_id); } -int skill_get_range( uint16 skill_id , uint16 skill_lv ) { skill_get2 (skill_db[skill_id].range[skill_lv-1], skill_id, skill_lv); } -int skill_get_splash( uint16 skill_id , uint16 skill_lv ) { skill_get2 ( (skill_db[skill_id].splash[skill_lv-1]>=0?skill_db[skill_id].splash[skill_lv-1]:AREA_SIZE), skill_id, skill_lv); } -int skill_get_num( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].num[skill_lv-1], skill_id, skill_lv); } -int skill_get_cast( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].cast[skill_lv-1], skill_id, skill_lv); } -int skill_get_delay( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].delay[skill_lv-1], skill_id, skill_lv); } -int skill_get_walkdelay( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].walkdelay[skill_lv-1], skill_id, skill_lv); } -int skill_get_time( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].upkeep_time[skill_lv-1], skill_id, skill_lv); } -int skill_get_time2( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].upkeep_time2[skill_lv-1], skill_id, skill_lv); } -int skill_get_castdef( uint16 skill_id ) { skill_get (skill_db[skill_id].cast_def_rate, skill_id); } -int skill_get_inf2( uint16 skill_id ) { skill_get (skill_db[skill_id].inf2, skill_id); } -int skill_get_inf3( uint16 skill_id ) { skill_get (skill_db[skill_id].inf3, skill_id); } -int skill_get_castcancel( uint16 skill_id ) { skill_get (skill_db[skill_id].castcancel, skill_id); } -int skill_get_maxcount( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].maxcount[skill_lv-1], skill_id, skill_lv); } -int skill_get_blewcount( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].blewcount[skill_lv-1], skill_id, skill_lv); } -int skill_get_castnodex( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].castnodex[skill_lv-1], skill_id, skill_lv); } -int skill_get_delaynodex( uint16 skill_id ,uint16 skill_lv ){ skill_get2 (skill_db[skill_id].delaynodex[skill_lv-1], skill_id, skill_lv); } -int skill_get_nocast ( uint16 skill_id ) { skill_get (skill_db[skill_id].nocast, skill_id); } -int skill_get_type( uint16 skill_id ) { skill_get (skill_db[skill_id].skill_type, skill_id); } -int skill_get_unit_id ( uint16 skill_id, int flag ){ skill_get3 (skill_db[skill_id].unit_id[flag], skill_id, flag); } -int skill_get_unit_interval( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_interval, skill_id); } -int skill_get_unit_range( uint16 skill_id, uint16 skill_lv ){ skill_get2 (skill_db[skill_id].unit_range[skill_lv-1], skill_id, skill_lv); } -int skill_get_unit_target( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_target&BCT_ALL, skill_id); } -int skill_get_unit_bl_target( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_target&BL_ALL, skill_id); } -int skill_get_unit_flag( uint16 skill_id ) { skill_get (skill_db[skill_id].unit_flag, skill_id); } -int skill_get_unit_layout_type( uint16 skill_id ,uint16 skill_lv ){ skill_get2 (skill_db[skill_id].unit_layout_type[skill_lv-1], skill_id, skill_lv); } -int skill_get_cooldown( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_db[skill_id].cooldown[skill_lv-1], skill_id, skill_lv); } +int skill_get_hit( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->hit); } +int skill_get_inf( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->inf); } +int skill_get_ele( uint16 skill_id , uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->element); } +int skill_get_nk( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->nk); } +int skill_get_max( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->max); } +int skill_get_range( uint16 skill_id , uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->range); } +int skill_get_splash_( uint16 skill_id , uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->splash); } +int skill_get_num( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->num); } +int skill_get_cast( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->cast); } +int skill_get_delay( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->delay); } +int skill_get_walkdelay( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->walkdelay); } +int skill_get_time( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->upkeep_time); } +int skill_get_time2( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->upkeep_time2); } +int skill_get_castdef( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->cast_def_rate); } +int skill_get_inf2( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->inf2); } +int skill_get_inf3( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->inf3); } +int skill_get_castcancel( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->castcancel); } +int skill_get_maxcount( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->maxcount); } +int skill_get_blewcount( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->blewcount); } +int skill_get_castnodex( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->castnodex); } +int skill_get_delaynodex( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->delaynodex); } +int skill_get_nocast ( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->nocast); } +int skill_get_type( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->skill_type); } +int skill_get_unit_id ( uint16 skill_id, int flag ) { skill_get (skill_id, skill_db[skill_id]->unit_id[flag]); } +int skill_get_unit_interval( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->unit_interval); } +int skill_get_unit_range( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->unit_range); } +int skill_get_unit_target( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->unit_target&BCT_ALL); } +int skill_get_unit_bl_target( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->unit_target&BL_ALL); } +int skill_get_unit_flag( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->unit_flag); } +int skill_get_unit_layout_type( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->unit_layout_type); } +int skill_get_cooldown( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->cooldown); } #ifdef RENEWAL_CAST -int skill_get_fixed_cast( uint16 skill_id ,uint16 skill_lv ){ skill_get2 (skill_db[skill_id].fixed_cast[skill_lv-1], skill_id, skill_lv); } +int skill_get_fixed_cast( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->fixed_cast); } #endif // Skill requirements -int skill_get_hp( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.hp[skill_lv-1], skill_id, skill_lv); } -int skill_get_mhp( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.mhp[skill_lv-1], skill_id, skill_lv); } -int skill_get_sp( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.sp[skill_lv-1], skill_id, skill_lv); } -int skill_get_hp_rate( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.hp_rate[skill_lv-1], skill_id, skill_lv); } -int skill_get_sp_rate( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.sp_rate[skill_lv-1], skill_id, skill_lv); } -int skill_get_zeny( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.zeny[skill_lv-1], skill_id, skill_lv); } -int skill_get_weapontype( uint16 skill_id ) { skill_get (skill_db[skill_id].require.weapon, skill_id); } -int skill_get_ammotype( uint16 skill_id ) { skill_get (skill_db[skill_id].require.ammo, skill_id); } -int skill_get_ammo_qty( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_db[skill_id].require.ammo_qty[skill_lv-1], skill_id, skill_lv); } -int skill_get_state( uint16 skill_id ) { skill_get (skill_db[skill_id].require.state, skill_id); } -//int skill_get_status( uint16 skill_id, int idx ) { skill_get3 (skill_db[skill_id].require.status[idx], skill_id, idx); } -int skill_get_status_count( uint16 skill_id ) { skill_get (skill_db[skill_id].require.status_count, skill_id); } -int skill_get_spiritball( uint16 skill_id, uint16 skill_lv ){ skill_get2 (skill_db[skill_id].require.spiritball[skill_lv-1], skill_id, skill_lv); } -int skill_get_itemid( uint16 skill_id, int idx ) { skill_get3 (skill_db[skill_id].require.itemid[idx], skill_id, idx); } -int skill_get_itemqty( uint16 skill_id, int idx ) { skill_get3 (skill_db[skill_id].require.amount[idx], skill_id, idx); } -int skill_get_itemeq( uint16 skill_id, int idx ) { skill_get3 (skill_db[skill_id].require.eqItem[idx], skill_id, idx); } - +int skill_get_hp( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.hp); } +int skill_get_mhp( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.mhp); } +int skill_get_sp( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.sp); } +int skill_get_hp_rate( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.hp_rate); } +int skill_get_sp_rate( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.sp_rate); } +int skill_get_zeny( uint16 skill_id ,uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.zeny); } +int skill_get_weapontype( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->require.weapon); } +int skill_get_ammotype( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->require.ammo); } +int skill_get_ammo_qty( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.ammo_qty); } +int skill_get_state( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->require.state); } +int skill_get_status_count( uint16 skill_id ) { skill_get (skill_id, skill_db[skill_id]->require.status_count); } +int skill_get_spiritball( uint16 skill_id, uint16 skill_lv ) { skill_get2 (skill_id, skill_lv, skill_db[skill_id]->require.spiritball); } +int skill_get_itemid( uint16 skill_id, int idx ) { skill_get3 (skill_id, idx, skill_db[skill_id]->require.itemid[idx]); } +int skill_get_itemqty( uint16 skill_id, int idx ) { skill_get3 (skill_id, idx, skill_db[skill_id]->require.amount[idx]); } +int skill_get_itemeq( uint16 skill_id, int idx ) { skill_get3 (skill_id, idx, skill_db[skill_id]->require.eqItem[idx]); } + +int skill_get_splash( uint16 skill_id , uint16 skill_lv ) { + int splash = skill_get_splash_(skill_id, skill_lv); + if (splash < 0) + return AREA_SIZE; + return splash; +} int skill_tree_get_max(uint16 skill_id, int b_class) { @@ -455,16 +506,14 @@ int skill_calc_heal(struct block_list *src, struct block_list *target, uint16 sk * @return 0 - Cannot be copied; 1 - Can be copied by Plagiarism 2 - Can be copied by Reproduce * @author Aru - for previous check; Jobbie for class restriction idea; Cydh expands the copyable skill */ -static char skill_isCopyable(struct map_session_data *sd, uint16 skill_id) { - uint16 idx = skill_get_index(skill_id); - +static int8 skill_isCopyable(struct map_session_data *sd, uint16 skill_idx) { // Only copy skill that player doesn't have or the skill is old clone - if (sd->status.skill[idx].id != 0 && sd->status.skill[idx].flag != SKILL_FLAG_PLAGIARIZED) + if (sd->status.skill[skill_idx].id != 0 && sd->status.skill[skill_idx].flag != SKILL_FLAG_PLAGIARIZED) return 0; // Check if the skill is copyable by class if (!pc_has_permission(sd,PC_PERM_ALL_SKILL)) { - uint16 job_allowed = skill_db[idx].copyable.joballowed; + uint16 job_allowed = skill_db[skill_idx]->copyable.joballowed; while (1) { if (job_allowed&0x01 && sd->status.class_ == JOB_ROGUE) break; if (job_allowed&0x02 && sd->status.class_ == JOB_STALKER) break; @@ -477,11 +526,11 @@ static char skill_isCopyable(struct map_session_data *sd, uint16 skill_id) { } //Plagiarism only able to copy skill while SC_PRESERVE is not active and skill is copyable by Plagiarism - if (skill_db[idx].copyable.option&1 && pc_checkskill(sd,RG_PLAGIARISM) && !sd->sc.data[SC_PRESERVE]) + if (skill_db[skill_idx]->copyable.option&1 && pc_checkskill(sd,RG_PLAGIARISM) && !sd->sc.data[SC_PRESERVE]) return 1; //Reproduce can copy skill if SC__REPRODUCE is active and the skill is copyable by Reproduce - if (skill_db[idx].copyable.option&2 && pc_checkskill(sd,SC_REPRODUCE) && &sd->sc && sd->sc.data[SC__REPRODUCE] && sd->sc.data[SC__REPRODUCE]->val1) + if (skill_db[skill_idx]->copyable.option&2 && pc_checkskill(sd,SC_REPRODUCE) && &sd->sc && sd->sc.data[SC__REPRODUCE] && sd->sc.data[SC__REPRODUCE]->val1) return 2; return 0; @@ -2500,35 +2549,38 @@ bool skill_is_combo(uint16 skill_id) { void skill_combo_toogle_inf(struct block_list* bl, uint16 skill_id, int inf){ TBL_PC *sd = BL_CAST(BL_PC, bl); switch (skill_id) { - case MH_MIDNIGHT_FRENZY: - case MH_EQC:{ - int skill_id2 = ((skill_id==MH_EQC)?MH_TINDER_BREAKER:MH_SONIC_CRAW); - int idx = skill_id2 - HM_SKILLBASE; - int flag = (inf?SKILL_FLAG_TMP_COMBO:SKILL_FLAG_PERMANENT); - TBL_HOM *hd = BL_CAST(BL_HOM, bl); - sd = hd->master; - hd->homunculus.hskill[idx].flag= flag; - if(sd) clif_homskillinfoblock(sd); //refresh info //@FIXME we only want to refresh one skill - } - break; - case MO_COMBOFINISH: - case CH_TIGERFIST: - case CH_CHAINCRUSH: - if (sd) clif_skillinfo(sd,MO_EXTREMITYFIST, inf); - break; - case TK_JUMPKICK: - if (sd) clif_skillinfo(sd,TK_JUMPKICK, inf); - break; - case MO_TRIPLEATTACK: - if (sd && pc_checkskill(sd, SR_DRAGONCOMBO) > 0) - clif_skillinfo(sd,SR_DRAGONCOMBO, inf); - break; - case SR_FALLENEMPIRE: - if (sd){ - clif_skillinfo(sd,SR_GATEOFHELL, inf); - clif_skillinfo(sd,SR_TIGERCANNON, inf); - } - break; + case MH_MIDNIGHT_FRENZY: + case MH_EQC: + { + int skill_id2 = ((skill_id==MH_EQC)?MH_TINDER_BREAKER:MH_SONIC_CRAW); + short idx = hom_skill_get_index(skill_id2); + int flag = (inf?SKILL_FLAG_TMP_COMBO:SKILL_FLAG_PERMANENT); + TBL_HOM *hd = BL_CAST(BL_HOM, bl); + if (idx == -1) + break; + sd = hd->master; + hd->homunculus.hskill[idx].flag= flag; + if(sd) clif_homskillinfoblock(sd); //refresh info //@FIXME we only want to refresh one skill + } + break; + case MO_COMBOFINISH: + case CH_TIGERFIST: + case CH_CHAINCRUSH: + if (sd) clif_skillinfo(sd,MO_EXTREMITYFIST, inf); + break; + case TK_JUMPKICK: + if (sd) clif_skillinfo(sd,TK_JUMPKICK, inf); + break; + case MO_TRIPLEATTACK: + if (sd && pc_checkskill(sd, SR_DRAGONCOMBO) > 0) + clif_skillinfo(sd,SR_DRAGONCOMBO, inf); + break; + case SR_FALLENEMPIRE: + if (sd){ + clif_skillinfo(sd,SR_GATEOFHELL, inf); + clif_skillinfo(sd,SR_TIGERCANNON, inf); + } + break; } } @@ -2686,10 +2738,10 @@ static void skill_do_copy(struct block_list* src,struct block_list *bl, uint16 s if (!(idx = skill_get_index(skill_id))) return; - switch (skill_isCopyable(tsd,skill_id)) { + switch (skill_isCopyable(tsd,idx)) { case 1: //Copied by Plagiarism { - if (tsd->cloneskill_idx >= 0 && tsd->status.skill[tsd->cloneskill_idx].flag == SKILL_FLAG_PLAGIARIZED) { + if (tsd->cloneskill_idx > 0 && tsd->status.skill[tsd->cloneskill_idx].flag == SKILL_FLAG_PLAGIARIZED) { tsd->status.skill[tsd->cloneskill_idx].id = 0; tsd->status.skill[tsd->cloneskill_idx].lv = 0; tsd->status.skill[tsd->cloneskill_idx].flag = SKILL_FLAG_PERMANENT; @@ -2709,7 +2761,7 @@ static void skill_do_copy(struct block_list* src,struct block_list *bl, uint16 s //Already did SC check //Skill level copied depends on Reproduce skill that used lv = (tsc) ? tsc->data[SC__REPRODUCE]->val1 : 1; - if( tsd->reproduceskill_idx >= 0 && tsd->status.skill[tsd->reproduceskill_idx].flag == SKILL_FLAG_PLAGIARIZED ) { + if( tsd->reproduceskill_idx > 0 && tsd->status.skill[tsd->reproduceskill_idx].flag == SKILL_FLAG_PLAGIARIZED ) { tsd->status.skill[tsd->reproduceskill_idx].id = 0; tsd->status.skill[tsd->reproduceskill_idx].lv = 0; tsd->status.skill[tsd->reproduceskill_idx].flag = SKILL_FLAG_PERMANENT; @@ -3183,7 +3235,7 @@ int64 skill_attack (int attack_type, struct block_list* src, struct block_list * map_freeblock_lock(); - if (bl->type == BL_PC && skill_id && skill_get_index(skill_id) > 0 && skill_db[skill_get_index(skill_id)].copyable.option && //Only copy skill that copyable [Cydh] + if (bl->type == BL_PC && skill_id && skill_get_index(skill_id) > 0 && skill_db[skill_get_index(skill_id)]->copyable.option && //Only copy skill that copyable [Cydh] dmg.flag&BF_SKILL && dmg.damage+dmg.damage2 > 0 && damage < status_get_hp(bl)) //Cannot copy skills if the blow will kill you. [Skotlex] skill_do_copy(src,bl,skill_id,skill_lv); @@ -3495,8 +3547,8 @@ static int skill_check_unit_range2 (struct block_list *bl, int x, int y, uint16 } //Check the additional range [Cydh] - if (isNearNPC && skill_db[skill_get_index(skill_id)].unit_nonearnpc_range) - range += skill_db[skill_get_index(skill_id)].unit_nonearnpc_range; + if (isNearNPC && skill_db[skill_get_index(skill_id)]->unit_nonearnpc_range) + range += skill_db[skill_get_index(skill_id)]->unit_nonearnpc_range; if (!isNearNPC) { //Doesn't check the NPC range //If the caster is a monster/NPC, only check for players. Otherwise just check characters @@ -3528,8 +3580,6 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill, int uint16 idx; int itemid[MAX_SKILL_ITEM_REQUIRE],amount[ARRAYLENGTH(itemid)],index[ARRAYLENGTH(itemid)]; - if( lv < 1 || lv > MAX_SKILL_LEVEL ) - return 0; nullpo_ret(bl); switch( bl->type ) @@ -3542,18 +3592,20 @@ static int skill_check_condition_mercenary(struct block_list *bl, int skill, int if( (idx = skill_get_index(skill)) == 0 ) return 0; + lv = cap_value(lv, 1, MAX_SKILL_LEVEL); + // Requirements for( i = 0; i < ARRAYLENGTH(itemid); i++ ) { - itemid[i] = skill_db[idx].require.itemid[i]; - amount[i] = skill_db[idx].require.amount[i]; - } - hp = skill_db[idx].require.hp[lv-1]; - sp = skill_db[idx].require.sp[lv-1]; - hp_rate = skill_db[idx].require.hp_rate[lv-1]; - sp_rate = skill_db[idx].require.sp_rate[lv-1]; - state = skill_db[idx].require.state; - if( (mhp = skill_db[idx].require.mhp[lv-1]) > 0 ) + itemid[i] = skill_db[idx]->require.itemid[i]; + amount[i] = skill_db[idx]->require.amount[i]; + } + hp = skill_db[idx]->require.hp[lv-1]; + sp = skill_db[idx]->require.sp[lv-1]; + hp_rate = skill_db[idx]->require.hp_rate[lv-1]; + sp_rate = skill_db[idx]->require.sp_rate[lv-1]; + state = skill_db[idx]->require.state; + if( (mhp = skill_db[idx]->require.mhp[lv-1]) > 0 ) hp += (status->max_hp * mhp) / 100; if( hp_rate > 0 ) hp += (status->hp * hp_rate) / 100; @@ -6749,7 +6801,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui // custom hack to make the mob display the skill, because these skills don't show the skill use text themselves //NOTE: mobs don't have the sprite animation that is used when performing this skill (will cause glitches) char temp[70]; - snprintf(temp, sizeof(temp), "%s : %s !!",md->name,skill_db[skill_get_index(skill_id)].desc); + snprintf(temp, sizeof(temp), "%s : %s !!",md->name,skill_db[skill_get_index(skill_id)]->desc); clif_disp_overhead(&md->bl,temp); } break; @@ -14101,8 +14153,9 @@ bool skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_i return false; } } - if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL ) - return false; + + //if (skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL) + // return false; require = skill_get_requirement(sd,skill_id,skill_lv); @@ -15176,23 +15229,23 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 idx = skill_get_index(skill_id); if( idx == 0 ) // invalid skill id - return req; - if( skill_lv < 1 || skill_lv > MAX_SKILL_LEVEL ) return req; + skill_lv = cap_value(skill_lv, 1, MAX_SKILL_LEVEL); + status = &sd->battle_status; - req.hp = skill_db[idx].require.hp[skill_lv-1]; - hp_rate = skill_db[idx].require.hp_rate[skill_lv-1]; + req.hp = skill_db[idx]->require.hp[skill_lv-1]; + hp_rate = skill_db[idx]->require.hp_rate[skill_lv-1]; if(hp_rate > 0) req.hp += (status->hp * hp_rate)/100; else req.hp += (status->max_hp * (-hp_rate))/100; - req.sp = skill_db[idx].require.sp[skill_lv-1]; + req.sp = skill_db[idx]->require.sp[skill_lv-1]; if((sd->skill_id_old == BD_ENCORE) && skill_id == sd->skill_id_dance) req.sp /= 2; - sp_rate = skill_db[idx].require.sp_rate[skill_lv-1]; + sp_rate = skill_db[idx]->require.sp_rate[skill_lv-1]; if(sp_rate > 0) req.sp += (status->sp * sp_rate)/100; else @@ -15222,7 +15275,7 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 req.sp -= req.sp * sc->data[SC_TELEKINESIS_INTENSE]->val2 / 100; } - req.zeny = skill_db[idx].require.zeny[skill_lv-1]; + req.zeny = skill_db[idx]->require.zeny[skill_lv-1]; if( sc && sc->data[SC__UNLUCKY] ) { if(sc->data[SC__UNLUCKY]->val1 < 3) @@ -15231,33 +15284,33 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 req.zeny += 1000; } - req.spiritball = skill_db[idx].require.spiritball[skill_lv-1]; - req.state = skill_db[idx].require.state; + req.spiritball = skill_db[idx]->require.spiritball[skill_lv-1]; + req.state = skill_db[idx]->require.state; - req.mhp = skill_db[idx].require.mhp[skill_lv-1]; - req.weapon = skill_db[idx].require.weapon; - req.ammo_qty = skill_db[idx].require.ammo_qty[skill_lv-1]; + req.mhp = skill_db[idx]->require.mhp[skill_lv-1]; + req.weapon = skill_db[idx]->require.weapon; + req.ammo_qty = skill_db[idx]->require.ammo_qty[skill_lv-1]; if (req.ammo_qty) - req.ammo = skill_db[idx].require.ammo; + req.ammo = skill_db[idx]->require.ammo; if (!req.ammo && skill_id && skill_isammotype(sd, skill_id)) { //Assume this skill is using the weapon, therefore it requires arrows. - req.ammo = 0xFFFFFFFF; //Enable use on all ammo types. + req.ammo = AMMO_TYPE_ALL; //Enable use on all ammo types. req.ammo_qty = 1; } - req.status_count = skill_db[idx].require.status_count; - req.status = skill_db[idx].require.status; - req.eqItem_count = skill_db[idx].require.eqItem_count; - req.eqItem = skill_db[idx].require.eqItem; + req.status_count = skill_db[idx]->require.status_count; + req.status = skill_db[idx]->require.status; + req.eqItem_count = skill_db[idx]->require.eqItem_count; + req.eqItem = skill_db[idx]->require.eqItem; switch( skill_id ) { /* Skill level-dependent checks */ case NC_SHAPESHIFT: case NC_REPAIR: //NOTE: Please make sure Magic_Gear_Fuel in the last position in skill_require_db.txt - req.itemid[1] = skill_db[idx].require.itemid[MAX_SKILL_ITEM_REQUIRE-1]; - req.amount[1] = skill_db[idx].require.amount[MAX_SKILL_ITEM_REQUIRE-1]; + req.itemid[1] = skill_db[idx]->require.itemid[MAX_SKILL_ITEM_REQUIRE-1]; + req.amount[1] = skill_db[idx]->require.amount[MAX_SKILL_ITEM_REQUIRE-1]; case GN_FIRE_EXPANSION: case SO_SUMMON_AGNI: case SO_SUMMON_AQUA: @@ -15268,8 +15321,8 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 case SO_WIND_INSIGNIA: case SO_EARTH_INSIGNIA: case WZ_FIREPILLAR: // no gems required at level 1-5 [celest] - req.itemid[0] = skill_db[idx].require.itemid[min(skill_lv-1,MAX_SKILL_ITEM_REQUIRE-1)]; - req.amount[0] = skill_db[idx].require.amount[min(skill_lv-1,MAX_SKILL_ITEM_REQUIRE-1)]; + req.itemid[0] = skill_db[idx]->require.itemid[min(skill_lv-1,MAX_SKILL_ITEM_REQUIRE-1)]; + req.amount[0] = skill_db[idx]->require.amount[min(skill_lv-1,MAX_SKILL_ITEM_REQUIRE-1)]; level_dependent = true; /* Normal skill requirements and gemstone checks */ @@ -15289,17 +15342,17 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 continue; break; case AB_ADORAMUS: - if( itemid_isgemstone(skill_db[idx].require.itemid[i]) && (sd->special_state.no_gemstone == 2 || skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 2)) ) + if( itemid_isgemstone(skill_db[idx]->require.itemid[i]) && (sd->special_state.no_gemstone == 2 || skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 2)) ) continue; break; case WL_COMET: - if( itemid_isgemstone(skill_db[idx].require.itemid[i]) && (sd->special_state.no_gemstone == 2 || skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 0)) ) + if( itemid_isgemstone(skill_db[idx]->require.itemid[i]) && (sd->special_state.no_gemstone == 2 || skill_check_pc_partner(sd,skill_id,&skill_lv, 1, 0)) ) continue; break; } - req.itemid[i] = skill_db[idx].require.itemid[i]; - req.amount[i] = skill_db[idx].require.amount[i]; + req.itemid[i] = skill_db[idx]->require.itemid[i]; + req.amount[i] = skill_db[idx]->require.amount[i]; if( skill_id >= HT_SKIDTRAP && skill_id <= HT_TALKIEBOX && pc_checkskill(sd, RA_RESEARCHTRAP) > 0){ int16 itIndex; @@ -15421,7 +15474,7 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, uint16 //Check if player is using the copied skill [Cydh] if (sd->status.skill[idx].flag == SKILL_FLAG_PLAGIARIZED) { - uint16 req_opt = skill_db[idx].copyable.req_opt; + uint16 req_opt = skill_db[idx]->copyable.req_opt; if (req_opt&0x0001) req.hp = 0; if (req_opt&0x0002) req.mhp = 0; @@ -15455,7 +15508,7 @@ int skill_castfix(struct block_list *bl, uint16 skill_id, uint16 skill_lv) { sd = BL_CAST(BL_PC, bl); // calculate base cast time (reduced by dex) - if( !(skill_get_castnodex(skill_id, skill_lv)&1) ) { + if( !(skill_get_castnodex(skill_id)&1) ) { int scale = battle_config.castrate_dex_scale - status_get_dex(bl); if( scale > 0 ) // not instant cast time = time * scale / battle_config.castrate_dex_scale; @@ -15464,7 +15517,7 @@ int skill_castfix(struct block_list *bl, uint16 skill_id, uint16 skill_lv) { } // calculate cast time reduced by item/card bonuses - if( !(skill_get_castnodex(skill_id, skill_lv)&4) && sd ) + if( !(skill_get_castnodex(skill_id)&4) && sd ) { int i; if( sd->castrate != 100 ) @@ -15551,7 +15604,7 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 struct map_session_data *sd = BL_CAST(BL_PC,bl); int fixed = skill_get_fixed_cast(skill_id, skill_lv); short fixcast_r = 0; - uint8 i = 0, flag = skill_get_castnodex(skill_id, skill_lv); + uint8 i = 0, flag = skill_get_castnodex(skill_id); #define FIXEDCASTRATE2(val) ( FIXEDCASTRATE(fixcast_r,(val)) ) @@ -15681,7 +15734,7 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16 *------------------------------------------*/ int skill_delayfix (struct block_list *bl, uint16 skill_id, uint16 skill_lv) { - int delaynodex = skill_get_delaynodex(skill_id, skill_lv); + int delaynodex = skill_get_delaynodex(skill_id); int time = skill_get_delay(skill_id, skill_lv); struct map_session_data *sd; struct status_change *sc = status_get_sc(bl); @@ -17086,7 +17139,7 @@ struct skill_unit_group* skill_id2group(int group_id) { return (struct skill_unit_group*)idb_get(skillunit_group_db, group_id); } -static int skill_unit_group_newid = MAX_SKILL_DB; /// Skill Unit Group ID +static int skill_unit_group_newid = MAX_SKILL; /// Skill Unit Group ID /** * Returns a new group_id that isn't being used in skillunit_group_db. @@ -17094,14 +17147,14 @@ static int skill_unit_group_newid = MAX_SKILL_DB; /// Skill Unit Group ID */ static int skill_get_new_group_id(void) { - if( skill_unit_group_newid >= MAX_SKILL_DB && skill_id2group(skill_unit_group_newid) == NULL ) + if( skill_unit_group_newid >= MAX_SKILL && skill_id2group(skill_unit_group_newid) == NULL ) return skill_unit_group_newid++;// available {// find next id int base_id = skill_unit_group_newid; while( base_id != ++skill_unit_group_newid ) { - if( skill_unit_group_newid < MAX_SKILL_DB ) - skill_unit_group_newid = MAX_SKILL_DB; + if( skill_unit_group_newid < MAX_SKILL ) + skill_unit_group_newid = MAX_SKILL; if( skill_id2group(skill_unit_group_newid) == NULL ) return skill_unit_group_newid++;// available } @@ -18842,7 +18895,8 @@ void skill_spellbook (struct map_session_data *sd, unsigned short nameid) { } int skill_select_menu(struct map_session_data *sd,uint16 skill_id) { - int id, lv, prob, aslvl = 0; + int lv, prob, aslvl = 0; + uint16 sk_idx = 0; nullpo_ret(sd); if (sd->sc.data[SC_STOP]) { @@ -18850,15 +18904,18 @@ int skill_select_menu(struct map_session_data *sd,uint16 skill_id) { status_change_end(&sd->bl,SC_STOP,INVALID_TIMER); } - if( !(skill_get_inf2(sd->status.skill[skill_id].id)&INF2_AUTOSHADOWSPELL) || (id = sd->status.skill[skill_id].id) == 0 || sd->status.skill[skill_id].flag != SKILL_FLAG_PLAGIARIZED ) { - clif_skill_fail(sd,SC_AUTOSHADOWSPELL,0,0); + if (!skill_id || (sk_idx = skill_get_index(skill_id))) + return 0; + + if( !(skill_get_inf2(skill_id)&INF2_AUTOSHADOWSPELL) || sd->status.skill[sk_idx].id == 0 || sd->status.skill[sk_idx].flag != SKILL_FLAG_PLAGIARIZED ) { + clif_skill_fail(sd,SC_AUTOSHADOWSPELL,USESKILL_FAIL_LEVEL,0); return 0; } lv = (aslvl + 1) / 2; // The level the skill will be autocasted - lv = min(lv,sd->status.skill[skill_id].lv); + lv = min(lv,sd->status.skill[sk_idx].lv); prob = (aslvl >= 10) ? 15 : (30 - 2 * aslvl); // Probability at level 10 was increased to 15. - sc_start4(&sd->bl,&sd->bl,SC__AUTOSHADOWSPELL,100,id,lv,prob,0,skill_get_time(SC_AUTOSHADOWSPELL,aslvl)); + sc_start4(&sd->bl,&sd->bl,SC__AUTOSHADOWSPELL,100,skill_id,lv,prob,0,skill_get_time(SC_AUTOSHADOWSPELL,aslvl)); return 0; } @@ -19104,7 +19161,7 @@ int skill_blockhomun_end(int tid, unsigned int tick, int id, intptr_t data) //[o { struct homun_data *hd = (TBL_HOM*) map_id2bl(id); - if (data <= 0 || data >= MAX_SKILL) + if (data <= 0 || data >= SKILL_MAX_DB()) return 0; if (hd) @@ -19136,7 +19193,7 @@ int skill_blockmerc_end(int tid, unsigned int tick, int id, intptr_t data) //[or { struct mercenary_data *md = (TBL_MER*)map_id2bl(id); - if( data <= 0 || data >= MAX_SKILL ) + if( data <= 0 || data >= SKILL_MAX_DB() ) return 0; if( md ) @@ -19277,25 +19334,22 @@ void skill_init_unit_layout (void) { // afterwards add special ones pos = i; - for (i=0;i= HM_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) { - int skill = i; - - if( i >= EL_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) { - skill -= EL_SKILLRANGEMIN; - skill += EL_SKILLBASE; - } - if( skill == EL_FIRE_MANTLE ) { - static const int dx[] = {-1, 0, 1, 1, 1, 0,-1,-1}; - static const int dy[] = { 1, 1, 1, 0,-1,-1,-1, 0}; - skill_unit_layout[pos].count = 8; - memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); - memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); - } + if (!skill_db[i]->unit_id[0] || skill_db[i]->unit_layout_type[0] != -1) + continue; + skill_id = skill_idx2id(i); + + if( skill_id == EL_FIRE_MANTLE ) { + static const int dx[] = {-1, 0, 1, 1, 1, 0,-1,-1}; + static const int dy[] = { 1, 1, 1, 0,-1,-1,-1, 0}; + skill_unit_layout[pos].count = 8; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); } else { - switch (i) { + switch (skill_id) { case MG_FIREWALL: case WZ_ICEWALL: case WL_EARTHSTRAIN: @@ -19401,26 +19455,26 @@ void skill_init_unit_layout (void) { skill_unit_layout[pos].count = 4; memcpy(skill_unit_layout[pos].dx,dx1,sizeof(dx1)); memcpy(skill_unit_layout[pos].dy,dy1,sizeof(dy1)); - skill_db[i].unit_layout_type[j] = pos; + skill_db[i]->unit_layout_type[j] = pos; //lv2/3 j++; pos++; skill_unit_layout[pos].count = 8; memcpy(skill_unit_layout[pos].dx,dx2,sizeof(dx2)); memcpy(skill_unit_layout[pos].dy,dy2,sizeof(dy2)); - skill_db[i].unit_layout_type[j] = pos; - skill_db[i].unit_layout_type[++j] = pos; + skill_db[i]->unit_layout_type[j] = pos; + skill_db[i]->unit_layout_type[++j] = pos; //lv4/5 j++; pos++; skill_unit_layout[pos].count = 12; memcpy(skill_unit_layout[pos].dx,dx3,sizeof(dx3)); memcpy(skill_unit_layout[pos].dy,dy3,sizeof(dy3)); - skill_db[i].unit_layout_type[j] = pos; - skill_db[i].unit_layout_type[++j] = pos; + skill_db[i]->unit_layout_type[j] = pos; + skill_db[i]->unit_layout_type[++j] = pos; //Fill in the rest using lv 5. for (;junit_layout_type[j] = pos; //Skip, this way the check below will fail and continue to the next skill. pos++; } @@ -19441,7 +19495,7 @@ void skill_init_unit_layout (void) { if (!skill_unit_layout[pos].count) continue; for (j=0;junit_layout_type[j] = pos; pos++; } @@ -19833,49 +19887,46 @@ int skill_get_elemental_type( uint16 skill_id , uint16 skill_lv ) { static bool skill_parse_row_skilldb(char* split[], int columns, int current) {// id,range,hit,inf,element,nk,splash,max,list_num,castcancel,cast_defence_rate,inf2,maxcount,skill_type,blow_count,name,description uint16 skill_id = atoi(split[0]); - uint16 idx; - if( (skill_id >= GD_SKILLRANGEMIN && skill_id <= GD_SKILLRANGEMAX) - || (skill_id >= HM_SKILLRANGEMIN && skill_id <= HM_SKILLRANGEMAX) - || (skill_id >= MC_SKILLRANGEMIN && skill_id <= MC_SKILLRANGEMAX) - || (skill_id >= EL_SKILLRANGEMIN && skill_id <= EL_SKILLRANGEMAX) ) { - ShowWarning("skill_parse_row_skilldb: Skill id %d is forbidden (interferes with guild/homun/mercenary skill mapping)!\n", skill_id); - return false; - } + uint16 idx = skill_get_index2(skill_id); - idx = skill_get_index(skill_id); - if( !idx ) // invalid skill id - return false; + if (!idx) { + if (SKILL_MAX_DB() >= MAX_SKILL) { + ShowError("Cannot add new skill. Limit is reached '%d' (mmo.h::MAX_SKILL).\n", MAX_SKILL); + return false; + } + idx = skill_db_create(skill_id); + } - skill_split_atoi(split[1],skill_db[idx].range); - skill_db[idx].hit = atoi(split[2]); - skill_db[idx].inf = atoi(split[3]); - skill_split_atoi(split[4],skill_db[idx].element); - skill_db[idx].nk = (int)strtol(split[5], NULL, 0); - skill_split_atoi(split[6],skill_db[idx].splash); - skill_db[idx].max = atoi(split[7]); - skill_split_atoi(split[8],skill_db[idx].num); + skill_split_atoi(split[1],skill_db[idx]->range); + skill_db[idx]->hit = atoi(split[2]); + skill_db[idx]->inf = atoi(split[3]); + skill_split_atoi(split[4],skill_db[idx]->element); + skill_db[idx]->nk = (uint8)strtol(split[5], NULL, 0); + skill_split_atoi(split[6],skill_db[idx]->splash); + skill_db[idx]->max = atoi(split[7]); + skill_split_atoi(split[8],skill_db[idx]->num); if( strcmpi(split[9],"yes") == 0 ) - skill_db[idx].castcancel = 1; + skill_db[idx]->castcancel = true; else - skill_db[idx].castcancel = 0; - skill_db[idx].cast_def_rate = atoi(split[10]); - skill_db[idx].inf2 = (int)strtol(split[11], NULL, 0); - skill_split_atoi(split[12],skill_db[idx].maxcount); + skill_db[idx]->castcancel = false; + skill_db[idx]->cast_def_rate = atoi(split[10]); + skill_db[idx]->inf2 = (unsigned int)strtol(split[11], NULL, 0); + skill_split_atoi(split[12],skill_db[idx]->maxcount); if( strcmpi(split[13],"weapon") == 0 ) - skill_db[idx].skill_type = BF_WEAPON; + skill_db[idx]->skill_type = BF_WEAPON; else if( strcmpi(split[13],"magic") == 0 ) - skill_db[idx].skill_type = BF_MAGIC; + skill_db[idx]->skill_type = BF_MAGIC; else if( strcmpi(split[13],"misc") == 0 ) - skill_db[idx].skill_type = BF_MISC; + skill_db[idx]->skill_type = BF_MISC; else - skill_db[idx].skill_type = 0; - skill_split_atoi(split[14],skill_db[idx].blewcount); - skill_db[idx].inf3 = (int)strtol(split[15], NULL,0); - safestrncpy(skill_db[idx].name, trim(split[16]), sizeof(skill_db[idx].name)); - safestrncpy(skill_db[idx].desc, trim(split[17]), sizeof(skill_db[idx].desc)); - strdb_iput(skilldb_name2id, skill_db[idx].name, skill_id); + skill_db[idx]->skill_type = 0; + skill_split_atoi(split[14],skill_db[idx]->blewcount); + skill_db[idx]->inf3 = (unsigned int)strtol(split[15], NULL,0); + safestrncpy(skill_db[idx]->name, trim(split[16]), sizeof(skill_db[idx]->name)); + safestrncpy(skill_db[idx]->desc, trim(split[17]), sizeof(skill_db[idx]->desc)); + strdb_iput(skilldb_name2id, skill_db[idx]->name, skill_id); return true; } @@ -19916,16 +19967,16 @@ uint8 skill_split_atoi2(char *str, int *val, const char *delim, int min_value, u } /// Clear status data from skill requirement -static void skill_destroy_requirement(void) { - uint16 i; - for (i = 0; i < MAX_SKILL; i++) { - if (skill_db[i].require.status_count) - aFree(skill_db[i].require.status); - skill_db[i].require.status_count = 0; - if (skill_db[i].require.eqItem_count) - aFree(skill_db[i].require.eqItem); - skill_db[i].require.eqItem_count = 0; - } +static void skill_destroy_requirement(uint16 idx) { + if (skill_db[idx]->require.status_count) + aFree(skill_db[idx]->require.status); + skill_db[idx]->require.status = NULL; + skill_db[idx]->require.status_count = 0; + + if (skill_db[idx]->require.eqItem_count) + aFree(skill_db[idx]->require.eqItem); + skill_db[idx]->require.eqItem = NULL; + skill_db[idx]->require.eqItem_count = 0; } /** @@ -19937,25 +19988,24 @@ static bool skill_parse_row_requiredb(char* split[], int columns, int current) char* p; uint16 skill_id = atoi(split[0]), idx, i; - if (!(idx = skill_get_index(skill_id))) // invalid skill id - return false; + idx = skill_db_isset(atoi(split[0]), __FUNCTION__); - skill_split_atoi(split[1],skill_db[idx].require.hp); - skill_split_atoi(split[2],skill_db[idx].require.mhp); - skill_split_atoi(split[3],skill_db[idx].require.sp); - skill_split_atoi(split[4],skill_db[idx].require.hp_rate); - skill_split_atoi(split[5],skill_db[idx].require.sp_rate); - skill_split_atoi(split[6],skill_db[idx].require.zeny); + skill_split_atoi(split[1],skill_db[idx]->require.hp); + skill_split_atoi(split[2],skill_db[idx]->require.mhp); + skill_split_atoi(split[3],skill_db[idx]->require.sp); + skill_split_atoi(split[4],skill_db[idx]->require.hp_rate); + skill_split_atoi(split[5],skill_db[idx]->require.sp_rate); + skill_split_atoi(split[6],skill_db[idx]->require.zeny); //Witch weapon type are required, see doc/item_db for weapon types (View column) p = split[7]; while (p) { int l = atoi(p); if( l == 99 ) { // Any weapon - skill_db[idx].require.weapon = 0; + skill_db[idx]->require.weapon = 0; break; } else - skill_db[idx].require.weapon |= 1<require.weapon |= 1<require.ammo = AMMO_TYPE_ALL; break; } else if( l ) // 0 stands for no requirement - skill_db[idx].require.ammo |= 1<require.ammo |= 1<require.ammo_qty); + + if( strcmpi(split[10],"hidden") == 0 ) skill_db[idx]->require.state = ST_HIDDEN; + else if( strcmpi(split[10],"riding") == 0 ) skill_db[idx]->require.state = ST_RIDING; + else if( strcmpi(split[10],"falcon") == 0 ) skill_db[idx]->require.state = ST_FALCON; + else if( strcmpi(split[10],"cart") == 0 ) skill_db[idx]->require.state = ST_CART; + else if( strcmpi(split[10],"shield") == 0 ) skill_db[idx]->require.state = ST_SHIELD; + else if( strcmpi(split[10],"recover_weight_rate") == 0 ) skill_db[idx]->require.state = ST_RECOV_WEIGHT_RATE; + else if( strcmpi(split[10],"move_enable") == 0 ) skill_db[idx]->require.state = ST_MOVE_ENABLE; + else if( strcmpi(split[10],"water") == 0 ) skill_db[idx]->require.state = ST_WATER; + else if( strcmpi(split[10],"dragon") == 0 ) skill_db[idx]->require.state = ST_RIDINGDRAGON; + else if( strcmpi(split[10],"warg") == 0 ) skill_db[idx]->require.state = ST_WUG; + else if( strcmpi(split[10],"ridingwarg") == 0 ) skill_db[idx]->require.state = ST_RIDINGWUG; + else if( strcmpi(split[10],"mado") == 0 ) skill_db[idx]->require.state = ST_MADO; + else if( strcmpi(split[10],"elementalspirit") == 0 ) skill_db[idx]->require.state = ST_ELEMENTALSPIRIT; + else if( strcmpi(split[10],"peco") == 0 ) skill_db[idx]->require.state = ST_PECO; + else skill_db[idx]->require.state = ST_NONE; // Unknown or no state //Status requirements //FIXME: Default entry should be -1/SC_ALL in skill_require_db.txt but it's 0/SC_STONE. trim(split[11]); if (split[11][0] != '\0' || atoi(split[11])) { int require[MAX_SKILL_STATUS_REQUIRE]; - if ((skill_db[idx].require.status_count = skill_split_atoi2(split[11], require, ":", SC_STONE, ARRAYLENGTH(require)))) { - CREATE(skill_db[idx].require.status, enum sc_type, skill_db[idx].require.status_count); - for (i = 0; i < skill_db[idx].require.status_count; i++) - skill_db[idx].require.status[i] = (sc_type)require[i]; + if ((skill_db[idx]->require.status_count = skill_split_atoi2(split[11], require, ":", SC_STONE, ARRAYLENGTH(require)))) { + CREATE(skill_db[idx]->require.status, enum sc_type, skill_db[idx]->require.status_count); + for (i = 0; i < skill_db[idx]->require.status_count; i++) + skill_db[idx]->require.status[i] = (sc_type)require[i]; } } - skill_split_atoi(split[12],skill_db[idx].require.spiritball); + skill_split_atoi(split[12],skill_db[idx]->require.spiritball); for( i = 0; i < MAX_SKILL_ITEM_REQUIRE; i++ ) { - skill_db[idx].require.itemid[i] = atoi(split[13+ 2*i]); - skill_db[idx].require.amount[i] = atoi(split[14+ 2*i]); + skill_db[idx]->require.itemid[i] = atoi(split[13+ 2*i]); + skill_db[idx]->require.amount[i] = atoi(split[14+ 2*i]); } //Equipped Item requirements. @@ -20018,10 +20068,10 @@ static bool skill_parse_row_requiredb(char* split[], int columns, int current) trim(split[33]); if (split[33][0] != '\0' || atoi(split[33])) { int require[MAX_SKILL_EQUIP_REQUIRE]; - if ((skill_db[idx].require.eqItem_count = skill_split_atoi2(split[33], require, ":", 500, ARRAYLENGTH(require)))) { - CREATE(skill_db[idx].require.eqItem, uint16, skill_db[idx].require.eqItem_count); - for (i = 0; i < skill_db[idx].require.eqItem_count; i++) - skill_db[idx].require.eqItem[i] = require[i]; + if ((skill_db[idx]->require.eqItem_count = skill_split_atoi2(split[33], require, ":", 500, ARRAYLENGTH(require)))) { + CREATE(skill_db[idx]->require.eqItem, uint16, skill_db[idx]->require.eqItem_count); + for (i = 0; i < skill_db[idx]->require.eqItem_count; i++) + skill_db[idx]->require.eqItem[i] = require[i]; } } return true; @@ -20032,19 +20082,16 @@ static bool skill_parse_row_requiredb(char* split[], int columns, int current) */ static bool skill_parse_row_castdb(char* split[], int columns, int current) { - uint16 idx = skill_get_index(atoi(split[0])); - - if( !idx ) // invalid skill id - return false; - - skill_split_atoi(split[1],skill_db[idx].cast); - skill_split_atoi(split[2],skill_db[idx].delay); - skill_split_atoi(split[3],skill_db[idx].walkdelay); - skill_split_atoi(split[4],skill_db[idx].upkeep_time); - skill_split_atoi(split[5],skill_db[idx].upkeep_time2); - skill_split_atoi(split[6],skill_db[idx].cooldown); + uint16 idx = skill_db_isset(atoi(split[0]), __FUNCTION__); + + skill_split_atoi(split[1],skill_db[idx]->cast); + skill_split_atoi(split[2],skill_db[idx]->delay); + skill_split_atoi(split[3],skill_db[idx]->walkdelay); + skill_split_atoi(split[4],skill_db[idx]->upkeep_time); + skill_split_atoi(split[5],skill_db[idx]->upkeep_time2); + skill_split_atoi(split[6],skill_db[idx]->cooldown); #ifdef RENEWAL_CAST - skill_split_atoi(split[7],skill_db[idx].fixed_cast); + skill_split_atoi(split[7],skill_db[idx]->fixed_cast); #endif return true; } @@ -20054,14 +20101,11 @@ static bool skill_parse_row_castdb(char* split[], int columns, int current) */ static bool skill_parse_row_castnodexdb(char* split[], int columns, int current) { - uint16 idx = skill_get_index(atoi(split[0])); - - if( !idx ) // invalid skill id - return false; + uint16 idx = skill_db_isset(atoi(split[0]), __FUNCTION__); - skill_split_atoi(split[1],skill_db[idx].castnodex); + skill_db[idx]->castnodex = atoi(split[1]); if( split[2] ) // optional column - skill_split_atoi(split[2],skill_db[idx].delaynodex); + skill_db[idx]->delaynodex = atoi(split[2]); return true; } @@ -20071,12 +20115,9 @@ static bool skill_parse_row_castnodexdb(char* split[], int columns, int current) */ static bool skill_parse_row_nocastdb(char* split[], int columns, int current) { - uint16 idx = skill_get_index(atoi(split[0])); + uint16 idx = skill_db_isset(atoi(split[0]), __FUNCTION__); - if( !idx ) // invalid skill id - return false; - - skill_db[idx].nocast |= atoi(split[1]); + skill_db[idx]->nocast |= atoi(split[1]); return true; } @@ -20086,42 +20127,39 @@ static bool skill_parse_row_nocastdb(char* split[], int columns, int current) */ static bool skill_parse_row_unitdb(char* split[], int columns, int current) { - uint16 idx = skill_get_index(atoi(split[0])); - - if( !idx ) // invalid skill id - return false; - - skill_db[idx].unit_id[0] = strtol(split[1],NULL,16); - skill_db[idx].unit_id[1] = strtol(split[2],NULL,16); - skill_split_atoi(split[3],skill_db[idx].unit_layout_type); - skill_split_atoi(split[4],skill_db[idx].unit_range); - skill_db[idx].unit_interval = atoi(split[5]); - - if( strcmpi(split[6],"noenemy")==0 ) skill_db[idx].unit_target = BCT_NOENEMY; - else if( strcmpi(split[6],"friend")==0 ) skill_db[idx].unit_target = BCT_NOENEMY; - else if( strcmpi(split[6],"party")==0 ) skill_db[idx].unit_target = BCT_PARTY; - else if( strcmpi(split[6],"ally")==0 ) skill_db[idx].unit_target = BCT_PARTY|BCT_GUILD; - else if( strcmpi(split[6],"guild")==0 ) skill_db[idx].unit_target = BCT_GUILD; - else if( strcmpi(split[6],"all")==0 ) skill_db[idx].unit_target = BCT_ALL; - else if( strcmpi(split[6],"enemy")==0 ) skill_db[idx].unit_target = BCT_ENEMY; - else if( strcmpi(split[6],"self")==0 ) skill_db[idx].unit_target = BCT_SELF; - else if( strcmpi(split[6],"sameguild")==0 ) skill_db[idx].unit_target = BCT_GUILD|BCT_SAMEGUILD; - else if( strcmpi(split[6],"noone")==0 ) skill_db[idx].unit_target = BCT_NOONE; - else skill_db[idx].unit_target = strtol(split[6],NULL,16); - - skill_db[idx].unit_flag = strtol(split[7],NULL,16); - - if (skill_db[idx].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy) - skill_db[idx].unit_target = BCT_NOENEMY; + uint16 idx = skill_db_isset(atoi(split[0]), __FUNCTION__); + + skill_db[idx]->unit_id[0] = (uint16)strtol(split[1],NULL,16); + skill_db[idx]->unit_id[1] = (uint16)strtol(split[2],NULL,16); + skill_split_atoi(split[3],skill_db[idx]->unit_layout_type); + skill_split_atoi(split[4],skill_db[idx]->unit_range); + skill_db[idx]->unit_interval = atoi(split[5]); + + if( strcmpi(split[6],"noenemy")==0 ) skill_db[idx]->unit_target = BCT_NOENEMY; + else if( strcmpi(split[6],"friend")==0 ) skill_db[idx]->unit_target = BCT_NOENEMY; + else if( strcmpi(split[6],"party")==0 ) skill_db[idx]->unit_target = BCT_PARTY; + else if( strcmpi(split[6],"ally")==0 ) skill_db[idx]->unit_target = BCT_PARTY|BCT_GUILD; + else if( strcmpi(split[6],"guild")==0 ) skill_db[idx]->unit_target = BCT_GUILD; + else if( strcmpi(split[6],"all")==0 ) skill_db[idx]->unit_target = BCT_ALL; + else if( strcmpi(split[6],"enemy")==0 ) skill_db[idx]->unit_target = BCT_ENEMY; + else if( strcmpi(split[6],"self")==0 ) skill_db[idx]->unit_target = BCT_SELF; + else if( strcmpi(split[6],"sameguild")==0 ) skill_db[idx]->unit_target = BCT_GUILD|BCT_SAMEGUILD; + else if( strcmpi(split[6],"noone")==0 ) skill_db[idx]->unit_target = BCT_NOONE; + else skill_db[idx]->unit_target = strtol(split[6],NULL,16); + + skill_db[idx]->unit_flag = strtol(split[7],NULL,16); + + if (skill_db[idx]->unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy) + skill_db[idx]->unit_target = BCT_NOENEMY; //By default, target just characters. - skill_db[idx].unit_target |= BL_CHAR; - if (skill_db[idx].unit_flag&UF_NOPC) - skill_db[idx].unit_target &= ~BL_PC; - if (skill_db[idx].unit_flag&UF_NOMOB) - skill_db[idx].unit_target &= ~BL_MOB; - if (skill_db[idx].unit_flag&UF_SKILL) - skill_db[idx].unit_target |= BL_SKILL; + skill_db[idx]->unit_target |= BL_CHAR; + if (skill_db[idx]->unit_flag&UF_NOPC) + skill_db[idx]->unit_target &= ~BL_PC; + if (skill_db[idx]->unit_flag&UF_NOMOB) + skill_db[idx]->unit_target &= ~BL_MOB; + if (skill_db[idx]->unit_flag&UF_SKILL) + skill_db[idx]->unit_target |= BL_SKILL; return true; } @@ -20332,26 +20370,24 @@ static bool skill_parse_row_copyabledb(char* split[], int column, int current) else id = skill_name2id(split[0]); - if ((id = skill_get_index(id)) == 0) { - ShowError("skill_parse_row_copyabledb: Invalid skill '%s'\n",split[0]); - return false; - } + id = skill_db_isset(id, __FUNCTION__); + if ((option = atoi(split[1])) > 3) { ShowError("skill_parse_row_copyabledb: Invalid option '%s'\n",split[1]); return false; } // Import just for clearing/disabling from original data if (option == 0) { - memset(&skill_db[id].copyable, 0, sizeof(skill_db[id].copyable)); + memset(&skill_db[id]->copyable, 0, sizeof(skill_db[id]->copyable)); //ShowInfo("skill_parse_row_copyabledb: Skill %s removed from list.\n", split[0]); return true; } - skill_db[id].copyable.option = option; - skill_db[id].copyable.joballowed = 63; + skill_db[id]->copyable.option = option; + skill_db[id]->copyable.joballowed = 63; if (atoi(split[2])) - skill_db[id].copyable.joballowed = cap_value(atoi(split[2]),1,63); - skill_db[id].copyable.req_opt = cap_value(atoi(split[3]),0,(0x2000)-1); + skill_db[id]->copyable.joballowed = cap_value(atoi(split[2]),1,63); + skill_db[id]->copyable.req_opt = cap_value(atoi(split[3]),0,(0x2000)-1); return true; } @@ -20369,13 +20405,10 @@ static bool skill_parse_row_nonearnpcrangedb(char* split[], int column, int curr else id = skill_name2id(split[0]); - if ((id = skill_get_index(id)) == 0) { // invalid skill id - ShowError("skill_parse_row_nonearnpcrangedb: Invalid skill '%s'\n",split[0]); - return false; - } + id = skill_db_isset(atoi(split[0]), __FUNCTION__); - skill_db[id].unit_nonearnpc_range = max(atoi(split[1]),0); - skill_db[id].unit_nonearnpc_type = (atoi(split[2])) ? cap_value(atoi(split[2]),1,15) : 15; + skill_db[id]->unit_nonearnpc_range = max(atoi(split[1]),0); + skill_db[id]->unit_nonearnpc_type = (atoi(split[2])) ? cap_value(atoi(split[2]),1,15) : 15; return true; } @@ -20478,24 +20511,62 @@ static bool skill_parse_row_changematerialdb(char* split[], int columns, int cur static bool skill_parse_row_skilldamage(char* split[], int columns, int current) { uint16 skill_id = skill_name2id(split[0]), idx; - if ((idx = skill_get_index(skill_id)) == 0) { // invalid skill id - ShowWarning("skill_parse_row_skilldamage: Invalid skill '%s'. Skipping..",split[0]); - return false; - } - memset(&skill_db[idx].damage,0,sizeof(struct s_skill_damage)); - skill_db[idx].damage.caster |= atoi(split[1]); - skill_db[idx].damage.map |= atoi(split[2]); - skill_db[idx].damage.pc = cap_value(atoi(split[3]),-100,INT_MAX); + + idx = skill_db_isset(atoi(split[0]), __FUNCTION__); + + memset(&skill_db[idx]->damage,0,sizeof(struct s_skill_damage)); + skill_db[idx]->damage.caster |= atoi(split[1]); + skill_db[idx]->damage.map |= atoi(split[2]); + skill_db[idx]->damage.pc = cap_value(atoi(split[3]),-100,INT_MAX); if (split[3]) - skill_db[idx].damage.mob = cap_value(atoi(split[4]),-100,INT_MAX); + skill_db[idx]->damage.mob = cap_value(atoi(split[4]),-100,INT_MAX); if (split[4]) - skill_db[idx].damage.boss = cap_value(atoi(split[5]),-100,INT_MAX); + skill_db[idx]->damage.boss = cap_value(atoi(split[5]),-100,INT_MAX); if (split[5]) - skill_db[idx].damage.other = cap_value(atoi(split[6]),-100,INT_MAX); + skill_db[idx]->damage.other = cap_value(atoi(split[6]),-100,INT_MAX); return true; } #endif +/** + * Init dummy skill db also init Skill DB allocation + * @param skill_id + * @return Skill Index + **/ +static uint16 skill_db_create(uint16 skill_id) { + if (skill_num >= MAX_SKILL) { + ShowError("Cannot add more skill. Limit is reached '%d'. Change 'MAX_SKILL' in mmo.h\n", MAX_SKILL); + return 0; + } + if (!skill_num) + CREATE(skill_db, struct s_skill_db *, 1); + else + RECREATE(skill_db, struct s_skill_db *, skill_num+1); + + CREATE(skill_db[skill_num], struct s_skill_db, 1); + if (skill_id > 0) { + safestrncpy(skill_db[skill_num]->name, "UNKNOWN_SKILL", sizeof(skill_db[skill_num]->name)); + safestrncpy(skill_db[skill_num]->desc, "Unknown Skill", sizeof(skill_db[skill_num]->desc)); + } + skill_db[skill_num]->nameid = skill_id; + uidb_iput(skilldb_id2idx, skill_id, skill_num); + return skill_next_idx(); +} + +static void skill_db_destroy(void) { + uint16 i; + for (i = 0; i < SKILL_MAX_DB(); i++) { + if (skill_db[i]) { + skill_destroy_requirement(i); + aFree(skill_db[i]); + } + skill_db[i] = NULL; + } + skill_num = 0; + aFree(skill_db); + skill_db = NULL; +} + /*=============================== * DB reading. * skill_db.txt @@ -20517,9 +20588,12 @@ static void skill_readdb(void) //add other path here }; - // init skill db structures db_clear(skilldb_name2id); - memset(skill_db,0,sizeof(skill_db)); + db_clear(skilldb_id2idx); + + skill_db_destroy(); + skill_db_create(0); + memset(skill_produce_db,0,sizeof(skill_produce_db)); memset(skill_arrow_db,0,sizeof(skill_arrow_db)); memset(skill_abra_db,0,sizeof(skill_abra_db)); @@ -20528,9 +20602,6 @@ static void skill_readdb(void) memset(skill_changematerial_db,0,sizeof(skill_changematerial_db)); skill_produce_count = skill_arrow_count = skill_abra_count = skill_improvise_count = skill_changematerial_count = skill_spellbook_count = skill_magicmushroom_count = 0; - // load skill databases - safestrncpy(skill_db[0].name, "UNKNOWN_SKILL", sizeof(skill_db[0].name)); - safestrncpy(skill_db[0].desc, "Unknown Skill", sizeof(skill_db[0].desc)); for(i=0; i= 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_ */ diff --git a/src/map/status.c b/src/map/status.c index 99ab0a3b3f3..c7381e42f5b 100644 --- a/src/map/status.c +++ b/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; diff --git a/src/map/status.h b/src/map/status.h index 7b51e005f82..3e8df8cc4e7 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -2065,6 +2065,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); diff --git a/src/map/unit.c b/src/map/unit.c index ad1fcbed32a..04180f064a3 100644 --- a/src/map/unit.c +++ b/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