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.
|
// If set to yes, the rest of the zeny above the char's capacity will disappear.
|
||||||
vending_over_max: yes
|
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
|
// Show the buyer's name when successfully vended an item
|
||||||
buyer_name: yes
|
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)
|
// Hides items from the player's favorite tab from being sold to a NPC. (Note 1)
|
||||||
hide_fav_sell: no
|
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.
|
773: This command requires packet version 2016-10-12 or newer.
|
||||||
774: This command is disabled via configuration.
|
774: This command is disabled via configuration.
|
||||||
775: You have already opened the refine UI.
|
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
|
782: Star Emperor
|
||||||
783: Soul Reaper
|
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 "quest.hpp"
|
||||||
#include "script.hpp"
|
#include "script.hpp"
|
||||||
#include "storage.hpp"
|
#include "storage.hpp"
|
||||||
|
#include "tax.hpp"
|
||||||
#include "trade.hpp"
|
#include "trade.hpp"
|
||||||
#include "vending.hpp"
|
#include "vending.hpp"
|
||||||
|
|
||||||
@ -4318,6 +4319,9 @@ ACMD_FUNC(reload) {
|
|||||||
} else if (strstr(command, "attendancedb") || strncmp(message, "attendancedb", 4) == 0) {
|
} else if (strstr(command, "attendancedb") || strncmp(message, "attendancedb", 4) == 0) {
|
||||||
attendance_db.reload();
|
attendance_db.reload();
|
||||||
clif_displaymessage(fd, msg_txt(sd, 795)); // Attendance database has been reloaded.
|
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;
|
return 0;
|
||||||
@ -10954,6 +10958,7 @@ void atcommand_basecommands(void) {
|
|||||||
ACMD_DEF2("reloadinstancedb", reload),
|
ACMD_DEF2("reloadinstancedb", reload),
|
||||||
ACMD_DEF2("reloadachievementdb",reload),
|
ACMD_DEF2("reloadachievementdb",reload),
|
||||||
ACMD_DEF2("reloadattendancedb",reload),
|
ACMD_DEF2("reloadattendancedb",reload),
|
||||||
|
ACMD_DEF2("reloadtaxdb",reload),
|
||||||
ACMD_DEF(partysharelvl),
|
ACMD_DEF(partysharelvl),
|
||||||
ACMD_DEF(mapinfo),
|
ACMD_DEF(mapinfo),
|
||||||
ACMD_DEF(dye),
|
ACMD_DEF(dye),
|
||||||
|
@ -10030,8 +10030,6 @@ static const struct _battle_data {
|
|||||||
{ "hom_rename", &battle_config.hom_rename, 0, 0, 1, },
|
{ "hom_rename", &battle_config.hom_rename, 0, 0, 1, },
|
||||||
{ "homunculus_show_growth", &battle_config.homunculus_show_growth, 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, },
|
{ "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, },
|
{ "day_duration", &battle_config.day_duration, 0, 0, INT_MAX, },
|
||||||
{ "night_duration", &battle_config.night_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, },
|
{ "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, },
|
{ "idletime_mer_option", &battle_config.idletime_mer_option, 0x1F, 0x1, 0xFFF, },
|
||||||
{ "feature.refineui", &battle_config.feature_refineui, 1, 0, 1, },
|
{ "feature.refineui", &battle_config.feature_refineui, 1, 0, 1, },
|
||||||
{ "rndopt_drop_pillar", &battle_config.rndopt_drop_pillar, 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_legacy_formula", &battle_config.pet_legacy_formula, 0, 0, 1, },
|
||||||
{ "pet_distance_check", &battle_config.pet_distance_check, 5, 0, 50, },
|
{ "pet_distance_check", &battle_config.pet_distance_check, 5, 0, 50, },
|
||||||
{ "pet_hide_check", &battle_config.pet_hide_check, 1, 0, 1, },
|
{ "pet_hide_check", &battle_config.pet_hide_check, 1, 0, 1, },
|
||||||
|
@ -320,8 +320,6 @@ struct Battle_Config
|
|||||||
int dead_branch_active;
|
int dead_branch_active;
|
||||||
int vending_max_value;
|
int vending_max_value;
|
||||||
int vending_over_max;
|
int vending_over_max;
|
||||||
int vending_tax;
|
|
||||||
int vending_tax_min;
|
|
||||||
int show_steal_in_same_party;
|
int show_steal_in_same_party;
|
||||||
int party_share_type;
|
int party_share_type;
|
||||||
int party_hp_mode;
|
int party_hp_mode;
|
||||||
@ -691,6 +689,7 @@ struct Battle_Config
|
|||||||
int idletime_mer_option;
|
int idletime_mer_option;
|
||||||
int feature_refineui;
|
int feature_refineui;
|
||||||
int rndopt_drop_pillar;
|
int rndopt_drop_pillar;
|
||||||
|
int display_tax_info;
|
||||||
int pet_legacy_formula;
|
int pet_legacy_formula;
|
||||||
int pet_distance_check;
|
int pet_distance_check;
|
||||||
int pet_hide_check;
|
int pet_hide_check;
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "log.hpp" // log_pick_pc, log_zeny
|
#include "log.hpp" // log_pick_pc, log_zeny
|
||||||
#include "npc.hpp"
|
#include "npc.hpp"
|
||||||
#include "pc.hpp" // struct map_session_data
|
#include "pc.hpp" // struct map_session_data
|
||||||
|
#include "tax.hpp"
|
||||||
|
|
||||||
//Autotrader
|
//Autotrader
|
||||||
static DBMap *buyingstore_autotrader_db; /// Holds autotrader info: char_id -> struct s_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
|
sd->buyingstore.slots = i; // store actual amount of items
|
||||||
safestrncpy(sd->message, storename, sizeof(sd->message));
|
safestrncpy(sd->message, storename, sizeof(sd->message));
|
||||||
|
|
||||||
|
tax_db.setBuyingstoreTax(sd);
|
||||||
|
|
||||||
Sql_EscapeString( mmysql_handle, message_sql, sd->message );
|
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`) "
|
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);
|
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
|
* Start transaction
|
||||||
* @param sd Player/Seller
|
* @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
|
* @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 ){
|
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;
|
unsigned int weight;
|
||||||
struct map_session_data* pl_sd;
|
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;
|
pl_sd->buyingstore.zenylimit = pl_sd->status.zeny;
|
||||||
}
|
}
|
||||||
weight = pl_sd->weight;
|
weight = pl_sd->weight;
|
||||||
|
int tax_total = buyinstore_tax_intotal(pl_sd, itemlist, count);
|
||||||
|
double zeny = 0, zeny_paid = 0;
|
||||||
|
|
||||||
// check item list
|
// check item list
|
||||||
for( int i = 0; i < count; i++ ){
|
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;
|
weight += item->amount * sd->inventory_data[index]->weight;
|
||||||
|
|
||||||
// buyer does not have enough zeny
|
zeny += ((double)item->amount * (double)pl_sd->buyingstore.items[listidx].price);
|
||||||
if( item->amount * pl_sd->buyingstore.items[listidx].price > pl_sd->buyingstore.zenylimit - zeny ){
|
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 );
|
clif_buyingstore_trade_failed_seller( sd, BUYINGSTORE_TRADE_SELLER_ZENY, item->itemId );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if ((double)sd->status.zeny + zeny > (double)MAX_ZENY) { // Seller zeny overflow
|
||||||
zeny += item->amount * pl_sd->buyingstore.items[listidx].price;
|
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
|
// process item list
|
||||||
for( int i = 0; i < count; i++ ){
|
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;
|
int listidx;
|
||||||
|
|
||||||
ARR_FIND( 0, pl_sd->buyingstore.slots, listidx, pl_sd->buyingstore.items[listidx].nameid == item->itemId );
|
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
|
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
|
if (battle_config.display_tax_info) {
|
||||||
pc_payzeny(pl_sd, zeny, LOG_TYPE_BUYING_STORE, sd);
|
char msg[CHAT_SIZE_MAX];
|
||||||
pc_getzeny(sd, zeny, LOG_TYPE_BUYING_STORE, pl_sd);
|
|
||||||
pl_sd->buyingstore.zenylimit-= zeny;
|
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
|
// notify clients
|
||||||
clif_buyingstore_delete_item(sd, index, item->amount, pl_sd->buyingstore.items[listidx].price);
|
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 ) {
|
if( save_settings&CHARSAVE_VENDING ) {
|
||||||
|
@ -15,9 +15,10 @@ struct map_session_data;
|
|||||||
|
|
||||||
struct s_buyingstore_item
|
struct s_buyingstore_item
|
||||||
{
|
{
|
||||||
int price;
|
int price; ///< Value
|
||||||
unsigned short amount;
|
unsigned short amount; ///< Amount of items in Buyingstore
|
||||||
t_itemid nameid;
|
t_itemid nameid; ///< Item ID
|
||||||
|
unsigned int price_vat; ///< Value after tax
|
||||||
};
|
};
|
||||||
|
|
||||||
struct s_buyingstore
|
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"
|
/// 5 = "cannot use an npc shop while in a trade"
|
||||||
/// 6 = Because the store information was incorrect the item was not purchased.
|
/// 6 = Because the store information was incorrect the item was not purchased.
|
||||||
/// 7 = No sales information.
|
/// 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;
|
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,0) = 0x135;
|
||||||
WFIFOW(fd,2) = index+2;
|
WFIFOW(fd,2) = index+2;
|
||||||
WFIFOW(fd,4) = amount;
|
WFIFOW(fd,4) = amount;
|
||||||
WFIFOB(fd,6) = fail;
|
WFIFOB(fd,6) = ack;
|
||||||
WFIFOSET(fd,packet_len(0x135));
|
WFIFOSET(fd,packet_len(0x135));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,6 +209,16 @@ enum class e_purchase_result : uint8{
|
|||||||
PURCHASE_FAIL_ADD = 0xff,
|
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
|
#define packet_len(cmd) packet_db[cmd].len
|
||||||
extern struct s_packet_db packet_db[MAX_PACKET_DB+1];
|
extern struct s_packet_db packet_db[MAX_PACKET_DB+1];
|
||||||
extern int packet_db_ack[MAX_ACK_FUNC + 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_showvendingboard(struct block_list* bl, const char* message, int fd);
|
||||||
void clif_closevendingboard(struct block_list* bl, 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_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_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);
|
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="skill.hpp" />
|
||||||
<ClInclude Include="status.hpp" />
|
<ClInclude Include="status.hpp" />
|
||||||
<ClInclude Include="storage.hpp" />
|
<ClInclude Include="storage.hpp" />
|
||||||
|
<ClInclude Include="tax.hpp" />
|
||||||
<ClInclude Include="trade.hpp" />
|
<ClInclude Include="trade.hpp" />
|
||||||
<ClInclude Include="unit.hpp" />
|
<ClInclude Include="unit.hpp" />
|
||||||
<ClInclude Include="vending.hpp" />
|
<ClInclude Include="vending.hpp" />
|
||||||
@ -271,6 +272,7 @@
|
|||||||
<ClCompile Include="skill.cpp" />
|
<ClCompile Include="skill.cpp" />
|
||||||
<ClCompile Include="status.cpp" />
|
<ClCompile Include="status.cpp" />
|
||||||
<ClCompile Include="storage.cpp" />
|
<ClCompile Include="storage.cpp" />
|
||||||
|
<ClCompile Include="tax.cpp" />
|
||||||
<ClCompile Include="trade.cpp" />
|
<ClCompile Include="trade.cpp" />
|
||||||
<ClCompile Include="unit.cpp" />
|
<ClCompile Include="unit.cpp" />
|
||||||
<ClCompile Include="vending.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\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\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\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_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_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')" />
|
<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">
|
<ClInclude Include="storage.hpp">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="tax.hpp">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="trade.hpp">
|
<ClInclude Include="trade.hpp">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@ -271,6 +274,9 @@
|
|||||||
<ClCompile Include="storage.cpp">
|
<ClCompile Include="storage.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="tax.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="trade.cpp">
|
<ClCompile Include="trade.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
#include "pet.hpp"
|
#include "pet.hpp"
|
||||||
#include "quest.hpp"
|
#include "quest.hpp"
|
||||||
#include "storage.hpp"
|
#include "storage.hpp"
|
||||||
|
#include "tax.hpp"
|
||||||
#include "trade.hpp"
|
#include "trade.hpp"
|
||||||
|
|
||||||
using namespace rathena;
|
using namespace rathena;
|
||||||
@ -4900,6 +4901,7 @@ void do_final(void){
|
|||||||
do_final_vending();
|
do_final_vending();
|
||||||
do_final_buyingstore();
|
do_final_buyingstore();
|
||||||
do_final_path();
|
do_final_path();
|
||||||
|
do_final_tax();
|
||||||
|
|
||||||
map_db->destroy(map_db, map_db_final);
|
map_db->destroy(map_db, map_db_final);
|
||||||
|
|
||||||
@ -5229,6 +5231,7 @@ int do_init(int argc, char *argv[])
|
|||||||
do_init_quest();
|
do_init_quest();
|
||||||
do_init_achievement();
|
do_init_achievement();
|
||||||
do_init_battleground();
|
do_init_battleground();
|
||||||
|
do_init_tax();
|
||||||
do_init_npc();
|
do_init_npc();
|
||||||
do_init_unit();
|
do_init_unit();
|
||||||
do_init_duel();
|
do_init_duel();
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
#include "pet.hpp"
|
#include "pet.hpp"
|
||||||
#include "quest.hpp"
|
#include "quest.hpp"
|
||||||
#include "storage.hpp"
|
#include "storage.hpp"
|
||||||
|
#include "tax.hpp" // e_tax_type
|
||||||
|
|
||||||
using namespace rathena;
|
using namespace rathena;
|
||||||
|
|
||||||
|
@ -8496,6 +8496,10 @@
|
|||||||
/* timer related */
|
/* timer related */
|
||||||
export_constant(INFINITE_TICK);
|
export_constant(INFINITE_TICK);
|
||||||
|
|
||||||
|
/* tax related */
|
||||||
|
export_constant(TAX_SELLING);
|
||||||
|
export_constant(TAX_BUYING);
|
||||||
|
|
||||||
/* block action */
|
/* block action */
|
||||||
export_constant(PCBLOCK_MOVE);
|
export_constant(PCBLOCK_MOVE);
|
||||||
export_constant(PCBLOCK_ATTACK);
|
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 "path.hpp"
|
||||||
#include "pc.hpp"
|
#include "pc.hpp"
|
||||||
#include "pc_groups.hpp"
|
#include "pc_groups.hpp"
|
||||||
|
#include "tax.hpp"
|
||||||
|
|
||||||
static uint32 vending_nextid = 0; ///Vending_id counter
|
static uint32 vending_nextid = 0; ///Vending_id counter
|
||||||
static DBMap *vending_db; ///DB holder the vender : charid -> map_session_data
|
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
|
* Return the total amount after taxes
|
||||||
* @param sd: Vender
|
* @param vsd: Vendor player
|
||||||
* @param zeny: Total amount to tax
|
* @param data: Item data
|
||||||
* @return Total amount after taxes
|
* @param count: Number of different items
|
||||||
|
* @return Total price after taxes
|
||||||
*/
|
*/
|
||||||
static double vending_calc_tax(struct map_session_data *sd, double zeny)
|
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);
|
||||||
if (battle_config.vending_tax && zeny >= battle_config.vending_tax_min)
|
double total = 0;
|
||||||
zeny -= zeny * (battle_config.vending_tax / 10000.);
|
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)
|
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];
|
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 s_vending vending[MAX_VENDING]; // against duplicate packets
|
||||||
struct map_session_data* vsd = map_id2sd(aid);
|
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
|
return; // invalid shop
|
||||||
|
|
||||||
if( vsd->vender_id != uid ) { // shop has changed
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,6 +178,7 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui
|
|||||||
// some checks
|
// some checks
|
||||||
z = 0.; // zeny counter
|
z = 0.; // zeny counter
|
||||||
w = 0; // weight counter
|
w = 0; // weight counter
|
||||||
|
int tax_total = vending_tax_intotal(vsd, data, count);
|
||||||
for( i = 0; i < count; i++ ) {
|
for( i = 0; i < count; i++ ) {
|
||||||
short amount = *(uint16*)(data + 4*i + 0);
|
short amount = *(uint16*)(data + 4*i + 0);
|
||||||
short idx = *(uint16*)(data + 4*i + 2);
|
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;
|
vend_list[i] = j;
|
||||||
|
|
||||||
z += ((double)vsd->vending[j].value * (double)amount);
|
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 ) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if( z + (double)vsd->status.zeny > (double)MAX_ZENY && !battle_config.vending_over_max ) {
|
if(z_gained + (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
|
clif_buyvending(sd, idx, vsd->vending[j].amount, VENDING_ACK_NOSTOCK); // too much zeny = overflow
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
w += itemdb_weight(vsd->cart.u.items_cart[idx].nameid) * amount;
|
w += itemdb_weight(vsd->cart.u.items_cart[idx].nameid) * amount;
|
||||||
if( w + sd->weight > sd->max_weight ) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +223,7 @@ void vending_purchasereq(struct map_session_data* sd, int aid, int uid, const ui
|
|||||||
// here, we check cumulative amounts
|
// here, we check cumulative amounts
|
||||||
if( vending[j].amount < amount ) {
|
if( vending[j].amount < amount ) {
|
||||||
// send more quantity is not a hack (an other player can have buy items just before)
|
// 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;
|
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);
|
pc_payzeny(sd, (int)z, LOG_TYPE_VENDING, vsd);
|
||||||
achievement_update_objective(sd, AG_SPEND_ZENY, 1, (int)z);
|
achievement_update_objective(sd, AG_SPEND_ZENY, 1, (int)z);
|
||||||
z = vending_calc_tax(sd, z);
|
pc_getzeny(vsd, (int)z_gained, LOG_TYPE_VENDING, sd);
|
||||||
pc_getzeny(vsd, (int)z, 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++ ) {
|
for( i = 0; i < count; i++ ) {
|
||||||
short amount = *(uint16*)(data + 4*i + 0);
|
short amount = *(uint16*)(data + 4*i + 0);
|
||||||
short idx = *(uint16*)(data + 4*i + 2);
|
short idx = *(uint16*)(data + 4*i + 2);
|
||||||
idx -= 2;
|
idx -= 2;
|
||||||
z = 0.; // zeny counter
|
|
||||||
|
|
||||||
// vending item
|
// vending item
|
||||||
pc_additem(sd, &vsd->cart.u.items_cart[idx], amount, LOG_TYPE_VENDING);
|
pc_additem(sd, &vsd->cart.u.items_cart[idx], amount, LOG_TYPE_VENDING);
|
||||||
vsd->vending[vend_list[i]].amount -= amount;
|
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( 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 ) {
|
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);
|
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_gained);
|
||||||
clif_vendingreport(vsd, idx, amount, sd->status.char_id, (int)z);
|
|
||||||
|
|
||||||
//print buyer's name
|
//print buyer's name
|
||||||
if( battle_config.buyer_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].index = vsd->vending[i].index;
|
||||||
vsd->vending[cursor].amount = vsd->vending[i].amount;
|
vsd->vending[cursor].amount = vsd->vending[i].amount;
|
||||||
vsd->vending[cursor].value = vsd->vending[i].value;
|
vsd->vending[cursor].value = vsd->vending[i].value;
|
||||||
|
vsd->vending[cursor].value_vat = vsd->vending[i].value_vat;
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor++;
|
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].index = index;
|
||||||
sd->vending[i].amount = amount;
|
sd->vending[i].amount = amount;
|
||||||
sd->vending[i].value = min(value, (unsigned int)battle_config.vending_max_value);
|
sd->vending[i].value = min(value, (unsigned int)battle_config.vending_max_value);
|
||||||
|
|
||||||
i++; // item successfully added
|
i++; // item successfully added
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,6 +410,8 @@ int8 vending_openvending(struct map_session_data* sd, const char* message, const
|
|||||||
sd->vend_num = i;
|
sd->vend_num = i;
|
||||||
safestrncpy(sd->message, message, MESSAGE_SIZE);
|
safestrncpy(sd->message, message, MESSAGE_SIZE);
|
||||||
|
|
||||||
|
tax_db.setVendingTax(sd);
|
||||||
|
|
||||||
Sql_EscapeString( mmysql_handle, message_sql, sd->message );
|
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`) "
|
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
|
* called in map::do_init
|
||||||
*/
|
*/
|
||||||
void do_final_vending(void)
|
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
|
* called in map::do_final
|
||||||
*/
|
*/
|
||||||
void do_init_vending(void)
|
void do_init_vending(void)
|
||||||
|
@ -13,9 +13,10 @@ struct s_search_store_search;
|
|||||||
struct s_autotrader;
|
struct s_autotrader;
|
||||||
|
|
||||||
struct s_vending {
|
struct s_vending {
|
||||||
short index; /// cart index (return item data)
|
short index; ///< Cart index (return item data)
|
||||||
short amount; ///amout of the item for vending
|
short amount; ///< Amount of the item for vending
|
||||||
unsigned int value; ///at wich price
|
unsigned int value; ///< Price
|
||||||
|
unsigned int value_vat; ///< Value after tax
|
||||||
};
|
};
|
||||||
|
|
||||||
DBMap * vending_getdb();
|
DBMap * vending_getdb();
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
#include "../map/script.hpp"
|
#include "../map/script.hpp"
|
||||||
#include "../map/skill.hpp"
|
#include "../map/skill.hpp"
|
||||||
#include "../map/storage.hpp"
|
#include "../map/storage.hpp"
|
||||||
|
#include "../map/tax.hpp"
|
||||||
|
|
||||||
using namespace rathena;
|
using namespace rathena;
|
||||||
|
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
#include "../map/script.hpp"
|
#include "../map/script.hpp"
|
||||||
#include "../map/skill.hpp"
|
#include "../map/skill.hpp"
|
||||||
#include "../map/storage.hpp"
|
#include "../map/storage.hpp"
|
||||||
|
#include "../map/tax.hpp"
|
||||||
|
|
||||||
using namespace rathena;
|
using namespace rathena;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user