|
@@ -2,6 +2,9 @@
|
|
|
// For more information, see LICENCE in the main folder
|
|
|
|
|
|
#include "vending.hpp"
|
|
|
+
|
|
|
+#include <stdlib.h> // atoi
|
|
|
+
|
|
|
#include "../common/nullpo.h"
|
|
|
#include "../common/malloc.h" // aMalloc, aFree
|
|
|
#include "../common/showmsg.h" // ShowInfo
|
|
@@ -21,8 +24,7 @@
|
|
|
#include "battle.hpp"
|
|
|
#include "log.hpp"
|
|
|
#include "achievement.hpp"
|
|
|
-
|
|
|
-#include <stdlib.h> // atoi
|
|
|
+#include "tax.hpp"
|
|
|
|
|
|
static uint32 vending_nextid = 0; ///Vending_id counter
|
|
|
static DBMap *vending_db; ///DB holder the vender : charid -> map_session_data
|
|
@@ -96,18 +98,36 @@ void vending_vendinglistreq(struct map_session_data* sd, int id)
|
|
|
clif_vendinglist(sd, id, vsd->vending);
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * Calculates taxes for vending
|
|
|
- * @param sd: Vender
|
|
|
- * @param zeny: Total amount to tax
|
|
|
- * @return Total amount after taxes
|
|
|
- */
|
|
|
-static double vending_calc_tax(struct map_session_data *sd, double zeny)
|
|
|
-{
|
|
|
- if (battle_config.vending_tax && zeny >= battle_config.vending_tax_min)
|
|
|
- zeny -= zeny * (battle_config.vending_tax / 10000.);
|
|
|
+static unsigned short vending_tax_intotal(struct map_session_data* vsd, const uint8* data, int count) {
|
|
|
+ s_tax *tax = tax_get(TAX_SELLING);
|
|
|
+ double total = 0;
|
|
|
+ int i, vend_list[MAX_VENDING]; // against duplicate packets
|
|
|
+
|
|
|
+ if (tax->total.size() == 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ for (i = 0; i < count; i++) {
|
|
|
+ short amount = *(uint16*)(data + 4 * i + 0);
|
|
|
+ short idx = *(uint16*)(data + 4 * i + 2), j;
|
|
|
+ idx -= 2;
|
|
|
+
|
|
|
+ if (amount <= 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ // check of item index in the cart
|
|
|
+ if (idx < 0 || idx >= MAX_CART)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ ARR_FIND(0, vsd->vend_num, j, vsd->vending[j].index == idx);
|
|
|
+ if (j == vsd->vend_num)
|
|
|
+ continue; //picked non-existing item
|
|
|
+ else
|
|
|
+ vend_list[i] = j;
|
|
|
+
|
|
|
+ total += ((double)vsd->vending[j].value * amount);
|
|
|
+ }
|
|
|
|
|
|
- return zeny;
|
|
|
+ return tax->get_tax(tax->total, total);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -122,7 +142,7 @@ static double vending_calc_tax(struct map_session_data *sd, double zeny)
|
|
|
void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const uint8* data, int count)
|
|
|
{
|
|
|
int i, j, cursor, w, new_ = 0, blank, vend_list[MAX_VENDING];
|
|
|
- double z;
|
|
|
+ double z, z_gained = 0;
|
|
|
struct s_vending vending[MAX_VENDING]; // against duplicate packets
|
|
|
struct map_session_data* vsd = map_id2sd(aid);
|
|
|
|
|
@@ -131,7 +151,7 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui
|
|
|
return; // invalid shop
|
|
|
|
|
|
if( vsd->vender_id != uid ) { // shop has changed
|
|
|
- clif_buyvending(sd, 0, 0, 6); // store information was incorrect
|
|
|
+ clif_buyvending(sd, 0, 0, VENDING_ACK_INVALID); // store information was incorrect
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -151,6 +171,7 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui
|
|
|
// some checks
|
|
|
z = 0.; // zeny counter
|
|
|
w = 0; // weight counter
|
|
|
+ int tax_total = vending_tax_intotal(vsd, data, count);
|
|
|
for( i = 0; i < count; i++ ) {
|
|
|
short amount = *(uint16*)(data + 4*i + 0);
|
|
|
short idx = *(uint16*)(data + 4*i + 2);
|
|
@@ -170,18 +191,20 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui
|
|
|
vend_list[i] = j;
|
|
|
|
|
|
z += ((double)vsd->vending[j].value * (double)amount);
|
|
|
+ z_gained += ((double)vsd->vending[j].value_vat * (double)amount);
|
|
|
+ z_gained = z_gained - (z_gained / 10000 * tax_total);
|
|
|
if( z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY ) {
|
|
|
- clif_buyvending(sd, idx, amount, 1); // you don't have enough zeny
|
|
|
+ clif_buyvending(sd, idx, amount, VENDING_ACK_NOZENY); // you don't have enough zeny
|
|
|
return;
|
|
|
}
|
|
|
- if( z + (double)vsd->status.zeny > (double)MAX_ZENY && !battle_config.vending_over_max ) {
|
|
|
- clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow
|
|
|
+ if(z_gained + (double)vsd->status.zeny > (double)MAX_ZENY && !battle_config.vending_over_max ) {
|
|
|
+ clif_buyvending(sd, idx, vsd->vending[j].amount, VENDING_ACK_NOSTOCK); // too much zeny = overflow
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
w += itemdb_weight(vsd->cart.u.items_cart[idx].nameid) * amount;
|
|
|
if( w + sd->weight > sd->max_weight ) {
|
|
|
- clif_buyvending(sd, idx, amount, 2); // you can not buy, because overweight
|
|
|
+ clif_buyvending(sd, idx, amount, VENDING_ACK_OVERWEIGHT); // you can not buy, because overweight
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -193,7 +216,7 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui
|
|
|
// here, we check cumulative amounts
|
|
|
if( vending[j].amount < amount ) {
|
|
|
// send more quantity is not a hack (an other player can have buy items just before)
|
|
|
- clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // not enough quantity
|
|
|
+ clif_buyvending(sd, idx, vsd->vending[j].amount, VENDING_ACK_NOSTOCK); // not enough quantity
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -214,19 +237,30 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui
|
|
|
|
|
|
pc_payzeny(sd, (int)z, LOG_TYPE_VENDING, vsd);
|
|
|
achievement_update_objective(sd, AG_SPEND_ZENY, 1, (int)z);
|
|
|
- z = vending_calc_tax(sd, z);
|
|
|
- pc_getzeny(vsd, (int)z, LOG_TYPE_VENDING, sd);
|
|
|
+ pc_getzeny(vsd, (int)z_gained, LOG_TYPE_VENDING, sd);
|
|
|
+
|
|
|
+ if (battle_config.etc_log) {
|
|
|
+ ShowInfo("vending_purchasereq: AID=%u CID=%u gained %u zeny. AID=%u CID=%u paid %u zeny\n",
|
|
|
+ vsd->status.account_id, vsd->status.char_id, (size_t)z_gained,
|
|
|
+ sd->status.account_id, sd->status.char_id, (size_t)z);
|
|
|
+ }
|
|
|
|
|
|
for( i = 0; i < count; i++ ) {
|
|
|
short amount = *(uint16*)(data + 4*i + 0);
|
|
|
short idx = *(uint16*)(data + 4*i + 2);
|
|
|
idx -= 2;
|
|
|
- z = 0.; // zeny counter
|
|
|
|
|
|
// vending item
|
|
|
pc_additem(sd, &vsd->cart.u.items_cart[idx], amount, LOG_TYPE_VENDING);
|
|
|
vsd->vending[vend_list[i]].amount -= amount;
|
|
|
- z += ((double)vsd->vending[i].value * (double)amount);
|
|
|
+ z_gained = ((double)vsd->vending[vend_list[i]].value_vat * (double)amount);
|
|
|
+ z_gained = z_gained - (z_gained / 10000 * tax_total);
|
|
|
+
|
|
|
+ if (battle_config.display_tax_info) {
|
|
|
+ char msg[CHAT_SIZE_MAX];
|
|
|
+ sprintf(msg, msg_txt(sd, 777), itemdb_jname(vsd->cart.u.items_cart[idx].nameid), (double)vsd->vending[vend_list[i]].value * amount, z_gained);
|
|
|
+ clif_displaymessage(vsd->fd, msg);
|
|
|
+ }
|
|
|
|
|
|
if( vsd->vending[vend_list[i]].amount ) {
|
|
|
if( Sql_Query( mmysql_handle, "UPDATE `%s` SET `amount` = %d WHERE `vending_id` = %d and `cartinventory_id` = %d", vending_items_table, vsd->vending[vend_list[i]].amount, vsd->vender_id, vsd->cart.u.items_cart[idx].id ) != SQL_SUCCESS ) {
|
|
@@ -239,8 +273,7 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui
|
|
|
}
|
|
|
|
|
|
pc_cart_delitem(vsd, idx, amount, 0, LOG_TYPE_VENDING);
|
|
|
- z = vending_calc_tax(sd, z);
|
|
|
- clif_vendingreport(vsd, idx, amount, sd->status.char_id, (int)z);
|
|
|
+ clif_vendingreport(vsd, idx, amount, sd->status.char_id, (int)z_gained);
|
|
|
|
|
|
//print buyer's name
|
|
|
if( battle_config.buyer_name ) {
|
|
@@ -299,8 +332,10 @@ int8 vending_openvending(struct map_session_data* sd, const char* message, const
|
|
|
int i, j;
|
|
|
int vending_skill_lvl;
|
|
|
char message_sql[MESSAGE_SIZE*2];
|
|
|
+ char msg[CHAT_SIZE_MAX];
|
|
|
StringBuf buf;
|
|
|
-
|
|
|
+ s_tax *taxdata;
|
|
|
+
|
|
|
nullpo_retr(false,sd);
|
|
|
|
|
|
if ( pc_isdead(sd) || !sd->state.prevend || pc_istrading(sd)) {
|
|
@@ -324,12 +359,18 @@ int8 vending_openvending(struct map_session_data* sd, const char* message, const
|
|
|
if (save_settings&CHARSAVE_VENDING) // Avoid invalid data from saving
|
|
|
chrif_save(sd, CSAVE_INVENTORY|CSAVE_CART);
|
|
|
|
|
|
+ taxdata = tax_get(TAX_SELLING);
|
|
|
+ if (battle_config.display_tax_info) {
|
|
|
+ clif_displaymessage(sd->fd, msg_txt(sd, 773)); // [ Tax Information ]
|
|
|
+ }
|
|
|
+
|
|
|
// filter out invalid items
|
|
|
i = 0;
|
|
|
for( j = 0; j < count; j++ ) {
|
|
|
short index = *(uint16*)(data + 8*j + 0);
|
|
|
short amount = *(uint16*)(data + 8*j + 2);
|
|
|
unsigned int value = *(uint32*)(data + 8*j + 4);
|
|
|
+ unsigned short tax;
|
|
|
|
|
|
index -= 2; // offset adjustment (client says that the first cart position is 2)
|
|
|
|
|
@@ -346,6 +387,16 @@ int8 vending_openvending(struct map_session_data* sd, const char* message, const
|
|
|
sd->vending[i].index = index;
|
|
|
sd->vending[i].amount = amount;
|
|
|
sd->vending[i].value = min(value, (unsigned int)battle_config.vending_max_value);
|
|
|
+
|
|
|
+ tax = taxdata->get_tax(taxdata->each, sd->vending[i].value);
|
|
|
+ sd->vending[i].value_vat = tax ? (size_t)(sd->vending[i].value - sd->vending[i].value / 10000. * tax) : sd->vending[i].value;
|
|
|
+
|
|
|
+ if (battle_config.display_tax_info) {
|
|
|
+ memset(msg, '\0', CHAT_SIZE_MAX);
|
|
|
+ sprintf(msg, msg_txt(sd, 774), itemdb_jname(sd->cart.u.items_cart[index].nameid), sd->vending[i].value, '-', tax / 100., sd->vending[i].value_vat); // // %s : %u %c %.2f%% => %u
|
|
|
+ clif_displaymessage(sd->fd, msg);
|
|
|
+ }
|
|
|
+
|
|
|
i++; // item successfully added
|
|
|
}
|
|
|
|
|
@@ -357,6 +408,15 @@ int8 vending_openvending(struct map_session_data* sd, const char* message, const
|
|
|
return 5;
|
|
|
}
|
|
|
|
|
|
+ if (battle_config.display_tax_info && taxdata->total.size()) {
|
|
|
+ clif_displaymessage(sd->fd, msg_txt(sd, 775)); // [ Total Transaction Tax ]
|
|
|
+ for (const auto &tax : taxdata->total) {
|
|
|
+ memset(msg, '\0', CHAT_SIZE_MAX);
|
|
|
+ sprintf(msg, msg_txt(sd, 776), tax.tax / 100., tax.minimal); // Tax: %.2f%% Minimal Transaction: %u
|
|
|
+ clif_displaymessage(sd->fd, msg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
sd->state.prevend = 0;
|
|
|
sd->state.vending = true;
|
|
|
sd->state.workinprogress = WIP_DISABLE_NONE;
|