Initial release of item enchant UI (#7186)

Thanks to @aleos89, @Atemo @idk-whoami
This commit is contained in:
Lemongrass3110 2022-09-05 00:52:37 +02:00 committed by GitHub
parent 3c43669425
commit 8629c6445a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 19481 additions and 3 deletions

View File

@ -924,7 +924,10 @@
// General packet version check messages
828: This command requires packet version %s or newer.
//829-899 free
// Enchant UI
829: Enchanting is not possible for your item's enchant grade.
//830-899 free
//------------------------------------
// More atcommands message

View File

@ -0,0 +1,72 @@
# 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 <http://www.gnu.org/licenses/>.
#
###########################################################################
# Item Enchant Database
###########################################################################
#
# Item Enchant Settings
#
###########################################################################
# - Id Client side LUA index.
# TargetItems: List of possible target items.
# <item name> Item name of possible target item.
# MinimumRefine Minimum refine of the target item. (Default: 0)
# MinimumEnchantgrade Minimum enchant grade of the target item. (Default: 0)
# AllowRandomOptions Enable random options in the target item. (Default: true)
# Reset: Reset options. (Default: null)
# Chance Chance of successful reset. (Default: 0)
# Price Price for resetting enchants. (Default: 0)
# Materials: Items required for resetting enchants. (Default: null)
# - Material Item name of the required item.
# Amount Amount of the required item. (Default: 1)
# Order: Order in which the slots can be enchanted.
# Overwriting via import will clear the currently defined order.
# - Slot Number of the slot (0-3).
# Slots: Enchant options.
# - Slot Number of the slot (0-3).
# Price Price required for the normal enchant process. (Default: 0)
# Materials: Items required for the normal enchant process. (Default: null)
# - Material Item name of the required item.
# Amount Amount of the required item. (Default: 1)
# Chance Base chance for the normal enchant process. (Default: 100000)
# EnchantgradeBonus: Additional bonus chance increase per enchant grade. (Default: null)
# - Enchantgrade Enchant grade required for the bonus.
# Chance Additional chance that is added to the base chance.
# Enchants: Available enchants for the normal enchant process per enchant grade. (Default: null)
# - Enchantgrade Enchant grade of the item to be enchanted.
# Items: Available enchants for the normal enchant process on the given enchant grade. (Default: null)
# - Item Item name of the available enchant item.
# Chance Chance to get this specific enchant item.
# PerfectEnchants: Available perfect enchants (100% chance + selectable) for this slot. (Default: null)
# - Item Item name of the available enchant item.
# Price Price required for this perfect enchant. (Default: 0)
# Materials: Items required for this perfect enchant. (Default: null)
# - Material Item name of the required item.
# Amount Amount of the required item. (Default: 1)
# Upgrades: Available enchant upgrades for this slot. (Default: null)
# - Enchant Item name of the enchant item available for upgrade.
# Upgrade Item name of the enchant it can be upgrade to.
# Price Price required for this enchant upgrade. (Default: 0)
# Materials: Items required for this enchant upgrade (Default: null)
# - Material Item name of the required item.
# Amount Amount of the required item. (Default: 1)
###########################################################################
Header:
Type: ITEM_ENCHANT_DB
Version: 1

78
db/item_enchant.yml Normal file
View File

@ -0,0 +1,78 @@
# 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 <http://www.gnu.org/licenses/>.
#
###########################################################################
# Item Enchant Database
###########################################################################
#
# Item Enchant Settings
#
###########################################################################
# - Id Client side LUA index.
# TargetItems: List of possible target items.
# <item name> Item name of possible target item.
# MinimumRefine Minimum refine of the target item. (Default: 0)
# MinimumEnchantgrade Minimum enchant grade of the target item. (Default: 0)
# AllowRandomOptions Enable random options in the target item. (Default: true)
# Reset: Reset options. (Default: null)
# Chance Chance of successful reset. (Default: 0)
# Price Price for resetting enchants. (Default: 0)
# Materials: Items required for resetting enchants. (Default: null)
# - Material Item name of the required item.
# Amount Amount of the required item. (Default: 1)
# Order: Order in which the slots can be enchanted.
# Overwriting via import will clear the currently defined order.
# - Slot Number of the slot (0-3).
# Slots: Enchant options.
# - Slot Number of the slot (0-3).
# Price Price required for the normal enchant process. (Default: 0)
# Materials: Items required for the normal enchant process. (Default: null)
# - Material Item name of the required item.
# Amount Amount of the required item. (Default: 1)
# Chance Base chance for the normal enchant process. (Default: 100000)
# EnchantgradeBonus: Additional bonus chance increase per enchant grade. (Default: null)
# - Enchantgrade Enchant grade required for the bonus.
# Chance Additional chance that is added to the base chance.
# Enchants: Available enchants for the normal enchant process per enchant grade. (Default: null)
# - Enchantgrade Enchant grade of the item to be enchanted.
# Items: Available enchants for the normal enchant process on the given enchant grade. (Default: null)
# - Item Item name of the available enchant item.
# Chance Chance to get this specific enchant item.
# PerfectEnchants: Available perfect enchants (100% chance + selectable) for this slot. (Default: null)
# - Item Item name of the available enchant item.
# Price Price required for this perfect enchant. (Default: 0)
# Materials: Items required for this perfect enchant. (Default: null)
# - Material Item name of the required item.
# Amount Amount of the required item. (Default: 1)
# Upgrades: Available enchant upgrades for this slot. (Default: null)
# - Enchant Item name of the enchant item available for upgrade.
# Upgrade Item name of the enchant it can be upgrade to.
# Price Price required for this enchant upgrade. (Default: 0)
# Materials: Items required for this enchant upgrade (Default: null)
# - Material Item name of the required item.
# Amount Amount of the required item. (Default: 1)
###########################################################################
Header:
Type: ITEM_ENCHANT_DB
Version: 1
Footer:
Imports:
- Path: db/re/item_enchant.yml
Mode: Renewal
- Path: db/import/item_enchant.yml

View File

@ -2217,6 +2217,20 @@ Body:
Weight: 10
Script: |
rentitem 22998,604800;
- Id: 9585
AegisName: Small_Spray_Of_Flowers
Name: Small Flower Branches
Type: Usable
Weight: 50
Trade:
NoDrop: true
NoTrade: true
NoCart: true
NoGuildStorage: true
NoMail: true
NoAuction: true
Script: |
/* TODO: sc_start MHP +3% and MSP +3% for 10 minutes */
- Id: 9785
AegisName: Frozen_Box_IL
Name: Frozen Refine Box

18096
db/re/item_enchant.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -8261,6 +8261,16 @@ If run from within an item script <item ID> or <item name> is optional.
This feature requires packet version 2021-11-03 or newer.
---------------------------------------
*item_enchant(<client side LUA index>{,<char ID>});
Opens the enchant UI for the attached character or the player given by the <char ID> parameter.
If the player exceeds 70% weight the client will not open the enchant UI and will trigger an
error message instead.
This command requires packet version 2021-11-03 or newer.
---------------------------------------
\\
6,1.- Unit-related commands

View File

@ -24056,6 +24056,532 @@ void clif_parse_item_reform_start( int fd, struct map_session_data* sd ){
#endif
}
void clif_enchantwindow_open( struct map_session_data& sd, uint64 clientLuaIndex ){
#if PACKETVER_RE_NUM >= 20211103
// Hardcoded clientside check
if( sd.weight > ( ( sd.max_weight * 70 ) / 100 ) ){
clif_msg_color( &sd, C_ENCHANT_OVERWEIGHT, color_table[COLOR_RED] );
sd.state.item_enchant_index = 0;
return;
}
struct PACKET_ZC_UI_OPEN_V3 p = {};
p.packetType = HEADER_ZC_UI_OPEN_V3;
p.type = OUT_UI_ENCHANT;
p.data = clientLuaIndex;
clif_send( &p, sizeof( p ), &sd.bl, SELF );
sd.state.item_enchant_index = clientLuaIndex;
#endif
}
void clif_enchantwindow_result( struct map_session_data& sd, bool success, t_itemid enchant = 0 ){
#if PACKETVER_RE_NUM >= 20211103
struct PACKET_ZC_RESPONSE_ENCHANT p = {};
p.packetType = HEADER_ZC_RESPONSE_ENCHANT;
if( success ){
p.messageId = C_ENCHANT_SUCCESS;
}else{
p.messageId = C_ENCHANT_FAILURE;
}
p.enchantItemId = enchant;
clif_send( &p, sizeof( p ), &sd.bl, SELF );
sd.state.item_enchant_index = 0;
#endif
}
bool clif_parse_enchant_basecheck( struct item& selected_item, std::shared_ptr<s_item_enchant> enchant ){
if( selected_item.equip != 0 ){
return false;
}
if( selected_item.equipSwitch != 0 ){
return false;
}
if( selected_item.attribute != 0 ){
return false;
}
if( !util::vector_exists( enchant->target_item_ids, selected_item.nameid ) ){
return false;
}
if( selected_item.refine < enchant->minimumRefine ){
return false;
}
if( selected_item.enchantgrade < enchant->minimumEnchantgrade ){
return false;
}
if( !enchant->allowRandomOptions ){
for( const s_item_randomoption& option : selected_item.option ){
if( option.id != 0 ){
return false;
}
}
}
return true;
}
void clif_parse_enchantwindow_general( int fd, struct map_session_data* sd ){
#if PACKETVER_RE_NUM >= 20211103
struct PACKET_CZ_REQUEST_RANDOM_ENCHANT *p = (struct PACKET_CZ_REQUEST_RANDOM_ENCHANT*)RFIFOP( fd, 0 );
if( sd->state.item_enchant_index != p->clientLuaIndex ){
return;
}
uint16 index = server_index( p->index );
if( index >= MAX_INVENTORY ){
return;
}
if( sd->inventory_data[index] == nullptr ){
return;
}
struct item& selected_item = sd->inventory.u.items_inventory[index];
std::shared_ptr<s_item_enchant> enchant = item_enchant_db.find( p->clientLuaIndex );
if( enchant == nullptr ){
return;
}
if( !clif_parse_enchant_basecheck( selected_item, enchant ) ){
return;
}
uint16 slot = MAX_SLOTS;
for( uint16 nextSlot : enchant->order ){
if( selected_item.card[nextSlot] == 0 ){
slot = nextSlot;
break;
}
}
if( slot == MAX_SLOTS ){
return;
}
if( slot < sd->inventory_data[index]->slots ){
return;
}
std::shared_ptr<s_item_enchant_slot> enchant_slot = util::umap_find( enchant->slots, slot );
if( enchant_slot == nullptr ){
return;
}
std::shared_ptr<s_item_enchant_normal> enchants_for_enchantgrade = util::umap_find( enchant_slot->normal.enchants, (uint16)selected_item.enchantgrade );
if( enchants_for_enchantgrade == nullptr ){
clif_messagecolor( &sd->bl, color_table[COLOR_RED], msg_txt( sd, 829 ), false, SELF); // Enchanting is not possible for your item's enchant grade.
clif_enchantwindow_result( *sd, false );
return;
}
if( sd->status.zeny < enchant_slot->normal.zeny ){
return;
}
std::unordered_map<uint16, uint16> materials;
for( const auto& entry : enchant_slot->normal.materials ){
int16 idx = pc_search_inventory( sd, entry.first );
if( idx < 0 ){
return;
}
if( sd->inventory.u.items_inventory[idx].amount < entry.second ){
return;
}
materials[idx] = entry.second;
}
if( pc_payzeny( sd, enchant_slot->normal.zeny, LOG_TYPE_ENCHANT, nullptr ) != 0 ){
return;
}
for( const auto& entry : materials ){
if( pc_delitem( sd, entry.first, entry.second, 0, 0, LOG_TYPE_ENCHANT ) != 0 ){
return;
}
}
uint32 chance = enchant_slot->normal.chance;
for( int i = 0; i <= MAX_ENCHANTGRADE; i++ ){
chance += enchant_slot->normal.enchantgradeChanceIncrease[i];
}
if( chance < 100000 && rnd_value( 0, 100000 ) > chance ){
clif_enchantwindow_result( *sd, false );
return;
}
// Log removal of item
log_pick_pc( sd, LOG_TYPE_ENCHANT, -1, &selected_item );
size_t maximum = 3 * enchant_slot->normal.enchants.size();
bool enchanted = false;
for( int i = 0; i < maximum; i++ ){
std::shared_ptr<s_item_enchant_normal_sub> normal_enchant = util::umap_random( enchants_for_enchantgrade->enchants );
if( rnd_value( 0, 10000 ) < normal_enchant->chance ){
selected_item.card[slot] = normal_enchant->item_id;
enchanted = true;
break;
}
}
if( !enchanted ){
std::shared_ptr<s_item_enchant_normal_sub> normal_enchant = util::umap_random( enchants_for_enchantgrade->enchants );
selected_item.card[slot] = normal_enchant->item_id;
}
// Log retrieving the item again -> with the new enchant
log_pick_pc( sd, LOG_TYPE_ENCHANT, 1, &selected_item );
clif_enchantwindow_result( *sd, true, selected_item.card[slot] );
#endif
}
void clif_parse_enchantwindow_perfect( int fd, struct map_session_data* sd ){
#if PACKETVER_RE_NUM >= 20211103
struct PACKET_CZ_REQUEST_PERFECT_ENCHANT *p = (struct PACKET_CZ_REQUEST_PERFECT_ENCHANT*)RFIFOP( fd, 0 );
if( sd->state.item_enchant_index != p->clientLuaIndex ){
return;
}
uint16 index = server_index( p->index );
if( index >= MAX_INVENTORY ){
return;
}
if( sd->inventory_data[index] == nullptr ){
return;
}
struct item& selected_item = sd->inventory.u.items_inventory[index];
std::shared_ptr<s_item_enchant> enchant = item_enchant_db.find( p->clientLuaIndex );
if( enchant == nullptr ){
return;
}
if( !clif_parse_enchant_basecheck( selected_item, enchant ) ){
return;
}
uint16 slot = MAX_SLOTS;
for( uint16 nextSlot : enchant->order ){
if( selected_item.card[nextSlot] == 0 ){
slot = nextSlot;
break;
}
}
if( slot == MAX_SLOTS ){
return;
}
if( slot < sd->inventory_data[index]->slots ){
return;
}
std::shared_ptr<s_item_enchant_slot> enchant_slot = util::umap_find( enchant->slots, slot );
if( enchant_slot == nullptr ){
return;
}
std::shared_ptr<s_item_enchant_perfect> perfect_enchant = util::umap_find( enchant_slot->perfect.enchants, p->itemId );
if( perfect_enchant == nullptr ){
return;
}
if( sd->status.zeny < perfect_enchant->zeny ){
return;
}
std::unordered_map<uint16, uint16> materials;
for( const auto& entry : perfect_enchant->materials ){
int16 idx = pc_search_inventory( sd, entry.first );
if( idx < 0 ){
return;
}
if( sd->inventory.u.items_inventory[idx].amount < entry.second ){
return;
}
materials[idx] = entry.second;
}
if( pc_payzeny( sd, perfect_enchant->zeny, LOG_TYPE_ENCHANT, nullptr ) != 0 ){
return;
}
for( const auto& entry : materials ){
if( pc_delitem( sd, entry.first, entry.second, 0, 0, LOG_TYPE_ENCHANT ) != 0 ){
return;
}
}
// Log removal of item
log_pick_pc( sd, LOG_TYPE_ENCHANT, -1, &selected_item );
selected_item.card[slot] = perfect_enchant->item_id;
// Log retrieving the item again -> with the new enchant
log_pick_pc( sd, LOG_TYPE_ENCHANT, 1, &selected_item );
clif_enchantwindow_result( *sd, true, selected_item.card[slot] );
#endif
}
void clif_parse_enchantwindow_upgrade( int fd, struct map_session_data* sd ){
#if PACKETVER_RE_NUM >= 20211103
struct PACKET_CZ_REQUEST_UPGRADE_ENCHANT *p = (struct PACKET_CZ_REQUEST_UPGRADE_ENCHANT*)RFIFOP( fd, 0 );
if( sd->state.item_enchant_index != p->clientLuaIndex ){
return;
}
uint16 index = server_index( p->index );
if( index >= MAX_INVENTORY ){
return;
}
if( sd->inventory_data[index] == nullptr ){
return;
}
struct item& selected_item = sd->inventory.u.items_inventory[index];
std::shared_ptr<s_item_enchant> enchant = item_enchant_db.find( p->clientLuaIndex );
if( enchant == nullptr ){
return;
}
if( !clif_parse_enchant_basecheck( selected_item, enchant ) ){
return;
}
uint16 slot = p->slot;
if( slot >= MAX_SLOTS ){
return;
}
if( slot < sd->inventory_data[index]->slots ){
return;
}
if( selected_item.card[slot] == 0 ){
return;
}
std::shared_ptr<s_item_enchant_slot> enchant_slot = util::umap_find( enchant->slots, slot );
if( enchant_slot == nullptr ){
return;
}
std::shared_ptr<s_item_enchant_upgrade> upgrade = util::umap_find( enchant_slot->upgrade.enchants, selected_item.card[slot] );
if( upgrade == nullptr ){
return;
}
if( sd->status.zeny < upgrade->zeny ){
return;
}
std::unordered_map<uint16, uint16> materials;
for( const auto& entry : upgrade->materials ){
int16 idx = pc_search_inventory( sd, entry.first );
if( idx < 0 ){
return;
}
if( sd->inventory.u.items_inventory[idx].amount < entry.second ){
return;
}
materials[idx] = entry.second;
}
if( pc_payzeny( sd, upgrade->zeny, LOG_TYPE_ENCHANT, nullptr ) != 0 ){
return;
}
for( const auto& entry : materials ){
if( pc_delitem( sd, entry.first, entry.second, 0, 0, LOG_TYPE_ENCHANT ) != 0 ){
return;
}
}
// Log removal of item
log_pick_pc( sd, LOG_TYPE_ENCHANT, -1, &selected_item );
selected_item.card[slot] = upgrade->upgrade_item_id;
// Log retrieving the item again -> with the new enchant
log_pick_pc( sd, LOG_TYPE_ENCHANT, 1, &selected_item );
clif_enchantwindow_result( *sd, true, selected_item.card[slot] );
#endif
}
void clif_parse_enchantwindow_reset( int fd, struct map_session_data* sd ){
#if PACKETVER_RE_NUM >= 20211103
struct PACKET_CZ_REQUEST_RESET_ENCHANT *p = (struct PACKET_CZ_REQUEST_RESET_ENCHANT*)RFIFOP( fd, 0 );
if( sd->state.item_enchant_index != p->clientLuaIndex ){
return;
}
uint16 index = server_index( p->index );
if( index >= MAX_INVENTORY ){
return;
}
if( sd->inventory_data[index] == nullptr ){
return;
}
struct item& selected_item = sd->inventory.u.items_inventory[index];
if( selected_item.equip != 0 ){
return;
}
if( selected_item.equipSwitch != 0 ){
return;
}
if( selected_item.attribute != 0 ){
return;
}
std::shared_ptr<s_item_enchant> enchant = item_enchant_db.find( p->clientLuaIndex );
if( enchant == nullptr ){
return;
}
if( !util::vector_exists( enchant->target_item_ids, selected_item.nameid ) ){
return;
}
if( selected_item.refine < enchant->minimumRefine ){
return;
}
if( selected_item.enchantgrade < enchant->minimumEnchantgrade ){
return;
}
bool is_enchanted = false;
for( int i = sd->inventory_data[index]->slots; i < MAX_SLOTS; i++ ){
if( selected_item.card[i] != 0 ){
is_enchanted = true;
break;
}
}
if( !is_enchanted ){
return;
}
if( sd->status.zeny < enchant->reset.zeny ){
return;
}
std::unordered_map<uint16, uint16> materials;
for( const auto& entry : enchant->reset.materials ){
int16 idx = pc_search_inventory( sd, entry.first );
if( idx < 0 ){
return;
}
if( sd->inventory.u.items_inventory[idx].amount < entry.second ){
return;
}
materials[idx] = entry.second;
}
if( pc_payzeny( sd, enchant->reset.zeny, LOG_TYPE_ENCHANT, nullptr ) != 0 ){
return;
}
for( const auto& entry : materials ){
if( pc_delitem( sd, entry.first, entry.second, 0, 0, LOG_TYPE_ENCHANT ) != 0 ){
return;
}
}
uint32 chance = enchant->reset.chance;
if( chance == 0 ){
return;
}
if( chance < 100000 && rnd_value( 0, 100000 ) > chance ){
clif_enchantwindow_result( *sd, false );
return;
}
// Log removal of item
log_pick_pc( sd, LOG_TYPE_ENCHANT, -1, &selected_item );
for( int i = sd->inventory_data[index]->slots; i < MAX_SLOTS; i++ ){
selected_item.card[i] = 0;
}
// Log retrieving the item again -> with the new enchant
log_pick_pc( sd, LOG_TYPE_ENCHANT, 1, &selected_item );
clif_enchantwindow_result( *sd, true );
#endif
}
void clif_parse_enchantwindow_close( int fd, struct map_session_data* sd ){
#if PACKETVER_RE_NUM >= 20211103
sd->state.item_enchant_index = 0;
#endif
}
/*==========================================
* Main client packet processing function
*------------------------------------------*/

View File

@ -555,6 +555,9 @@ enum clif_messages : uint16_t {
// Unofficial names
C_ITEM_EQUIP_SWITCH = 0xbc7,
C_ITEM_NOEQUIP = 0x174, /// <"You can't put this item on."
C_ENCHANT_OVERWEIGHT = 0xEFD,
C_ENCHANT_SUCCESS = 0xF11,
C_ENCHANT_FAILURE = 0xF12,
};
enum e_personalinfo : uint8_t {
@ -1165,6 +1168,7 @@ enum out_ui_type : int8 {
OUT_UI_QUEST = 6,
OUT_UI_ATTENDANCE,
OUT_UI_ENCHANTGRADE,
OUT_UI_ENCHANT = 10,
};
void clif_ui_open( struct map_session_data& sd, enum out_ui_type ui_type, int32 data );
@ -1207,4 +1211,7 @@ void clif_reputation_list( struct map_session_data& sd );
// Item Reform UI
void clif_item_reform_open( struct map_session_data& sd, t_itemid item );
// Item Enchant UI
void clif_enchantwindow_open( struct map_session_data& sd, uint64 clientLuaIndex );
#endif /* CLIF_HPP */

View File

@ -2458,6 +2458,11 @@
parseable_packet( 0xb93, 12, clif_parse_dull, 0 );
parseable_packet( HEADER_CZ_CLOSE_REFORM_UI, sizeof( struct PACKET_CZ_CLOSE_REFORM_UI ), clif_parse_item_reform_close, 0 );
parseable_packet( HEADER_CZ_ITEM_REFORM, sizeof( struct PACKET_CZ_ITEM_REFORM ), clif_parse_item_reform_start, 0 );
parseable_packet( HEADER_CZ_REQUEST_RANDOM_ENCHANT, sizeof( struct PACKET_CZ_REQUEST_RANDOM_ENCHANT ), clif_parse_enchantwindow_general, 0 );
parseable_packet( HEADER_CZ_REQUEST_PERFECT_ENCHANT, sizeof( struct PACKET_CZ_REQUEST_PERFECT_ENCHANT ), clif_parse_enchantwindow_perfect, 0 );
parseable_packet( HEADER_CZ_REQUEST_UPGRADE_ENCHANT, sizeof( struct PACKET_CZ_REQUEST_UPGRADE_ENCHANT ), clif_parse_enchantwindow_upgrade, 0 );
parseable_packet( HEADER_CZ_REQUEST_RESET_ENCHANT, sizeof( struct PACKET_CZ_REQUEST_RESET_ENCHANT ), clif_parse_enchantwindow_reset, 0 );
parseable_packet( HEADER_CZ_CLOSE_UI_ENCHANT, sizeof( struct PACKET_CZ_CLOSE_UI_ENCHANT ), clif_parse_enchantwindow_close, 0 );
#endif
#endif /* CLIF_PACKETDB_HPP */

View File

@ -1959,6 +1959,525 @@ uint64 ItemReformDatabase::parseBodyNode( const ryml::NodeRef& node ){
ItemReformDatabase item_reform_db;
const std::string ItemEnchantDatabase::getDefaultLocation(){
return std::string( db_path ) + "/item_enchant.yml";
}
bool ItemEnchantDatabase::parseMaterials( const ryml::NodeRef& node, std::unordered_map<t_itemid, uint16>& materials ){
if( this->nodeExists( node, "Materials" ) ){
for( const ryml::NodeRef& materialNode : node["Materials"] ){
std::string name;
if( !this->asString( materialNode, "Material", name ) ){
return false;
}
std::shared_ptr<item_data> item = item_db.search_aegisname( name.c_str() );
if( item == nullptr ){
this->invalidWarning( materialNode["Material"], "Unknown item \"%s\".\n", name.c_str() );
return false;
}
t_itemid material_id = item->nameid;
bool material_exists = util::umap_find( materials, material_id ) != nullptr;
uint16 amount;
if( this->nodeExists( materialNode, "Amount" ) ){
if( !this->asUInt16( materialNode, "Amount", amount ) ){
return false;
}
if( amount > MAX_AMOUNT ){
this->invalidWarning( materialNode["Amount"], "Amount %hu is too high, capping to MAX_AMOUNT...\n", amount );
amount = MAX_AMOUNT;
}
}else{
if( !material_exists ){
amount = 1;
}else{
amount = materials[material_id];
}
}
if( amount > 0 ){
materials[material_id] = amount;
}else{
materials.erase( material_id );
}
}
}
return true;
}
uint64 ItemEnchantDatabase::parseBodyNode( const ryml::NodeRef& node ){
uint64 id;
if( !this->asUInt64( node, "Id", id ) ){
return 0;
}
std::shared_ptr<s_item_enchant> enchant = this->find( id );
bool exists = enchant != nullptr;
if( !exists ){
if( !this->nodesExist( node, { "TargetItems", "Order", "Slots" } ) ){
return 0;
}
enchant = std::make_shared<s_item_enchant>();
enchant->id = id;
}
if( this->nodeExists( node, "TargetItems" ) ){
const ryml::NodeRef& targetItemsNode = node["TargetItems"];
for( const auto& it : targetItemsNode ){
std::string name;
c4::from_chars( it.key(), &name );
std::shared_ptr<item_data> item = item_db.search_aegisname( name.c_str() );
if( item == nullptr ){
this->invalidWarning( it, "Unknown item \"%s\".\n", name.c_str() );
return 0;
}
bool enable;
if( !this->asBool( targetItemsNode, name, enable ) ){
return 0;
}
if( enable ){
if( util::vector_exists( enchant->target_item_ids, item->nameid ) ){
this->invalidWarning( it, "Target item \"%s\" is already in the list.\n", name.c_str() );
}else{
enchant->target_item_ids.push_back( item->nameid );
}
}else{
if( !util::vector_exists( enchant->target_item_ids, item->nameid ) ){
this->invalidWarning( it, "Target item \"%s\" is not in the list.\n", name.c_str() );
}else{
util::vector_erase_if_exists( enchant->target_item_ids, item->nameid );
}
}
}
}
if( this->nodeExists( node, "MinimumRefine" ) ){
uint16 refine;
if( !this->asUInt16( node, "MinimumRefine", refine ) ){
return 0;
}
if( refine > MAX_REFINE ){
this->invalidWarning( node["MinimumRefine"], "Minimum refine %hu exceeds MAX_REFINE. Capping...\n", refine );
refine = MAX_REFINE;
}
enchant->minimumRefine = refine;
}else{
if( !exists ){
enchant->minimumRefine = 0;
}
}
if( this->nodeExists( node, "MinimumEnchantgrade" ) ){
uint16 enchantgrade;
if( !this->asUInt16( node, "MinimumEnchantgrade", enchantgrade ) ){
return 0;
}
if( enchantgrade > MAX_ENCHANTGRADE ){
this->invalidWarning( node["MinimumEnchantgrade"], "Minimum enchantgrade %hu exceeds MAX_ENCHANTGRADE. Capping...\n", enchantgrade );
enchantgrade = MAX_ENCHANTGRADE;
}
enchant->minimumEnchantgrade = enchantgrade;
}else{
if( !exists ){
enchant->minimumEnchantgrade = 0;
}
}
if( this->nodeExists( node, "AllowRandomOptions" ) ){
bool allow;
if( !this->asBool( node, "AllowRandomOptions", allow ) ){
return 0;
}
enchant->allowRandomOptions = allow;
}else{
if( !exists ){
enchant->allowRandomOptions = true;
}
}
if( this->nodeExists( node, "Reset" ) ){
const ryml::NodeRef& resetNode = node["Reset"];
if( this->nodeExists( resetNode, "Chance" ) ){
uint32 chance;
if( !this->asUInt32Rate( resetNode, "Chance", chance, 100000 ) ){
return 0;
}
enchant->reset.chance = chance;
}else{
if( !exists ){
enchant->reset.chance = 0;
}
}
if( this->nodeExists( resetNode, "Price" ) ){
uint32 zeny;
if( !this->asUInt32( resetNode, "Price", zeny ) ){
return 0;
}
if( zeny > MAX_ZENY ){
this->invalidWarning( resetNode["Price"], "Price %u exceeds MAX_ZENY. Capping...\n", zeny );
zeny = MAX_ZENY;
}
enchant->reset.zeny = zeny;
}else{
if( !exists ){
enchant->reset.zeny = 0;
}
}
if( !this->parseMaterials( resetNode, enchant->reset.materials ) ){
return 0;
}
}else{
if( !exists ){
enchant->reset = {};
}
}
if( this->nodeExists( node, "Order" ) ){
enchant->order.clear();
for( const auto& it : node["Order"] ){
uint16 slot;
if( !this->asUInt16( it, "Slot", slot ) ){
return 0;
}
if( slot >= MAX_SLOTS ){
this->invalidWarning( it, "Slot %hu exceeds MAX_SLOTS...\n", slot );
return 0;
}
enchant->order.push_back( slot );
}
}
if( this->nodeExists( node, "Slots" ) ){
for( const ryml::NodeRef& slotNode : node["Slots"] ){
uint16 slot;
if( !this->asUInt16( slotNode, "Slot", slot ) ){
return 0;
}
if( slot >= MAX_SLOTS ){
this->invalidWarning( slotNode["Slot"], "Slot %hu exceeds MAX_SLOTS...\n", slot );
return 0;
}
std::shared_ptr<s_item_enchant_slot> enchant_slot = util::umap_find( enchant->slots, slot );
bool slot_exists = enchant_slot != nullptr;
if( !slot_exists ){
enchant_slot = std::make_shared<s_item_enchant_slot>();
enchant_slot->slot = slot;
for( int i = 0; i <= MAX_ENCHANTGRADE; i++ ){
enchant_slot->normal.enchantgradeChanceIncrease[i] = 0;
}
}
if( this->nodeExists( slotNode, "Price" ) ){
uint32 zeny;
if( !this->asUInt32( slotNode, "Price", zeny ) ){
return 0;
}
if( zeny > MAX_ZENY ){
this->invalidWarning( slotNode["Price"], "Price %u exceeds MAX_ZENY. Capping...\n", zeny );
zeny = MAX_ZENY;
}
enchant_slot->normal.zeny = zeny;
}else{
if( !slot_exists ){
enchant_slot->normal.zeny = 0;
}
}
if( !this->parseMaterials( slotNode, enchant_slot->normal.materials ) ){
return 0;
}
if( this->nodeExists( slotNode, "Chance" ) ){
uint32 chance;
if( !this->asUInt32Rate( slotNode, "Chance", chance, 100000 ) ){
return 0;
}
enchant_slot->normal.chance = chance;
}else{
if( !slot_exists ){
enchant_slot->normal.chance = 100000;
}
}
if( this->nodeExists( slotNode, "EnchantgradeBonus" ) ){
for( const ryml::NodeRef& enchantgradeNode : slotNode["EnchantgradeBonus"] ){
uint16 enchantgrade;
if( !this->asUInt16( enchantgradeNode, "Enchantgrade", enchantgrade ) ){
return 0;
}
if( enchantgrade > MAX_ENCHANTGRADE ){
this->invalidWarning( enchantgradeNode["Enchantgrade"], "Enchant grade %hu exceeds MAX_ENCHANTGRADE.\n", enchantgrade );
return 0;
}
uint32 chance;
if( !this->asUInt32Rate( slotNode, "Chance", chance, 100000 ) ){
return 0;
}
enchant_slot->normal.enchantgradeChanceIncrease[enchantgrade] = chance;
}
}
if( this->nodeExists( slotNode, "Enchants" ) ){
for( const ryml::NodeRef& enchantNode : slotNode["Enchants"] ){
uint16 enchantgrade;
if( !this->asUInt16( enchantNode, "Enchantgrade", enchantgrade ) ){
return 0;
}
if( enchantgrade > MAX_ENCHANTGRADE ){
this->invalidWarning( enchantNode["Enchantgrade"], "Enchant grade %hu exceeds MAX_ENCHANTGRADE...\n", enchantgrade );
return 0;
}
std::shared_ptr<s_item_enchant_normal> enchants_for_enchantgrade = util::umap_find( enchant_slot->normal.enchants, enchantgrade );
bool enchants_for_enchantgrade_exists = enchants_for_enchantgrade != nullptr;
if( !enchants_for_enchantgrade_exists ){
enchants_for_enchantgrade = std::make_shared<s_item_enchant_normal>();
enchants_for_enchantgrade->enchantgrade = enchantgrade;
}
if( this->nodeExists( enchantNode, "Items" ) ){
for( const ryml::NodeRef& itemNode : enchantNode["Items"] ){
std::string enchant_name;
if( !this->asString( itemNode, "Item", enchant_name ) ){
return 0;
}
std::shared_ptr<item_data> enchant_item = item_db.search_aegisname( enchant_name.c_str() );
if( enchant_item == nullptr ){
this->invalidWarning( itemNode["Item"], "Unknown item \"%s\".\n", enchant_name.c_str() );
return 0;
}
std::shared_ptr<s_item_enchant_normal_sub> enchant = util::umap_find( enchants_for_enchantgrade->enchants, enchant_item->nameid );
bool enchant_exists = enchant != nullptr;
if( !enchant_exists ){
if( !this->nodesExist( itemNode, { "Chance" } ) ){
return 0;
}
enchant = std::make_shared<s_item_enchant_normal_sub>();
enchant->item_id = enchant_item->nameid;
}
if( this->nodeExists( itemNode, "Chance" ) ){
uint32 chance;
if( !this->asUInt32Rate( itemNode, "Chance", chance, 100000 ) ){
return 0;
}
enchant->chance = chance;
}
if( !enchant_exists ){
enchants_for_enchantgrade->enchants[enchant->item_id] = enchant;
}
}
}
if( !enchants_for_enchantgrade_exists ){
enchant_slot->normal.enchants[enchantgrade] = enchants_for_enchantgrade;
}
}
}
if( this->nodeExists( slotNode, "PerfectEnchants" ) ){
for( const ryml::NodeRef& enchantNode : slotNode["PerfectEnchants"] ){
std::string enchant_name;
if( !this->asString( enchantNode, "Item", enchant_name ) ){
return 0;
}
std::shared_ptr<item_data> enchant_item = item_db.search_aegisname( enchant_name.c_str() );
if( enchant_item == nullptr ){
this->invalidWarning( enchantNode["Item"], "Unknown item \"%s\".\n", enchant_name.c_str() );
return 0;
}
std::shared_ptr<s_item_enchant_perfect> enchant = util::umap_find( enchant_slot->perfect.enchants, enchant_item->nameid );
bool enchant_exists = enchant != nullptr;
if( !enchant_exists ){
enchant = std::make_shared<s_item_enchant_perfect>();
enchant->item_id = enchant_item->nameid;
}
if( this->nodeExists( enchantNode, "Price" ) ){
uint32 zeny;
if( !this->asUInt32( enchantNode, "Price", zeny ) ){
return 0;
}
if( zeny > MAX_ZENY ){
this->invalidWarning( enchantNode["Price"], "Price %u exceeds MAX_ZENY. Capping...\n", zeny );
zeny = MAX_ZENY;
}
enchant->zeny = zeny;
}else{
if( !slot_exists ){
enchant->zeny = 0;
}
}
if( !this->parseMaterials( enchantNode, enchant->materials ) ){
return 0;
}
if( !enchant_exists ){
enchant_slot->perfect.enchants[enchant->item_id] = enchant;
}
}
}
if( this->nodeExists( slotNode, "Upgrades" ) ){
for( const ryml::NodeRef& upgradeNode : slotNode["Upgrades"] ){
std::string enchant_name;
if( !this->asString( upgradeNode, "Enchant", enchant_name ) ){
return 0;
}
std::shared_ptr<item_data> enchant_item = item_db.search_aegisname( enchant_name.c_str() );
if( enchant_item == nullptr ){
this->invalidWarning( upgradeNode["Enchant"], "Unknown item \"%s\".\n", enchant_name.c_str() );
return 0;
}
std::shared_ptr<s_item_enchant_upgrade> enchant_upgrade = util::umap_find( enchant_slot->upgrade.enchants, enchant_item->nameid );
bool enchant_upgrade_exists = enchant_upgrade != nullptr;
if( !enchant_upgrade_exists ){
if( !this->nodesExist( upgradeNode, { "Upgrade" } ) ){
return 0;
}
enchant_upgrade = std::make_shared<s_item_enchant_upgrade>();
enchant_upgrade->enchant_item_id = enchant_item->nameid;
}
if( this->nodeExists( upgradeNode, "Upgrade" ) ){
std::string upgrade_name;
if( !this->asString( upgradeNode, "Upgrade", upgrade_name ) ){
return 0;
}
std::shared_ptr<item_data> upgrade_item = item_db.search_aegisname( upgrade_name.c_str() );
if( upgrade_item == nullptr ){
this->invalidWarning( upgradeNode["Upgrade"], "Unknown item \"%s\".\n", upgrade_name.c_str() );
return 0;
}
enchant_upgrade->upgrade_item_id = upgrade_item->nameid;
}
if( this->nodeExists( upgradeNode, "Price" ) ){
uint32 zeny;
if( !this->asUInt32( upgradeNode, "Price", zeny ) ){
return 0;
}
if( zeny > MAX_ZENY ){
this->invalidWarning( upgradeNode["Price"], "Price %u exceeds MAX_ZENY. Capping...\n", zeny );
zeny = MAX_ZENY;
}
enchant_upgrade->zeny = zeny;
}else{
if( !enchant_upgrade_exists ){
enchant_upgrade->zeny = 0;
}
}
if( !this->parseMaterials( upgradeNode, enchant_upgrade->materials ) ){
return 0;
}
if( !enchant_upgrade_exists ){
enchant_slot->upgrade.enchants[enchant_upgrade->enchant_item_id] = enchant_upgrade;
}
}
}
if( !slot_exists ){
enchant->slots[slot] = enchant_slot;
}
}
}
if( !exists ){
this->put( enchant->id, enchant );
}
return 1;
}
ItemEnchantDatabase item_enchant_db;
/*==========================================
* Finds up to N matches. Returns number of matches [Skotlex]
* @param *data
@ -3775,6 +4294,7 @@ static void itemdb_read(void) {
laphine_synthesis_db.load();
laphine_upgrade_db.load();
item_reform_db.load();
item_enchant_db.load();
if (battle_config.feature_roulette)
itemdb_parse_roulette_db();
@ -3842,6 +4362,7 @@ void do_final_itemdb(void) {
laphine_synthesis_db.clear();
laphine_upgrade_db.clear();
item_reform_db.clear();
item_enchant_db.clear();
if (battle_config.feature_roulette)
itemdb_roulette_free();
}

View File

@ -1417,6 +1417,76 @@ public:
extern ItemReformDatabase item_reform_db;
struct s_item_enchant_normal_sub{
t_itemid item_id;
uint32 chance;
};
struct s_item_enchant_normal{
uint16 enchantgrade;
std::unordered_map<t_itemid, std::shared_ptr<s_item_enchant_normal_sub>> enchants;
};
struct s_item_enchant_perfect{
t_itemid item_id;
uint32 zeny;
std::unordered_map<t_itemid, uint16> materials;
};
struct s_item_enchant_upgrade{
t_itemid enchant_item_id;
t_itemid upgrade_item_id;
uint32 zeny;
std::unordered_map<t_itemid, uint16> materials;
};
struct s_item_enchant_slot{
uint16 slot;
struct{
uint32 zeny;
std::unordered_map<t_itemid, uint16> materials;
uint32 chance;
std::unordered_map<uint16, uint32> enchantgradeChanceIncrease;
std::unordered_map<uint16, std::shared_ptr<s_item_enchant_normal>> enchants;
} normal;
struct{
std::unordered_map<t_itemid, std::shared_ptr<s_item_enchant_perfect>> enchants;
} perfect;
struct{
std::unordered_map<t_itemid, std::shared_ptr<s_item_enchant_upgrade>> enchants;
} upgrade;
};
struct s_item_enchant{
uint64 id;
std::vector<t_itemid> target_item_ids;
uint16 minimumRefine;
uint16 minimumEnchantgrade;
bool allowRandomOptions;
struct {
uint32 zeny;
std::unordered_map<t_itemid, uint16> materials;
uint32 chance;
} reset;
std::vector<uint16> order;
std::unordered_map<uint16, std::shared_ptr<s_item_enchant_slot>> slots;
};
class ItemEnchantDatabase : public TypesafeYamlDatabase<uint64, s_item_enchant>{
private:
bool parseMaterials( const ryml::NodeRef& node, std::unordered_map<t_itemid, uint16>& materials );
public:
ItemEnchantDatabase() : TypesafeYamlDatabase( "ITEM_ENCHANT_DB", 1 ){
}
const std::string getDefaultLocation();
uint64 parseBodyNode( const ryml::NodeRef& node );
};
extern ItemEnchantDatabase item_enchant_db;
uint16 itemdb_searchname_array(std::map<t_itemid, std::shared_ptr<item_data>> &data, uint16 size, const char *str);
struct item_data* itemdb_search(t_itemid nameid);
std::shared_ptr<item_data> itemdb_exists(t_itemid nameid);

View File

@ -334,6 +334,7 @@
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_cash_db.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_cash_db.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_combos.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_combos.yml')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_db.yml')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_enchant.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_enchant.yml')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_group_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_group_db.yml')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_noequip.txt" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_noequip.txt')" />
<Copy SourceFiles="$(SolutionDir)db\import-tmpl\item_randomopt_db.yml" DestinationFolder="$(SolutionDir)db\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)db\import\item_randomopt_db.yml')" />

View File

@ -279,6 +279,48 @@ struct PACKET_ZC_ITEM_REFORM_ACK{
uint8 result;
} __attribute__((packed));
struct PACKET_ZC_UI_OPEN_V3{
int16 packetType;
uint8 type;
uint64 data;
} __attribute__((packed));
struct PACKET_CZ_REQUEST_RANDOM_ENCHANT{
int16 packetType;
uint64 clientLuaIndex;
uint16 index;
} __attribute__((packed));
struct PACKET_CZ_REQUEST_PERFECT_ENCHANT{
int16 packetType;
uint64 clientLuaIndex;
uint16 index;
uint32 itemId;
} __attribute__((packed));
struct PACKET_CZ_REQUEST_UPGRADE_ENCHANT{
int16 packetType;
uint64 clientLuaIndex;
uint16 index;
uint16 slot;
} __attribute__((packed));
struct PACKET_CZ_REQUEST_RESET_ENCHANT{
int16 packetType;
uint64 clientLuaIndex;
uint16 index;
} __attribute__((packed));
struct PACKET_ZC_RESPONSE_ENCHANT{
int16 packetType;
uint32 messageId;
uint32 enchantItemId;
} __attribute__((packed));
struct PACKET_CZ_CLOSE_UI_ENCHANT{
int16 packetType;
} __attribute__((packed));
// NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
#if !defined( sun ) && ( !defined( __NETBSD__ ) || __NetBSD_Version__ >= 600000000 )
#pragma pack( pop )
@ -338,6 +380,13 @@ DEFINE_PACKET_HEADER(ZC_OPEN_REFORM_UI, 0x0b8f)
DEFINE_PACKET_HEADER(CZ_CLOSE_REFORM_UI, 0x0b90)
DEFINE_PACKET_HEADER(CZ_ITEM_REFORM, 0x0b91)
DEFINE_PACKET_HEADER(ZC_ITEM_REFORM_ACK, 0x0b92)
DEFINE_PACKET_HEADER(ZC_UI_OPEN_V3, 0x0b9a)
DEFINE_PACKET_HEADER(CZ_REQUEST_RANDOM_ENCHANT, 0x0b9b)
DEFINE_PACKET_HEADER(CZ_REQUEST_PERFECT_ENCHANT, 0x0b9c)
DEFINE_PACKET_HEADER(CZ_REQUEST_UPGRADE_ENCHANT, 0x0b9d)
DEFINE_PACKET_HEADER(CZ_REQUEST_RESET_ENCHANT, 0x0b9e)
DEFINE_PACKET_HEADER(ZC_RESPONSE_ENCHANT, 0x0b9f)
DEFINE_PACKET_HEADER(CZ_CLOSE_UI_ENCHANT, 0x0ba0)
const int16 MAX_INVENTORY_ITEM_PACKET_NORMAL = ( ( INT16_MAX - ( sizeof( struct packet_itemlist_normal ) - ( sizeof( struct NORMALITEM_INFO ) * MAX_ITEMLIST) ) ) / sizeof( struct NORMALITEM_INFO ) );
const int16 MAX_INVENTORY_ITEM_PACKET_EQUIP = ( ( INT16_MAX - ( sizeof( struct packet_itemlist_equip ) - ( sizeof( struct EQUIPITEM_INFO ) * MAX_ITEMLIST ) ) ) / sizeof( struct EQUIPITEM_INFO ) );

View File

@ -396,6 +396,7 @@ struct map_session_data {
t_itemid laphine_upgrade;
bool roulette_open;
t_itemid item_reform;
uint64 item_enchant_index;
} state;
struct {
unsigned char no_weapon_damage, no_magic_damage, no_misc_damage;
@ -1070,7 +1071,7 @@ static bool pc_cant_act2( struct map_session_data* sd ){
|| sd->state.barter_open || sd->state.barter_extended_open
|| sd->state.laphine_synthesis || sd->state.laphine_upgrade
|| sd->state.roulette_open || sd->state.enchantgrade_open
|| sd->state.item_reform;
|| sd->state.item_reform || sd->state.item_enchant_index;
}
// equals pc_cant_act2 and additionally checks for chat rooms and npcs
static bool pc_cant_act( struct map_session_data* sd ){

View File

@ -26393,6 +26393,30 @@ BUILDIN_FUNC(item_reform){
#endif
}
BUILDIN_FUNC(item_enchant){
#if PACKETVER_RE_NUM < 20211103
ShowError( "buildin_item_enchant: This command requires packet version 2021-11-03 or newer.\n" );
return SCRIPT_CMD_FAILURE;
#else
struct map_session_data* sd;
if( !script_charid2sd( 3, sd ) ){
return SCRIPT_CMD_FAILURE;
}
uint64 clientLuaIndex = script_getnum64( st, 2 );
if( !item_enchant_db.exists( clientLuaIndex ) ){
ShowError( "buildin_item_enchant: %" PRIu64 " is not a valid item enchant index.\n", clientLuaIndex );
return SCRIPT_CMD_FAILURE;
}
clif_enchantwindow_open( *sd, clientLuaIndex );
return SCRIPT_CMD_SUCCESS;
#endif
}
#include "../custom/script.inc"
// declarations that were supposed to be exported from npc_chat.cpp
@ -27130,6 +27154,7 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(set_reputation_points, "ii?"),
BUILDIN_DEF(get_reputation_points, "i?"),
BUILDIN_DEF(item_reform, "??"),
BUILDIN_DEF(item_enchant, "i?"),
#include "../custom/script_def.inc"
{NULL,NULL,NULL},