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,