Initial implementation of enchantgrade UI (#6913)
Includes walkscript conversion of kRO scripts Thanks to @Asheraf, @Balferian, @JohnnyPlayy, @aleos89, @Atemo, @eppc0330 and @Pokye. Co-authored-by: JohnnyPlayy <lenon32@gmail.com> Co-authored-by: Asheraf <Asheraf@users.noreply.github.com> Co-authored-by: Balferian <balfear@yandex.ru> Co-authored-by: Aleos <aleos89@users.noreply.github.com> Co-authored-by: Atemo <Atemo@users.noreply.github.com>
This commit is contained in:
363
src/map/clif.cpp
363
src/map/clif.cpp
@@ -21551,23 +21551,32 @@ void clif_parse_changedress( int fd, struct map_session_data* sd ){
|
||||
|
||||
/// Opens an UI window of the given type and initializes it with the given data
|
||||
/// 0AE2 <type>.B <data>.L
|
||||
void clif_ui_open( struct map_session_data *sd, enum out_ui_type ui_type, int32 data ){
|
||||
nullpo_retv(sd);
|
||||
|
||||
void clif_ui_open( struct map_session_data& sd, enum out_ui_type ui_type, int32 data ){
|
||||
#if PACKETVER >= 20151202
|
||||
// If the UI requires state tracking
|
||||
switch( ui_type ){
|
||||
case OUT_UI_STYLIST:
|
||||
sd->state.stylist_open = true;
|
||||
sd.state.stylist_open = true;
|
||||
break;
|
||||
case OUT_UI_ENCHANTGRADE:
|
||||
#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
|
||||
sd.state.enchantgrade_open = true;
|
||||
break;
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
int fd = sd->fd;
|
||||
struct PACKET_ZC_UI_OPEN p = {};
|
||||
|
||||
WFIFOHEAD(fd,packet_len(0xae2));
|
||||
WFIFOW(fd,0) = 0xae2;
|
||||
WFIFOB(fd,2) = ui_type;
|
||||
WFIFOL(fd,3) = data;
|
||||
WFIFOSET(fd,packet_len(0xae2));
|
||||
p.PacketType = HEADER_ZC_UI_OPEN;
|
||||
p.UIType = ui_type;
|
||||
#if PACKETVER >= 20171122
|
||||
p.data = data;
|
||||
#endif
|
||||
|
||||
clif_send( &p, sizeof( p ), &sd.bl, SELF );
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Request to open an UI window of the given type
|
||||
@@ -21578,7 +21587,7 @@ void clif_parse_open_ui( int fd, struct map_session_data* sd ){
|
||||
if( !pc_has_permission( sd, PC_PERM_ATTENDANCE ) ){
|
||||
clif_messagecolor( &sd->bl, color_table[COLOR_RED], msg_txt( sd, 791 ), false, SELF ); // You are not allowed to use the attendance system.
|
||||
}else if( pc_attendance_enabled() ){
|
||||
clif_ui_open( sd, OUT_UI_ATTENDANCE, pc_attendance_counter( sd ) );
|
||||
clif_ui_open( *sd, OUT_UI_ATTENDANCE, pc_attendance_counter( sd ) );
|
||||
}else{
|
||||
clif_msg_color( sd, MSG_ATTENDANCE_DISABLED, color_table[COLOR_RED] );
|
||||
}
|
||||
@@ -23454,6 +23463,338 @@ void clif_parse_laphine_upgrade( int fd, struct map_session_data* sd ){
|
||||
#endif
|
||||
}
|
||||
|
||||
void clif_enchantgrade_add( struct map_session_data& sd, uint16 index = UINT16_MAX, std::shared_ptr<s_enchantgradelevel> gradeLevel = nullptr ){
|
||||
#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
|
||||
struct PACKET_ZC_GRADE_ENCHANT_MATERIAL_LIST* p = (struct PACKET_ZC_GRADE_ENCHANT_MATERIAL_LIST*)packet_buffer;
|
||||
|
||||
p->PacketType = HEADER_ZC_GRADE_ENCHANT_MATERIAL_LIST;
|
||||
p->PacketLength = sizeof( struct PACKET_ZC_GRADE_ENCHANT_MATERIAL_LIST );
|
||||
|
||||
if( index < UINT16_MAX ){
|
||||
p->index = client_index( index );
|
||||
if( sd.inventory.u.items_inventory[index].refine >= gradeLevel->refine ){
|
||||
p->success_chance = gradeLevel->chance / 100;
|
||||
}else{
|
||||
p->success_chance = 0;
|
||||
}
|
||||
p->blessing_info.id = client_nameid( gradeLevel->catalyst.item );
|
||||
p->blessing_info.amount = gradeLevel->catalyst.amountPerStep;
|
||||
p->blessing_info.max_blessing = gradeLevel->catalyst.maximumSteps;
|
||||
p->blessing_info.bonus = gradeLevel->catalyst.chanceIncrease / 100;
|
||||
// Not displayed by client
|
||||
p->protect_itemid = 0;
|
||||
p->protect_amount = 0;
|
||||
|
||||
int i = 0;
|
||||
for( const auto& pair : gradeLevel->options ){
|
||||
std::shared_ptr<s_enchantgradeoption> option = pair.second;
|
||||
|
||||
p->material_info[i].nameid = client_nameid( option->item );
|
||||
p->material_info[i].amount = option->amount;
|
||||
p->material_info[i].price = option->zeny;
|
||||
p->material_info[i].downgrade = option->downgrade_amount > 0;
|
||||
p->material_info[i].breakable = option->breaking_rate > 0;
|
||||
|
||||
p->PacketLength += sizeof( struct GRADE_ENCHANT_MATERIAL );
|
||||
i++;
|
||||
}
|
||||
}else{
|
||||
p->index = -1;
|
||||
p->success_chance = 0;
|
||||
p->blessing_info.id = 0;
|
||||
p->blessing_info.amount = 0;
|
||||
p->blessing_info.max_blessing = 0;
|
||||
p->blessing_info.bonus = 0;
|
||||
p->protect_itemid = 0;
|
||||
p->protect_amount = 0;
|
||||
}
|
||||
|
||||
clif_send( p, p->PacketLength, &sd.bl, SELF );
|
||||
#endif
|
||||
}
|
||||
|
||||
void clif_parse_enchantgrade_add( int fd, struct map_session_data* sd ){
|
||||
#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
|
||||
nullpo_retv( sd );
|
||||
|
||||
if( !sd->state.enchantgrade_open ){
|
||||
return;
|
||||
}
|
||||
|
||||
struct PACKET_CZ_GRADE_ENCHANT_SELECT_EQUIPMENT* p = (struct PACKET_CZ_GRADE_ENCHANT_SELECT_EQUIPMENT*)RFIFOP( fd, 0 );
|
||||
|
||||
uint16 index = server_index( p->index );
|
||||
|
||||
if( index >= MAX_INVENTORY || sd->inventory_data[index] == nullptr ){
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<s_enchantgrade> enchantgrade = enchantgrade_db.find( sd->inventory_data[index]->type );
|
||||
|
||||
// Unsupported item type - no answer, because client should have actually prevented this request
|
||||
if( enchantgrade == nullptr ){
|
||||
return;
|
||||
}
|
||||
|
||||
uint16 level = 0;
|
||||
|
||||
if( sd->inventory_data[index]->type == IT_WEAPON ){
|
||||
level = sd->inventory_data[index]->weapon_level;
|
||||
}else if( sd->inventory_data[index]->type == IT_ARMOR ){
|
||||
level = sd->inventory_data[index]->armor_level;
|
||||
}
|
||||
|
||||
const auto& enchantgradelevels = enchantgrade->levels.find( level );
|
||||
|
||||
// Cannot upgrade this weapon or armor level
|
||||
if( enchantgradelevels == enchantgrade->levels.end() ){
|
||||
clif_enchantgrade_add( *sd );
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<s_enchantgradelevel> enchantgradelevel = util::map_find( enchantgradelevels->second, (e_enchantgrade)sd->inventory.u.items_inventory[index].enchantgrade );
|
||||
|
||||
// Cannot increase enchantgrade any further - no answer, because client should have actually prevented this request
|
||||
if( enchantgradelevel == nullptr ){
|
||||
return;
|
||||
}
|
||||
|
||||
clif_enchantgrade_add( *sd, index, enchantgradelevel );
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the result for trying to enchant an item
|
||||
/// </summary>
|
||||
/// <param name="sd">The player session</param>
|
||||
/// <param name="index">The target item</param>
|
||||
/// <param name="result">
|
||||
/// 0= The grade has been successfully upgraded.
|
||||
/// 1= Refinement failed.
|
||||
/// 2= The refine level has decreased.
|
||||
/// 3= Equipment destroyed.
|
||||
/// 4= The equipment is protected.
|
||||
/// </param>
|
||||
void clif_enchantgrade_result( struct map_session_data& sd, uint16 index, e_enchantgrade_result result ){
|
||||
#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
|
||||
struct PACKET_ZC_GRADE_ENCHANT_ACK p = {};
|
||||
|
||||
p.PacketType = HEADER_ZC_GRADE_ENCHANT_ACK;
|
||||
p.index = client_index( index );
|
||||
p.enchantgrade = sd.inventory.u.items_inventory[index].enchantgrade;
|
||||
p.result = result;
|
||||
|
||||
clif_send( &p, sizeof( p ), &sd.bl, SELF );
|
||||
#endif
|
||||
}
|
||||
|
||||
void clif_enchantgrade_announce( struct map_session_data& sd, struct item& item, bool success ){
|
||||
#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
|
||||
struct PACKET_ZC_GRADE_ENCHANT_BROADCAST_RESULT p = {};
|
||||
|
||||
p.packetType = HEADER_ZC_GRADE_ENCHANT_BROADCAST_RESULT;
|
||||
safestrncpy( p.name, sd.status.name, sizeof( p.name ) );
|
||||
p.itemId = client_nameid( item.nameid );
|
||||
p.enchantgrade = item.enchantgrade;
|
||||
p.status = success;
|
||||
|
||||
clif_send( &p, sizeof( p ), nullptr, ALL_CLIENT );
|
||||
#endif
|
||||
}
|
||||
|
||||
void clif_parse_enchantgrade_start( int fd, struct map_session_data* sd ){
|
||||
#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
|
||||
nullpo_retv( sd );
|
||||
|
||||
if( !sd->state.enchantgrade_open ){
|
||||
return;
|
||||
}
|
||||
|
||||
struct PACKET_CZ_GRADE_ENCHANT_REQUEST* p = (struct PACKET_CZ_GRADE_ENCHANT_REQUEST*)RFIFOP( fd, 0 );
|
||||
|
||||
uint16 index = server_index( p->index );
|
||||
|
||||
if( index >= MAX_INVENTORY || sd->inventory_data[index] == nullptr ){
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<s_enchantgrade> enchantgrade = enchantgrade_db.find( sd->inventory_data[index]->type );
|
||||
|
||||
// Unsupported item type - no answer
|
||||
if( enchantgrade == nullptr ){
|
||||
return;
|
||||
}
|
||||
|
||||
uint16 level = 0;
|
||||
|
||||
if( sd->inventory_data[index]->type == IT_WEAPON ){
|
||||
level = sd->inventory_data[index]->weapon_level;
|
||||
}else if( sd->inventory_data[index]->type == IT_ARMOR ){
|
||||
level = sd->inventory_data[index]->armor_level;
|
||||
}
|
||||
|
||||
const auto& enchantgradelevels = enchantgrade->levels.find( level );
|
||||
|
||||
// Cannot upgrade this weapon or armor level - no answer
|
||||
if( enchantgradelevels == enchantgrade->levels.end() ){
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<s_enchantgradelevel> enchantgradelevel = util::map_find( enchantgradelevels->second, (e_enchantgrade)sd->inventory.u.items_inventory[index].enchantgrade );
|
||||
|
||||
// Cannot increase enchantgrade any further - no answer
|
||||
if( enchantgradelevel == nullptr ){
|
||||
return;
|
||||
}
|
||||
|
||||
// Not refined enough
|
||||
if( sd->inventory.u.items_inventory[index].refine < enchantgradelevel->refine ){
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<s_enchantgradeoption> option = util::map_find( enchantgradelevel->options, (uint16)p->material_index );
|
||||
|
||||
// Unknown option id - no answer
|
||||
if( option == nullptr ){
|
||||
return;
|
||||
}
|
||||
|
||||
// Not enough zeny
|
||||
if( sd->status.zeny < option->zeny ){
|
||||
return;
|
||||
}
|
||||
|
||||
uint16 totalChance = enchantgradelevel->chance;
|
||||
uint16 steps = min( p->blessing_amount, enchantgradelevel->catalyst.maximumSteps );
|
||||
std::unordered_map<uint16, uint16> requiredItems;
|
||||
|
||||
if( p->blessing_flag ){
|
||||
// If the catalysator item is the same as the option item build the sum of amounts
|
||||
if( enchantgradelevel->catalyst.item == option->item ){
|
||||
uint16 amount = enchantgradelevel->catalyst.amountPerStep * steps + option->amount;
|
||||
|
||||
int16 index = pc_search_inventory( sd, enchantgradelevel->catalyst.item );
|
||||
|
||||
if( index < 0 ){
|
||||
return;
|
||||
}
|
||||
|
||||
if( sd->inventory.u.items_inventory[index].amount < amount ){
|
||||
return;
|
||||
}
|
||||
|
||||
requiredItems[index] = amount;
|
||||
}else{
|
||||
uint16 amount = enchantgradelevel->catalyst.amountPerStep * steps;
|
||||
|
||||
// Check catalysator item
|
||||
int16 index = pc_search_inventory( sd, enchantgradelevel->catalyst.item );
|
||||
|
||||
if( index < 0 ){
|
||||
return;
|
||||
}
|
||||
|
||||
if( sd->inventory.u.items_inventory[index].amount < amount ){
|
||||
return;
|
||||
}
|
||||
|
||||
requiredItems[index] = amount;
|
||||
|
||||
// Check option item
|
||||
index = pc_search_inventory( sd, option->item );
|
||||
|
||||
if( index < 0 ){
|
||||
return;
|
||||
}
|
||||
|
||||
if( sd->inventory.u.items_inventory[index].amount < option->amount ){
|
||||
return;
|
||||
}
|
||||
|
||||
requiredItems[index] = option->amount;
|
||||
}
|
||||
|
||||
totalChance += steps * enchantgradelevel->catalyst.chanceIncrease;
|
||||
}else{
|
||||
// Check option item
|
||||
int16 index = pc_search_inventory( sd, option->item );
|
||||
|
||||
if( index < 0 ){
|
||||
return;
|
||||
}
|
||||
|
||||
if( sd->inventory.u.items_inventory[index].amount < option->amount ){
|
||||
return;
|
||||
}
|
||||
|
||||
requiredItems[index] = option->amount;
|
||||
}
|
||||
|
||||
// All items should be there, start deleting
|
||||
for( const auto& pair : requiredItems ){
|
||||
if( pc_delitem( sd, pair.first, pair.second, 0, 0, LOG_TYPE_ENCHANTGRADE ) != 0 ){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( pc_payzeny( sd, option->zeny, LOG_TYPE_ENCHANTGRADE, nullptr ) > 0 ){
|
||||
return;
|
||||
}
|
||||
|
||||
if( rnd()%10000 < totalChance ){
|
||||
// Log removal of item
|
||||
log_pick_pc( sd, LOG_TYPE_ENCHANTGRADE, -1, &sd->inventory.u.items_inventory[index] );
|
||||
// Increase enchantgrade
|
||||
sd->inventory.u.items_inventory[index].enchantgrade = min( sd->inventory.u.items_inventory[index].enchantgrade + 1, MAX_ENCHANTGRADE );
|
||||
// On successful enchantgrade increase the refine is reset
|
||||
sd->inventory.u.items_inventory[index].refine = 0;
|
||||
// Log retrieving the item again -> with the new refine and enchantgrade
|
||||
log_pick_pc( sd, LOG_TYPE_ENCHANTGRADE, 1, &sd->inventory.u.items_inventory[index] );
|
||||
// Show success
|
||||
clif_enchantgrade_result( *sd, index, ENCHANTGRADE_UPGRADE_SUCCESS );
|
||||
|
||||
// Check if it has to be announced
|
||||
if( enchantgradelevel->announce ){
|
||||
clif_enchantgrade_announce( *sd, sd->inventory.u.items_inventory[index], true );
|
||||
}
|
||||
}else{
|
||||
// Check if it has to be announced (has to be done before deleting the item from inventory)
|
||||
if( enchantgradelevel->announce ){
|
||||
clif_enchantgrade_announce( *sd, sd->inventory.u.items_inventory[index], false );
|
||||
}
|
||||
|
||||
// Delete the item if it is breakable
|
||||
if( option->breaking_rate > 0 && ( rnd() % 10000 ) < option->breaking_rate ){
|
||||
// Delete the item
|
||||
pc_delitem( sd, index, 1, 0, 0, LOG_TYPE_ENCHANTGRADE );
|
||||
// Show failure
|
||||
clif_enchantgrade_result( *sd, index, ENCHANTGRADE_UPGRADE_BREAK );
|
||||
// Downgrade the item if necessary
|
||||
}else if( option->downgrade_amount > 0 ){
|
||||
// Log removal of item
|
||||
log_pick_pc( sd, LOG_TYPE_ENCHANTGRADE, -1, &sd->inventory.u.items_inventory[index] );
|
||||
// Decrease refine level
|
||||
sd->inventory.u.items_inventory[index].refine = cap_value( sd->inventory.u.items_inventory[index].refine - option->downgrade_amount, 0, MAX_REFINE );
|
||||
// Log retrieving the item again -> with the new refine
|
||||
log_pick_pc( sd, LOG_TYPE_ENCHANTGRADE, 1, &sd->inventory.u.items_inventory[index] );
|
||||
// Show downgrade
|
||||
clif_enchantgrade_result( *sd, index, ENCHANTGRADE_UPGRADE_DOWNGRADE );
|
||||
// Only show failure, but dont do anything
|
||||
}else{
|
||||
clif_enchantgrade_result( *sd, index, ENCHANTGRADE_UPGRADE_FAILED );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void clif_parse_enchantgrade_close( int fd, struct map_session_data* sd ){
|
||||
#if PACKETVER_MAIN_NUM >= 20200916 || PACKETVER_RE_NUM >= 20200724
|
||||
nullpo_retv( sd );
|
||||
|
||||
sd->state.enchantgrade_open = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*==========================================
|
||||
* Main client packet processing function
|
||||
*------------------------------------------*/
|
||||
|
||||
Reference in New Issue
Block a user