diff --git a/conf/battle/items.conf b/conf/battle/items.conf index c995c1baea..448d1af505 100644 --- a/conf/battle/items.conf +++ b/conf/battle/items.conf @@ -12,8 +12,10 @@ // The highest value at which an item can be sold via the merchant vend skill. (in zeny) vending_max_value: 1000000000 -// Whether to allow buying from vending chars that are at their max. zeny limit. -// If set to yes, the rest of the zeny above the char's capacity will disappear. +// Whether to allow placing items on a vending store when the player's zeny plus the total price +// of the items exceeds the maximum zeny allowed. (Note 1) +// If set to "yes", the items will be placed in the store but other players will not be able to buy them. +// Official behavior is "yes", but on some official servers the client doesn't allow this. vending_over_max: yes // Tax to apply to all vending transactions (eg: 10000 = 100%, 50 = 0.50%) diff --git a/src/map/clif.cpp b/src/map/clif.cpp index c127953f7f..4fad10c048 100644 --- a/src/map/clif.cpp +++ b/src/map/clif.cpp @@ -7674,15 +7674,12 @@ void clif_buyvending( map_session_data& sd, uint16 index, uint16 amount, e_pc_pu /// Show's vending player its list of items for sale. /// 0a28 .B (ZC_ACK_OPENSTORE2) -/// result: -/// 0 = Success -/// 1 = Failed -void clif_openvending_ack( map_session_data& sd, bool failure ){ +void clif_openvending_ack( map_session_data& sd, e_ack_openstore2 result ){ #if PACKETVER >= 20141022 PACKET_ZC_ACK_OPENSTORE2 packet{}; packet.packetType = HEADER_ZC_ACK_OPENSTORE2; - packet.result = failure; + packet.result = static_cast(result); clif_send( &packet, sizeof( packet ), &sd.bl, SELF ); #endif @@ -7722,7 +7719,7 @@ void clif_openvending( map_session_data& sd ){ clif_send( p, p->packetLength, &sd.bl, SELF ); - clif_openvending_ack( sd, false ); + clif_openvending_ack( sd, OPENSTORE2_SUCCESS ); } diff --git a/src/map/clif.hpp b/src/map/clif.hpp index ec4c6c9467..7ef89d1703 100644 --- a/src/map/clif.hpp +++ b/src/map/clif.hpp @@ -639,6 +639,9 @@ enum clif_messages : uint16_t { // Currently there is no attendance check event. MSI_CHECK_ATTENDANCE_NOT_EVENT = 3474, + // The total amount of items to sell exceeds the amount of Zeny you can have. \nPlease modify the quantity and price. + MSI_MERCHANTSHOP_TOTA_LOVER_ZENY_ERR = 3826, + // It weighs more than 70%. Decrease the Weight and try again. MSI_ENCHANT_FAILED_OVER_WEIGHT = 3837, @@ -784,6 +787,21 @@ enum e_pc_purchase_result_frommc : uint8 { PURCHASEMC_NO_SALES_INFO = 7, }; +enum e_ack_openstore2 : uint8 { + // Success + OPENSTORE2_SUCCESS = 0, + + // (Pop-up) Failed to open stalls. (MSI_MERCHANTSHOP_MAKING_FAIL / 2639) + OPENSTORE2_FAILED = 1, + + // 2 is unused + +#if PACKETVER >= 20170419 + // Unable to open a shop at the current location. (MSI_MERCHANTSHOP_FAIL_POSITION / 3229) + OPENSTORE2_NOVENDING = 3, +#endif +}; + enum e_ack_whisper : uint8 { ACKWHISPER_SUCCESS = 0, ACKWHISPER_TARGET_OFFLINE = 1, @@ -998,6 +1016,7 @@ void clif_closevendingboard(struct block_list* bl, int fd); void clif_vendinglist( map_session_data& sd, map_session_data& vsd ); void clif_buyvending( map_session_data& sd, uint16 index, uint16 amount, e_pc_purchase_result_frommc result ); void clif_openvending( map_session_data& sd ); +void clif_openvending_ack(map_session_data& sd, e_ack_openstore2 result); void clif_vendingreport( map_session_data& sd, uint16 index, uint16 amount, uint32 char_id, int32 zeny ); void clif_movetoattack( map_session_data& sd, block_list& bl ); diff --git a/src/map/vending.cpp b/src/map/vending.cpp index 39e76ae598..eb8fbedb5c 100755 --- a/src/map/vending.cpp +++ b/src/map/vending.cpp @@ -175,7 +175,7 @@ void vending_purchasereq(map_session_data* sd, int aid, int uid, const uint8* da clif_buyvending( *sd, idx, amount, PURCHASEMC_NO_ZENY ); // you don't have enough zeny return; } - if( z + (double)vsd->status.zeny > (double)MAX_ZENY && !battle_config.vending_over_max ) { + if( z + (double)vsd->status.zeny > (double)MAX_ZENY ) { clif_buyvending( *sd, idx, vsd->vending[j].amount, PURCHASEMC_OUT_OF_STOCK ); // too much zeny = overflow return; @@ -310,12 +310,18 @@ int8 vending_openvending( map_session_data& sd, const char* message, const uint8 // skill level and cart check if( !vending_skill_lvl || !pc_iscarton(&sd) ) { clif_skill_fail( sd, MC_VENDING ); + sd.state.prevend = 0; + sd.state.workinprogress = WIP_DISABLE_NONE; + clif_openvending_ack( sd, OPENSTORE2_FAILED ); return 2; } // check number of items in shop if( count < 1 || count > MAX_VENDING || count > 2 + vending_skill_lvl ) { // invalid item count clif_skill_fail( sd, MC_VENDING ); + sd.state.prevend = 0; + sd.state.workinprogress = WIP_DISABLE_NONE; + clif_openvending_ack( sd, OPENSTORE2_FAILED ); return 3; } @@ -324,6 +330,7 @@ int8 vending_openvending( map_session_data& sd, const char* message, const uint8 // filter out invalid items i = 0; + int64 total = 0; for( j = 0; j < count; j++ ) { short index = *(uint16*)(data + 8*j + 0); short amount = *(uint16*)(data + 8*j + 2); @@ -344,17 +351,36 @@ int8 vending_openvending( map_session_data& sd, const char* message, const uint8 sd.vending[i].index = index; sd.vending[i].amount = amount; sd.vending[i].value = min(value, (unsigned int)battle_config.vending_max_value); + total += static_cast(sd.vending[i].value) * amount; i++; // item successfully added } + // check if the total value of the items plus the current zeny is over the limit + if ( !battle_config.vending_over_max && (static_cast(sd.status.zeny) + total) > MAX_ZENY ) { +#if PACKETVER >= 20200819 + clif_msg_color( &sd, MSI_MERCHANTSHOP_TOTA_LOVER_ZENY_ERR, color_table[COLOR_RED] ); +#endif + clif_skill_fail( sd, MC_VENDING ); + sd.state.prevend = 0; + sd.state.workinprogress = WIP_DISABLE_NONE; + clif_openvending_ack( sd, OPENSTORE2_FAILED ); + return 1; + } + if (i != j) { clif_displaymessage(sd.fd, msg_txt(&sd, 266)); //"Some of your items cannot be vended and were removed from the shop." clif_skill_fail( sd, MC_VENDING ); // custom reply packet + sd.state.prevend = 0; + sd.state.workinprogress = WIP_DISABLE_NONE; + clif_openvending_ack( sd, OPENSTORE2_FAILED ); return 5; } if( i == 0 ) { // no valid item found clif_skill_fail( sd, MC_VENDING ); // custom reply packet + sd.state.prevend = 0; + sd.state.workinprogress = WIP_DISABLE_NONE; + clif_openvending_ack( sd, OPENSTORE2_FAILED ); return 5; }