diff --git a/conf/atcommands.yml b/conf/atcommands.yml index ddd8ce31a3..836b942038 100644 --- a/conf/atcommands.yml +++ b/conf/atcommands.yml @@ -1010,6 +1010,9 @@ Body: Aliases: - famepoint - famepoints + - Command: enchantgradeui + Help: | + Opens the enchantgrade UI. Footer: Imports: diff --git a/conf/log_athena.conf b/conf/log_athena.conf index e25b974f7d..b81b29aabe 100644 --- a/conf/log_athena.conf +++ b/conf/log_athena.conf @@ -8,37 +8,40 @@ //-------------------------------------------------------------- // Enable Logs? (Note 3) -// 0x0000000 - Don't log at all -// 0x0000001 - (T) Log trades -// 0x0000002 - (V) Log vending transactions -// 0x0000004 - (P) Log items drop/picked by players -// 0x0000008 - (L) Log items drop/looted by monsters -// 0x0000010 - (S) Log NPC transactions (buy/sell) -// 0x0000020 - (N) Log Script transactions (items deleted/acquired through quests) -// 0x0000040 - (D) Log items stolen from mobs (Steal/Gank) -// 0x0000080 - (C) Log player-used items (consumables/pet&hom&merc food/items used for skills&attacks) -// 0x0000100 - (O) Log produced/ingredient items -// 0x0000200 - (U) Log MVP prize items -// 0x0000400 - (A) Log player created/deleted items (through @/# commands) -// 0x0000800 - (R) Log items placed/retrieved from storage. -// 0x0001000 - (G) Log items placed/retrieved from guild storage. -// 0x0002000 - (E) Log mail system transactions. -// 0x0004000 - (I) Log auction system transactions. -// 0x0008000 - (B) Log buying store transactions -// 0x0010000 - (X) Log all other transactions (rentals expiring/inserting cards/items removed by item_check/ +// 0x00000000 - Don't log at all +// 0x00000001 - (T) Log trades +// 0x00000002 - (V) Log vending transactions +// 0x00000004 - (P) Log items drop/picked by players +// 0x00000008 - (L) Log items drop/looted by monsters +// 0x00000010 - (S) Log NPC transactions (buy/sell) +// 0x00000020 - (N) Log Script transactions (items deleted/acquired through quests) +// 0x00000040 - (D) Log items stolen from mobs (Steal/Gank) +// 0x00000080 - (C) Log player-used items (consumables/pet&hom&merc food/items used for skills&attacks) +// 0x00000100 - (O) Log produced/ingredient items +// 0x00000200 - (U) Log MVP prize items +// 0x00000400 - (A) Log player created/deleted items (through @/# commands) +// 0x00000800 - (R) Log items placed/retrieved from storage. +// 0x00001000 - (G) Log items placed/retrieved from guild storage. +// 0x00002000 - (E) Log mail system transactions. +// 0x00004000 - (I) Log auction system transactions. +// 0x00008000 - (B) Log buying store transactions +// 0x00010000 - (X) Log all other transactions (rentals expiring/inserting cards/items removed by item_check/ // rings deleted by divorce/pet egg (un)hatching/pet armor (un)equipping/Weapon Refine skill/Remove Trap skill/Stylist) -// 0x0020000 - ($) Log cash transactions -// 0x0040000 - (K) Log account bank transactions -// 0x0080000 - (F) Removed bound items when guild/party is broken -// 0x0100000 - (Y) Log Roulette Lottery -// 0x0200000 - (Z) Merged items from item mergers process. -// 0x0400000 - (Q) Log items given from quest-granted drops. -// 0x0800000 - (H) Log items consumed by Private Airship system -// 0x1000000 - (J) Log Barter Shop transactions -// 0x2000000 - (W) Log Laphine system transactions +// 0x00020000 - ($) Log cash transactions +// 0x00040000 - (K) Log account bank transactions +// 0x00080000 - (F) Removed bound items when guild/party is broken +// 0x00100000 - (Y) Log Roulette Lottery +// 0x00200000 - (Z) Merged items from item mergers process. +// 0x00400000 - (Q) Log items given from quest-granted drops. +// 0x00800000 - (H) Log items consumed by Private Airship system +// 0x01000000 - (J) Log Barter Shop transactions +// 0x02000000 - (W) Log Laphine system transactions +// 0x04000000 - (0) Enchantgrade UI +// 0x08000000 - (1) Reform UI +// 0x10000000 - (2) Enchant UI // Example: Log trades+vending+script items+created items: 1+2+32+1024 = 1059 // Please note that moving items from inventory to cart and back is not logged by design. -enable_logs: 0xFFFFFFF +enable_logs: 0xFFFFFFFF // Use MySQL Logs? (Note 1) sql_logs: yes diff --git a/conf/msg_conf/map_msg.conf b/conf/msg_conf/map_msg.conf index 0a857f8ee3..93b7ba7507 100644 --- a/conf/msg_conf/map_msg.conf +++ b/conf/msg_conf/map_msg.conf @@ -921,7 +921,10 @@ // @mobinfo RES/MRES 827: RES:%d MRES:%d -//828-899 free +// General packet version check messages +828: This command requires packet version %s or newer. + +//829-899 free //------------------------------------ // More atcommands message diff --git a/db/enchantgrade.yml b/db/enchantgrade.yml new file mode 100644 index 0000000000..c5bf69b5a8 --- /dev/null +++ b/db/enchantgrade.yml @@ -0,0 +1,58 @@ +# This file is a part of rAthena. +# Copyright(C) 2022 rAthena Development Team +# https://rathena.org - https://github.com/rathena +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +########################################################################### +# Enchantgrade Database +########################################################################### +# +# Enchantgrade Settings +# +########################################################################### +# - Type Item type. +# Levels: Enchantgrade settings per item level. +# - Level Item level. +# Grades: Enchantgrade settings per grade level. +# - Grade Enchantgrade level. +# Refine Required refine level. +# Chance Base chance of success out of 0~10000. +# Bonus Enchantgrade bonus. (Default: 0) +# Announce Announce if someone tries to increase the enchantgrade. (Default: true) +# Catalyst: Catalyst item to increase chance of success. +# Item The item that can be used. +# AmountPerStep Amount of Item needed. +# Set to 0 to disable the catalyst. +# MaximumSteps Maximum amount of times Item can be used. +# ChanceIncrease Amount at which the chance increases for each Item used. +# Options: Success chance based on cost type. +# - Option Index of the client option. +# Item Required item. +# Amount Amount of required item. (Default: 1) +# Set to 0 to remove an option. +# Price Amount of zeny required. (Default: 0) +# BreakingRate Chance of item breaking out of 0~10000. (Default: 0) +# DowngradeAmount Number of refine levels reduced on failure. (Default: 0) +########################################################################### + +Header: + Type: ENCHANTGRADE_DB + Version: 1 + +Footer: + Imports: + - Path: db/re/enchantgrade.yml + Mode: Renewal + - Path: db/import/enchantgrade.yml diff --git a/db/import-tmpl/enchantgrade.yml b/db/import-tmpl/enchantgrade.yml new file mode 100644 index 0000000000..22727a2d07 --- /dev/null +++ b/db/import-tmpl/enchantgrade.yml @@ -0,0 +1,52 @@ +# This file is a part of rAthena. +# Copyright(C) 2022 rAthena Development Team +# https://rathena.org - https://github.com/rathena +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +########################################################################### +# Enchantgrade Database +########################################################################### +# +# Enchantgrade Settings +# +########################################################################### +# - Type Item type. +# Levels: Enchantgrade settings per item level. +# - Level Item level. +# Grades: Enchantgrade settings per grade level. +# - Grade Enchantgrade level. +# Refine Required refine level. +# Chance Base chance of success out of 0~10000. +# Bonus Enchantgrade bonus. (Default: 0) +# Announce Announce if someone tries to increase the enchantgrade. (Default: true) +# Catalyst: Catalyst item to increase chance of success. +# Item The item that can be used. +# AmountPerStep Amount of Item needed. +# Set to 0 to disable the catalyst. +# MaximumSteps Maximum amount of times Item can be used. +# ChanceIncrease Amount at which the chance increases for each Item used. +# Options: Success chance based on cost type. +# - Option Index of the client option. +# Item Required item. +# Amount Amount of required item. (Default: 1) +# Set to 0 to remove an option. +# Price Amount of zeny required. (Default: 0) +# BreakingRate Chance of item breaking out of 0~10000. (Default: 0) +# DowngradeAmount Number of refine levels reduced on failure. (Default: 0) +########################################################################### + +Header: + Type: ENCHANTGRADE_DB + Version: 1 diff --git a/db/re/enchantgrade.yml b/db/re/enchantgrade.yml new file mode 100644 index 0000000000..98daddd8a2 --- /dev/null +++ b/db/re/enchantgrade.yml @@ -0,0 +1,214 @@ +# This file is a part of rAthena. +# Copyright(C) 2022 rAthena Development Team +# https://rathena.org - https://github.com/rathena +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +########################################################################### +# Enchantgrade Database +########################################################################### +# +# Enchantgrade Settings +# +########################################################################### +# - Type Item type. +# Levels: Enchantgrade settings per item level. +# - Level Item level. +# Grades: Enchantgrade settings per grade level. +# - Grade Enchantgrade level. +# Refine Required refine level. +# Chance Base chance of success out of 0~10000. +# Bonus Enchantgrade bonus. (Default: 0) +# Announce Announce if someone tries to increase the enchantgrade. (Default: true) +# Catalyst: Catalyst item to increase chance of success. +# Item The item that can be used. +# AmountPerStep Amount of Item needed. +# Set to 0 to disable the catalyst. +# MaximumSteps Maximum amount of times Item can be used. +# ChanceIncrease Amount at which the chance increases for each Item used. +# Options: Success chance based on cost type. +# - Option Index of the client option. +# Item Required item. +# Amount Amount of required item. (Default: 1) +# Set to 0 to remove an option. +# Price Amount of zeny required. (Default: 0) +# BreakingRate Chance of item breaking out of 0~10000. (Default: 0) +# DowngradeAmount Number of refine levels reduced on failure. (Default: 0) +########################################################################### + +Header: + Type: ENCHANTGRADE_DB + Version: 1 + +Body: + - Type: Armor + Levels: + - Level: 2 + Grades: + - Grade: None + Refine: 11 + Chance: 7000 + Bonus: 10 + Catalyst: + Item: Blessed_Etel_Dust + AmountPerStep: 1 + MaximumSteps: 10 + ChanceIncrease: 100 + Options: + - Option: 0 + Item: Etel_Skyblue_Jewel + Amount: 1 + Zeny: 175000 + BreakingRate: 10000 + - Option: 1 + Item: Etel_Skyblue_Jewel + Amount: 5 + Zeny: 875000 + - Grade: D + Refine: 11 + Chance: 6000 + Bonus: 30 + Catalyst: + Item: Blessed_Etel_Dust + AmountPerStep: 3 + MaximumSteps: 10 + ChanceIncrease: 100 + Options: + - Option: 0 + Item: Etel_Topaz + Amount: 1 + Zeny: 175000 + BreakingRate: 10000 + - Option: 1 + Item: Etel_Topaz + Amount: 5 + Zeny: 875000 + - Grade: C + Refine: 11 + Chance: 5000 + Bonus: 50 + Catalyst: + Item: Blessed_Etel_Dust + AmountPerStep: 5 + MaximumSteps: 10 + ChanceIncrease: 100 + Options: + - Option: 0 + Item: Etel_Violet_Jewel + Amount: 1 + Zeny: 175000 + BreakingRate: 10000 + - Option: 1 + Item: Etel_Violet_Jewel + Amount: 5 + Zeny: 875000 + - Grade: B + Refine: 11 + Chance: 4000 + Bonus: 100 + Catalyst: + Item: Blessed_Etel_Dust + AmountPerStep: 7 + MaximumSteps: 10 + ChanceIncrease: 100 + Options: + - Option: 0 + Item: Etel_Amber + Amount: 2 + Zeny: 175000 + BreakingRate: 10000 + - Option: 1 + Item: Etel_Amber + Amount: 10 + Zeny: 875000 + - Type: Weapon + Levels: + - Level: 5 + Grades: + - Grade: None + Refine: 11 + Chance: 7000 + Bonus: 10 + Catalyst: + Item: Blessed_Etel_Dust + AmountPerStep: 1 + MaximumSteps: 10 + ChanceIncrease: 100 + Options: + - Option: 0 + Item: Etel_Skyblue_Jewel + Amount: 1 + Zeny: 175000 + BreakingRate: 10000 + - Option: 1 + Item: Etel_Skyblue_Jewel + Amount: 5 + Zeny: 875000 + - Grade: D + Refine: 11 + Chance: 6000 + Bonus: 30 + Catalyst: + Item: Blessed_Etel_Dust + AmountPerStep: 3 + MaximumSteps: 10 + ChanceIncrease: 100 + Options: + - Option: 0 + Item: Etel_Topaz + Amount: 1 + Zeny: 175000 + BreakingRate: 10000 + - Option: 1 + Item: Etel_Topaz + Amount: 5 + Zeny: 875000 + - Grade: C + Refine: 11 + Chance: 5000 + Bonus: 50 + Catalyst: + Item: Blessed_Etel_Dust + AmountPerStep: 5 + MaximumSteps: 10 + ChanceIncrease: 100 + Options: + - Option: 0 + Item: Etel_Violet_Jewel + Amount: 1 + Zeny: 175000 + BreakingRate: 10000 + - Option: 1 + Item: Etel_Violet_Jewel + Amount: 5 + Zeny: 875000 + - Grade: B + Refine: 11 + Chance: 4000 + Bonus: 100 + Catalyst: + Item: Blessed_Etel_Dust + AmountPerStep: 7 + MaximumSteps: 10 + ChanceIncrease: 100 + Options: + - Option: 0 + Item: Etel_Amber + Amount: 2 + Zeny: 175000 + BreakingRate: 10000 + - Option: 1 + Item: Etel_Amber + Amount: 10 + Zeny: 875000 diff --git a/db/re/item_db_etc.yml b/db/re/item_db_etc.yml index 0ccb1aba88..f3d2ca3019 100644 --- a/db/re/item_db_etc.yml +++ b/db/re/item_db_etc.yml @@ -61756,6 +61756,7 @@ Body: AegisName: Amber Name: Amber Type: Etc + Buy: 45000 Weight: 100 - Id: 1000322 AegisName: Etel_Dust diff --git a/doc/atcommands.txt b/doc/atcommands.txt index 6c8a77d424..0b29c10738 100644 --- a/doc/atcommands.txt +++ b/doc/atcommands.txt @@ -1178,6 +1178,12 @@ If args are given, sets camera position. --------------------------------------- +@enchantgradeui + +Opens the enchantgrade UI. + +--------------------------------------- + ============================== | 5. Administrative Commands | ============================== diff --git a/doc/script_commands.txt b/doc/script_commands.txt index 808c6274f1..fcfd1390ae 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -8164,7 +8164,15 @@ This function is intended for use in item scripts. Opens the Bank UI for the attached player or the given character ID. -This command requires packet version 2015-01-28 or newer. +This command requires packet version 2015-12-02 or newer. + +--------------------------------------- + +*enchantgradeui {}; + +Opens the enchantgrade UI for the attached character or the player given by the char ID parameter. + +This command requires packet version 2020-07-24 or newer. --------------------------------------- \\ @@ -9784,6 +9792,8 @@ QMARK_PURPLE - Purple Marker Opens the quest UI for the attached player or the given character ID. Use 0 as the quest ID to open the main quest UI. If the quest ID is not 0 then the quest UI is opened to the given quest. If the quest data is not populated in the client LUB then a message will be displayed saying the quest doesn't exist. +This command requires packet version 2015-12-02 or newer. + --------------------------------------- ============================ diff --git a/npc/re/merchants/barters.yml b/npc/re/merchants/barters.yml index 74e43871af..381c0b7cea 100644 --- a/npc/re/merchants/barters.yml +++ b/npc/re/merchants/barters.yml @@ -52,3 +52,4 @@ Footer: - Path: npc/re/merchants/barters/quests_16_2.yml - Path: npc/re/merchants/barters/quests_17_1.yml - Path: npc/re/merchants/barters/refine.yml + - Path: npc/re/merchants/barters/enchantgrade.yml diff --git a/npc/re/merchants/barters/enchantgrade.yml b/npc/re/merchants/barters/enchantgrade.yml new file mode 100644 index 0000000000..4eb86f61a9 --- /dev/null +++ b/npc/re/merchants/barters/enchantgrade.yml @@ -0,0 +1,110 @@ +# This file is a part of rAthena. +# Copyright(C) 2022 rAthena Development Team +# https://rathena.org - https://github.com/rathena +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +########################################################################### +# Barter Database +########################################################################### +# +# Barter Settings +# +########################################################################### +# - Name NPC name. +# Map Map name. (Default: not on a map) +# X Map x coordinate. (Default: 0) +# Y Map y coordinate. (Default: 0) +# Direction Direction the NPC is looking. (Default: North) +# Sprite Sprite name of the NPC. (Default: FakeNpc) +# Items: List of sold items. +# - Index Index of the item inside the shop. (0-...) +# Maximum index depends on client. +# Item Aegis name of the item. +# Stock Amount of item in stock. 0 means unlimited. (Default: 0) +# Zeny Cost of them item in Zeny. (Default: 0) +# RequiredItems: List of required items (Optional) +# - Index Index of the required item. (0-4) +# Item Aegis name of required item. +# Amount Amount of required item. (Default: 1) +# Refine Refine level of required item. (Default: 0) +########################################################################### + +Header: + Type: BARTER_DB + Version: 1 + +Body: +########################################################################### +##= Enchant Grade Exchange +########################################################################### + - Name: EnchantGradeExchange + Items: + - Index: 0 + Item: Etel_Stone + Zeny: 100000 + RequiredItems: + - Index: 0 + Item: Etel_Dust + Amount: 5 + - Index: 1 + Item: Blessed_Etel_Dust + Zeny: 100000 + RequiredItems: + - Index: 0 + Item: Etel_Dust + Amount: 5 + - Index: 1 + Item: Blacksmith_Blessing + Amount: 1 + - Index: 2 + Item: Etel_Skyblue_Jewel + Zeny: 100000 + RequiredItems: + - Index: 0 + Item: Etel_Stone + Amount: 3 + - Index: 1 + Item: Skyblue_Jewel + Amount: 1 + - Index: 3 + Item: Etel_Topaz + Zeny: 200000 + RequiredItems: + - Index: 0 + Item: Etel_Stone + Amount: 6 + - Index: 1 + Item: Golden_Jewel + Amount: 1 + - Index: 4 + Item: Etel_Violet_Jewel + Zeny: 300000 + RequiredItems: + - Index: 0 + Item: Etel_Stone + Amount: 10 + - Index: 1 + Item: Violet_Jewel + Amount: 1 + - Index: 5 + Item: Etel_Amber + Zeny: 300000 + RequiredItems: + - Index: 0 + Item: Etel_Stone + Amount: 15 + - Index: 1 + Item: Amber + Amount: 1 diff --git a/npc/re/merchants/enchantgrade.txt b/npc/re/merchants/enchantgrade.txt new file mode 100644 index 0000000000..e4a9340942 --- /dev/null +++ b/npc/re/merchants/enchantgrade.txt @@ -0,0 +1,131 @@ +//===== rAthena Script ======================================= +//= Enchant Grade +//===== Changelogs: ========================================== +//= 1.0 First Version. [JohnnyPlayy] +//= 1.1 Fixed small issues. [Lemongrass] +//= 1.2 Added translation. [Asheraf] +//= 1.3 Added paramarket NPC and warps. [Balfear] +//= 1.4 Translated paramarket NPC. [Lemongrass] +//============================================================ + +grademk,34,184,4 script Sratos#sratos 4_JP_GARM_H,{ + mes "[Sratos]"; + mes "Hello, dear customer who walked in, let's hope for a miracle today."; + mes "How can I help you?"; + next; + switch( select( "Enhance the equipment's grade.", "Exchange Etel items", "What is Grade Enhancement?" ) ){ + case 1: + mes "[Sratos]"; + mes "Our customer. You want to enhance the grade of equipment."; + mes "It is not easy to move the magical power of jewels."; + next; + mes "[Sratos]"; + mes "Magical power can explode on the spot."; + mes "Then the weapon that will inherit the magical power also explodes!"; + next; + switch( select( "I'll still do it!", "I'll think about it." ) ){ + case 1: + mes "[Sratos]"; + mes "I wish good luck to our courageous customers!"; + close2; + enchantgradeui(); + end; + case 2: + mes "[Sratos]"; + mes "Whenever you have the courage to challenge, please come back."; + close; + } + case 2: + mes "[Sratos]"; + mes "These are jewels used to enhance grades."; + mes "Etel Dust, the jewels that will be the base, and if you give me a small fee I'll exchange it for Etel jewels."; + close2; + callshop "EnchantGradeExchange"; + end; + + case 3: + mes "[Sratos]"; + mes "Occasionally, unstable jewels with a lot of pure magical power are found."; + mes "We call these etheric gems, right?"; + next; + mes "[Sratos]"; + mes "You can transfer the magical power of the etheric gem to other weapons, but of course there is some risk."; + mes "It's delicate, so if you aren't careful, pop! It will explode."; + next; + mes "[Sratos]"; + mes "Anyway, if you use this etheric gem, you can enhance the weapon you are using."; + mes "Performance gets better. We call this grade enhancement."; + next; + mes "[Sratos]"; + mes "You too, wouldn't you be happy if the weapons you love grow one step further?"; + mes "We are the people who assist it. It's a bit risky, though. ahahaha."; + close; + } + +OnInit: + setunittitle(getnpcid(0), ""); + end; +} + +paramk,34,184,4 script Suribell#suribell 4_F_FRUIT,{ + mes "[Suribell]"; + mes "May good luck always be with you!"; + mes "Welcome to Paramarket's Grade Enhancement Center~"; + next; + switch( select( "Enhance the equipment's grade.", "Exchange Etel items", "What is Grade Enhancement?" )) { + case 1: + mes "[Suribell]"; + mes "Do you want to unlock the potential of your favorite equipment?"; + mes "Explosions may also occur in the process of dealing with magical powers."; + next; + mes "[Suribell]"; + mes "Still, if you're trying to enhance your grade... !"; + mes "I, Suribell, will do my best!"; + next; + switch( select( "I'll still do it!", "I'll think about it." ) ){ + case 1: + mes "[Suribell]"; + mes "Let's hold hands and have faith! Try clenching your teeth!"; + close2; + enchantgradeui(); + end; + case 2: + mes "[Suribell]"; + mes "Okay! Let's drink a glass of cold water!"; + close; + } + case 2: + mes "[Suribell]"; + mes "You need etheric gems to enhance grades!"; + mes "If you combine Etel Dust and jewels to make it, it is fine! An Etel jewel for adventurers is complete!"; + mes "Would you like to combine jewels and Etel Dust?"; + close2; + callshop "EnchantGradeExchange"; + end; + case 3: + mes "[Suribell]"; + mes "Occasionally, unstable jewels with a lot of pure magical power are found."; + mes "I decided to call such gems etheric gems."; + mes "Who? I am Suribell!"; + next; + mes "[Suribell]"; + mes "The magical power of etheric gems sometimes awakens the hidden potential of equipment."; + mes "We call it grade enhancement because it goes up one tier!"; + next; + mes "[Suribell]"; + mes "The adventurer may have a potential that is unknown to him."; + mes "There is a risk that it can be destroyed if done wrong, but try it with faith!"; + close; + } + +OnInit: + setunittitle(getnpcid(0), ""); + end; +} + +// = Portals +//============================================================ +prontera,50,293,0 warp Grademk_Int 1,1,grademk,13,172 +grademk,9,172,0 warp Grademk_Out 1,1,prontera,50,290 +paramk,8,171,0 warp grade_in 1,1,paramk,141,64 +paramk,145,64,0 warp grade_out 1,1,paramk,11,171 diff --git a/npc/re/merchants/shops.txt b/npc/re/merchants/shops.txt index 5485128aa6..402685cc92 100644 --- a/npc/re/merchants/shops.txt +++ b/npc/re/merchants/shops.txt @@ -125,7 +125,7 @@ morocc,154,55,6 shop Jeweler#moc3 99,730:-1,2613:-1 morocc,171,103,4 shop Item Collector#moc3 85,911:-1,528:-1,919:-1,925:-1 morocc,205,247,2 shop Item Collector#moc4 85,911:-1,528:-1,919:-1,925:-1 morocc,140,90,6 shop Trader#moc6 99,513:-1,513:-1,513:-1,513:-1,513:-1,513:-1 -morocc,166,54,2 shop Jeweler#moc4 102,721:-1,723:-1,726:-1,728:-1,729:-1 +morocc,166,54,2 shop Jeweler#moc4 102,721:-1,723:-1,726:-1,728:-1,729:-1,1000321:-1 morocc,34,68,0 shop Trader#moc7 93,748:-1 morocc,269,193,4 shop Trader#moc8 89,2609:-1,1516:-1,1522:-1 morocc,256,191,5 shop Trader#moc9 93,2612:-1 diff --git a/npc/re/scripts_athena.conf b/npc/re/scripts_athena.conf index 503324c021..271858251a 100644 --- a/npc/re/scripts_athena.conf +++ b/npc/re/scripts_athena.conf @@ -126,6 +126,7 @@ npc: npc/re/merchants/enchan_mal.txt npc: npc/re/merchants/enchan_mora.txt npc: npc/re/merchants/enchan_rockridge.txt npc: npc/re/merchants/enchan_verus.txt +npc: npc/re/merchants/enchantgrade.txt npc: npc/re/merchants/Extended_Ammunition.txt npc: npc/re/merchants/Extended_Stylist.txt npc: npc/re/merchants/flute.txt diff --git a/sql-files/logs.sql b/sql-files/logs.sql index cd3e524ab4..d024a98120 100644 --- a/sql-files/logs.sql +++ b/sql-files/logs.sql @@ -169,12 +169,15 @@ CREATE TABLE IF NOT EXISTS `npclog` ( # Private Airs(H)ip # Barter Shop (J) # Laphine systems (W) +# Enchantgrade UI (0) +# Reform UI (1) +# Enchant UI (2) CREATE TABLE IF NOT EXISTS `picklog` ( `id` int(11) NOT NULL auto_increment, `time` datetime NOT NULL, `char_id` int(11) NOT NULL default '0', - `type` enum('M','P','L','T','V','S','N','C','A','R','G','E','B','O','I','X','D','U','$','F','Y','Z','Q','H','J','W') NOT NULL default 'P', + `type` enum('M','P','L','T','V','S','N','C','A','R','G','E','B','O','I','X','D','U','$','F','Y','Z','Q','H','J','W','0','1','2') NOT NULL default 'P', `nameid` int(10) unsigned NOT NULL default '0', `amount` int(11) NOT NULL default '1', `refine` tinyint(3) unsigned NOT NULL default '0', @@ -224,13 +227,15 @@ CREATE TABLE IF NOT EXISTS `picklog` ( # Ban(K) Transactions # Barter Shop (J) # (X) Other +# Enchantgrade UI (0) +# Enchant UI (2) CREATE TABLE IF NOT EXISTS `zenylog` ( `id` int(11) NOT NULL auto_increment, `time` datetime NOT NULL, `char_id` int(11) NOT NULL default '0', `src_id` int(11) NOT NULL default '0', - `type` enum('T','V','P','M','S','N','D','C','A','E','I','B','K','J','X') NOT NULL default 'S', + `type` enum('T','V','P','M','S','N','D','C','A','E','I','B','K','J','X','0','2') NOT NULL default 'S', `amount` int(11) NOT NULL default '0', `map` varchar(11) NOT NULL default '', PRIMARY KEY (`id`), diff --git a/sql-files/upgrades/upgrade_20220607_logs.sql b/sql-files/upgrades/upgrade_20220607_logs.sql new file mode 100644 index 0000000000..4c1e8a1575 --- /dev/null +++ b/sql-files/upgrades/upgrade_20220607_logs.sql @@ -0,0 +1,7 @@ +ALTER TABLE `picklog` + MODIFY `type` enum('M','P','L','T','V','S','N','C','A','R','G','E','B','O','I','X','D','U','$','F','Y','Z','Q','H','J','W','0','1','2') NOT NULL default 'P' +; + +ALTER TABLE `zenylog` + MODIFY `type` enum('T','V','P','M','S','N','D','C','A','E','I','B','K','J','X','0','2') NOT NULL default 'S' +; diff --git a/src/common/mmo.hpp b/src/common/mmo.hpp index 5a2414dc29..a873b56ebe 100644 --- a/src/common/mmo.hpp +++ b/src/common/mmo.hpp @@ -111,12 +111,22 @@ typedef uint32 t_itemid; #define MAX_BARTER_REQUIREMENTS 5 #endif +enum e_enchantgrade : uint16{ + ENCHANTGRADE_NONE = 0, + ENCHANTGRADE_D, + ENCHANTGRADE_C, + ENCHANTGRADE_B, + ENCHANTGRADE_A +}; + #ifdef RENEWAL #define MAX_WEAPON_LEVEL 5 #define MAX_ARMOR_LEVEL 2 + #define MAX_ENCHANTGRADE ENCHANTGRADE_A #else #define MAX_WEAPON_LEVEL 4 #define MAX_ARMOR_LEVEL 1 + #define MAX_ENCHANTGRADE ENCHANTGRADE_NONE #endif // for produce diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index eab323fb6a..34749868e7 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -10662,7 +10662,7 @@ ACMD_FUNC( stylist ){ return -1; } - clif_ui_open( sd, OUT_UI_STYLIST, 0 ); + clif_ui_open( *sd, OUT_UI_STYLIST, 0 ); return 0; #endif } @@ -10697,6 +10697,23 @@ ACMD_FUNC(addfame) return 0; } +/** + * Opens the enchantgrade UI + * Usage: @enchantgradeui + */ +ACMD_FUNC( enchantgradeui ){ + nullpo_retr( -1, sd ); + +#if !( PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724 ) + sprintf( atcmd_output, msg_txt( sd, 798 ), "2020-07-24" ); // This command requires packet version %s or newer. + clif_displaymessage( fd, atcmd_output ); + return -1; +#else + clif_ui_open( *sd, OUT_UI_ENCHANTGRADE, 0 ); + return 0; +#endif +} + #include "../custom/atcommand.inc" /** @@ -11019,6 +11036,7 @@ void atcommand_basecommands(void) { ACMD_DEF(refineui), ACMD_DEFR(stylist, ATCMD_NOCONSOLE|ATCMD_NOAUTOTRADE), ACMD_DEF(addfame), + ACMD_DEFR(enchantgradeui, ATCMD_NOCONSOLE|ATCMD_NOAUTOTRADE), }; AtCommandInfo* atcommand; int i; diff --git a/src/map/clif.cpp b/src/map/clif.cpp index 075603e74f..c5e0a4fe19 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -21551,23 +21551,32 @@ void clif_parse_changedress( int fd, struct map_session_data* sd ){ /// Opens an UI window of the given type and initializes it with the given data /// 0AE2 .B .L -void clif_ui_open( struct map_session_data *sd, enum out_ui_type ui_type, int32 data ){ - nullpo_retv(sd); - +void clif_ui_open( struct map_session_data& sd, enum out_ui_type ui_type, int32 data ){ +#if PACKETVER >= 20151202 // If the UI requires state tracking switch( ui_type ){ case OUT_UI_STYLIST: - sd->state.stylist_open = true; + sd.state.stylist_open = true; break; + case OUT_UI_ENCHANTGRADE: +#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724 + sd.state.enchantgrade_open = true; + break; +#else + return; +#endif } - int fd = sd->fd; + struct PACKET_ZC_UI_OPEN p = {}; - WFIFOHEAD(fd,packet_len(0xae2)); - WFIFOW(fd,0) = 0xae2; - WFIFOB(fd,2) = ui_type; - WFIFOL(fd,3) = data; - WFIFOSET(fd,packet_len(0xae2)); + p.PacketType = HEADER_ZC_UI_OPEN; + p.UIType = ui_type; +#if PACKETVER >= 20171122 + p.data = data; +#endif + + clif_send( &p, sizeof( p ), &sd.bl, SELF ); +#endif } /// Request to open an UI window of the given type @@ -21578,7 +21587,7 @@ void clif_parse_open_ui( int fd, struct map_session_data* sd ){ if( !pc_has_permission( sd, PC_PERM_ATTENDANCE ) ){ clif_messagecolor( &sd->bl, color_table[COLOR_RED], msg_txt( sd, 791 ), false, SELF ); // You are not allowed to use the attendance system. }else if( pc_attendance_enabled() ){ - clif_ui_open( sd, OUT_UI_ATTENDANCE, pc_attendance_counter( sd ) ); + clif_ui_open( *sd, OUT_UI_ATTENDANCE, pc_attendance_counter( sd ) ); }else{ clif_msg_color( sd, MSG_ATTENDANCE_DISABLED, color_table[COLOR_RED] ); } @@ -23454,6 +23463,338 @@ void clif_parse_laphine_upgrade( int fd, struct map_session_data* sd ){ #endif } +void clif_enchantgrade_add( struct map_session_data& sd, uint16 index = UINT16_MAX, std::shared_ptr gradeLevel = nullptr ){ +#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724 + struct PACKET_ZC_GRADE_ENCHANT_MATERIAL_LIST* p = (struct PACKET_ZC_GRADE_ENCHANT_MATERIAL_LIST*)packet_buffer; + + p->PacketType = HEADER_ZC_GRADE_ENCHANT_MATERIAL_LIST; + p->PacketLength = sizeof( struct PACKET_ZC_GRADE_ENCHANT_MATERIAL_LIST ); + + if( index < UINT16_MAX ){ + p->index = client_index( index ); + if( sd.inventory.u.items_inventory[index].refine >= gradeLevel->refine ){ + p->success_chance = gradeLevel->chance / 100; + }else{ + p->success_chance = 0; + } + p->blessing_info.id = client_nameid( gradeLevel->catalyst.item ); + p->blessing_info.amount = gradeLevel->catalyst.amountPerStep; + p->blessing_info.max_blessing = gradeLevel->catalyst.maximumSteps; + p->blessing_info.bonus = gradeLevel->catalyst.chanceIncrease / 100; + // Not displayed by client + p->protect_itemid = 0; + p->protect_amount = 0; + + int i = 0; + for( const auto& pair : gradeLevel->options ){ + std::shared_ptr option = pair.second; + + p->material_info[i].nameid = client_nameid( option->item ); + p->material_info[i].amount = option->amount; + p->material_info[i].price = option->zeny; + p->material_info[i].downgrade = option->downgrade_amount > 0; + p->material_info[i].breakable = option->breaking_rate > 0; + + p->PacketLength += sizeof( struct GRADE_ENCHANT_MATERIAL ); + i++; + } + }else{ + p->index = -1; + p->success_chance = 0; + p->blessing_info.id = 0; + p->blessing_info.amount = 0; + p->blessing_info.max_blessing = 0; + p->blessing_info.bonus = 0; + p->protect_itemid = 0; + p->protect_amount = 0; + } + + clif_send( p, p->PacketLength, &sd.bl, SELF ); +#endif +} + +void clif_parse_enchantgrade_add( int fd, struct map_session_data* sd ){ +#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724 + nullpo_retv( sd ); + + if( !sd->state.enchantgrade_open ){ + return; + } + + struct PACKET_CZ_GRADE_ENCHANT_SELECT_EQUIPMENT* p = (struct PACKET_CZ_GRADE_ENCHANT_SELECT_EQUIPMENT*)RFIFOP( fd, 0 ); + + uint16 index = server_index( p->index ); + + if( index >= MAX_INVENTORY || sd->inventory_data[index] == nullptr ){ + return; + } + + std::shared_ptr enchantgrade = enchantgrade_db.find( sd->inventory_data[index]->type ); + + // Unsupported item type - no answer, because client should have actually prevented this request + if( enchantgrade == nullptr ){ + return; + } + + uint16 level = 0; + + if( sd->inventory_data[index]->type == IT_WEAPON ){ + level = sd->inventory_data[index]->weapon_level; + }else if( sd->inventory_data[index]->type == IT_ARMOR ){ + level = sd->inventory_data[index]->armor_level; + } + + const auto& enchantgradelevels = enchantgrade->levels.find( level ); + + // Cannot upgrade this weapon or armor level + if( enchantgradelevels == enchantgrade->levels.end() ){ + clif_enchantgrade_add( *sd ); + return; + } + + std::shared_ptr enchantgradelevel = util::map_find( enchantgradelevels->second, (e_enchantgrade)sd->inventory.u.items_inventory[index].enchantgrade ); + + // Cannot increase enchantgrade any further - no answer, because client should have actually prevented this request + if( enchantgradelevel == nullptr ){ + return; + } + + clif_enchantgrade_add( *sd, index, enchantgradelevel ); +#endif +} + +/// +/// Sends the result for trying to enchant an item +/// +/// The player session +/// The target item +/// +/// 0= The grade has been successfully upgraded. +/// 1= Refinement failed. +/// 2= The refine level has decreased. +/// 3= Equipment destroyed. +/// 4= The equipment is protected. +/// +void clif_enchantgrade_result( struct map_session_data& sd, uint16 index, e_enchantgrade_result result ){ +#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724 + struct PACKET_ZC_GRADE_ENCHANT_ACK p = {}; + + p.PacketType = HEADER_ZC_GRADE_ENCHANT_ACK; + p.index = client_index( index ); + p.enchantgrade = sd.inventory.u.items_inventory[index].enchantgrade; + p.result = result; + + clif_send( &p, sizeof( p ), &sd.bl, SELF ); +#endif +} + +void clif_enchantgrade_announce( struct map_session_data& sd, struct item& item, bool success ){ +#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724 + struct PACKET_ZC_GRADE_ENCHANT_BROADCAST_RESULT p = {}; + + p.packetType = HEADER_ZC_GRADE_ENCHANT_BROADCAST_RESULT; + safestrncpy( p.name, sd.status.name, sizeof( p.name ) ); + p.itemId = client_nameid( item.nameid ); + p.enchantgrade = item.enchantgrade; + p.status = success; + + clif_send( &p, sizeof( p ), nullptr, ALL_CLIENT ); +#endif +} + +void clif_parse_enchantgrade_start( int fd, struct map_session_data* sd ){ +#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724 + nullpo_retv( sd ); + + if( !sd->state.enchantgrade_open ){ + return; + } + + struct PACKET_CZ_GRADE_ENCHANT_REQUEST* p = (struct PACKET_CZ_GRADE_ENCHANT_REQUEST*)RFIFOP( fd, 0 ); + + uint16 index = server_index( p->index ); + + if( index >= MAX_INVENTORY || sd->inventory_data[index] == nullptr ){ + return; + } + + std::shared_ptr enchantgrade = enchantgrade_db.find( sd->inventory_data[index]->type ); + + // Unsupported item type - no answer + if( enchantgrade == nullptr ){ + return; + } + + uint16 level = 0; + + if( sd->inventory_data[index]->type == IT_WEAPON ){ + level = sd->inventory_data[index]->weapon_level; + }else if( sd->inventory_data[index]->type == IT_ARMOR ){ + level = sd->inventory_data[index]->armor_level; + } + + const auto& enchantgradelevels = enchantgrade->levels.find( level ); + + // Cannot upgrade this weapon or armor level - no answer + if( enchantgradelevels == enchantgrade->levels.end() ){ + return; + } + + std::shared_ptr enchantgradelevel = util::map_find( enchantgradelevels->second, (e_enchantgrade)sd->inventory.u.items_inventory[index].enchantgrade ); + + // Cannot increase enchantgrade any further - no answer + if( enchantgradelevel == nullptr ){ + return; + } + + // Not refined enough + if( sd->inventory.u.items_inventory[index].refine < enchantgradelevel->refine ){ + return; + } + + std::shared_ptr option = util::map_find( enchantgradelevel->options, (uint16)p->material_index ); + + // Unknown option id - no answer + if( option == nullptr ){ + return; + } + + // Not enough zeny + if( sd->status.zeny < option->zeny ){ + return; + } + + uint16 totalChance = enchantgradelevel->chance; + uint16 steps = min( p->blessing_amount, enchantgradelevel->catalyst.maximumSteps ); + std::unordered_map requiredItems; + + if( p->blessing_flag ){ + // If the catalysator item is the same as the option item build the sum of amounts + if( enchantgradelevel->catalyst.item == option->item ){ + uint16 amount = enchantgradelevel->catalyst.amountPerStep * steps + option->amount; + + int16 index = pc_search_inventory( sd, enchantgradelevel->catalyst.item ); + + if( index < 0 ){ + return; + } + + if( sd->inventory.u.items_inventory[index].amount < amount ){ + return; + } + + requiredItems[index] = amount; + }else{ + uint16 amount = enchantgradelevel->catalyst.amountPerStep * steps; + + // Check catalysator item + int16 index = pc_search_inventory( sd, enchantgradelevel->catalyst.item ); + + if( index < 0 ){ + return; + } + + if( sd->inventory.u.items_inventory[index].amount < amount ){ + return; + } + + requiredItems[index] = amount; + + // Check option item + index = pc_search_inventory( sd, option->item ); + + if( index < 0 ){ + return; + } + + if( sd->inventory.u.items_inventory[index].amount < option->amount ){ + return; + } + + requiredItems[index] = option->amount; + } + + totalChance += steps * enchantgradelevel->catalyst.chanceIncrease; + }else{ + // Check option item + int16 index = pc_search_inventory( sd, option->item ); + + if( index < 0 ){ + return; + } + + if( sd->inventory.u.items_inventory[index].amount < option->amount ){ + return; + } + + requiredItems[index] = option->amount; + } + + // All items should be there, start deleting + for( const auto& pair : requiredItems ){ + if( pc_delitem( sd, pair.first, pair.second, 0, 0, LOG_TYPE_ENCHANTGRADE ) != 0 ){ + return; + } + } + + if( pc_payzeny( sd, option->zeny, LOG_TYPE_ENCHANTGRADE, nullptr ) > 0 ){ + return; + } + + if( rnd()%10000 < totalChance ){ + // Log removal of item + log_pick_pc( sd, LOG_TYPE_ENCHANTGRADE, -1, &sd->inventory.u.items_inventory[index] ); + // Increase enchantgrade + sd->inventory.u.items_inventory[index].enchantgrade = min( sd->inventory.u.items_inventory[index].enchantgrade + 1, MAX_ENCHANTGRADE ); + // On successful enchantgrade increase the refine is reset + sd->inventory.u.items_inventory[index].refine = 0; + // Log retrieving the item again -> with the new refine and enchantgrade + log_pick_pc( sd, LOG_TYPE_ENCHANTGRADE, 1, &sd->inventory.u.items_inventory[index] ); + // Show success + clif_enchantgrade_result( *sd, index, ENCHANTGRADE_UPGRADE_SUCCESS ); + + // Check if it has to be announced + if( enchantgradelevel->announce ){ + clif_enchantgrade_announce( *sd, sd->inventory.u.items_inventory[index], true ); + } + }else{ + // Check if it has to be announced (has to be done before deleting the item from inventory) + if( enchantgradelevel->announce ){ + clif_enchantgrade_announce( *sd, sd->inventory.u.items_inventory[index], false ); + } + + // Delete the item if it is breakable + if( option->breaking_rate > 0 && ( rnd() % 10000 ) < option->breaking_rate ){ + // Delete the item + pc_delitem( sd, index, 1, 0, 0, LOG_TYPE_ENCHANTGRADE ); + // Show failure + clif_enchantgrade_result( *sd, index, ENCHANTGRADE_UPGRADE_BREAK ); + // Downgrade the item if necessary + }else if( option->downgrade_amount > 0 ){ + // Log removal of item + log_pick_pc( sd, LOG_TYPE_ENCHANTGRADE, -1, &sd->inventory.u.items_inventory[index] ); + // Decrease refine level + sd->inventory.u.items_inventory[index].refine = cap_value( sd->inventory.u.items_inventory[index].refine - option->downgrade_amount, 0, MAX_REFINE ); + // Log retrieving the item again -> with the new refine + log_pick_pc( sd, LOG_TYPE_ENCHANTGRADE, 1, &sd->inventory.u.items_inventory[index] ); + // Show downgrade + clif_enchantgrade_result( *sd, index, ENCHANTGRADE_UPGRADE_DOWNGRADE ); + // Only show failure, but dont do anything + }else{ + clif_enchantgrade_result( *sd, index, ENCHANTGRADE_UPGRADE_FAILED ); + } + } +#endif +} + +void clif_parse_enchantgrade_close( int fd, struct map_session_data* sd ){ +#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724 + nullpo_retv( sd ); + + sd->state.enchantgrade_open = false; +#endif +} + /*========================================== * Main client packet processing function *------------------------------------------*/ diff --git a/src/map/clif.hpp b/src/map/clif.hpp index b3d6089050..cbb0a6cc49 100644 --- a/src/map/clif.hpp +++ b/src/map/clif.hpp @@ -1162,10 +1162,11 @@ enum out_ui_type : int8 { OUT_UI_BANK = 0, OUT_UI_STYLIST, OUT_UI_QUEST = 6, - OUT_UI_ATTENDANCE = 7 + OUT_UI_ATTENDANCE, + OUT_UI_ENCHANTGRADE, }; -void clif_ui_open( struct map_session_data *sd, enum out_ui_type ui_type, int32 data ); +void clif_ui_open( struct map_session_data& sd, enum out_ui_type ui_type, int32 data ); void clif_attendence_response( struct map_session_data *sd, int32 data ); void clif_weight_limit( struct map_session_data* sd ); diff --git a/src/map/clif_packetdb.hpp b/src/map/clif_packetdb.hpp index 515b5158fb..99558cf2a7 100644 --- a/src/map/clif_packetdb.hpp +++ b/src/map/clif_packetdb.hpp @@ -2375,7 +2375,6 @@ // 2018-03-07bRagexeRE #if PACKETVER >= 20180307 parseable_packet(0x0A68,3,clif_parse_open_ui,2); - packet(0x0AE2,7); parseable_packet(0x0AEF,2,clif_parse_attendance_request,0); packet(0x0AF0,10); #endif @@ -2447,6 +2446,9 @@ #if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724 parseable_packet( HEADER_CZ_UNCONFIRMED_TSTATUS_UP, sizeof( PACKET_CZ_UNCONFIRMED_TSTATUS_UP ), clif_parse_traitstatus_up, 0 ); + parseable_packet( HEADER_CZ_GRADE_ENCHANT_SELECT_EQUIPMENT, sizeof( struct PACKET_CZ_GRADE_ENCHANT_SELECT_EQUIPMENT ), clif_parse_enchantgrade_add, 0 ); + parseable_packet( HEADER_CZ_GRADE_ENCHANT_REQUEST, sizeof( struct PACKET_CZ_GRADE_ENCHANT_REQUEST ), clif_parse_enchantgrade_start, 0 ); + parseable_packet( HEADER_CZ_GRADE_ENCHANT_CLOSE_UI, sizeof( struct PACKET_CZ_GRADE_ENCHANT_CLOSE_UI ), clif_parse_enchantgrade_close, 0 ); #endif #if PACKETVER_RE_NUM >= 20211103 || PACKETVER_ZERO_NUM >= 20210818 diff --git a/src/map/log.cpp b/src/map/log.cpp index b850b79d7f..47b27ca540 100644 --- a/src/map/log.cpp +++ b/src/map/log.cpp @@ -86,7 +86,10 @@ static char log_picktype2char(e_log_pick_type type) case LOG_TYPE_QUEST: return 'Q'; // (Q)uest Item case LOG_TYPE_PRIVATE_AIRSHIP: return 'H'; // Private Airs(H)ip case LOG_TYPE_BARTER: return 'J'; // Barter Shop - case LOG_TYPE_LAPHINE: return 'W'; // Laphine UI + case LOG_TYPE_LAPHINE: return 'W'; // Laphine UI + case LOG_TYPE_ENCHANTGRADE: return '0'; // Enchantgrade UI + case LOG_TYPE_REFORM: return '1'; // Reform UI + case LOG_TYPE_ENCHANT: return '2'; // Echant UI } // should not get here, fallback diff --git a/src/map/log.hpp b/src/map/log.hpp index 1aa133eaf4..9cb636a3b6 100644 --- a/src/map/log.hpp +++ b/src/map/log.hpp @@ -26,37 +26,40 @@ enum e_log_chat_type : uint8 enum e_log_pick_type : uint32 { - LOG_TYPE_NONE = 0x0000000, - LOG_TYPE_TRADE = 0x0000001, - LOG_TYPE_VENDING = 0x0000002, - LOG_TYPE_PICKDROP_PLAYER = 0x0000004, - LOG_TYPE_PICKDROP_MONSTER = 0x0000008, - LOG_TYPE_NPC = 0x0000010, - LOG_TYPE_SCRIPT = 0x0000020, - LOG_TYPE_STEAL = 0x0000040, - LOG_TYPE_CONSUME = 0x0000080, - LOG_TYPE_PRODUCE = 0x0000100, - LOG_TYPE_MVP = 0x0000200, - LOG_TYPE_COMMAND = 0x0000400, - LOG_TYPE_STORAGE = 0x0000800, - LOG_TYPE_GSTORAGE = 0x0001000, - LOG_TYPE_MAIL = 0x0002000, - LOG_TYPE_AUCTION = 0x0004000, - LOG_TYPE_BUYING_STORE = 0x0008000, - LOG_TYPE_OTHER = 0x0010000, - LOG_TYPE_CASH = 0x0020000, - LOG_TYPE_BANK = 0x0040000, - LOG_TYPE_BOUND_REMOVAL = 0x0080000, - LOG_TYPE_ROULETTE = 0x0100000, - LOG_TYPE_MERGE_ITEM = 0x0200000, - LOG_TYPE_QUEST = 0x0400000, - LOG_TYPE_PRIVATE_AIRSHIP = 0x0800000, - LOG_TYPE_BARTER = 0x1000000, - LOG_TYPE_LAPHINE = 0x2000000, + LOG_TYPE_NONE = 0x00000000, + LOG_TYPE_TRADE = 0x00000001, + LOG_TYPE_VENDING = 0x00000002, + LOG_TYPE_PICKDROP_PLAYER = 0x00000004, + LOG_TYPE_PICKDROP_MONSTER = 0x00000008, + LOG_TYPE_NPC = 0x00000010, + LOG_TYPE_SCRIPT = 0x00000020, + LOG_TYPE_STEAL = 0x00000040, + LOG_TYPE_CONSUME = 0x00000080, + LOG_TYPE_PRODUCE = 0x00000100, + LOG_TYPE_MVP = 0x00000200, + LOG_TYPE_COMMAND = 0x00000400, + LOG_TYPE_STORAGE = 0x00000800, + LOG_TYPE_GSTORAGE = 0x00001000, + LOG_TYPE_MAIL = 0x00002000, + LOG_TYPE_AUCTION = 0x00004000, + LOG_TYPE_BUYING_STORE = 0x00008000, + LOG_TYPE_OTHER = 0x00010000, + LOG_TYPE_CASH = 0x00020000, + LOG_TYPE_BANK = 0x00040000, + LOG_TYPE_BOUND_REMOVAL = 0x00080000, + LOG_TYPE_ROULETTE = 0x00100000, + LOG_TYPE_MERGE_ITEM = 0x00200000, + LOG_TYPE_QUEST = 0x00400000, + LOG_TYPE_PRIVATE_AIRSHIP = 0x00800000, + LOG_TYPE_BARTER = 0x01000000, + LOG_TYPE_LAPHINE = 0x02000000, + LOG_TYPE_ENCHANTGRADE = 0x04000000, + LOG_TYPE_REFORM = 0x08000000, + LOG_TYPE_ENCHANT = 0x10000000, // combinations LOG_TYPE_LOOT = LOG_TYPE_PICKDROP_MONSTER|LOG_TYPE_CONSUME, // all - LOG_TYPE_ALL = 0xFFFFFFF, + LOG_TYPE_ALL = 0xFFFFFFFF, }; enum e_log_cash_type : uint8 diff --git a/src/map/map-server.vcxproj b/src/map/map-server.vcxproj index d748f626df..b43924d941 100644 --- a/src/map/map-server.vcxproj +++ b/src/map/map-server.vcxproj @@ -322,6 +322,7 @@ + diff --git a/src/map/packets_struct.hpp b/src/map/packets_struct.hpp index d7c0a132e7..4b977c30c7 100644 --- a/src/map/packets_struct.hpp +++ b/src/map/packets_struct.hpp @@ -5340,7 +5340,7 @@ DEFINE_PACKET_HEADER(CZ_GRADE_ENCHANT_CLOSE_UI, 0x0b5c); struct PACKET_ZC_GRADE_ENCHANT_ACK { int16 PacketType; int16 index; - int16 grade; + int16 enchantgrade; int result; } __attribute__((packed)); DEFINE_PACKET_HEADER(ZC_GRADE_ENCHANT_ACK, 0x0b5d); @@ -5351,7 +5351,7 @@ struct PACKET_ZC_GRADE_ENCHANT_BROADCAST_RESULT { int16 packetType; char name[NAME_LENGTH]; uint32 itemId; - int16 grade; + int16 enchantgrade; int8 status; } __attribute__((packed)); DEFINE_PACKET_HEADER(ZC_GRADE_ENCHANT_BROADCAST_RESULT, 0x0b5e); diff --git a/src/map/pc.cpp b/src/map/pc.cpp index 86f546a0ba..28cd75ee51 100755 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -13995,7 +13995,7 @@ void pc_scdata_received(struct map_session_data *sd) { clif_weight_limit( sd ); if( pc_has_permission( sd, PC_PERM_ATTENDANCE ) && pc_attendance_enabled() && !pc_attendance_rewarded_today( sd ) ){ - clif_ui_open( sd, OUT_UI_ATTENDANCE, pc_attendance_counter( sd ) ); + clif_ui_open( *sd, OUT_UI_ATTENDANCE, pc_attendance_counter( sd ) ); } sd->state.pc_loaded = true; diff --git a/src/map/pc.hpp b/src/map/pc.hpp index f568615180..8b7f945d0f 100644 --- a/src/map/pc.hpp +++ b/src/map/pc.hpp @@ -387,6 +387,7 @@ struct map_session_data { bool stylist_open; bool barter_open; bool barter_extended_open; + bool enchantgrade_open; // Whether the enchantgrade window is open or not unsigned int block_action : 10; bool refineui_open; t_itemid inventory_expansion_confirmation; @@ -1067,7 +1068,7 @@ static bool pc_cant_act2( struct map_session_data* sd ){ || sd->state.stylist_open || sd->state.inventory_expansion_confirmation || sd->npc_shopid || sd->state.barter_open || sd->state.barter_extended_open || sd->state.laphine_synthesis || sd->state.laphine_upgrade - || sd->state.roulette_open; + || sd->state.roulette_open || sd->state.enchantgrade_open; } // equals pc_cant_act2 and additionally checks for chat rooms and npcs static bool pc_cant_act( struct map_session_data* sd ){ diff --git a/src/map/script.cpp b/src/map/script.cpp index e0f25550e4..49e5e9fc14 100644 --- a/src/map/script.cpp +++ b/src/map/script.cpp @@ -25739,7 +25739,7 @@ BUILDIN_FUNC( openstylist ){ return SCRIPT_CMD_FAILURE; } - clif_ui_open( sd, OUT_UI_STYLIST, 0 ); + clif_ui_open( *sd, OUT_UI_STYLIST, 0 ); return SCRIPT_CMD_SUCCESS; #else @@ -25860,6 +25860,10 @@ BUILDIN_FUNC(randomoptgroup) } BUILDIN_FUNC( open_quest_ui ){ +#if PACKETVER < 20151202 + ShowError( "buildin_open_quest_ui: This command requires PACKETVER 20151202 or newer.\n" ); + return SCRIPT_CMD_FAILURE; +#else struct map_session_data* sd; if (!script_charid2sd(3, sd)) @@ -25874,14 +25878,15 @@ BUILDIN_FUNC( open_quest_ui ){ ShowWarning("buildin_open_quest_ui: Character %d doesn't have quest %d.\n", sd->status.char_id, quest_id); } - clif_ui_open( sd, OUT_UI_QUEST, quest_id ); + clif_ui_open( *sd, OUT_UI_QUEST, quest_id ); return SCRIPT_CMD_SUCCESS; +#endif } BUILDIN_FUNC(openbank){ -#if PACKETVER < 20150128 - ShowError( "buildin_openbank: This command requires PACKETVER 20150128 or newer.\n" ); +#if PACKETVER < 20151202 + ShowError( "buildin_openbank: This command requires PACKETVER 20151202 or newer.\n" ); return SCRIPT_CMD_FAILURE; #else struct map_session_data* sd = nullptr; @@ -25895,7 +25900,7 @@ BUILDIN_FUNC(openbank){ return SCRIPT_CMD_FAILURE; } - clif_ui_open( sd, OUT_UI_BANK, 0 ); + clif_ui_open( *sd, OUT_UI_BANK, 0 ); return SCRIPT_CMD_SUCCESS; #endif } @@ -25994,6 +25999,23 @@ BUILDIN_FUNC(getjobexp_ratio){ return SCRIPT_CMD_SUCCESS; } +BUILDIN_FUNC( enchantgradeui ){ +#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724 + struct map_session_data* sd; + + if( !script_charid2sd( 2, sd ) ){ + return SCRIPT_CMD_FAILURE; + } + + clif_ui_open( *sd, OUT_UI_ENCHANTGRADE, 0 ); + + return SCRIPT_CMD_SUCCESS; +#else + ShowError( "buildin_enchantgradeui: This command requires PACKETVER 2020-07-24 or newer.\n" ); + return SCRIPT_CMD_FAILURE; +#endif +} + #include "../custom/script.inc" // declarations that were supposed to be exported from npc_chat.cpp @@ -26713,6 +26735,8 @@ struct script_function buildin_func[] = { BUILDIN_DEF(openbank,"?"), BUILDIN_DEF(getbaseexp_ratio, "i??"), BUILDIN_DEF(getjobexp_ratio, "i??"), + BUILDIN_DEF(enchantgradeui, "?" ), + #include "../custom/script_def.inc" {NULL,NULL,NULL}, diff --git a/src/map/script_constants.hpp b/src/map/script_constants.hpp index 776215cef8..4331c1bbc7 100644 --- a/src/map/script_constants.hpp +++ b/src/map/script_constants.hpp @@ -8996,6 +8996,14 @@ export_constant(SCF_REMOVEONUNEQUIPWEAPON); export_constant(SCF_REMOVEONUNEQUIPARMOR); + /* enchantgrades */ + export_constant(ENCHANTGRADE_NONE); + export_constant(ENCHANTGRADE_D); + export_constant(ENCHANTGRADE_C); + export_constant(ENCHANTGRADE_B); + export_constant(ENCHANTGRADE_A); + export_constant(MAX_ENCHANTGRADE); + #undef export_constant #undef export_constant2 #undef export_parameter diff --git a/src/map/status.cpp b/src/map/status.cpp index 3e9dd3d6b8..c84178f0ca 100644 --- a/src/map/status.cpp +++ b/src/map/status.cpp @@ -544,6 +544,399 @@ uint64 SizeFixDatabase::parseBodyNode(const ryml::NodeRef& node) { SizeFixDatabase size_fix_db; +const std::string EnchantgradeDatabase::getDefaultLocation(){ + return std::string(db_path) + "/enchantgrade.yml"; +} + +uint64 EnchantgradeDatabase::parseBodyNode( const ryml::NodeRef& node ){ + if( !this->nodesExist( node, { "Type", "Levels" } ) ){ + return 0; + } + + std::string itemtype_constant; + + if( !this->asString( node, "Type", itemtype_constant ) ){ + return 0; + } + + int64 constant_value; + + if( !script_get_constant( ( "IT_" + itemtype_constant ).c_str(), &constant_value ) ){ + this->invalidWarning( node["Type"], "Unknown item type \"%s\".\n", itemtype_constant.c_str() ); + return 0; + } + + uint16 itemtype = static_cast( constant_value ); + uint16 itemtype_maxlevel; + + if( itemtype == IT_WEAPON ){ + itemtype_maxlevel = MAX_WEAPON_LEVEL; + }else if( itemtype == IT_ARMOR ){ + itemtype_maxlevel = MAX_ARMOR_LEVEL; + }else{ + this->invalidWarning( node["Type"], "Item type \"%s\" is not supported.\n", itemtype_constant.c_str() ); + return 0; + } + + std::shared_ptr enchantgrade = this->find( itemtype ); + bool exists = enchantgrade != nullptr; + + if( !exists ){ + enchantgrade = std::make_shared(); + enchantgrade->itemtype = itemtype; + } + + for( const ryml::NodeRef& levelNode : node["Levels"] ){ + if( !this->nodesExist( levelNode, { "Level", "Grades" } ) ){ + return 0; + } + + uint16 level; + + if( !this->asUInt16( levelNode, "Level", level ) ){ + return 0; + } + + if( level == 0 || level > itemtype_maxlevel ){ + this->invalidWarning( levelNode["Level"], "Level %hu is invalid for item type %s[1~%hu].\n", level, itemtype_constant.c_str(), itemtype_maxlevel ); + return 0; + } + + std::map>& grades = enchantgrade->levels[level]; + + for( const ryml::NodeRef& gradeNode : levelNode["Grades"] ){ + std::string gradeConstant; + + if( !this->asString( gradeNode, "Grade", gradeConstant ) ){ + return 0; + } + + if( !script_get_constant( ( "ENCHANTGRADE_" + gradeConstant ).c_str(), &constant_value ) ){ + this->invalidWarning( node["Grade"], "Unknown grade \"%s\".\n", gradeConstant.c_str() ); + return 0; + } + + if( constant_value >= MAX_ENCHANTGRADE ){ + this->invalidWarning( gradeNode["Grade"], "Grade %" PRId64 " is too high. Maximum: %hu.\n", constant_value, MAX_ENCHANTGRADE - 1 ); + return 0; + } + + e_enchantgrade gradeLevel = (e_enchantgrade)constant_value; + + std::shared_ptr grade = util::map_find( grades, gradeLevel ); + bool gradeExists = grade != nullptr; + + if( !gradeExists ){ + grade = std::make_shared(); + grade->grade = gradeLevel; + + if( !this->nodesExist( gradeNode, { "Refine", "Chance", "Options" } ) ){ + return 0; + } + } + + if( this->nodeExists( gradeNode, "Refine" ) ){ + uint16 refine; + + if( !this->asUInt16( gradeNode, "Refine", refine ) ){ + return 0; + } + + if( refine > MAX_REFINE ){ + this->invalidWarning( gradeNode["Refine"], "Refine %hu is too high, capping to %hu...\n", refine, MAX_REFINE ); + refine = MAX_REFINE; + } + + grade->refine = refine; + } + + if( this->nodeExists( gradeNode, "Chance" ) ){ + uint16 chance; + + if( !this->asUInt16Rate( gradeNode, "Chance", chance ) ){ + return 0; + } + + grade->chance = chance; + } + + if( this->nodeExists( gradeNode, "Bonus" ) ){ + uint16 bonus; + + if( !this->asUInt16( gradeNode, "Bonus", bonus ) ){ + return 0; + } + + grade->bonus = bonus; + }else{ + if( !gradeExists ){ + grade->bonus = 0; + } + } + + if( this->nodeExists( gradeNode, "Announce" ) ){ + bool announce; + + if( !this->asBool( gradeNode, "Announce", announce ) ){ + return 0; + } + + grade->announce = announce; + }else{ + if( !gradeExists ){ + grade->announce = true; + } + } + + if( this->nodeExists( gradeNode, "Catalyst") ){ + const ryml::NodeRef& catalystNode = gradeNode["Catalyst"]; + + if( this->nodeExists( catalystNode, "Item" ) ){ + std::string itemName; + + if( !this->asString( catalystNode, "Item", itemName ) ){ + return 0; + } + + std::shared_ptr id = item_db.search_aegisname( itemName.c_str() ); + + if( id == nullptr ){ + this->invalidWarning( catalystNode["Item"], "Unknown item \"%s\".\n", itemName.c_str() ); + return 0; + } + + grade->catalyst.item = id->nameid; + }else{ + if( !gradeExists ){ + grade->catalyst.item = 0; + } + } + + if( this->nodeExists( catalystNode, "AmountPerStep" ) ){ + uint16 amountPerStep; + + if( !this->asUInt16( catalystNode, "AmountPerStep", amountPerStep ) ){ + return 0; + } + + grade->catalyst.amountPerStep = amountPerStep; + }else{ + if( !gradeExists ){ + grade->catalyst.amountPerStep = 0; + } + } + + if( this->nodeExists( catalystNode, "MaximumSteps" ) ){ + uint16 maximumSteps; + + if( !this->asUInt16( catalystNode, "MaximumSteps", maximumSteps ) ){ + return 0; + } + + grade->catalyst.maximumSteps = maximumSteps; + }else{ + if( !gradeExists ){ + grade->catalyst.maximumSteps = 0; + } + } + + if( this->nodeExists( catalystNode, "ChanceIncrease" ) ){ + uint16 chanceIncrease; + + if( !this->asUInt16Rate( catalystNode, "ChanceIncrease", chanceIncrease ) ){ + return 0; + } + + grade->catalyst.chanceIncrease = chanceIncrease; + }else{ + if( !gradeExists ){ + grade->catalyst.chanceIncrease = 0; + } + } + }else{ + if( !gradeExists ){ + grade->catalyst.item = 0; + grade->catalyst.amountPerStep = 0; + grade->catalyst.maximumSteps = 0; + grade->catalyst.chanceIncrease = 0; + } + } + + if( this->nodeExists( gradeNode, "Options" ) ){ + for( const ryml::NodeRef& optionNode : gradeNode["Options"] ){ + uint16 optionIndex; + + if( !this->asUInt16( optionNode, "Option", optionIndex ) ){ + return 0; + } + + std::shared_ptr option = util::map_find( grade->options, optionIndex ); + bool optionExists = option != nullptr; + + if( !optionExists ){ + option = std::make_shared(); + option->id = optionIndex; + } + + if( this->nodeExists( optionNode, "Amount" ) ){ + uint16 amount; + + if( !this->asUInt16( optionNode, "Amount", amount ) ){ + return 0; + } + + if( amount > MAX_AMOUNT ){ + this->invalidWarning( optionNode["Amount"], "Amount %hu is too high, capping to %hu...\n", amount, MAX_AMOUNT ); + amount = MAX_AMOUNT; + } + + if( amount == 0 ){ + if( grade->options.erase( optionIndex ) > 0 ){ + continue; + }else{ + this->invalidWarning( optionNode["Amount"], "Trying to remove invalid option %hu...\n", optionIndex ); + return 0; + } + } + + option->amount = amount; + }else{ + if( !optionExists ){ + option->amount = 1; + } + } + + if( this->nodeExists( optionNode, "Item" ) ){ + std::string itemName; + + if( !this->asString( optionNode, "Item", itemName ) ){ + return 0; + } + + std::shared_ptr id = item_db.search_aegisname( itemName.c_str() ); + + if( id == nullptr ){ + this->invalidWarning( optionNode["Item"], "Unknown item \"%s\".\n", itemName.c_str() ); + return 0; + } + + option->item = id->nameid; + }else{ + if( !optionExists ){ + option->item = 0; + } + } + + if( this->nodeExists( optionNode, "Zeny" ) ){ + uint32 zeny; + + if( !this->asUInt32( optionNode, "Zeny", zeny ) ){ + return 0; + } + + option->zeny = zeny; + }else{ + if( !optionExists ){ + option->zeny = 0; + } + } + + if( this->nodeExists( optionNode, "BreakingRate" ) ){ + uint16 breaking_rate; + + if( !this->asUInt16Rate( optionNode, "BreakingRate", breaking_rate ) ){ + return 0; + } + + option->breaking_rate = breaking_rate; + }else{ + if( !optionExists ){ + option->breaking_rate = 0; + } + } + + if( this->nodeExists( optionNode, "DowngradeAmount" ) ){ + uint16 downgrade_amount; + + if( !this->asUInt16( optionNode, "DowngradeAmount", downgrade_amount ) ){ + return 0; + } + + if( downgrade_amount > MAX_REFINE ){ + this->invalidWarning( optionNode["DowngradeAmount"], "Downgrade amount %hu is invalid, skipping.\n", downgrade_amount ); + return 0; + } + + option->downgrade_amount = downgrade_amount; + }else{ + if( !optionExists ){ + option->downgrade_amount = 0; + } + } + + if( !optionExists ){ + grade->options[optionIndex] = option; + } + } + } + + if( !gradeExists ){ + grades[gradeLevel] = grade; + } + } + } + + if( !exists ){ + this->put( itemtype, enchantgrade ); + } + + return 1; +} + +std::shared_ptr EnchantgradeDatabase::findCurrentLevelInfo( const struct item_data& data, struct item& item ){ + std::shared_ptr enchantgrade = enchantgrade_db.find( data.type ); + + // Unsupported item type - no answer + if( enchantgrade == nullptr ){ + return nullptr; + } + + uint16 level = 0; + + if( data.type == IT_WEAPON ){ + level = data.weapon_level; + }else if( data.type == IT_ARMOR ){ + level = data.armor_level; + } + + const auto& enchantgradelevels = enchantgrade->levels.find( level ); + + // Cannot upgrade this weapon or armor level - no answer + if( enchantgradelevels == enchantgrade->levels.end() ){ + return nullptr; + } + + return util::map_find( enchantgradelevels->second, (e_enchantgrade)( item.enchantgrade - 1 ) ); +} + +void EnchantgradeDatabase::loadingFinished(){ + for( const auto& it_itemTypes : *this ){ + for( const auto& it_itemLevels : it_itemTypes.second->levels ){ + for( const auto& it_enchantgrades : it_itemLevels.second ){ + std::shared_ptr enchantgradelevel = it_enchantgrades.second; + + if( enchantgradelevel->catalyst.amountPerStep == 0 ){ + enchantgradelevel->catalyst.item = 0; + enchantgradelevel->catalyst.chanceIncrease = 0; + enchantgradelevel->catalyst.maximumSteps = 0; + } + } + } + } +} + +EnchantgradeDatabase enchantgrade_db; + /** * Get icon ID of SC * @param type: SC type @@ -3256,6 +3649,13 @@ int status_calc_pc_sub(struct map_session_data* sd, uint8 opt) sd->inventory.u.items_inventory[index].refine = MAX_REFINE; std::shared_ptr info = refine_db.findCurrentLevelInfo( *sd->inventory_data[index], sd->inventory.u.items_inventory[index] ); +#ifdef RENEWAL + std::shared_ptr enchantgrade_info = nullptr; + + if( sd->inventory.u.items_inventory[index].enchantgrade > 0 ){ + enchantgrade_info = enchantgrade_db.findCurrentLevelInfo( *sd->inventory_data[index], sd->inventory.u.items_inventory[index] ); + } +#endif if (sd->inventory_data[index]->type == IT_WEAPON) { int wlv = sd->inventory_data[index]->weapon_level; @@ -3277,7 +3677,9 @@ int status_calc_pc_sub(struct map_session_data* sd, uint8 opt) wa->atk2 += info->bonus / 100; #ifdef RENEWAL - // TODO: additional grade bonus + if( enchantgrade_info != nullptr ){ + wa->atk2 += ( ( ( info->bonus / 100 ) * enchantgrade_info->bonus ) / 100 ); + } if( wlv == 5 ){ base_status->patk += sd->inventory.u.items_inventory[index].refine * 2; @@ -3294,7 +3696,9 @@ int status_calc_pc_sub(struct map_session_data* sd, uint8 opt) if( info != nullptr && sd->weapontype1 != W_BOW ){ wa->matk += info->bonus / 100; - // TODO: additional grade bonus + if( enchantgrade_info != nullptr ){ + wa->matk += ( ( ( info->bonus / 100 ) * enchantgrade_info->bonus ) / 100 ); + } } #endif // Overrefine bonus. @@ -15184,10 +15588,12 @@ void status_readdb( bool reload ){ size_fix_db.reload(); refine_db.reload(); status_db.reload(); + enchantgrade_db.reload(); }else{ size_fix_db.load(); refine_db.load(); status_db.load(); + enchantgrade_db.load(); } elemental_attribute_db.load(); } @@ -15211,6 +15617,7 @@ void do_init_status(void) { /** Destroy status data */ void do_final_status(void) { ers_destroy(sc_data_ers); + enchantgrade_db.clear(); size_fix_db.clear(); refine_db.clear(); status_db.clear(); diff --git a/src/map/status.hpp b/src/map/status.hpp index f28f111a1e..400b7f3858 100644 --- a/src/map/status.hpp +++ b/src/map/status.hpp @@ -144,6 +144,59 @@ public: extern AttributeDatabase elemental_attribute_db; +enum e_enchantgrade_result{ + ENCHANTGRADE_UPGRADE_SUCCESS, + ENCHANTGRADE_UPGRADE_FAILED, + ENCHANTGRADE_UPGRADE_DOWNGRADE, + ENCHANTGRADE_UPGRADE_BREAK, + ENCHANTGRADE_UPGRADE_PROTECTED, +}; + +struct s_enchantgradeoption{ + uint16 id; + t_itemid item; + uint16 amount; + uint32 zeny; + uint16 breaking_rate; + uint16 downgrade_amount; +}; + +struct s_enchantgradelevel{ + e_enchantgrade grade; + uint16 refine; + uint16 chance; + uint16 bonus; + bool announce; + struct{ + t_itemid item; + uint16 amountPerStep; + uint16 maximumSteps; + uint16 chanceIncrease; + }catalyst; + std::map> options; +}; + +struct s_enchantgrade{ + uint16 itemtype; + std::map>> levels; +}; + +class EnchantgradeDatabase : public TypesafeYamlDatabase{ +public: + EnchantgradeDatabase() : TypesafeYamlDatabase( "ENCHANTGRADE_DB", 1 ){ + + } + + const std::string getDefaultLocation() override; + uint64 parseBodyNode( const ryml::NodeRef& node ) override; + void loadingFinished() override; + + // Additional + std::shared_ptr findCurrentLevelInfo( const struct item_data& data, struct item& item ); +}; + +extern EnchantgradeDatabase enchantgrade_db; + /// Status changes listing. These code are for use by the server. enum sc_type : int16 { SC_NONE = -1,