Compare commits
38 Commits
master
...
feature/st
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e0cb658b7f | ||
![]() |
cc16f46885 | ||
![]() |
98cf69206a | ||
![]() |
a9754cc198 | ||
![]() |
5c00d26e42 | ||
![]() |
efb25cde9a | ||
![]() |
ad8501cd1d | ||
![]() |
e86daf339c | ||
![]() |
725ae2e899 | ||
![]() |
2d681f2061 | ||
![]() |
812895b98a | ||
![]() |
0c4ae23f0d | ||
![]() |
b0fdf80961 | ||
![]() |
4d4f0eb1b8 | ||
![]() |
faafa5661e | ||
![]() |
b1299f54b1 | ||
![]() |
27e1c40818 | ||
![]() |
6ffc505066 | ||
![]() |
eef0561fc1 | ||
![]() |
eaa7fdf2d0 | ||
![]() |
6eae3bf74b | ||
![]() |
e605adbd17 | ||
![]() |
060838b9d8 | ||
![]() |
04606d25d4 | ||
![]() |
3286468786 | ||
![]() |
5690891c40 | ||
![]() |
e61ea13ad8 | ||
![]() |
fa93bbd90c | ||
![]() |
212ce7fd25 | ||
![]() |
024a5de8dc | ||
![]() |
0f88530860 | ||
![]() |
1031189146 | ||
![]() |
12768c49bc | ||
![]() |
a5897a1c3c | ||
![]() |
68c8f6e30a | ||
![]() |
de46196963 | ||
![]() |
dc0512a3b8 | ||
![]() |
77fec5825e |
@ -16,16 +16,6 @@ vending_max_value: 1000000000
|
||||
// If set to yes, the rest of the zeny above the char's capacity will disappear.
|
||||
vending_over_max: yes
|
||||
|
||||
// Tax to apply to all vending transactions (eg: 10000 = 100%, 50 = 0.50%)
|
||||
// When a tax is applied, the item's full price is charged to the buyer, but
|
||||
// the vender will not get the whole price paid (they get 100% - this tax).
|
||||
vending_tax: 500
|
||||
|
||||
// Minimum total of purchase until taxes are applied.
|
||||
// Officially there is no tax for anything less than 100 million zeny.
|
||||
// 0 will apply taxes to all transactions.
|
||||
vending_tax_min: 100000000
|
||||
|
||||
// Show the buyer's name when successfully vended an item
|
||||
buyer_name: yes
|
||||
|
||||
|
@ -183,3 +183,6 @@ mail_delay: 1000
|
||||
|
||||
// Hides items from the player's favorite tab from being sold to a NPC. (Note 1)
|
||||
hide_fav_sell: no
|
||||
|
||||
// Display tax info given from conf/tax.yml (Note 1)
|
||||
display_tax_info: yes
|
||||
|
40
conf/import-tmpl/tax.yml
Normal file
40
conf/import-tmpl/tax.yml
Normal file
@ -0,0 +1,40 @@
|
||||
# This file is a part of rAthena.
|
||||
# Copyright(C) 2019 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/>.
|
||||
#
|
||||
###########################################################################
|
||||
# Tax Database
|
||||
###########################################################################
|
||||
#
|
||||
# Tax Settings
|
||||
#
|
||||
###########################################################################
|
||||
# Type Tax type. Valid types are TAX_SELLING for vending and TAX_BUYING for buyingstore.
|
||||
# InTotal Tax applied for total transaction. Supports unlimited entries.
|
||||
# - MinimalValue Minimum Zeny value before Tax is applied. (Default: 0)
|
||||
# Tax Tax percentage applied to MinimalValue. (Default: 0)
|
||||
# EachEntry Tax by selling entry. Supports unlimited entries.
|
||||
# - MinimalValue Minimum Zeny value before Tax is applied. (Default: 0)
|
||||
# Tax Tax percentage applied to MinimalValue. (Default: 0)
|
||||
###########################################################################
|
||||
|
||||
Header:
|
||||
Type: TAX_DB
|
||||
Version: 1
|
||||
|
||||
#Body:
|
||||
# - Type: TAX_SELLING
|
||||
# - Type: TAX_BUYING
|
@ -840,7 +840,14 @@
|
||||
773: This command requires packet version 2016-10-12 or newer.
|
||||
774: This command is disabled via configuration.
|
||||
775: You have already opened the refine UI.
|
||||
//776-781 reserved for tax system
|
||||
|
||||
// Tax:
|
||||
776: [ Tax Information ]
|
||||
777: %s : %u %c %.2f%% => %u
|
||||
778: [ Total Transaction Tax ]
|
||||
779: Tax: %.2f%% Minimal Transaction: %u
|
||||
780: %s : %.0f => %.0f
|
||||
781: Tax database has been reloaded.
|
||||
|
||||
782: Star Emperor
|
||||
783: Soul Reaper
|
||||
|
88
conf/tax.yml
Normal file
88
conf/tax.yml
Normal file
@ -0,0 +1,88 @@
|
||||
# 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/>.
|
||||
#
|
||||
###########################################################################
|
||||
# Tax Database
|
||||
###########################################################################
|
||||
#
|
||||
# Tax Settings
|
||||
#
|
||||
###########################################################################
|
||||
# Type Tax type. Valid types are TAX_SELLING for vending and TAX_BUYING for buyingstore.
|
||||
# InTotal Tax applied for total transaction. Supports unlimited entries.
|
||||
# - MinimalValue Minimum Zeny value before Tax is applied. (Default: 0)
|
||||
# Tax Tax percentage applied to MinimalValue. (Default: 0)
|
||||
# EachEntry Tax by selling entry. Supports unlimited entries.
|
||||
# - MinimalValue Minimum Zeny value before Tax is applied. (Default: 0)
|
||||
# Tax Tax percentage applied to MinimalValue. (Default: 0)
|
||||
###########################################################################
|
||||
|
||||
Header:
|
||||
Type: TAX_DB
|
||||
Version: 1
|
||||
|
||||
Body:
|
||||
# Zeny received for the seller will be reduced after taxes from the total selling price but the buyer pays just the total selling price
|
||||
- Type: TAX_SELLING
|
||||
InTotal:
|
||||
- MinimalValue: 0
|
||||
Tax: 0
|
||||
|
||||
EachEntry:
|
||||
# 10% if >= 100,000,001
|
||||
- MinimalValue: 100000001
|
||||
Tax: 1000
|
||||
# 8% if >= 10,000,001
|
||||
- MinimalValue: 10000001
|
||||
Tax: 800
|
||||
# 6% if >= 1,000,001
|
||||
- MinimalValue: 1000001
|
||||
Tax: 600
|
||||
# 4% if >= 100,001
|
||||
- MinimalValue: 100001
|
||||
Tax: 400
|
||||
# 2% if >= 10,001
|
||||
- MinimalValue: 10001
|
||||
Tax: 200
|
||||
|
||||
# Zeny received for the seller will be the total selling price but the buyer must pay taxes for the total selling price
|
||||
- Type: TAX_BUYING
|
||||
InTotal:
|
||||
- MinimalValue: 0
|
||||
Tax: 0
|
||||
|
||||
# Tax by buying entry
|
||||
EachEntry:
|
||||
# 10% if >= 100,000,001
|
||||
- MinimalValue: 100000001
|
||||
Tax: 1000
|
||||
# 8% if >= 10,000,001
|
||||
- MinimalValue: 10000001
|
||||
Tax: 800
|
||||
# 6% if >= 1,000,001
|
||||
- MinimalValue: 1000001
|
||||
Tax: 600
|
||||
# 4% if >= 100,001
|
||||
- MinimalValue: 100001
|
||||
Tax: 400
|
||||
# 2% if >= 10,001
|
||||
- MinimalValue: 10001
|
||||
Tax: 200
|
||||
|
||||
Footer:
|
||||
Imports:
|
||||
- Path: conf/import/tax.yml
|
@ -52,6 +52,7 @@
|
||||
#include "quest.hpp"
|
||||
#include "script.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "tax.hpp"
|
||||
#include "trade.hpp"
|
||||
#include "vending.hpp"
|
||||
|
||||
@ -4318,6 +4319,9 @@ ACMD_FUNC(reload) {
|
||||
} else if (strstr(command, "attendancedb") || strncmp(message, "attendancedb", 4) == 0) {
|
||||
attendance_db.reload();
|
||||
clif_displaymessage(fd, msg_txt(sd, 795)); // Attendance database has been reloaded.
|
||||
} else if (strstr(command, "taxdb") || strncmp(message, "taxdb", 3) == 0) {
|
||||
tax_db_reload();
|
||||
clif_displaymessage(fd, msg_txt(sd,781)); // Tax database has been reloaded.
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -10954,6 +10958,7 @@ void atcommand_basecommands(void) {
|
||||
ACMD_DEF2("reloadinstancedb", reload),
|
||||
ACMD_DEF2("reloadachievementdb",reload),
|
||||
ACMD_DEF2("reloadattendancedb",reload),
|
||||
ACMD_DEF2("reloadtaxdb",reload),
|
||||
ACMD_DEF(partysharelvl),
|
||||
ACMD_DEF(mapinfo),
|
||||
ACMD_DEF(dye),
|
||||
|
@ -10030,8 +10030,6 @@ static const struct _battle_data {
|
||||
{ "hom_rename", &battle_config.hom_rename, 0, 0, 1, },
|
||||
{ "homunculus_show_growth", &battle_config.homunculus_show_growth, 0, 0, 1, },
|
||||
{ "homunculus_friendly_rate", &battle_config.homunculus_friendly_rate, 100, 0, INT_MAX, },
|
||||
{ "vending_tax", &battle_config.vending_tax, 0, 0, 10000, },
|
||||
{ "vending_tax_min", &battle_config.vending_tax_min, 0, 0, MAX_ZENY, },
|
||||
{ "day_duration", &battle_config.day_duration, 0, 0, INT_MAX, },
|
||||
{ "night_duration", &battle_config.night_duration, 0, 0, INT_MAX, },
|
||||
{ "mob_remove_delay", &battle_config.mob_remove_delay, 60000, 1000, INT_MAX, },
|
||||
@ -10250,6 +10248,7 @@ static const struct _battle_data {
|
||||
{ "idletime_mer_option", &battle_config.idletime_mer_option, 0x1F, 0x1, 0xFFF, },
|
||||
{ "feature.refineui", &battle_config.feature_refineui, 1, 0, 1, },
|
||||
{ "rndopt_drop_pillar", &battle_config.rndopt_drop_pillar, 1, 0, 1, },
|
||||
{ "display_tax_info", &battle_config.display_tax_info, 0, 0, 1, },
|
||||
{ "pet_legacy_formula", &battle_config.pet_legacy_formula, 0, 0, 1, },
|
||||
{ "pet_distance_check", &battle_config.pet_distance_check, 5, 0, 50, },
|
||||
{ "pet_hide_check", &battle_config.pet_hide_check, 1, 0, 1, },
|
||||
|
@ -320,8 +320,6 @@ struct Battle_Config
|
||||
int dead_branch_active;
|
||||
int vending_max_value;
|
||||
int vending_over_max;
|
||||
int vending_tax;
|
||||
int vending_tax_min;
|
||||
int show_steal_in_same_party;
|
||||
int party_share_type;
|
||||
int party_hp_mode;
|
||||
@ -691,6 +689,7 @@ struct Battle_Config
|
||||
int idletime_mer_option;
|
||||
int feature_refineui;
|
||||
int rndopt_drop_pillar;
|
||||
int display_tax_info;
|
||||
int pet_legacy_formula;
|
||||
int pet_distance_check;
|
||||
int pet_hide_check;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "log.hpp" // log_pick_pc, log_zeny
|
||||
#include "npc.hpp"
|
||||
#include "pc.hpp" // struct map_session_data
|
||||
#include "tax.hpp"
|
||||
|
||||
//Autotrader
|
||||
static DBMap *buyingstore_autotrader_db; /// Holds autotrader info: char_id -> struct s_autotrader
|
||||
@ -229,6 +230,8 @@ int8 buyingstore_create( struct map_session_data* sd, int zenylimit, unsigned ch
|
||||
sd->buyingstore.slots = i; // store actual amount of items
|
||||
safestrncpy(sd->message, storename, sizeof(sd->message));
|
||||
|
||||
tax_db.setBuyingstoreTax(sd);
|
||||
|
||||
Sql_EscapeString( mmysql_handle, message_sql, sd->message );
|
||||
|
||||
if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`id`, `account_id`, `char_id`, `sex`, `map`, `x`, `y`, `title`, `limit`, `autotrade`, `body_direction`, `head_direction`, `sit`) "
|
||||
@ -316,6 +319,39 @@ void buyingstore_open(struct map_session_data* sd, uint32 account_id)
|
||||
clif_buyingstore_itemlist(sd, pl_sd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates taxes for Buyingstore purchases.
|
||||
* @param sd: Player data.
|
||||
* @param itemlist: List of sold items { <index>.W, <nameid>.W, <amount>.W }*.
|
||||
* @param count: Item list count.
|
||||
* @return Taxed price
|
||||
*/
|
||||
static unsigned short buyinstore_tax_intotal(struct map_session_data* sd, const struct PACKET_CZ_REQ_TRADE_BUYING_STORE_sub* itemlist, int count) {
|
||||
std::shared_ptr<s_tax> tax = tax_db.find(TAX_BUYING);
|
||||
|
||||
if (tax == nullptr || tax->total.empty())
|
||||
return 0;
|
||||
|
||||
double total = 0;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
const struct PACKET_CZ_REQ_TRADE_BUYING_STORE_sub* item = &itemlist[i];
|
||||
|
||||
if (item->amount <= 0)
|
||||
continue;
|
||||
|
||||
int listidx;
|
||||
|
||||
ARR_FIND(0, sd->buyingstore.slots, listidx, sd->buyingstore.items[listidx].nameid == item->itemId);
|
||||
if (listidx == sd->buyingstore.slots || sd->buyingstore.items[listidx].amount == 0)
|
||||
continue;
|
||||
|
||||
total += ((double)sd->buyingstore.items[listidx].price * item->amount);
|
||||
}
|
||||
|
||||
return tax->taxPercentage(tax->total, total);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start transaction
|
||||
* @param sd Player/Seller
|
||||
@ -324,7 +360,6 @@ void buyingstore_open(struct map_session_data* sd, uint32 account_id)
|
||||
* @param count Number of item on the itemlist
|
||||
*/
|
||||
void buyingstore_trade( struct map_session_data* sd, uint32 account_id, unsigned int buyer_id, const struct PACKET_CZ_REQ_TRADE_BUYING_STORE_sub* itemlist, unsigned int count ){
|
||||
int zeny = 0;
|
||||
unsigned int weight;
|
||||
struct map_session_data* pl_sd;
|
||||
|
||||
@ -367,6 +402,8 @@ void buyingstore_trade( struct map_session_data* sd, uint32 account_id, unsigned
|
||||
pl_sd->buyingstore.zenylimit = pl_sd->status.zeny;
|
||||
}
|
||||
weight = pl_sd->weight;
|
||||
int tax_total = buyinstore_tax_intotal(pl_sd, itemlist, count);
|
||||
double zeny = 0, zeny_paid = 0;
|
||||
|
||||
// check item list
|
||||
for( int i = 0; i < count; i++ ){
|
||||
@ -427,14 +464,23 @@ void buyingstore_trade( struct map_session_data* sd, uint32 account_id, unsigned
|
||||
|
||||
weight += item->amount * sd->inventory_data[index]->weight;
|
||||
|
||||
// buyer does not have enough zeny
|
||||
if( item->amount * pl_sd->buyingstore.items[listidx].price > pl_sd->buyingstore.zenylimit - zeny ){
|
||||
zeny += ((double)item->amount * (double)pl_sd->buyingstore.items[listidx].price);
|
||||
zeny_paid += ((double)item->amount * (double)pl_sd->buyingstore.items[listidx].price_vat);
|
||||
zeny_paid += (zeny_paid / 10000. * tax_total);
|
||||
if (zeny_paid > (double)pl_sd->buyingstore.zenylimit) {// buyer does not have enough zeny
|
||||
clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_ZENY, item->itemId );
|
||||
return;
|
||||
}
|
||||
|
||||
zeny += item->amount * pl_sd->buyingstore.items[listidx].price;
|
||||
if ((double)sd->status.zeny + zeny > (double)MAX_ZENY) { // Seller zeny overflow
|
||||
clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_BUYER_ZENY, item->itemId );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// pay up
|
||||
pc_payzeny(pl_sd, (int)zeny_paid, LOG_TYPE_BUYING_STORE, sd);
|
||||
pc_getzeny(sd, (int)zeny, LOG_TYPE_BUYING_STORE, pl_sd);
|
||||
pl_sd->buyingstore.zenylimit -= (int)zeny_paid;
|
||||
|
||||
// process item list
|
||||
for( int i = 0; i < count; i++ ){
|
||||
@ -442,7 +488,9 @@ void buyingstore_trade( struct map_session_data* sd, uint32 account_id, unsigned
|
||||
int listidx;
|
||||
|
||||
ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == item->itemId );
|
||||
zeny = item->amount * pl_sd->buyingstore.items[listidx].price;
|
||||
zeny = ((double)item->amount * (double)pl_sd->buyingstore.items[listidx].price);
|
||||
zeny_paid = ((double)item->amount * (double)pl_sd->buyingstore.items[listidx].price_vat);
|
||||
zeny_paid = zeny_paid + (zeny_paid / 10000. * tax_total);
|
||||
|
||||
int index = item->index - 2; // TODO: clif::server_index
|
||||
|
||||
@ -461,14 +509,16 @@ void buyingstore_trade( struct map_session_data* sd, uint32 account_id, unsigned
|
||||
}
|
||||
}
|
||||
|
||||
// pay up
|
||||
pc_payzeny(pl_sd, zeny, LOG_TYPE_BUYING_STORE, sd);
|
||||
pc_getzeny(sd, zeny, LOG_TYPE_BUYING_STORE, pl_sd);
|
||||
pl_sd->buyingstore.zenylimit-= zeny;
|
||||
if (battle_config.display_tax_info) {
|
||||
char msg[CHAT_SIZE_MAX];
|
||||
|
||||
sprintf(msg, msg_txt(sd, 780), itemdb_ename(item->itemId), (double)zeny, (double)zeny_paid); // %s : %.0f => %.0f
|
||||
clif_displaymessage(pl_sd->fd, msg);
|
||||
}
|
||||
|
||||
// notify clients
|
||||
clif_buyingstore_delete_item(sd, index, item->amount, pl_sd->buyingstore.items[listidx].price);
|
||||
clif_buyingstore_update_item(pl_sd, item->itemId, item->amount, sd->status.char_id, zeny);
|
||||
clif_buyingstore_update_item(pl_sd, item->itemId, item->amount, sd->status.char_id, (int)zeny_paid);
|
||||
}
|
||||
|
||||
if( save_settings&CHARSAVE_VENDING ) {
|
||||
|
@ -15,9 +15,10 @@ struct map_session_data;
|
||||
|
||||
struct s_buyingstore_item
|
||||
{
|
||||
int price;
|
||||
unsigned short amount;
|
||||
t_itemid nameid;
|
||||
int price; ///< Value
|
||||
unsigned short amount; ///< Amount of items in Buyingstore
|
||||
t_itemid nameid; ///< Item ID
|
||||
unsigned int price_vat; ///< Value after tax
|
||||
};
|
||||
|
||||
struct s_buyingstore
|
||||
|
@ -7720,7 +7720,7 @@ void clif_vendinglist( struct map_session_data* sd, struct map_session_data* vsd
|
||||
/// 5 = "cannot use an npc shop while in a trade"
|
||||
/// 6 = Because the store information was incorrect the item was not purchased.
|
||||
/// 7 = No sales information.
|
||||
void clif_buyvending(struct map_session_data* sd, int index, int amount, int fail)
|
||||
void clif_buyvending(struct map_session_data* sd, int index, int amount, enum e_vending_ack ack)
|
||||
{
|
||||
int fd;
|
||||
|
||||
@ -7731,7 +7731,7 @@ void clif_buyvending(struct map_session_data* sd, int index, int amount, int fai
|
||||
WFIFOW(fd,0) = 0x135;
|
||||
WFIFOW(fd,2) = index+2;
|
||||
WFIFOW(fd,4) = amount;
|
||||
WFIFOB(fd,6) = fail;
|
||||
WFIFOB(fd,6) = ack;
|
||||
WFIFOSET(fd,packet_len(0x135));
|
||||
}
|
||||
|
||||
|
@ -209,6 +209,16 @@ enum class e_purchase_result : uint8{
|
||||
PURCHASE_FAIL_ADD = 0xff,
|
||||
};
|
||||
|
||||
enum e_vending_ack : uint8_t {
|
||||
VENDING_ACK_OK = 0,
|
||||
VENDING_ACK_NOZENY = 1,
|
||||
VENDING_ACK_OVERWEIGHT = 2,
|
||||
VENDING_ACK_NOSTOCK = 4,
|
||||
VENDING_ACK_NOTALKNPC = 5,
|
||||
VENDING_ACK_INVALID = 6,
|
||||
VENDING_ACK_NOITEM = 7,
|
||||
};
|
||||
|
||||
#define packet_len(cmd) packet_db[cmd].len
|
||||
extern struct s_packet_db packet_db[MAX_PACKET_DB+1];
|
||||
extern int packet_db_ack[MAX_ACK_FUNC + 1];
|
||||
@ -807,7 +817,7 @@ void clif_openvendingreq(struct map_session_data* sd, int num);
|
||||
void clif_showvendingboard(struct block_list* bl, const char* message, int fd);
|
||||
void clif_closevendingboard(struct block_list* bl, int fd);
|
||||
void clif_vendinglist( struct map_session_data* sd, struct map_session_data* vsd );
|
||||
void clif_buyvending(struct map_session_data* sd, int index, int amount, int fail);
|
||||
void clif_buyvending(struct map_session_data* sd, int index, int amount, enum e_vending_ack ack);
|
||||
void clif_openvending(struct map_session_data* sd, int id, struct s_vending* vending);
|
||||
void clif_vendingreport(struct map_session_data* sd, int index, int amount, uint32 char_id, int zeny);
|
||||
|
||||
|
@ -225,6 +225,7 @@
|
||||
<ClInclude Include="skill.hpp" />
|
||||
<ClInclude Include="status.hpp" />
|
||||
<ClInclude Include="storage.hpp" />
|
||||
<ClInclude Include="tax.hpp" />
|
||||
<ClInclude Include="trade.hpp" />
|
||||
<ClInclude Include="unit.hpp" />
|
||||
<ClInclude Include="vending.hpp" />
|
||||
@ -271,6 +272,7 @@
|
||||
<ClCompile Include="skill.cpp" />
|
||||
<ClCompile Include="status.cpp" />
|
||||
<ClCompile Include="storage.cpp" />
|
||||
<ClCompile Include="tax.cpp" />
|
||||
<ClCompile Include="trade.cpp" />
|
||||
<ClCompile Include="unit.cpp" />
|
||||
<ClCompile Include="vending.cpp" />
|
||||
@ -304,6 +306,7 @@
|
||||
<Copy SourceFiles="$(SolutionDir)conf\import-tmpl\map_conf.txt" DestinationFolder="$(SolutionDir)conf\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)conf\import\map_conf.txt')" />
|
||||
<Copy SourceFiles="$(SolutionDir)conf\import-tmpl\packet_conf.txt" DestinationFolder="$(SolutionDir)conf\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)conf\import\packet_conf.txt')" />
|
||||
<Copy SourceFiles="$(SolutionDir)conf\import-tmpl\script_conf.txt" DestinationFolder="$(SolutionDir)conf\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)conf\import\script_conf.txt')" />
|
||||
<Copy SourceFiles="$(SolutionDir)conf\import-tmpl\tax.yml" DestinationFolder="$(SolutionDir)conf\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)conf\import\tax.yml')" />
|
||||
<Copy SourceFiles="$(SolutionDir)conf\msg_conf\import-tmpl\map_msg_chn_conf.txt" DestinationFolder="$(SolutionDir)conf\msg_conf\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)conf\msg_conf\import\map_msg_chn_conf.txt')" />
|
||||
<Copy SourceFiles="$(SolutionDir)conf\msg_conf\import-tmpl\map_msg_eng_conf.txt" DestinationFolder="$(SolutionDir)conf\msg_conf\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)conf\msg_conf\import\map_msg_eng_conf.txt')" />
|
||||
<Copy SourceFiles="$(SolutionDir)conf\msg_conf\import-tmpl\map_msg_frn_conf.txt" DestinationFolder="$(SolutionDir)conf\msg_conf\import\" ContinueOnError="true" Condition="!Exists('$(SolutionDir)conf\msg_conf\import\map_msg_frn_conf.txt')" />
|
||||
|
@ -143,6 +143,9 @@
|
||||
<ClInclude Include="storage.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="tax.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="trade.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -271,6 +274,9 @@
|
||||
<ClCompile Include="storage.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="tax.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="trade.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "pet.hpp"
|
||||
#include "quest.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "tax.hpp"
|
||||
#include "trade.hpp"
|
||||
|
||||
using namespace rathena;
|
||||
@ -4900,6 +4901,7 @@ void do_final(void){
|
||||
do_final_vending();
|
||||
do_final_buyingstore();
|
||||
do_final_path();
|
||||
do_final_tax();
|
||||
|
||||
map_db->destroy(map_db, map_db_final);
|
||||
|
||||
@ -5229,6 +5231,7 @@ int do_init(int argc, char *argv[])
|
||||
do_init_quest();
|
||||
do_init_achievement();
|
||||
do_init_battleground();
|
||||
do_init_tax();
|
||||
do_init_npc();
|
||||
do_init_unit();
|
||||
do_init_duel();
|
||||
|
@ -61,6 +61,7 @@
|
||||
#include "pet.hpp"
|
||||
#include "quest.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "tax.hpp" // e_tax_type
|
||||
|
||||
using namespace rathena;
|
||||
|
||||
|
@ -8496,6 +8496,10 @@
|
||||
/* timer related */
|
||||
export_constant(INFINITE_TICK);
|
||||
|
||||
/* tax related */
|
||||
export_constant(TAX_SELLING);
|
||||
export_constant(TAX_BUYING);
|
||||
|
||||
/* block action */
|
||||
export_constant(PCBLOCK_MOVE);
|
||||
export_constant(PCBLOCK_ATTACK);
|
||||
|
272
src/map/tax.cpp
Normal file
272
src/map/tax.cpp
Normal file
@ -0,0 +1,272 @@
|
||||
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
|
||||
#include "tax.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
#include "../common/cbasetypes.hpp"
|
||||
#include "../common/nullpo.hpp"
|
||||
#include "../common/showmsg.hpp"
|
||||
|
||||
#include "battle.hpp"
|
||||
#include "buyingstore.hpp"
|
||||
#include "clif.hpp"
|
||||
#include "pc.hpp"
|
||||
#include "vending.hpp"
|
||||
|
||||
/*
|
||||
* Tax Database
|
||||
*/
|
||||
TaxDatabase tax_db;
|
||||
|
||||
const std::string TaxDatabase::getDefaultLocation() {
|
||||
return "conf/tax.yml";
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and parses an entry from the tax_db.
|
||||
* @param node: YAML node containing the entry.
|
||||
* @return count of successfully parsed rows
|
||||
*/
|
||||
uint64 TaxDatabase::parseBodyNode(const YAML::Node &node) {
|
||||
std::string type_str;
|
||||
|
||||
if (!this->nodeExists(node, "Type")) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!this->asString(node, "Type", type_str))
|
||||
return 0;
|
||||
|
||||
int64 type;
|
||||
|
||||
if (!script_get_constant(type_str.c_str(), &type)) {
|
||||
this->invalidWarning(node["Type"], "Invalid tax type '%s'.\n", type_str.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<s_tax> taxdata = this->find(type);
|
||||
bool exists = taxdata != nullptr;
|
||||
|
||||
if (!exists) {
|
||||
taxdata = std::make_shared<s_tax>();
|
||||
}
|
||||
|
||||
if (this->nodeExists(node, "InTotal")) {
|
||||
taxdata->total.clear();
|
||||
|
||||
for (const auto &taxNode : node["InTotal"]) {
|
||||
if (!this->nodesExist(taxNode, { "MinimalValue", "Tax" }))
|
||||
continue;
|
||||
|
||||
s_tax_entry entry = {};
|
||||
|
||||
if (!this->asUInt64(taxNode, "MinimalValue", entry.minimal)) {
|
||||
this->invalidWarning(taxNode["MinimalValue"], "Invalid value, defaulting to 0.\n");
|
||||
entry.minimal = 0;
|
||||
}
|
||||
|
||||
if (!this->asUInt16(taxNode, "Tax", entry.tax)) {
|
||||
this->invalidWarning(taxNode["Tax"], "Invalid value, defaulting to 0.\n");
|
||||
entry.tax = 0;
|
||||
}
|
||||
|
||||
taxdata->total.push_back(entry);
|
||||
|
||||
std::sort(taxdata->total.begin(), taxdata->total.end(),
|
||||
[](const s_tax_entry &a, const s_tax_entry &b) -> bool { return a.minimal > b.minimal; });
|
||||
}
|
||||
}
|
||||
|
||||
if (this->nodeExists(node, "EachEntry")) {
|
||||
taxdata->each.clear();
|
||||
|
||||
for (const auto &taxNode : node["EachEntry"]) {
|
||||
if (!this->nodesExist(taxNode, { "MinimalValue", "Tax" }))
|
||||
continue;
|
||||
|
||||
s_tax_entry entry = { 0 };
|
||||
|
||||
if (!this->asUInt64(taxNode, "MinimalValue", entry.minimal)) {
|
||||
this->invalidWarning(taxNode["MinimalValue"], "Invalid value, defaulting to 0.\n");
|
||||
entry.minimal = 0;
|
||||
}
|
||||
|
||||
if (!this->asUInt16(taxNode, "Tax", entry.tax)) {
|
||||
this->invalidWarning(taxNode["Tax"], "Invalid value, defaulting to 0.\n");
|
||||
entry.tax = 0;
|
||||
}
|
||||
|
||||
taxdata->each.push_back(entry);
|
||||
|
||||
std::sort(taxdata->each.begin(), taxdata->each.end(),
|
||||
[](const s_tax_entry &a, const s_tax_entry &b) -> bool { return a.minimal > b.minimal; });
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
this->put(type, taxdata);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set vending tax to a player
|
||||
* @param sd Player
|
||||
*/
|
||||
void TaxDatabase::setVendingTax(map_session_data *sd)
|
||||
{
|
||||
std::shared_ptr<s_tax> taxdata = this->find(TAX_SELLING);
|
||||
|
||||
if (taxdata != nullptr) {
|
||||
taxdata->vendingVAT(sd); // Calculate value after taxes
|
||||
taxdata->inTotalInfo(sd);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set buyingstore tax to a player
|
||||
* @param sd Player
|
||||
*/
|
||||
void TaxDatabase::setBuyingstoreTax(map_session_data *sd)
|
||||
{
|
||||
std::shared_ptr<s_tax> taxdata = this->find(TAX_BUYING);
|
||||
|
||||
if (taxdata != nullptr) {
|
||||
taxdata->buyingstoreVAT(sd); // Calculate value after taxes
|
||||
taxdata->inTotalInfo(sd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tax rate of a given amount.
|
||||
* @param entry: Tax data.
|
||||
* @param price: Value of item.
|
||||
* @return Tax rate
|
||||
*/
|
||||
uint16 s_tax::taxPercentage(const std::vector <s_tax_entry> entry, double price) {
|
||||
const auto &tax = std::find_if(entry.begin(), entry.end(),
|
||||
[&price](const s_tax_entry &e) { return price >= e.minimal; });
|
||||
return tax != entry.end() ? tax->tax : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the value after tax for Vendors.
|
||||
* @param sd: Player data
|
||||
*/
|
||||
void s_tax::vendingVAT(map_session_data *sd) {
|
||||
nullpo_retv(sd);
|
||||
|
||||
if (battle_config.display_tax_info)
|
||||
clif_displaymessage(sd->fd, msg_txt(sd, 776)); // [ Tax Information ]
|
||||
|
||||
for (int i = 0; i < sd->vend_num; i++) {
|
||||
if (sd->vending[i].amount == 0)
|
||||
continue;
|
||||
|
||||
uint16 tax = this->taxPercentage(this->each, sd->vending[i].value);
|
||||
|
||||
sd->vending[i].value_vat = tax ? (unsigned int)(sd->vending[i].value - sd->vending[i].value / 10000. * tax) : sd->vending[i].value;
|
||||
|
||||
if (battle_config.display_tax_info) {
|
||||
char msg[CHAT_SIZE_MAX];
|
||||
|
||||
sprintf(msg, msg_txt(sd, 777), itemdb_ename(sd->cart.u.items_cart[sd->vending[i].index].nameid), sd->vending[i].value, '-', tax / 100., sd->vending[i].value_vat); // %s : %u %c %.2f%% => %u
|
||||
clif_displaymessage(sd->fd, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the value after tax for Buyingstores.
|
||||
* @param sd: Player data
|
||||
*/
|
||||
void s_tax::buyingstoreVAT(map_session_data *sd) {
|
||||
nullpo_retv(sd);
|
||||
|
||||
if (battle_config.display_tax_info)
|
||||
clif_displaymessage(sd->fd, msg_txt(sd, 776)); // [ Tax Information ]
|
||||
|
||||
for (uint8 i = 0; i < sd->buyingstore.slots; i++) {
|
||||
if (sd->buyingstore.items[i].nameid == 0)
|
||||
continue;
|
||||
|
||||
uint16 tax = this->taxPercentage(this->each, sd->buyingstore.items[i].price);
|
||||
|
||||
sd->buyingstore.items[i].price_vat = tax ? (unsigned int)(sd->buyingstore.items[i].price + sd->buyingstore.items[i].price / 10000. * tax) : sd->buyingstore.items[i].price;
|
||||
|
||||
if (battle_config.display_tax_info) {
|
||||
char msg[CHAT_SIZE_MAX];
|
||||
|
||||
sprintf(msg, msg_txt(sd, 777), itemdb_ename(sd->buyingstore.items[i].nameid), sd->buyingstore.items[i].price, '+', tax / 100., sd->buyingstore.items[i].price_vat); // %s : %u %c %.2f%% => %u
|
||||
clif_displaymessage(sd->fd, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Show selling/buying tax for total purchase value
|
||||
* @param sd Vendor or buyer data
|
||||
*/
|
||||
void s_tax::inTotalInfo(map_session_data *sd)
|
||||
{
|
||||
if (!battle_config.display_tax_info || this->total.empty())
|
||||
return;
|
||||
|
||||
clif_displaymessage(sd->fd, msg_txt(sd, 778)); // [ Total Transaction Tax ]
|
||||
|
||||
for (const auto &tax : this->total) {
|
||||
char msg[CHAT_SIZE_MAX];
|
||||
|
||||
sprintf(msg, msg_txt(sd, 779), tax.tax / 100., tax.minimal); // Tax: %.2f%% Minimal Transaction: %u
|
||||
clif_displaymessage(sd->fd, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload value after taxes for players with a Vending/Buyingstore shop.
|
||||
*/
|
||||
void tax_reload_vat(void) {
|
||||
map_session_data *sd;
|
||||
s_mapiterator *iter = mapit_getallusers();
|
||||
std::shared_ptr<s_tax> vending_tax = tax_db.find(TAX_SELLING);
|
||||
std::shared_ptr<s_tax> buyingstore_tax = tax_db.find(TAX_BUYING);
|
||||
|
||||
for (sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter)) {
|
||||
if (sd->state.vending && vending_tax != nullptr) {
|
||||
vending_tax->vendingVAT(sd);
|
||||
vending_tax->inTotalInfo(sd);
|
||||
} else if (sd->state.buyingstore && buyingstore_tax != nullptr) {
|
||||
buyingstore_tax->buyingstoreVAT(sd);
|
||||
buyingstore_tax->inTotalInfo(sd);
|
||||
}
|
||||
}
|
||||
mapit_free(iter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the tax database
|
||||
*/
|
||||
void tax_db_reload(void) {
|
||||
do_final_tax();
|
||||
do_init_tax();
|
||||
tax_reload_vat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the tax database
|
||||
*/
|
||||
void do_init_tax(void) {
|
||||
tax_db.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes the tax database
|
||||
*/
|
||||
void do_final_tax(void) {
|
||||
tax_db.clear();
|
||||
}
|
56
src/map/tax.hpp
Normal file
56
src/map/tax.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
|
||||
// For more information, see LICENCE in the main folder
|
||||
|
||||
#ifndef _TAX_HPP_
|
||||
#define _TAX_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../common/cbasetypes.hpp"
|
||||
#include "../common/database.hpp"
|
||||
|
||||
#include "pc.hpp"
|
||||
|
||||
enum e_tax_type : uint8 {
|
||||
TAX_SELLING = 0,
|
||||
TAX_BUYING,
|
||||
TAX_MAX,
|
||||
};
|
||||
|
||||
struct s_tax_entry {
|
||||
uint64 minimal;
|
||||
uint16 tax;
|
||||
};
|
||||
|
||||
struct s_tax {
|
||||
std::vector<s_tax_entry> each;
|
||||
std::vector<s_tax_entry> total;
|
||||
|
||||
uint16 taxPercentage(const std::vector <s_tax_entry> entry, double price);
|
||||
void vendingVAT(map_session_data * sd);
|
||||
void buyingstoreVAT(map_session_data * sd);
|
||||
void inTotalInfo(map_session_data *sd);
|
||||
};
|
||||
|
||||
class TaxDatabase : public TypesafeYamlDatabase<uint64, s_tax> {
|
||||
public:
|
||||
TaxDatabase() : TypesafeYamlDatabase("TAX_DB", 1) {
|
||||
|
||||
}
|
||||
|
||||
const std::string getDefaultLocation();
|
||||
uint64 parseBodyNode(const YAML::Node& node);
|
||||
|
||||
void setVendingTax(map_session_data *sd);
|
||||
void setBuyingstoreTax(map_session_data *sd);
|
||||
};
|
||||
|
||||
extern TaxDatabase tax_db;
|
||||
|
||||
void tax_reload_vat(void);
|
||||
void tax_db_reload(void);
|
||||
void do_init_tax(void);
|
||||
void do_final_tax(void);
|
||||
|
||||
#endif /* _TAX_HPP_ */
|
92
src/map/vending.cpp
Executable file → Normal file
92
src/map/vending.cpp
Executable file → Normal file
@ -24,6 +24,7 @@
|
||||
#include "path.hpp"
|
||||
#include "pc.hpp"
|
||||
#include "pc_groups.hpp"
|
||||
#include "tax.hpp"
|
||||
|
||||
static uint32 vending_nextid = 0; ///Vending_id counter
|
||||
static DBMap *vending_db; ///DB holder the vender : charid -> map_session_data
|
||||
@ -98,17 +99,42 @@ void vending_vendinglistreq(struct map_session_data* sd, int id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates taxes for vending
|
||||
* @param sd: Vender
|
||||
* @param zeny: Total amount to tax
|
||||
* @return Total amount after taxes
|
||||
* Return the total amount after taxes
|
||||
* @param vsd: Vendor player
|
||||
* @param data: Item data
|
||||
* @param count: Number of different items
|
||||
* @return Total price 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) {
|
||||
std::shared_ptr<s_tax> tax = tax_db.find(TAX_SELLING);
|
||||
double total = 0;
|
||||
int i, j, vend_list[MAX_VENDING]; // against duplicate packets
|
||||
|
||||
return zeny;
|
||||
if (tax == nullptr || tax->total.empty())
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
short amount = *(uint16*)(data + 4 * i + 0);
|
||||
short idx = *(uint16*)(data + 4 * i + 2);
|
||||
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 tax->taxPercentage(tax->total, total);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -123,7 +149,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);
|
||||
|
||||
@ -132,7 +158,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;
|
||||
}
|
||||
|
||||
@ -152,6 +178,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);
|
||||
@ -171,18 +198,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;
|
||||
}
|
||||
|
||||
@ -194,7 +223,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;
|
||||
}
|
||||
|
||||
@ -215,19 +244,31 @@ 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, (unsigned int)z_gained,
|
||||
sd->status.account_id, sd->status.char_id, (unsigned int)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[vend_list[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, 780), itemdb_ename(vsd->cart.u.items_cart[idx].nameid), (double)vsd->vending[vend_list[i]].value * amount, z_gained); // %s : %.0f => %.0f
|
||||
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 ) {
|
||||
@ -240,8 +281,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 ) {
|
||||
@ -260,6 +300,7 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui
|
||||
vsd->vending[cursor].index = vsd->vending[i].index;
|
||||
vsd->vending[cursor].amount = vsd->vending[i].amount;
|
||||
vsd->vending[cursor].value = vsd->vending[i].value;
|
||||
vsd->vending[cursor].value_vat = vsd->vending[i].value_vat;
|
||||
}
|
||||
|
||||
cursor++;
|
||||
@ -347,6 +388,7 @@ 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);
|
||||
|
||||
i++; // item successfully added
|
||||
}
|
||||
|
||||
@ -368,6 +410,8 @@ int8 vending_openvending(struct map_session_data* sd, const char* message, const
|
||||
sd->vend_num = i;
|
||||
safestrncpy(sd->message, message, MESSAGE_SIZE);
|
||||
|
||||
tax_db.setVendingTax(sd);
|
||||
|
||||
Sql_EscapeString( mmysql_handle, message_sql, sd->message );
|
||||
|
||||
if( Sql_Query( mmysql_handle, "INSERT INTO `%s`(`id`, `account_id`, `char_id`, `sex`, `map`, `x`, `y`, `title`, `autotrade`, `body_direction`, `head_direction`, `sit`) "
|
||||
@ -725,7 +769,7 @@ void vending_update(map_session_data &sd)
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the vending module
|
||||
* Destroy the vending module
|
||||
* called in map::do_init
|
||||
*/
|
||||
void do_final_vending(void)
|
||||
@ -735,7 +779,7 @@ void do_final_vending(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* Destory the vending module
|
||||
* Initialise the vending module
|
||||
* called in map::do_final
|
||||
*/
|
||||
void do_init_vending(void)
|
||||
|
@ -13,9 +13,10 @@ struct s_search_store_search;
|
||||
struct s_autotrader;
|
||||
|
||||
struct s_vending {
|
||||
short index; /// cart index (return item data)
|
||||
short amount; ///amout of the item for vending
|
||||
unsigned int value; ///at wich price
|
||||
short index; ///< Cart index (return item data)
|
||||
short amount; ///< Amount of the item for vending
|
||||
unsigned int value; ///< Price
|
||||
unsigned int value_vat; ///< Value after tax
|
||||
};
|
||||
|
||||
DBMap * vending_getdb();
|
||||
|
@ -54,6 +54,7 @@
|
||||
#include "../map/script.hpp"
|
||||
#include "../map/skill.hpp"
|
||||
#include "../map/storage.hpp"
|
||||
#include "../map/tax.hpp"
|
||||
|
||||
using namespace rathena;
|
||||
|
||||
|
@ -51,6 +51,7 @@
|
||||
#include "../map/script.hpp"
|
||||
#include "../map/skill.hpp"
|
||||
#include "../map/storage.hpp"
|
||||
#include "../map/tax.hpp"
|
||||
|
||||
using namespace rathena;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user