[Feature] Add Parcel Feature for RoF2 Clients (#4198)

* Add Parcel Feature

Add the parcel system for RoF2 client

* Fixed a duplicate define

* Reformat

reformating and review changes

* Further Formatting

* Memory Mgmt Updates

Refactored to using unique_ptr/make_unique/etc to avoid manual memory mgmt.

Other format changes

* Refactor db structure

Refactor for db structure of parcels to character_parcels
Removal of parcel_merchants
Addition of npc_types.is_parcel_merchant
Cleanup as a result

* Refactor to use item id 99990 for money transfers.  Removed the money string function as a result, though simplified the messaging related to money.  Other updates based on feedback.

* Move prune routine out of scheduler and into a world process.
Removed RuleI from #define

* Update

* Update database.cpp

* Update database_update_manifest.cpp

* Update main.cpp

* Update client_process.cpp

* Update parcels.cpp

* Remove parcel merchant content to optional sql instead of manifest.

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
Mitch Freeman
2024-04-20 23:15:56 -03:00
committed by GitHub
parent 64fefaebe4
commit fcffc6b3d4
58 changed files with 2505 additions and 230 deletions
+115 -43
View File
@@ -69,6 +69,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/events/player_event_logs.h"
#include "../common/repositories/character_stats_record_repository.h"
#include "dialogue_window.h"
#include "../common/rulesys.h"
extern QueryServ* QServ;
extern Zone* zone;
@@ -321,6 +322,8 @@ void MapOpcodes()
ConnectedOpcodes[OP_OpenGuildTributeMaster] = &Client::Handle_OP_OpenGuildTributeMaster;
ConnectedOpcodes[OP_OpenInventory] = &Client::Handle_OP_OpenInventory;
ConnectedOpcodes[OP_OpenTributeMaster] = &Client::Handle_OP_OpenTributeMaster;
ConnectedOpcodes[OP_ShopSendParcel] = &Client::Handle_OP_ShopSendParcel;
ConnectedOpcodes[OP_ShopRetrieveParcel] = &Client::Handle_OP_ShopRetrieveParcel;
ConnectedOpcodes[OP_PDeletePetition] = &Client::Handle_OP_PDeletePetition;
ConnectedOpcodes[OP_PetCommands] = &Client::Handle_OP_PetCommands;
ConnectedOpcodes[OP_Petition] = &Client::Handle_OP_Petition;
@@ -818,6 +821,10 @@ void Client::CompleteConnect()
);
}
if(ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) {
SendParcelStatus();
}
if (zone && zone->GetInstanceTimer()) {
bool is_permanent = false;
uint32 remaining_time = database.GetTimeRemainingInstance(zone->GetInstanceID(), is_permanent);
@@ -4271,6 +4278,12 @@ void Client::Handle_OP_Bug(const EQApplicationPacket *app)
void Client::Handle_OP_Camp(const EQApplicationPacket *app)
{
if (ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants) && GetEngagedWithParcelMerchant()) {
Stand();
MessageString(Chat::Yellow, TRADER_BUSY_TWO);
return;
}
if (IsLFP())
worldserver.StopLFP(CharacterID());
@@ -4324,6 +4337,12 @@ void Client::Handle_OP_CancelTrade(const EQApplicationPacket *app)
FinishTrade(this);
trade->Reset();
}
if (ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) {
DoParcelCancel();
SetEngagedWithParcelMerchant(false);
}
EQApplicationPacket end_trade1(OP_FinishWindow, 0);
QueuePacket(&end_trade1);
@@ -13877,6 +13896,11 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app)
void Client::Handle_OP_ShopEnd(const EQApplicationPacket *app)
{
if (ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) {
DoParcelCancel();
SetEngagedWithParcelMerchant(false);
}
EQApplicationPacket empty(OP_ShopEndConfirm);
QueuePacket(&empty);
return;
@@ -14423,82 +14447,107 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app)
{
if (app->size != sizeof(Merchant_Click_Struct)) {
LogError("Wrong size: OP_ShopRequest, size=[{}], expected [{}]", app->size, sizeof(Merchant_Click_Struct));
if (app->size != sizeof(MerchantClick_Struct)) {
LogError("Wrong size: OP_ShopRequest, size=[{}], expected [{}]", app->size, sizeof(MerchantClick_Struct));
return;
}
Merchant_Click_Struct* mc = (Merchant_Click_Struct*)app->pBuffer;
MerchantClick_Struct *mc = (MerchantClick_Struct *) app->pBuffer;
// Send back opcode OP_ShopRequest - tells client to open merchant window.
//EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct));
//Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer;
int merchantid = 0;
Mob* tmp = entity_list.GetMob(mc->npcid);
// EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct));
// Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer;
int merchant_id = 0;
int tabs_to_display = None;
Mob *tmp = entity_list.GetMob(mc->npc_id);
if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != Class::Merchant)
if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != Class::Merchant) {
return;
}
//you have to be somewhat close to them to be properly using them
if (DistanceSquared(m_Position, tmp->GetPosition()) > USE_NPC_RANGE2)
// you have to be somewhat close to them to be properly using them
if (DistanceSquared(m_Position, tmp->GetPosition()) > USE_NPC_RANGE2) {
return;
}
merchantid = tmp->CastToNPC()->MerchantType;
merchant_id = tmp->CastToNPC()->MerchantType;
if (ClientVersion() == EQ::versions::ClientVersion::RoF2 && tmp->CastToNPC()->GetParcelMerchant()) {
tabs_to_display = SellBuyParcel;
}
else {
tabs_to_display = SellBuy;
}
int action = MerchantActions::Open;
if (merchant_id == 0) {
auto outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(MerchantClick_Struct));
auto mco = (MerchantClick_Struct *) outapp->pBuffer;
mco->npc_id = mc->npc_id;
mco->player_id = 0;
mco->command = MerchantActions::Open;
mco->rate = 1.0;
mco->tab_display = tabs_to_display;
int action = 1;
if (merchantid == 0) {
auto outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct));
Merchant_Click_Struct* mco = (Merchant_Click_Struct*)outapp->pBuffer;
mco->npcid = mc->npcid;
mco->playerid = 0;
mco->command = 1; //open...
mco->rate = 1.0;
QueuePacket(outapp);
safe_delete(outapp);
return;
}
if (tmp->IsEngaged()) {
MessageString(Chat::White, MERCHANT_BUSY);
action = 0;
}
if (GetFeigned() || IsInvisible())
{
Message(Chat::White, "You cannot use a merchant right now.");
action = 0;
}
int primaryfaction = tmp->CastToNPC()->GetPrimaryFaction();
int factionlvl = GetFactionLevel(CharacterID(), tmp->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), primaryfaction, tmp);
if (factionlvl >= 7) {
MerchantRejectMessage(tmp, primaryfaction);
action = 0;
action = MerchantActions::Close;
}
if (tmp->Charmed())
action = 0;
if (GetFeigned() || IsInvisible()) {
Message(Chat::White, "You cannot use a merchant right now.");
action = MerchantActions::Close;
}
int primaryfaction = tmp->CastToNPC()->GetPrimaryFaction();
int factionlvl = GetFactionLevel(
CharacterID(), tmp->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(),
primaryfaction, tmp
);
if (factionlvl >= 7) {
MerchantRejectMessage(tmp, primaryfaction);
action = MerchantActions::Close;
}
if (tmp->Charmed()) {
action = MerchantActions::Close;
}
if (!tmp->CastToNPC()->IsMerchantOpen()) {
tmp->SayString(zone->random.Int(MERCHANT_CLOSED_ONE, MERCHANT_CLOSED_THREE));
action = 0;
action = MerchantActions::Close;
}
auto outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct));
Merchant_Click_Struct* mco = (Merchant_Click_Struct*)outapp->pBuffer;
auto outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(MerchantClick_Struct));
auto mco = (MerchantClick_Struct *) outapp->pBuffer;
mco->npc_id = mc->npc_id;
mco->player_id = 0;
mco->command = action; // Merchant command 0x01 = open
mco->tab_display = tabs_to_display;
mco->npcid = mc->npcid;
mco->playerid = 0;
mco->command = action; // Merchant command 0x01 = open
if (RuleB(Merchant, UsePriceMod)) {
mco->rate = 1 / ((RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(tmp, true)); // works
mco->rate = 1 / ((RuleR(Merchant, BuyCostMod)) * Client::CalcPriceMod(tmp, true)); // works
}
else
else {
mco->rate = 1 / (RuleR(Merchant, BuyCostMod));
}
outapp->priority = 6;
QueuePacket(outapp);
safe_delete(outapp);
if (action == 1)
BulkSendMerchantInventory(merchantid, tmp->GetNPCTypeID());
if (action == MerchantActions::Open) {
BulkSendMerchantInventory(merchant_id, tmp->GetNPCTypeID());
if ((tabs_to_display & Parcel) == Parcel) {
SendBulkParcels();
}
}
return;
}
@@ -17162,3 +17211,26 @@ void Client::Handle_OP_GuildTributeDonatePlat(const EQApplicationPacket *app)
}
}
void Client::Handle_OP_ShopSendParcel(const EQApplicationPacket *app)
{
if (app->size != sizeof(Parcel_Struct)) {
LogError("Received Handle_OP_ShopSendParcel packet. Expected size {}, received size {}.", sizeof(Parcel_Struct),
app->size);
return;
}
auto parcel_in = (Parcel_Struct *)app->pBuffer;
DoParcelSend(parcel_in);
}
void Client::Handle_OP_ShopRetrieveParcel(const EQApplicationPacket *app)
{
if (app->size != sizeof(ParcelRetrieve_Struct)) {
LogError("Received Handle_OP_ShopRetrieveParcel packet. Expected size {}, received size {}.",
sizeof(ParcelRetrieve_Struct), app->size);
return;
}
auto parcel_in = (ParcelRetrieve_Struct *)app->pBuffer;
DoParcelRetrieve(*parcel_in);
}