mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-17 03:08:26 +00:00
[Merchants] Add New Classic Greed/Faction/Charisma Prices Rule (#4301)
* [Merchants] Add New Classic Greed/Faction/Charisma Prices Rule
* Fix size of greed field.
* Fix { formatting and add {} to one liners
* Fix return type of GetGreedPercent
* Remove code that slipped in from another patch
* Fix greed to be unsigned
* Update client.cpp
* Update client_packet.cpp
* Update client.cpp
Fix bad name in extra log message added manually from merge.
* Update client_packet.cpp
Spacing.
* Update client.cpp
---------
Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
This commit is contained in:
+89
-2
@@ -3761,7 +3761,83 @@ void Client::Escape()
|
||||
MessageString(Chat::Skills, ESCAPE);
|
||||
}
|
||||
|
||||
float Client::CalcPriceMod(Mob* other, bool reverse)
|
||||
float Client::CalcClassicPriceMod(Mob* other, bool reverse) {
|
||||
float price_multiplier = 0.8f;
|
||||
|
||||
if (other && other->IsNPC()) {
|
||||
FACTION_VALUE faction_level = GetFactionLevel(CharacterID(), other->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), other->CastToNPC()->GetPrimaryFaction(), other);
|
||||
int32 cha = GetCHA();
|
||||
|
||||
if (faction_level <= FACTION_AMIABLY) {
|
||||
cha += 11; // amiable faction grants a defacto 11 charisma bonus
|
||||
}
|
||||
|
||||
uint8 greed = other->CastToNPC()->GetGreedPercent();
|
||||
|
||||
// Sony's precise algorithm is unknown, but this produces output that is virtually identical
|
||||
if (faction_level <= FACTION_INDIFFERENTLY) {
|
||||
if (cha > 75) {
|
||||
if (greed) {
|
||||
// this is derived from curve fitting to a lot of price data
|
||||
price_multiplier = -0.2487768 + (1.599635 - -0.2487768) / (1 + pow((cha / 135.1495), 1.001983));
|
||||
price_multiplier += (greed + 25u) / 100.0f; // default vendor markup is 25%; anything above that is 'greedy'
|
||||
price_multiplier = 1.0f / price_multiplier;
|
||||
}
|
||||
else {
|
||||
// non-greedy merchants use a linear scale
|
||||
price_multiplier = 1.0f - ((115.0f - cha) * 0.004f);
|
||||
}
|
||||
}
|
||||
else if (cha > 60) {
|
||||
price_multiplier = 1.0f / (1.25f + (greed / 100.0f));
|
||||
}
|
||||
else {
|
||||
price_multiplier = 1.0f / ((1.0f - (cha - 120.0f) / 220.0f) + (greed / 100.0f));
|
||||
}
|
||||
}
|
||||
else { // apprehensive
|
||||
if (cha > 75) {
|
||||
if (greed) {
|
||||
// this is derived from curve fitting to a lot of price data
|
||||
price_multiplier = -0.25f + (1.823662 - -0.25f) / (1 + (cha / 135.0f));
|
||||
price_multiplier += (greed + 25u) / 100.0f; // default vendor markup is 25%; anything above that is 'greedy'
|
||||
price_multiplier = 1.0f / price_multiplier;
|
||||
}
|
||||
else {
|
||||
price_multiplier = (100.0f - (145.0f - cha) / 2.8f) / 100.0f;
|
||||
}
|
||||
}
|
||||
else if (cha > 60) {
|
||||
price_multiplier = 1.0f / (1.4f + greed / 100.0f);
|
||||
}
|
||||
else {
|
||||
price_multiplier = 1.0f / ((1.0f + (143.574 - cha) / 196.434) + (greed / 100.0f));
|
||||
}
|
||||
}
|
||||
|
||||
float maxResult = 1.0f / 1.05; // price reduction caps at this amount
|
||||
if (price_multiplier > maxResult) {
|
||||
price_multiplier = maxResult;
|
||||
}
|
||||
|
||||
if (!reverse) {
|
||||
price_multiplier = 1.0f / price_multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
LogMerchants(
|
||||
"[{}] [{}] items at [{}] price multiplier [{}] [{}]",
|
||||
other->GetName(),
|
||||
reverse ? "buys" : "sells",
|
||||
price_multiplier,
|
||||
reverse ? "from" : "to",
|
||||
GetName()
|
||||
);
|
||||
|
||||
return price_multiplier;
|
||||
}
|
||||
|
||||
float Client::CalcNewPriceMod(Mob* other, bool reverse)
|
||||
{
|
||||
float chaformula = 0;
|
||||
if (other)
|
||||
@@ -3807,6 +3883,17 @@ float Client::CalcPriceMod(Mob* other, bool reverse)
|
||||
return chaformula; //Returns 1.10, expensive stuff!
|
||||
}
|
||||
|
||||
float Client::CalcPriceMod(Mob* other, bool reverse)
|
||||
{
|
||||
float price_mod = CalcNewPriceMod(other, reverse);
|
||||
|
||||
if (RuleB(Merchant, UseClassicPriceMod)) {
|
||||
price_mod = CalcClassicPriceMod(other, reverse);
|
||||
}
|
||||
|
||||
return price_mod;
|
||||
}
|
||||
|
||||
void Client::GetGroupAAs(GroupLeadershipAA_Struct *into) const {
|
||||
memcpy(into, &m_pp.leader_abilities.group, sizeof(GroupLeadershipAA_Struct));
|
||||
}
|
||||
@@ -12474,4 +12561,4 @@ void Client::RemoveItemBySerialNumber(uint32 serial_number, uint32 quantity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1127,6 +1127,8 @@ public:
|
||||
void GoFish(bool guarantee = false, bool use_bait = true);
|
||||
void ForageItem(bool guarantee = false);
|
||||
//Calculate vendor price modifier based on CHA: (reverse==selling)
|
||||
float CalcClassicPriceMod(Mob* other = 0, bool reverse = false);
|
||||
float CalcNewPriceMod(Mob* other = 0, bool reverse = false);
|
||||
float CalcPriceMod(Mob* other = 0, bool reverse = false);
|
||||
void ResetTrade();
|
||||
void DropInst(const EQ::ItemInstance* inst);
|
||||
|
||||
+37
-16
@@ -14041,16 +14041,21 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
|
||||
|
||||
EQ::ItemInstance* inst = database.CreateItem(item, charges);
|
||||
|
||||
int SinglePrice = 0;
|
||||
if (RuleB(Merchant, UsePriceMod))
|
||||
SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(tmp, false));
|
||||
else
|
||||
SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate);
|
||||
int single_price = (item->Price * item->SellRate);
|
||||
|
||||
// Don't use SellCostMod if using UseClassicPriceMod
|
||||
if (!RuleB(Merchant, UseClassicPriceMod)) {
|
||||
single_price *= RuleR(Merchant, SellCostMod);
|
||||
}
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
single_price *= Client::CalcPriceMod(tmp, false);
|
||||
}
|
||||
|
||||
if (item->MaxCharges > 1)
|
||||
mpo->price = SinglePrice;
|
||||
mpo->price = single_price;
|
||||
else
|
||||
mpo->price = SinglePrice * mp->quantity;
|
||||
mpo->price = single_price * mp->quantity;
|
||||
|
||||
if (mpo->price < 0)
|
||||
{
|
||||
@@ -14131,7 +14136,7 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
|
||||
else {
|
||||
// Update the charges/quantity in the merchant window
|
||||
inst->SetCharges(new_charges);
|
||||
inst->SetPrice(SinglePrice);
|
||||
inst->SetPrice(single_price);
|
||||
inst->SetMerchantSlot(mp->itemslot);
|
||||
inst->SetMerchantCount(new_charges);
|
||||
|
||||
@@ -14310,7 +14315,15 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
for (i = 1; i <= cost_quantity; i++) {
|
||||
price = (uint32)((item->Price * i)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor, true) + 0.5); // need to round up, because client does it automatically when displaying price
|
||||
price = (uint32)(item->Price * i) * Client::CalcPriceMod(vendor, true);
|
||||
|
||||
// Don't use SellCostMod if using UseClassicPriceMod
|
||||
if (!RuleB(Merchant, UseClassicPriceMod)) {
|
||||
price *= RuleR(Merchant, BuyCostMod);
|
||||
}
|
||||
|
||||
price += 0.5; // need to round up, because client does it automatically when displaying price
|
||||
|
||||
if (price > 4000000000) {
|
||||
cost_quantity = i;
|
||||
mp->quantity = i;
|
||||
@@ -14360,11 +14373,12 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
break;
|
||||
}
|
||||
|
||||
uint32 price = (
|
||||
item->Price *
|
||||
RuleR(Merchant, SellCostMod) *
|
||||
item->SellRate
|
||||
);
|
||||
uint32 price = (item->Price * item->SellRate);
|
||||
|
||||
// Don't use SellCostMod if using UseClassicPriceMod
|
||||
if (!RuleB(Merchant, UseClassicPriceMod)) {
|
||||
price *= RuleR(Merchant, SellCostMod);
|
||||
}
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
price *= Client::CalcPriceMod(vendor, false);
|
||||
@@ -14571,11 +14585,18 @@ void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app)
|
||||
mco->command = action; // Merchant command 0x01 = open
|
||||
mco->tab_display = tabs_to_display;
|
||||
|
||||
float buy_cost_mod = 1;
|
||||
|
||||
// Only use the BuyCostMod if we're not using the classic function.
|
||||
if (!RuleB(Merchant, UseClassicPriceMod)) {
|
||||
buy_cost_mod = RuleR(Merchant, BuyCostMod);
|
||||
}
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
mco->rate = 1 / ((RuleR(Merchant, BuyCostMod)) * Client::CalcPriceMod(tmp, true)); // works
|
||||
mco->rate = 1 / (buy_cost_mod * Client::CalcPriceMod(tmp, true));
|
||||
}
|
||||
else {
|
||||
mco->rate = 1 / (RuleR(Merchant, BuyCostMod));
|
||||
mco->rate = 1 / buy_cost_mod;
|
||||
}
|
||||
|
||||
outapp->priority = 6;
|
||||
|
||||
+12
-2
@@ -902,9 +902,14 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
|
||||
|
||||
auto inst = database.CreateItem(item, charges);
|
||||
if (inst) {
|
||||
auto item_price = static_cast<uint32>(item->Price * RuleR(Merchant, SellCostMod) * item->SellRate);
|
||||
auto item_price = static_cast<uint32>(item->Price * item->SellRate);
|
||||
auto item_charges = charges ? charges : 1;
|
||||
|
||||
// Don't use SellCostMod if using UseClassicPriceMod
|
||||
if (!RuleB(Merchant, UseClassicPriceMod)) {
|
||||
item_price *= RuleR(Merchant, SellCostMod);
|
||||
}
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
item_price *= Client::CalcPriceMod(npc);
|
||||
}
|
||||
@@ -948,9 +953,14 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
|
||||
auto charges = item->MaxCharges;
|
||||
auto inst = database.CreateItem(item, charges);
|
||||
if (inst) {
|
||||
auto item_price = static_cast<uint32>(item->Price * RuleR(Merchant, SellCostMod) * item->SellRate);
|
||||
auto item_price = static_cast<uint32>(item->Price * item->SellRate);
|
||||
auto item_charges = charges ? charges : 1;
|
||||
|
||||
// Don't use SellCostMod if using UseClassicPriceMod
|
||||
if (!RuleB(Merchant, UseClassicPriceMod)) {
|
||||
item_price *= RuleR(Merchant, SellCostMod);
|
||||
}
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
item_price *= Client::CalcPriceMod(npc);
|
||||
}
|
||||
|
||||
@@ -268,6 +268,7 @@ public:
|
||||
inline void MerchantOpenShop() { merchant_open = true; }
|
||||
inline void MerchantCloseShop() { merchant_open = false; }
|
||||
inline bool IsMerchantOpen() { return merchant_open; }
|
||||
inline uint8 GetGreedPercent() { return NPCTypedata->greed; }
|
||||
inline bool GetParcelMerchant() { return NPCTypedata->is_parcel_merchant; }
|
||||
void Depop(bool start_spawn_timer = false);
|
||||
void Stun(int duration);
|
||||
|
||||
@@ -1793,6 +1793,7 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
|
||||
t->max_dmg = n.maxdmg;
|
||||
t->attack_count = n.attack_count;
|
||||
t->is_parcel_merchant = n.is_parcel_merchant ? true : false;
|
||||
t->greed = n.greed;
|
||||
|
||||
if (!n.special_abilities.empty()) {
|
||||
strn0cpy(t->special_abilities, n.special_abilities.c_str(), 512);
|
||||
|
||||
@@ -155,6 +155,7 @@ struct NPCType
|
||||
int heroic_strikethrough;
|
||||
bool keeps_sold_items;
|
||||
bool is_parcel_merchant;
|
||||
uint8 greed;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
Reference in New Issue
Block a user