[Feature] Add Min/Max Status to Merchants (#2806)

# Notes
- Allows operators to set a minimum and maximum status that an item will show for players.
- Allows operators to have items on a merchant that only a GM can see.
- Some servers may use status for different things, so having a minimum and a maximum will allow for more functionality.
- Default of `min_status` is `0` (Player) and default of `max_status` is `255` (Max).
This commit is contained in:
Alex King 2023-01-29 15:03:41 -05:00 committed by GitHub
parent b867d40774
commit 0fef46a6c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 120 additions and 95 deletions

View File

@ -3632,17 +3632,19 @@ struct LevelAppearance_Struct { //Sends a little graphic on level up
}; };
struct MerchantList { struct MerchantList {
uint32 id; uint32 id;
uint32 slot; uint32 slot;
uint32 item; uint32 item;
int16 faction_required; int16 faction_required;
int8 level_required; int8 level_required;
uint16 alt_currency_cost; uint8 min_status;
uint32 classes_required; uint8 max_status;
uint8 probability; uint16 alt_currency_cost;
uint32 classes_required;
uint8 probability;
std::string bucket_name; std::string bucket_name;
std::string bucket_value; std::string bucket_value;
uint8 bucket_comparison; uint8 bucket_comparison;
}; };
struct TempMerchantList { struct TempMerchantList {

View File

@ -16,6 +16,7 @@
#include "../../strings.h" #include "../../strings.h"
#include <ctime> #include <ctime>
class BaseMerchantlistRepository { class BaseMerchantlistRepository {
public: public:
struct Merchantlist { struct Merchantlist {
@ -24,6 +25,8 @@ public:
int32_t item; int32_t item;
int16_t faction_required; int16_t faction_required;
uint8_t level_required; uint8_t level_required;
uint8_t min_status;
uint8_t max_status;
uint16_t alt_currency_cost; uint16_t alt_currency_cost;
int32_t classes_required; int32_t classes_required;
int32_t probability; int32_t probability;
@ -49,6 +52,8 @@ public:
"item", "item",
"faction_required", "faction_required",
"level_required", "level_required",
"min_status",
"max_status",
"alt_currency_cost", "alt_currency_cost",
"classes_required", "classes_required",
"probability", "probability",
@ -70,6 +75,8 @@ public:
"item", "item",
"faction_required", "faction_required",
"level_required", "level_required",
"min_status",
"max_status",
"alt_currency_cost", "alt_currency_cost",
"classes_required", "classes_required",
"probability", "probability",
@ -125,6 +132,8 @@ public:
e.item = 0; e.item = 0;
e.faction_required = -100; e.faction_required = -100;
e.level_required = 0; e.level_required = 0;
e.min_status = 0;
e.max_status = 255;
e.alt_currency_cost = 0; e.alt_currency_cost = 0;
e.classes_required = 65535; e.classes_required = 65535;
e.probability = 100; e.probability = 100;
@ -160,8 +169,9 @@ public:
{ {
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
"{} WHERE id = {} LIMIT 1", "{} WHERE {} = {} LIMIT 1",
BaseSelect(), BaseSelect(),
PrimaryKey(),
merchantlist_id merchantlist_id
) )
); );
@ -175,16 +185,18 @@ public:
e.item = static_cast<int32_t>(atoi(row[2])); e.item = static_cast<int32_t>(atoi(row[2]));
e.faction_required = static_cast<int16_t>(atoi(row[3])); e.faction_required = static_cast<int16_t>(atoi(row[3]));
e.level_required = static_cast<uint8_t>(strtoul(row[4], nullptr, 10)); e.level_required = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
e.alt_currency_cost = static_cast<uint16_t>(strtoul(row[5], nullptr, 10)); e.min_status = static_cast<uint8_t>(strtoul(row[5], nullptr, 10));
e.classes_required = static_cast<int32_t>(atoi(row[6])); e.max_status = static_cast<uint8_t>(strtoul(row[6], nullptr, 10));
e.probability = static_cast<int32_t>(atoi(row[7])); e.alt_currency_cost = static_cast<uint16_t>(strtoul(row[7], nullptr, 10));
e.bucket_name = row[8] ? row[8] : ""; e.classes_required = static_cast<int32_t>(atoi(row[8]));
e.bucket_value = row[9] ? row[9] : ""; e.probability = static_cast<int32_t>(atoi(row[9]));
e.bucket_comparison = static_cast<uint8_t>(strtoul(row[10], nullptr, 10)); e.bucket_name = row[10] ? row[10] : "";
e.min_expansion = static_cast<int8_t>(atoi(row[11])); e.bucket_value = row[11] ? row[11] : "";
e.max_expansion = static_cast<int8_t>(atoi(row[12])); e.bucket_comparison = static_cast<uint8_t>(strtoul(row[12], nullptr, 10));
e.content_flags = row[13] ? row[13] : ""; e.min_expansion = static_cast<int8_t>(atoi(row[13]));
e.content_flags_disabled = row[14] ? row[14] : ""; e.max_expansion = static_cast<int8_t>(atoi(row[14]));
e.content_flags = row[15] ? row[15] : "";
e.content_flags_disabled = row[16] ? row[16] : "";
return e; return e;
} }
@ -223,16 +235,18 @@ public:
v.push_back(columns[2] + " = " + std::to_string(e.item)); v.push_back(columns[2] + " = " + std::to_string(e.item));
v.push_back(columns[3] + " = " + std::to_string(e.faction_required)); v.push_back(columns[3] + " = " + std::to_string(e.faction_required));
v.push_back(columns[4] + " = " + std::to_string(e.level_required)); v.push_back(columns[4] + " = " + std::to_string(e.level_required));
v.push_back(columns[5] + " = " + std::to_string(e.alt_currency_cost)); v.push_back(columns[5] + " = " + std::to_string(e.min_status));
v.push_back(columns[6] + " = " + std::to_string(e.classes_required)); v.push_back(columns[6] + " = " + std::to_string(e.max_status));
v.push_back(columns[7] + " = " + std::to_string(e.probability)); v.push_back(columns[7] + " = " + std::to_string(e.alt_currency_cost));
v.push_back(columns[8] + " = '" + Strings::Escape(e.bucket_name) + "'"); v.push_back(columns[8] + " = " + std::to_string(e.classes_required));
v.push_back(columns[9] + " = '" + Strings::Escape(e.bucket_value) + "'"); v.push_back(columns[9] + " = " + std::to_string(e.probability));
v.push_back(columns[10] + " = " + std::to_string(e.bucket_comparison)); v.push_back(columns[10] + " = '" + Strings::Escape(e.bucket_name) + "'");
v.push_back(columns[11] + " = " + std::to_string(e.min_expansion)); v.push_back(columns[11] + " = '" + Strings::Escape(e.bucket_value) + "'");
v.push_back(columns[12] + " = " + std::to_string(e.max_expansion)); v.push_back(columns[12] + " = " + std::to_string(e.bucket_comparison));
v.push_back(columns[13] + " = '" + Strings::Escape(e.content_flags) + "'"); v.push_back(columns[13] + " = " + std::to_string(e.min_expansion));
v.push_back(columns[14] + " = '" + Strings::Escape(e.content_flags_disabled) + "'"); v.push_back(columns[14] + " = " + std::to_string(e.max_expansion));
v.push_back(columns[15] + " = '" + Strings::Escape(e.content_flags) + "'");
v.push_back(columns[16] + " = '" + Strings::Escape(e.content_flags_disabled) + "'");
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@ -259,6 +273,8 @@ public:
v.push_back(std::to_string(e.item)); v.push_back(std::to_string(e.item));
v.push_back(std::to_string(e.faction_required)); v.push_back(std::to_string(e.faction_required));
v.push_back(std::to_string(e.level_required)); v.push_back(std::to_string(e.level_required));
v.push_back(std::to_string(e.min_status));
v.push_back(std::to_string(e.max_status));
v.push_back(std::to_string(e.alt_currency_cost)); v.push_back(std::to_string(e.alt_currency_cost));
v.push_back(std::to_string(e.classes_required)); v.push_back(std::to_string(e.classes_required));
v.push_back(std::to_string(e.probability)); v.push_back(std::to_string(e.probability));
@ -303,6 +319,8 @@ public:
v.push_back(std::to_string(e.item)); v.push_back(std::to_string(e.item));
v.push_back(std::to_string(e.faction_required)); v.push_back(std::to_string(e.faction_required));
v.push_back(std::to_string(e.level_required)); v.push_back(std::to_string(e.level_required));
v.push_back(std::to_string(e.min_status));
v.push_back(std::to_string(e.max_status));
v.push_back(std::to_string(e.alt_currency_cost)); v.push_back(std::to_string(e.alt_currency_cost));
v.push_back(std::to_string(e.classes_required)); v.push_back(std::to_string(e.classes_required));
v.push_back(std::to_string(e.probability)); v.push_back(std::to_string(e.probability));
@ -351,16 +369,18 @@ public:
e.item = static_cast<int32_t>(atoi(row[2])); e.item = static_cast<int32_t>(atoi(row[2]));
e.faction_required = static_cast<int16_t>(atoi(row[3])); e.faction_required = static_cast<int16_t>(atoi(row[3]));
e.level_required = static_cast<uint8_t>(strtoul(row[4], nullptr, 10)); e.level_required = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
e.alt_currency_cost = static_cast<uint16_t>(strtoul(row[5], nullptr, 10)); e.min_status = static_cast<uint8_t>(strtoul(row[5], nullptr, 10));
e.classes_required = static_cast<int32_t>(atoi(row[6])); e.max_status = static_cast<uint8_t>(strtoul(row[6], nullptr, 10));
e.probability = static_cast<int32_t>(atoi(row[7])); e.alt_currency_cost = static_cast<uint16_t>(strtoul(row[7], nullptr, 10));
e.bucket_name = row[8] ? row[8] : ""; e.classes_required = static_cast<int32_t>(atoi(row[8]));
e.bucket_value = row[9] ? row[9] : ""; e.probability = static_cast<int32_t>(atoi(row[9]));
e.bucket_comparison = static_cast<uint8_t>(strtoul(row[10], nullptr, 10)); e.bucket_name = row[10] ? row[10] : "";
e.min_expansion = static_cast<int8_t>(atoi(row[11])); e.bucket_value = row[11] ? row[11] : "";
e.max_expansion = static_cast<int8_t>(atoi(row[12])); e.bucket_comparison = static_cast<uint8_t>(strtoul(row[12], nullptr, 10));
e.content_flags = row[13] ? row[13] : ""; e.min_expansion = static_cast<int8_t>(atoi(row[13]));
e.content_flags_disabled = row[14] ? row[14] : ""; e.max_expansion = static_cast<int8_t>(atoi(row[14]));
e.content_flags = row[15] ? row[15] : "";
e.content_flags_disabled = row[16] ? row[16] : "";
all_entries.push_back(e); all_entries.push_back(e);
} }
@ -390,16 +410,18 @@ public:
e.item = static_cast<int32_t>(atoi(row[2])); e.item = static_cast<int32_t>(atoi(row[2]));
e.faction_required = static_cast<int16_t>(atoi(row[3])); e.faction_required = static_cast<int16_t>(atoi(row[3]));
e.level_required = static_cast<uint8_t>(strtoul(row[4], nullptr, 10)); e.level_required = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
e.alt_currency_cost = static_cast<uint16_t>(strtoul(row[5], nullptr, 10)); e.min_status = static_cast<uint8_t>(strtoul(row[5], nullptr, 10));
e.classes_required = static_cast<int32_t>(atoi(row[6])); e.max_status = static_cast<uint8_t>(strtoul(row[6], nullptr, 10));
e.probability = static_cast<int32_t>(atoi(row[7])); e.alt_currency_cost = static_cast<uint16_t>(strtoul(row[7], nullptr, 10));
e.bucket_name = row[8] ? row[8] : ""; e.classes_required = static_cast<int32_t>(atoi(row[8]));
e.bucket_value = row[9] ? row[9] : ""; e.probability = static_cast<int32_t>(atoi(row[9]));
e.bucket_comparison = static_cast<uint8_t>(strtoul(row[10], nullptr, 10)); e.bucket_name = row[10] ? row[10] : "";
e.min_expansion = static_cast<int8_t>(atoi(row[11])); e.bucket_value = row[11] ? row[11] : "";
e.max_expansion = static_cast<int8_t>(atoi(row[12])); e.bucket_comparison = static_cast<uint8_t>(strtoul(row[12], nullptr, 10));
e.content_flags = row[13] ? row[13] : ""; e.min_expansion = static_cast<int8_t>(atoi(row[13]));
e.content_flags_disabled = row[14] ? row[14] : ""; e.max_expansion = static_cast<int8_t>(atoi(row[14]));
e.content_flags = row[15] ? row[15] : "";
e.content_flags_disabled = row[16] ? row[16] : "";
all_entries.push_back(e); all_entries.push_back(e);
} }

View File

@ -42,7 +42,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/ */
#define CURRENT_BINARY_DATABASE_VERSION 9218 #define CURRENT_BINARY_DATABASE_VERSION 9219
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9037 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9037
#endif #endif

View File

@ -472,6 +472,7 @@
9216|2023_01_15_merc_data.sql|SHOW TABLES LIKE 'mercs'|empty| 9216|2023_01_15_merc_data.sql|SHOW TABLES LIKE 'mercs'|empty|
9217|2023_01_15_chatchannel_reserved_names.sql|SHOW TABLES LIKE 'chatchannel_reserved_names'|empty| 9217|2023_01_15_chatchannel_reserved_names.sql|SHOW TABLES LIKE 'chatchannel_reserved_names'|empty|
9218|2023_01_24_item_recast.sql|show columns from character_item_recast like '%recast_type%'|contains|smallint 9218|2023_01_24_item_recast.sql|show columns from character_item_recast like '%recast_type%'|contains|smallint
9219|2023_01_29_merchant_status_requirements.sql|SHOW COLUMNS FROM merchantlist LIKE 'min_status'|empty|
# Upgrade conditions: # Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not # This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,3 @@
ALTER TABLE `merchantlist`
ADD COLUMN `min_status` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `level_required`,
ADD COLUMN `max_status` tinyint(3) UNSIGNED NOT NULL DEFAULT 255 AFTER `min_status`;

View File

@ -872,6 +872,9 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
continue; continue;
} }
if (!EQ::ValueWithin(Admin(), static_cast<int16>(ml.min_status), static_cast<int16>(ml.max_status))) {
continue;
}
int32 faction_id = npc ? npc->GetPrimaryFaction() : 0; int32 faction_id = npc ? npc->GetPrimaryFaction() : 0;
int32 faction_level = ( int32 faction_level = (

View File

@ -59,6 +59,7 @@
#include "zone_reload.h" #include "zone_reload.h"
#include "../common/repositories/criteria/content_filter_criteria.h" #include "../common/repositories/criteria/content_filter_criteria.h"
#include "../common/repositories/content_flags_repository.h" #include "../common/repositories/content_flags_repository.h"
#include "../common/repositories/merchantlist_repository.h"
#include "../common/repositories/rule_sets_repository.h" #include "../common/repositories/rule_sets_repository.h"
#include "../common/repositories/zone_points_repository.h" #include "../common/repositories/zone_points_repository.h"
#include "../common/serverinfo.h" #include "../common/serverinfo.h"
@ -611,45 +612,34 @@ void Zone::LoadNewMerchantData(uint32 merchantid) {
std::list<MerchantList> merchant_list; std::list<MerchantList> merchant_list;
auto query = fmt::format( const auto& l = MerchantlistRepository::GetWhere(
SQL( content_db,
SELECT fmt::format(
item, "merchantid = {} {} ORDER BY slot",
slot, merchantid,
faction_required, ContentFilterCriteria::apply()
level_required, )
alt_currency_cost,
classes_required,
probability,
bucket_name,
bucket_value,
bucket_comparison
FROM merchantlist
WHERE merchantid = {} {}
ORDER BY slot
),
merchantid,
ContentFilterCriteria::apply()
); );
auto results = content_db.QueryDatabase(query); if (l.empty()) {
if (!results.Success()) { return;
return;
} }
for (auto row : results) { for (const auto& e : l) {
MerchantList ml; MerchantList ml;
ml.id = merchantid; ml.id = merchantid;
ml.item = std::stoul(row[0]); ml.item = e.item;
ml.slot = std::stoul(row[1]); ml.slot = e.slot;
ml.faction_required = static_cast<int16>(std::stoi(row[2])); ml.faction_required = e.faction_required;
ml.level_required = static_cast<uint8>(std::stoul(row[3])); ml.level_required = e.level_required;
ml.alt_currency_cost = static_cast<uint16>(std::stoul(row[4])); ml.min_status = e.min_status;
ml.classes_required = std::stoul(row[5]); ml.max_status = e.max_status;
ml.probability = static_cast<uint8>(std::stoul(row[6])); ml.alt_currency_cost = e.alt_currency_cost;
ml.bucket_name = row[7]; ml.classes_required = e.classes_required;
ml.bucket_value = row[8]; ml.probability = e.probability;
ml.bucket_comparison = static_cast<uint8>(std::stoul(row[9])); ml.bucket_name = e.bucket_name;
ml.bucket_value = e.bucket_value;
ml.bucket_comparison = e.bucket_comparison;
merchant_list.push_back(ml); merchant_list.push_back(ml);
} }
@ -665,6 +655,8 @@ void Zone::GetMerchantDataForZoneLoad() {
item, item,
faction_required, faction_required,
level_required, level_required,
min_status,
max_status,
alt_currency_cost, alt_currency_cost,
classes_required, classes_required,
probability, probability,
@ -725,16 +717,18 @@ void Zone::GetMerchantDataForZoneLoad() {
continue; continue;
} }
mle.slot = std::stoul(row[1]); mle.slot = std::stoul(row[1]);
mle.item = std::stoul(row[2]); mle.item = std::stoul(row[2]);
mle.faction_required = static_cast<int16>(std::stoi(row[3])); mle.faction_required = static_cast<int16>(std::stoi(row[3]));
mle.level_required = static_cast<uint8>(std::stoul(row[4])); mle.level_required = static_cast<uint8>(std::stoul(row[4]));
mle.alt_currency_cost = static_cast<uint16>(std::stoul(row[5])); mle.min_status = static_cast<uint8>(std::stoul(row[5]));
mle.classes_required = std::stoul(row[6]); mle.max_status = static_cast<uint8>(std::stoul(row[6]));
mle.probability = static_cast<uint8>(std::stoul(row[7])); mle.alt_currency_cost = static_cast<uint16>(std::stoul(row[7]));
mle.bucket_name = row[8]; mle.classes_required = std::stoul(row[8]);
mle.bucket_value = row[9]; mle.probability = static_cast<uint8>(std::stoul(row[9]));
mle.bucket_comparison = static_cast<uint8>(std::stoul(row[10])); mle.bucket_name = row[10];
mle.bucket_value = row[11];
mle.bucket_comparison = static_cast<uint8>(std::stoul(row[12]));
merchant_list->second.push_back(mle); merchant_list->second.push_back(mle);
} }