Initial release of item enchant UI (#7186)
Thanks to @aleos89, @Atemo @idk-whoami
This commit is contained in:
parent
3c43669425
commit
8629c6445a
@ -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
|
||||
|
72
db/import-tmpl/item_enchant.yml
Normal file
72
db/import-tmpl/item_enchant.yml
Normal 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
78
db/item_enchant.yml
Normal 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
|
@ -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
18096
db/re/item_enchant.yml
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
526
src/map/clif.cpp
526
src/map/clif.cpp
@ -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
|
||||
*------------------------------------------*/
|
||||
|
@ -553,8 +553,11 @@ enum clif_messages : uint16_t {
|
||||
MSG_ATTENDANCE_DISABLED = 0xd92,
|
||||
|
||||
// Unofficial names
|
||||
C_ITEM_EQUIP_SWITCH = 0xbc7,
|
||||
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 */
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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')" />
|
||||
|
@ -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 ) );
|
||||
|
@ -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 ){
|
||||
|
@ -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},
|
||||
|
Loading…
x
Reference in New Issue
Block a user