Vault, Broker support for DoF, KoS, AoM. Shop support for DoF, KoS.
Broker/Vault (Fix #10): - Traditional broker integrated (/frombroker), GM itemsearch is still available via /itemsearch. AoM client cannot sell/use shop, can only buy from broker. - DoF and KoS client now support House Shop (sell inventory, vault to broker). - House containers now function like actual containers and can be stored in the vault which will allow placement in the home (to serve as a merchant). - Sales crate merchant windows in player houses implemented for broker. - Peering URI's added for broker/shop support: /addseller, /removeseller, /additemsale, /removeitemsale - House vault and vault slots implemented for DoF, KoS and AoM clients. Stability and Functionality: - World Database now has seq_character_items (Sequence) to sync self and peers instantiating new unique IDs for character items. This is to avoid conflict/overriding another unique id. - spawn and spawn_houses tables have a lua_script that can now be defined outside the existing spawn_scripts table for a singular spawn. - Fixed a watchdog/hangup when clearing hate lists inside a spawn list lock. - Item unique id is being transitioned to int64 (although older clients still only support int32, clients later on are int64). - Fixed so that spells that have no duration will no longer be added to maintained effects. - Fixed spell cleanup, maintained effect does not remain on the Player causing a crash. - Fixed spell conflicts to that check all targets are applicable for the spell. - Fixed issues with maintained effects or spell effects stacking repeatedly. - Fixed assigning items to non inventory slots when 'adding' an item to the Player. - Fixed locking orders between maintained effects and spell effects to avoid deadlocks. - Fixed entering house and visiting houses, targetting of the house door is now enforced server side. - Item locking is now enforced by the type of locking (eg. house placement, crafting, shop list for sale). Locks no longer override/interfere with each other. - Additional logging around spell casting and targets. - Spawns/Objects/Widgets so on related to houses now have their own sub tables _houses, eg. spawn_houses, spawn_npc_houses, spawn_object_houses, so on to avoid conflicting with existing tables non-house. - new LUA Functions: ShowShopWindow(Player, FromSpawn) - opens shop window for player (if in their house) for listing, pricing items, retrieving sales log and coin, etc. SetSpawnHouseScript(Target, LuaScript) - Utilized in the item script 'placed' function to set the spawned house item's lua script. SendBook(Target, ItemID) - Sends the book to the target player based on the item id. GetPickupItemID(Spawn) - Gets the item id that the house spawn would represent SetHouseCharacterID(Spawn, CharID) - Sets the house spawn character id (should be used on Spawn). Set CharID to 0 to set to the current houses character id. - Updated LUA Functions: StartHeroicOpportunity(Caster, ClassID, OverrideTarget) - OverrideTarget now available to change the heroic opportunity target HasItem(Player, ItemID, IncludeBank) - No parameter change, include bank, which serves as 'all' should now work correctly (previously did not check bank/other negative slots), default is false. - Slash Commands Added: /sle - Fix #41, Set Location Entry (DB command for setting spawn location entry values such as offsets and overrides) /store_list_item - used to list an item for broker shop /store_set_price - used for setting items price for shop /store_set_price_local - used for inventory items price for shop /store_start_selling - Begin selling from inventory for shop /store_stop_selling - Stop selling from inventory /store_unlist_item - used to unlist an item from broker shop /close_store_keep_selling - Closes store shop window, but keep selling from inventory while in house /cancel_store - cancel selling from inventory
This commit is contained in:
parent
21052e3289
commit
f60d261f00
@ -10469,15 +10469,21 @@ to zero and treated like placeholders." />
|
||||
<Data ElementName="your_item_count" Type="int16" />
|
||||
<Data ElementName="your_item_array" Type="Array" ArraySizeVariable="your_item_count">
|
||||
<Data ElementName="your_item_name" Type="EQ2_8Bit_String" />
|
||||
<Data ElementName="unique_id" Type="int64" />
|
||||
<Data ElementName="cost" Type="int64" />
|
||||
<Data ElementName="unique_id2" Type="int64" />
|
||||
<Data ElementName="your_item_quantity" Type="int16" />
|
||||
<Data ElementName="your_item_icon" Type="int16" />
|
||||
<Data ElementName="your_item_unknown4" Type ="int8" Size="4" />
|
||||
<Data ElementName="your_item_background" Type="int8" />
|
||||
<Data ElementName="your_item_unknown4" Type ="int8" Size="9" />
|
||||
<Data ElementName="your_item_unknown5" Type ="int8" Size="1" />
|
||||
<Data ElementName="storage_flags" Type ="int8" />
|
||||
<Data ElementName="your_item_unknown6" Type ="int8" Size="12" />
|
||||
</Data>
|
||||
<Data ElementName="type" Type="int8"/> <!-- 4 opens window -->
|
||||
<Data ElementName="type" Type="int8"/> <!-- 4 opens window & start selling button, 6 opens window & stop selling button -->
|
||||
</Struct>
|
||||
<Struct Name="WS_HouseStoreLog" ClientVersion="546" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqStoreLogCmd">
|
||||
<Data ElementName="data" Type="EQ2_16Bit_String" />
|
||||
<Data ElementName="coin_gain_session" Type="int64" /> <!-- sending twice will reset coin session -->
|
||||
<Data ElementName="coin_gain_alltime" Type="int64" />
|
||||
<Data ElementName="sales_log_open" Type="int8" />
|
||||
</Struct>
|
||||
<Struct Name="WS_UpdateMerchant" ClientVersion="1" OpcodeName="OP_ClientCmdMsg" OpcodeType="OP_EqUpdateMerchantCmd">
|
||||
<Data ElementName="spawn_id" Type="int32" />
|
||||
|
954
source/WorldServer/Broker/BrokerManager.cpp
Normal file
954
source/WorldServer/Broker/BrokerManager.cpp
Normal file
@ -0,0 +1,954 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
EQ2Emulator 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.
|
||||
|
||||
EQ2Emulator 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 EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "BrokerManager.h"
|
||||
#include "../Items/Items.h"
|
||||
#include "../../common/Log.h"
|
||||
#include "../WorldDatabase.h"
|
||||
#include "../World.h"
|
||||
#include "../Web/PeerManager.h"
|
||||
#include <cstdlib>
|
||||
|
||||
extern WorldDatabase database;
|
||||
extern ZoneList zone_list;
|
||||
extern PeerManager peer_manager;
|
||||
|
||||
BrokerManager::BrokerManager() {
|
||||
|
||||
}
|
||||
|
||||
void BrokerManager::AddSeller(int32 cid,
|
||||
const std::string& name,
|
||||
int32 hid,
|
||||
bool enabled,
|
||||
bool invFlag)
|
||||
{
|
||||
|
||||
int64 prevSession = 0, prevTotal = 0;
|
||||
{
|
||||
std::shared_lock lock(mtx_);
|
||||
auto sit = players_.find(cid);
|
||||
if (sit != players_.end()) {
|
||||
prevSession = sit->second.coin_session;
|
||||
prevTotal = sit->second.coin_total;
|
||||
}
|
||||
}
|
||||
|
||||
SellerInfo info{cid, name, hid, enabled, invFlag, prevSession, prevTotal};
|
||||
{
|
||||
std::unique_lock lock(mtx_);
|
||||
players_[cid] = info;
|
||||
}
|
||||
|
||||
SavePlayerToDB(info);
|
||||
peer_manager.sendPeersAddSeller(cid, hid, name, enabled, invFlag);
|
||||
}
|
||||
|
||||
void BrokerManager::LoadSeller(int32 cid, const std::string& name,
|
||||
int32 hid, bool enabled, bool invFlag,
|
||||
int64 coin_session, int64 coin_total)
|
||||
{
|
||||
SellerInfo info{cid, name, hid, enabled, invFlag, coin_session, coin_total};
|
||||
{
|
||||
std::unique_lock lock(mtx_);
|
||||
players_[cid] = info;
|
||||
}
|
||||
}
|
||||
|
||||
int64 BrokerManager::ResetSellerSessionCoins(int32 cid) {
|
||||
database.ClearSellerSession(cid);
|
||||
|
||||
int64 session_coin = 0;
|
||||
{
|
||||
std::unique_lock lock(mtx_);
|
||||
auto it = players_.find(cid);
|
||||
if (it == players_.end()) return 0;
|
||||
session_coin = it->second.coin_session;
|
||||
it->second.coin_total += it->second.coin_session;
|
||||
it->second.coin_session = 0;
|
||||
}
|
||||
|
||||
return session_coin;
|
||||
}
|
||||
|
||||
void BrokerManager::AddSellerSessionCoins(int32 cid, uint64 session) {
|
||||
database.AddToSellerSession(cid, session);
|
||||
{
|
||||
std::unique_lock lock(mtx_);
|
||||
auto it = players_.find(cid);
|
||||
if (it == players_.end()) return;
|
||||
it->second.coin_session += session;
|
||||
}
|
||||
}
|
||||
|
||||
void BrokerManager::RemoveSeller(int32 cid, bool peerCacheOnly)
|
||||
{
|
||||
{
|
||||
std::unique_lock lock(mtx_);
|
||||
players_.erase(cid);
|
||||
active_items_by_char_.erase(cid);
|
||||
inactive_items_by_char_.erase(cid);
|
||||
}
|
||||
if(!peerCacheOnly)
|
||||
peer_manager.sendPeersRemoveSeller(cid);
|
||||
}
|
||||
|
||||
void BrokerManager::AddItem(const SaleItem& item, bool peerCacheOnly)
|
||||
{
|
||||
{
|
||||
std::unique_lock lock(mtx_);
|
||||
auto& a = active_items_by_char_[item.character_id];
|
||||
auto& i = inactive_items_by_char_[item.character_id];
|
||||
a.erase(item.unique_id);
|
||||
i.erase(item.unique_id);
|
||||
if (item.for_sale) a[item.unique_id] = item;
|
||||
else i[item.unique_id] = item;
|
||||
}
|
||||
SaveItemToDB(item);
|
||||
if(!peerCacheOnly)
|
||||
peer_manager.sendPeersAddItemSale(item.character_id, item.house_id, item.item_id, item.unique_id, item.cost_copper, item.inv_slot_id,
|
||||
item.slot_id, item.count, item.from_inventory, item.for_sale, item.creator);
|
||||
}
|
||||
|
||||
void BrokerManager::LoadItem(const SaleItem& item)
|
||||
{
|
||||
std::unique_lock lock(mtx_);
|
||||
if (item.for_sale)
|
||||
active_items_by_char_[item.character_id][item.unique_id] = item;
|
||||
else
|
||||
inactive_items_by_char_[item.character_id][item.unique_id] = item;
|
||||
}
|
||||
|
||||
void BrokerManager::SetSaleStatus(int32 cid, int64 uid, bool for_sale)
|
||||
{
|
||||
std::optional<SaleItem> toUpdate;
|
||||
{
|
||||
std::unique_lock lock(mtx_);
|
||||
auto& a = active_items_by_char_[cid];
|
||||
auto& i = inactive_items_by_char_[cid];
|
||||
|
||||
if (for_sale) {
|
||||
LogWrite(PLAYER__ERROR, 5, "Broker",
|
||||
"--SetSaleStatus: %u (%u), for_sale=%u",
|
||||
cid, uid, for_sale
|
||||
);
|
||||
if (auto it = i.find(uid); it != i.end()) {
|
||||
LogWrite(PLAYER__ERROR, 5, "Broker",
|
||||
"--SetSaleStatusFuckOff: %u (%u), for_sale=%u",
|
||||
cid, uid, for_sale
|
||||
);
|
||||
SaleItem copy = it->second;
|
||||
copy.for_sale = true;
|
||||
i.erase(it);
|
||||
a[uid] = copy;
|
||||
toUpdate = copy;
|
||||
}
|
||||
} else {
|
||||
LogWrite(PLAYER__ERROR, 5, "Broker",
|
||||
"--SetSaleStatusInactive: %u (%u), for_sale=%u",
|
||||
cid, uid, for_sale
|
||||
);
|
||||
if (auto it = a.find(uid); it != a.end()) {
|
||||
LogWrite(PLAYER__ERROR, 5, "Broker",
|
||||
"--SetSaleStatusFuckyou: %u (%u), for_sale=%u",
|
||||
cid, uid, for_sale
|
||||
);
|
||||
SaleItem copy = it->second;
|
||||
copy.for_sale = false;
|
||||
a.erase(it);
|
||||
i[uid] = copy;
|
||||
toUpdate = copy;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (toUpdate) {
|
||||
SaveItemToDB(*toUpdate);
|
||||
peer_manager.sendPeersAddItemSale(toUpdate->character_id, toUpdate->house_id, toUpdate->item_id, toUpdate->unique_id, toUpdate->cost_copper, toUpdate->inv_slot_id,
|
||||
toUpdate->slot_id, toUpdate->count, toUpdate->from_inventory, toUpdate->for_sale, toUpdate->creator);
|
||||
}
|
||||
}
|
||||
|
||||
bool BrokerManager::IsItemListed(int32 cid, int64 uid) {
|
||||
std::shared_lock lock(mtx_);
|
||||
auto& active_map = active_items_by_char_[cid];
|
||||
auto& inactive_map = inactive_items_by_char_[cid];
|
||||
|
||||
auto it = inactive_map.find(uid);
|
||||
if (it != inactive_map.end()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto it2 = active_map.find(uid);
|
||||
if (it2 != active_map.end()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BrokerManager::SetSalePrice(int32 cid, int64 uid, int64 price)
|
||||
{
|
||||
std::optional<SaleItem> toUpdate;
|
||||
{
|
||||
std::unique_lock lock(mtx_);
|
||||
if (auto ait = active_items_by_char_.find(cid); ait != active_items_by_char_.end()) {
|
||||
if (auto it = ait->second.find(uid); it != ait->second.end()) {
|
||||
|
||||
LogWrite(PLAYER__ERROR, 5, "Broker",
|
||||
"--SetSalePriceActive: %u (%u), cost=%u",
|
||||
cid, uid, price
|
||||
);
|
||||
it->second.cost_copper = price;
|
||||
toUpdate = it->second;
|
||||
}
|
||||
}
|
||||
if (auto iit = inactive_items_by_char_.find(cid); iit != inactive_items_by_char_.end()) {
|
||||
if (auto it = iit->second.find(uid); it != iit->second.end()) {
|
||||
LogWrite(PLAYER__ERROR, 5, "Broker",
|
||||
"--SetSalePriceInactive: %u (%u), cost=%u",
|
||||
cid, uid, price
|
||||
);
|
||||
it->second.cost_copper = price;
|
||||
toUpdate = it->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (toUpdate) {
|
||||
SaveItemToDB(*toUpdate);
|
||||
peer_manager.sendPeersAddItemSale(toUpdate->character_id, toUpdate->house_id, toUpdate->item_id, toUpdate->unique_id, toUpdate->cost_copper, toUpdate->inv_slot_id,
|
||||
toUpdate->slot_id, toUpdate->count, toUpdate->from_inventory, toUpdate->for_sale, toUpdate->creator);
|
||||
}
|
||||
}
|
||||
|
||||
void BrokerManager::RemoveItem(int32 cid, int64 uid, int16 qty)
|
||||
{
|
||||
bool didDelete = false;
|
||||
SaleItem snapshot;
|
||||
{
|
||||
std::unique_lock lock(mtx_);
|
||||
if (auto ait = active_items_by_char_.find(cid); ait != active_items_by_char_.end()) {
|
||||
auto& m = ait->second;
|
||||
if (auto it = m.find(uid); it != m.end()) {
|
||||
it->second.count -= qty;
|
||||
if (it->second.count <= 0) {
|
||||
didDelete = true;
|
||||
snapshot = it->second;
|
||||
m.erase(it);
|
||||
} else {
|
||||
snapshot = it->second;
|
||||
}
|
||||
if (m.empty())
|
||||
active_items_by_char_.erase(ait);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (didDelete) {
|
||||
DeleteItemFromDB(cid, uid);
|
||||
peer_manager.sendPeersRemoveItemSale(cid, uid);
|
||||
}
|
||||
else if (snapshot.count > 0) {
|
||||
SaveItemToDB(snapshot);
|
||||
peer_manager.sendPeersAddItemSale(snapshot.character_id, snapshot.house_id, snapshot.item_id, snapshot.unique_id, snapshot.cost_copper, snapshot.inv_slot_id,
|
||||
snapshot.slot_id, snapshot.count, snapshot.from_inventory, snapshot.for_sale, snapshot.creator);
|
||||
}
|
||||
}
|
||||
|
||||
bool BrokerManager::BuyItem(Client* buyer, int32 seller_cid, int64 uid, int32 quantity)
|
||||
{
|
||||
Client* seller = zone_list.GetClientByCharID(seller_cid); // establish if seller is online
|
||||
|
||||
if(buyer && buyer->GetCharacterID() == seller->GetCharacterID()) {
|
||||
buyer->Message(CHANNEL_COLOR_RED, "You cannot buy from yourself!");
|
||||
return false;
|
||||
}
|
||||
if (quantity <= 0 || !IsSaleEnabled(seller_cid) || !IsItemForSale(seller_cid, uid)) {
|
||||
if(buyer)
|
||||
buyer->Message(CHANNEL_COLOR_RED, "Quantity not provided (%u), sale is not enabled (sale enabled? %u) or item is not for sale (itemforsale? %u).", quantity, IsSaleEnabled(seller_cid), IsItemForSale(seller_cid, uid));
|
||||
return false;
|
||||
}
|
||||
int64 price = GetSalePrice(seller_cid, uid) * quantity;
|
||||
|
||||
if(!buyer || !buyer->GetPlayer() || !buyer->GetPlayer()->RemoveCoins(price)) {
|
||||
if(buyer)
|
||||
buyer->Message(CHANNEL_COLOR_RED, "You do not have enough coin to purchase.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!database.RemoveBrokerItem(seller_cid, uid, quantity)) {
|
||||
buyer->GetPlayer()->AddCoins(price);
|
||||
buyer->Message(CHANNEL_COLOR_RED, "Failed to remove broker item from database.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Item* giveItem = nullptr;
|
||||
int16 result_count = 0;
|
||||
std::string creator;
|
||||
bool deleteItem = false;
|
||||
int16 quantity_left = 0;
|
||||
// 2) Mirror in-memory
|
||||
|
||||
std::optional<SaleItem> toUpdate;
|
||||
{
|
||||
std::unique_lock lock(mtx_);
|
||||
if (auto sit = active_items_by_char_.find(seller_cid); sit != active_items_by_char_.end()) {
|
||||
auto& m = sit->second;
|
||||
if (auto it = m.find(uid); it != m.end()) {
|
||||
creator = it->second.creator;
|
||||
giveItem = master_item_list.GetItem(it->second.item_id);
|
||||
SaleItem copy = it->second;
|
||||
toUpdate = copy;
|
||||
if (it->second.count > quantity) {
|
||||
it->second.count -= quantity;
|
||||
quantity_left = it->second.count;
|
||||
result_count = quantity;
|
||||
if(seller && seller->GetPlayer()) {
|
||||
seller->GetPlayer()->item_list.SetVaultItemUniqueIDCount(seller, uid, it->second.count);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result_count = it->second.count;
|
||||
if(seller && seller->GetPlayer()) {
|
||||
seller->GetPlayer()->item_list.RemoveVaultItemFromUniqueID(seller, uid);
|
||||
}
|
||||
m.erase(it);
|
||||
deleteItem = true;
|
||||
}
|
||||
|
||||
if (m.empty())
|
||||
active_items_by_char_.erase(sit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!giveItem) {
|
||||
buyer->GetPlayer()->AddCoins(price);
|
||||
buyer->Message(CHANNEL_COLOR_RED, "Failed to find item on broker memory.");
|
||||
if(toUpdate)
|
||||
AddItem(*toUpdate);
|
||||
return false;
|
||||
}
|
||||
Item* resultItem = new Item(giveItem);
|
||||
resultItem->details.count = result_count;
|
||||
resultItem->creator = creator;
|
||||
if (buyer->GetPlayer()->item_list.HasFreeSlot() || buyer->GetPlayer()->item_list.CanStack(resultItem, false)) {
|
||||
if(!buyer->AddItem(resultItem, nullptr, AddItemType::BUY_FROM_BROKER)) {
|
||||
buyer->GetPlayer()->AddCoins(price);
|
||||
safe_delete(resultItem);
|
||||
if(toUpdate)
|
||||
AddItem(*toUpdate);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
buyer->Message(CHANNEL_COLOR_RED, "You have no free slot available.");
|
||||
buyer->GetPlayer()->AddCoins(price);
|
||||
safe_delete(resultItem);
|
||||
if(toUpdate)
|
||||
AddItem(*toUpdate);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(deleteItem) {
|
||||
DeleteItemFromDB(seller_cid, uid);
|
||||
DeleteCharacterItemFromDB(seller_cid, uid);
|
||||
}
|
||||
else if(quantity_left) {
|
||||
UpdateCharacterItemDB(seller_cid, uid, quantity_left);
|
||||
}
|
||||
|
||||
AddSellerSessionCoins(seller_cid, price);
|
||||
LogSale(seller_cid, std::string(buyer->GetPlayer()->GetName()), std::string(resultItem->name), result_count, price);
|
||||
if(seller) {
|
||||
std::string logMsg = GetShopPurchaseMessage(buyer->GetPlayer()->GetName(), std::string(resultItem->name), result_count, price);
|
||||
auto seller_info = GetSellerInfo(seller_cid);
|
||||
seller->SendHouseSaleLog(logMsg, 0, seller_info ? seller_info->coin_total : 0, 0);
|
||||
seller->OpenShopWindow(nullptr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BrokerManager::OnPeerRemoveItem(int32 cid, int64 uid)
|
||||
{
|
||||
std::unique_lock lock(mtx_);
|
||||
if (auto ait = active_items_by_char_.find(cid); ait != active_items_by_char_.end())
|
||||
ait->second.erase(uid);
|
||||
if (auto iit = inactive_items_by_char_.find(cid); iit != inactive_items_by_char_.end())
|
||||
iit->second.erase(uid);
|
||||
}
|
||||
|
||||
bool BrokerManager::IsItemForSale(int32 cid, int64 uid) const
|
||||
{
|
||||
std::shared_lock lock(mtx_);
|
||||
if (auto pit = active_items_by_char_.find(cid); pit != active_items_by_char_.end())
|
||||
return pit->second.find(uid) != pit->second.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
int64 BrokerManager::GetSalePrice(int32 cid, int64 uid)
|
||||
{
|
||||
std::shared_lock lock(mtx_);
|
||||
if (auto pit = active_items_by_char_.find(cid); pit != active_items_by_char_.end()) {
|
||||
if (auto it = pit->second.find(uid); it != pit->second.end())
|
||||
return it->second.cost_copper;
|
||||
}
|
||||
if (auto pit2 = inactive_items_by_char_.find(cid); pit2 != inactive_items_by_char_.end()) {
|
||||
if (auto it = pit2->second.find(uid); it != pit2->second.end())
|
||||
return it->second.cost_copper;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<SaleItem> BrokerManager::GetActiveForSaleItems(int32 cid) const
|
||||
{
|
||||
std::shared_lock lock(mtx_);
|
||||
std::vector<SaleItem> out;
|
||||
if (auto pit = active_items_by_char_.find(cid); pit != active_items_by_char_.end()) {
|
||||
for (auto const& kv : pit->second)
|
||||
out.push_back(kv.second);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::optional<SaleItem> BrokerManager::GetActiveItem(int32 cid, int64 uid) const
|
||||
{
|
||||
std::shared_lock lock(mtx_);
|
||||
auto pit = active_items_by_char_.find(cid);
|
||||
if (pit == active_items_by_char_.end())
|
||||
return std::nullopt;
|
||||
|
||||
auto it = pit->second.find(uid);
|
||||
if (it == pit->second.end())
|
||||
return std::nullopt;
|
||||
|
||||
return it->second; // copy out the SaleItem
|
||||
}
|
||||
|
||||
bool BrokerManager::IsSellingItems(int32 cid, bool vaultOnly) const
|
||||
{
|
||||
// Grab shared lock for thread‐safe read
|
||||
std::shared_lock lock(mtx_);
|
||||
|
||||
// Find this character’s active sales
|
||||
auto pit = active_items_by_char_.find(cid);
|
||||
if (pit == active_items_by_char_.end())
|
||||
return false; // no items => not selling from vault
|
||||
|
||||
// Scan through each SaleItem; if any has sell_from_inventory == false,
|
||||
// that means it’s coming from the vault
|
||||
for (auto const& kv : pit->second) {
|
||||
const SaleItem& item = kv.second;
|
||||
if ((item.for_sale && (!item.from_inventory || !vaultOnly)))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<SaleItem> BrokerManager::GetInactiveItems(int32 cid) const
|
||||
{
|
||||
std::shared_lock lock(mtx_);
|
||||
std::vector<SaleItem> out;
|
||||
if (auto pit = inactive_items_by_char_.find(cid); pit != inactive_items_by_char_.end()) {
|
||||
for (auto const& kv : pit->second)
|
||||
out.push_back(kv.second);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<std::pair<int64,int32>>
|
||||
BrokerManager::GetUniqueIDsAndCost(int32 cid) const
|
||||
{
|
||||
std::shared_lock lock(mtx_);
|
||||
std::vector<std::pair<int64,int32>> out;
|
||||
if (auto pit = active_items_by_char_.find(cid); pit != active_items_by_char_.end()) {
|
||||
for (auto const& kv : pit->second)
|
||||
out.emplace_back(kv.second.unique_id, kv.second.cost_copper);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
vector<Item*>* BrokerManager::GetItems(
|
||||
const std::string& name,
|
||||
int64 itype,
|
||||
int64 ltype,
|
||||
int64 btype,
|
||||
int64 minprice,
|
||||
int64 maxprice,
|
||||
int8 minskill,
|
||||
int8 maxskill,
|
||||
const std::string& seller,
|
||||
const std::string& adornment,
|
||||
int8 mintier,
|
||||
int8 maxtier,
|
||||
int16 minlevel,
|
||||
int16 maxlevel,
|
||||
int8 itemclass
|
||||
) const {
|
||||
vector<Item*>* ret = new vector<Item*>;
|
||||
string lower_name = ::ToLower(string(name.c_str()));
|
||||
std::shared_lock lock(mtx_);
|
||||
std::vector<SaleItem> out;
|
||||
for (auto const& char_pair : active_items_by_char_) {
|
||||
int32 cid = char_pair.first;
|
||||
auto pit_player = players_.find(cid);
|
||||
if (pit_player == players_.end())
|
||||
continue;
|
||||
bool allowInv = pit_player->second.sell_from_inventory;
|
||||
for (auto const& kv : char_pair.second) {
|
||||
auto const& itm = kv.second;
|
||||
|
||||
LogWrite(PLAYER__ERROR, 5, "Broker",
|
||||
"--GetItems: %u (selling: %u), allowinv: %u",
|
||||
itm.unique_id, itm.for_sale, allowInv
|
||||
);
|
||||
if (!itm.for_sale) continue;
|
||||
if (!allowInv && itm.from_inventory) continue;
|
||||
Item* def = master_item_list.GetItem(itm.item_id);
|
||||
if (!def) continue;
|
||||
|
||||
LogWrite(PLAYER__ERROR, 5, "Broker",
|
||||
"--GetItems#1: %u (selling: %u), allowinv: %u",
|
||||
itm.unique_id, itm.for_sale, allowInv
|
||||
);
|
||||
|
||||
if (!name.empty() && def->lowername.find(lower_name) == std::string::npos) continue;
|
||||
if (itype!=ITEM_BROKER_TYPE_ANY && itype !=ITEM_BROKER_TYPE_ANY64BIT && !master_item_list.ShouldAddItemBrokerType(def, itype)) continue;
|
||||
if (ltype!=ITEM_BROKER_SLOT_ANY && !master_item_list.ShouldAddItemBrokerSlot(def, ltype)) continue;
|
||||
if (btype!=0xFFFFFFFF && !master_item_list.ShouldAddItemBrokerStat(def, btype)) continue;
|
||||
|
||||
LogWrite(PLAYER__ERROR, 5, "Broker",
|
||||
"--GetItems#2: %u (cost_copper: %u), seller: %s",
|
||||
itm.unique_id, itm.cost_copper, seller.c_str()
|
||||
);
|
||||
|
||||
//if (itm.cost_copper < minprice || itm.cost_copper > maxprice) continue;
|
||||
//if (!seller.empty() && pit_player->second.seller_name.find(seller)==std::string::npos) continue;
|
||||
/*if(mintier > 1 && def->details.tier < mintier)
|
||||
continue;
|
||||
if(maxtier > 0 && def->details.tier > maxtier)
|
||||
continue;*/
|
||||
|
||||
LogWrite(PLAYER__ERROR, 5, "Broker",
|
||||
"--GetItems#3: %u (selling: %u), allowinv: %u",
|
||||
itm.unique_id, itm.for_sale, allowInv
|
||||
);
|
||||
|
||||
if(def->generic_info.adventure_default_level == 0 && def->generic_info.tradeskill_default_level == 0 && minlevel > 0 && maxlevel > 0){
|
||||
if(def->details.recommended_level < minlevel)
|
||||
continue;
|
||||
if(def->details.recommended_level > maxlevel)
|
||||
continue;
|
||||
}
|
||||
else{
|
||||
if(minlevel > 0 && ((def->generic_info.adventure_default_level == 0 && def->generic_info.tradeskill_default_level == 0) || (def->generic_info.adventure_default_level > 0 && def->generic_info.adventure_default_level < minlevel) || (def->generic_info.tradeskill_default_level > 0 && def->generic_info.tradeskill_default_level < minlevel)))
|
||||
continue;
|
||||
if(maxlevel > 0 && ((def->generic_info.adventure_default_level > 0 && def->generic_info.adventure_default_level > maxlevel) || (def->generic_info.tradeskill_default_level > 0 && def->generic_info.tradeskill_default_level > maxlevel)))
|
||||
continue;
|
||||
}
|
||||
if (itemclass>0) {
|
||||
int64 bit = ((int64)2 << (itemclass-1));
|
||||
if (!(def->generic_info.adventure_classes & bit) &&
|
||||
!(def->generic_info.tradeskill_classes & bit))
|
||||
continue;
|
||||
}
|
||||
|
||||
LogWrite(PLAYER__ERROR, 5, "Broker",
|
||||
"--GetItemsPass: %u (selling: %u), allowinv: %u",
|
||||
itm.unique_id, itm.for_sale, allowInv
|
||||
);
|
||||
ret->push_back(new Item(def, itm.unique_id, itm.creator, pit_player->second.seller_name, cid, itm.cost_copper, itm.count, pit_player->second.house_id));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string BrokerManager::GetSellerName(int32 cid) const {
|
||||
std::shared_lock lock(mtx_);
|
||||
auto it = players_.find(cid);
|
||||
return (it != players_.end()) ? it->second.seller_name : std::string();
|
||||
}
|
||||
|
||||
bool BrokerManager::IsSaleEnabled(int32 cid) const {
|
||||
std::shared_lock lock(mtx_);
|
||||
auto it = players_.find(cid);
|
||||
return (it != players_.end()) ? it->second.sale_enabled : false;
|
||||
}
|
||||
|
||||
bool BrokerManager::CanSellFromInventory(int32 cid) const {
|
||||
std::shared_lock lock(mtx_);
|
||||
auto it = players_.find(cid);
|
||||
return (it != players_.end()) ? it->second.sell_from_inventory : false;
|
||||
}
|
||||
|
||||
int32 BrokerManager::GetHouseID(int32 cid) const {
|
||||
std::shared_lock lock(mtx_);
|
||||
auto it = players_.find(cid);
|
||||
return (it != players_.end()) ? it->second.house_id : -1;
|
||||
}
|
||||
|
||||
std::optional<SellerInfo> BrokerManager::GetSellerInfo(int32 cid) const {
|
||||
std::shared_lock lock(mtx_);
|
||||
auto it = players_.find(cid);
|
||||
if (it == players_.end())
|
||||
return std::nullopt;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void BrokerManager::SavePlayerToDB(const SellerInfo& p) {
|
||||
Query q;
|
||||
std::ostringstream oss;
|
||||
oss << "INSERT INTO broker_sellers "
|
||||
"(character_id,seller_name,house_id,sale_enabled,sell_from_inventory,coin_session,coin_total) "
|
||||
"VALUES("
|
||||
<< p.character_id << ",'"
|
||||
<< EscapeSQLString(p.seller_name) << "',"
|
||||
<< p.house_id << ","
|
||||
<< (p.sale_enabled ? 1 : 0) << ","
|
||||
<< (p.sell_from_inventory ? 1 : 0) << ","
|
||||
<< p.coin_session << ","
|
||||
<< p.coin_total
|
||||
<< ") ON DUPLICATE KEY UPDATE "
|
||||
"seller_name=VALUES(seller_name),"
|
||||
"house_id=VALUES(house_id),"
|
||||
"sale_enabled=VALUES(sale_enabled),"
|
||||
"sell_from_inventory=VALUES(sell_from_inventory),"
|
||||
"coin_session=VALUES(coin_session), "
|
||||
"coin_total=VALUES(coin_total)";
|
||||
|
||||
std::string sql = oss.str();
|
||||
|
||||
q.AddQueryAsync(p.character_id, &database, Q_INSERT,
|
||||
sql.c_str()
|
||||
);
|
||||
}
|
||||
#include <sstream>
|
||||
|
||||
void BrokerManager::SaveItemToDB(const SaleItem& i) {
|
||||
// 2) Build full SQL
|
||||
std::ostringstream oss;
|
||||
oss
|
||||
<< "INSERT INTO broker_items "
|
||||
"(unique_id,character_id,house_id,item_id,cost_copper,for_sale,"
|
||||
"inv_slot_id,slot_id,`count`,from_inventory,creator) "
|
||||
"VALUES("
|
||||
<< i.unique_id << ","
|
||||
<< i.character_id << ","
|
||||
<< i.house_id << ","
|
||||
<< i.item_id << ","
|
||||
<< i.cost_copper << ","
|
||||
<< (i.for_sale ? 1 : 0) << ","
|
||||
<< i.inv_slot_id << ","
|
||||
<< i.slot_id << ","
|
||||
<< i.count << ","
|
||||
<< (i.from_inventory ? 1 : 0) << ",'"
|
||||
<< EscapeSQLString(i.creator) << "') "
|
||||
"ON DUPLICATE KEY UPDATE "
|
||||
"house_id=VALUES(house_id),"
|
||||
"item_id=VALUES(item_id),"
|
||||
"cost_copper=VALUES(cost_copper),"
|
||||
"for_sale=VALUES(for_sale),"
|
||||
"inv_slot_id=VALUES(inv_slot_id),"
|
||||
"slot_id=VALUES(slot_id),"
|
||||
"`count`=VALUES(`count`),"
|
||||
"from_inventory=VALUES(from_inventory),"
|
||||
"creator=VALUES(creator)";
|
||||
|
||||
std::string sql = oss.str();
|
||||
Query q;
|
||||
q.AddQueryAsync(i.character_id, &database, Q_INSERT, sql.c_str());
|
||||
}
|
||||
|
||||
void BrokerManager::UpdateItemInDB(const SaleItem& i) {
|
||||
Query q;
|
||||
std::ostringstream oss;
|
||||
oss << "UPDATE broker_items SET "
|
||||
<< "house_id=" << i.house_id
|
||||
<< ",item_id=" << i.item_id
|
||||
<< ",cost_copper=" << i.cost_copper
|
||||
<< ",for_sale=" << (i.for_sale ? 1 : 0)
|
||||
<< ",inv_slot_id=" << i.inv_slot_id
|
||||
<< ",slot_id=" << i.slot_id
|
||||
<< ",count=" << i.count
|
||||
<< ",from_inventory=" << (i.from_inventory ? 1 : 0)
|
||||
<< ",creator='" << EscapeSQLString(i.creator) << "' "
|
||||
<< "WHERE unique_id=" << i.unique_id
|
||||
<< " AND character_id=" << i.character_id;
|
||||
|
||||
std::string sql = oss.str();
|
||||
|
||||
q.AddQueryAsync(i.character_id, &database, Q_UPDATE,
|
||||
sql.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
void BrokerManager::DeleteItemFromDB(int32 cid, int64 uid) {
|
||||
Query q;
|
||||
q.AddQueryAsync(cid, &database, Q_DELETE,
|
||||
"DELETE FROM broker_items WHERE unique_id=%llu AND character_id=%u",
|
||||
uid, cid
|
||||
);
|
||||
}
|
||||
|
||||
void BrokerManager::DeleteCharacterItemFromDB(int32 cid, int64 uid) {
|
||||
Query q;
|
||||
q.AddQueryAsync(cid, &database, Q_DELETE,
|
||||
"DELETE FROM character_items WHERE id=%llu AND char_id=%u",
|
||||
uid, cid
|
||||
);
|
||||
}
|
||||
|
||||
void BrokerManager::UpdateCharacterItemDB(int32 cid, int64 uid, int16 count) {
|
||||
Query q;
|
||||
q.AddQueryAsync(cid, &database, Q_UPDATE,
|
||||
"UPDATE character_items set count=%u WHERE id=%llu AND char_id=%u",
|
||||
count, uid, cid
|
||||
);
|
||||
}
|
||||
|
||||
void BrokerManager::DeletePlayerFromDB(int32 cid) {
|
||||
Query q;
|
||||
// delete from broker_sellers
|
||||
q.AddQueryAsync(cid, &database, Q_DELETE,
|
||||
"DELETE FROM broker_sellers WHERE character_id=%u", cid);
|
||||
// delete all their items
|
||||
q.AddQueryAsync(cid, &database, Q_DELETE,
|
||||
"DELETE FROM broker_items WHERE character_id=%u", cid);
|
||||
}
|
||||
|
||||
bool BrokerManager::IsItemFromInventory(int32 cid, int64 uid) const {
|
||||
std::shared_lock lock(mtx_);
|
||||
|
||||
// 1) Check active items for that character
|
||||
auto pit = active_items_by_char_.find(cid);
|
||||
if (pit != active_items_by_char_.end()) {
|
||||
auto it = pit->second.find(uid);
|
||||
if (it != pit->second.end())
|
||||
return it->second.from_inventory;
|
||||
}
|
||||
|
||||
// 2) Otherwise check inactive items
|
||||
auto iit = inactive_items_by_char_.find(cid);
|
||||
if (iit != inactive_items_by_char_.end()) {
|
||||
auto it = iit->second.find(uid);
|
||||
if (it != iit->second.end())
|
||||
return it->second.from_inventory;
|
||||
}
|
||||
|
||||
// 3) Not found → false
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void BrokerManager::LockActiveItemsForClient(Client* client) const {
|
||||
int32 cid = client->GetCharacterID();
|
||||
|
||||
auto infoOpt = GetSellerInfo(cid);
|
||||
if (!infoOpt)
|
||||
return;
|
||||
const SellerInfo& info = *infoOpt;
|
||||
|
||||
{
|
||||
auto items = GetActiveForSaleItems(cid);
|
||||
for (auto const& itm : items) {
|
||||
if (!itm.for_sale) {
|
||||
client->GetPlayer()->item_list.SetVaultItemLockUniqueID(client, itm.unique_id, false, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
LogWrite(PLAYER__ERROR, 5, "Broker",
|
||||
"--LockActiveItemsForClient: %u (selling: %u), allowinv: %u, sellfrominv: %u",
|
||||
itm.unique_id, itm.for_sale, itm.from_inventory, info.sell_from_inventory
|
||||
);
|
||||
|
||||
if (!info.sell_from_inventory && itm.from_inventory) {
|
||||
client->GetPlayer()->item_list.SetVaultItemLockUniqueID(client, itm.unique_id, false, true);
|
||||
continue;
|
||||
}
|
||||
|
||||
client->GetPlayer()->item_list.SetVaultItemLockUniqueID(client, itm.unique_id, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WorldDatabase::ClearSellerSession(int32 character_id) {
|
||||
Query q;
|
||||
std::ostringstream sql;
|
||||
sql << "UPDATE broker_sellers SET coin_session = 0"
|
||||
<< " WHERE character_id = " << character_id;
|
||||
|
||||
std::string full = sql.str();
|
||||
q.AddQueryAsync(character_id, &database, Q_INSERT, full.c_str());
|
||||
}
|
||||
|
||||
void WorldDatabase::AddToSellerSession(int32 character_id, int64 amount) {
|
||||
Query q;
|
||||
std::ostringstream sql;
|
||||
sql << "UPDATE broker_sellers SET coin_session = coin_session + "
|
||||
<< amount
|
||||
<< " WHERE character_id = " << character_id;
|
||||
|
||||
std::string full = sql.str();
|
||||
q.AddQueryAsync(character_id, &database, Q_INSERT, full.c_str());
|
||||
}
|
||||
|
||||
int64 WorldDatabase::GetSellerSession(int32 character_id) {
|
||||
Query query;
|
||||
MYSQL_RES* result = query.RunQuery2(
|
||||
Q_SELECT,
|
||||
"SELECT "
|
||||
"coin_session "
|
||||
"FROM broker_sellers "
|
||||
"WHERE character_id = %d "
|
||||
"LIMIT 1",
|
||||
character_id
|
||||
);
|
||||
|
||||
if (result) {
|
||||
MYSQL_ROW row;
|
||||
if ((row = mysql_fetch_row(result))) {
|
||||
return strtoull(row[0], NULL, 0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string BrokerManager::GetShopPurchaseMessage(const std::string& buyer_name, const std::string& item_desc, int16 quantity, int64 coin) {
|
||||
int64 platinum = static_cast<int64>(coin / 1000000);
|
||||
// 1) Break totalCopper into denominations
|
||||
int64 rem = coin % 1000000;
|
||||
int64 gold = static_cast<int64>(rem / 10000);
|
||||
rem %= 10000;
|
||||
int64 silver = static_cast<int64>(rem / 100);
|
||||
int64 copper = static_cast<int64>(rem % 100);
|
||||
|
||||
// 1) Timestamp
|
||||
auto now = std::time(nullptr);
|
||||
std::tm tm;
|
||||
localtime_r(&now, &tm);
|
||||
char timebuf[64];
|
||||
std::strftime(timebuf, sizeof(timebuf),
|
||||
"%B %d, %Y, %I:%M:%S %p", &tm);
|
||||
|
||||
// 2) Build the sale line
|
||||
std::ostringstream msg;
|
||||
msg << timebuf
|
||||
<< " " << buyer_name << " buys ";
|
||||
if (quantity > 1)
|
||||
msg << quantity << " ";
|
||||
msg << item_desc
|
||||
<< " for ";
|
||||
|
||||
// 3) Denominations
|
||||
std::vector<std::string> parts;
|
||||
if (platinum > 0) parts.push_back(std::to_string(platinum) + " Platinum");
|
||||
if (gold > 0) parts.push_back(std::to_string(gold) + " Gold");
|
||||
if (silver > 0) parts.push_back(std::to_string(silver) + " Silver");
|
||||
if (copper > 0) parts.push_back(std::to_string(copper) + " Copper");
|
||||
|
||||
// If all are zero (unlikely), still print "0 Copper"
|
||||
if (parts.empty())
|
||||
parts.push_back("0 Copper");
|
||||
|
||||
// Join with ", "
|
||||
for (size_t i = 0; i < parts.size(); ++i) {
|
||||
if (i) msg << ", ";
|
||||
msg << parts[i];
|
||||
}
|
||||
|
||||
return msg.str();
|
||||
}
|
||||
|
||||
void BrokerManager::LogSale(int32 cid,
|
||||
const std::string& buyer_name,
|
||||
const std::string& item_desc,
|
||||
int16 quantity,
|
||||
int64 coin)
|
||||
{
|
||||
std::string msg = GetShopPurchaseMessage(buyer_name, item_desc, quantity, coin);
|
||||
|
||||
std::string esc = EscapeSQLString(msg.c_str());
|
||||
std::ostringstream sql;
|
||||
sql << "INSERT INTO broker_seller_log "
|
||||
"(character_id,log_time,message) VALUES ("
|
||||
<< cid << ",NOW(),'"
|
||||
<< esc << "')";
|
||||
|
||||
std::string full = sql.str();
|
||||
|
||||
Query q;
|
||||
q.AddQueryAsync(cid, &database, Q_INSERT, full.c_str());
|
||||
}
|
||||
|
||||
void BrokerManager::LogSaleMessage(int32 cid,
|
||||
const std::string& log_message)
|
||||
{
|
||||
auto now = std::time(nullptr);
|
||||
std::tm tm;
|
||||
localtime_r(&now, &tm);
|
||||
char timebuf[64];
|
||||
std::strftime(timebuf, sizeof(timebuf),
|
||||
"%B %d, %Y, %I:%M:%S %p", &tm);
|
||||
|
||||
std::ostringstream msg;
|
||||
msg << timebuf
|
||||
<< " " << log_message;
|
||||
|
||||
std::string esc = EscapeSQLString(msg.str());
|
||||
std::ostringstream sql;
|
||||
sql << "INSERT INTO broker_seller_log "
|
||||
"(character_id,log_time,message) VALUES ("
|
||||
<< cid << ",NOW(),'"
|
||||
<< esc << "')";
|
||||
|
||||
std::string full = sql.str();
|
||||
|
||||
Query q;
|
||||
q.AddQueryAsync(cid, &database, Q_INSERT, full.c_str());
|
||||
}
|
||||
|
||||
std::vector<SellerLog> BrokerManager::GetSellerLog(int32 cid) const {
|
||||
std::vector<SellerLog> out;
|
||||
|
||||
Query query;
|
||||
MYSQL_RES* result = query.RunQuery2(
|
||||
Q_SELECT,
|
||||
"SELECT "
|
||||
"log_id, "
|
||||
"DATE_FORMAT(log_time, '%%M %%d, %%Y, %%r') as ts, "
|
||||
"message "
|
||||
"FROM broker_seller_log "
|
||||
"WHERE character_id=%u "
|
||||
"ORDER BY log_time ASC",
|
||||
cid
|
||||
);
|
||||
|
||||
if (result) {
|
||||
MYSQL_ROW row;
|
||||
while ((row = mysql_fetch_row(result))) {
|
||||
SellerLog entry;
|
||||
entry.log_id = row[0] ? atoll(row[0]) : 0;
|
||||
entry.timestamp = row[1] ? row[1] : "";
|
||||
entry.message = row[2] ? row[2] : "";
|
||||
out.push_back(std::move(entry));
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
182
source/WorldServer/Broker/BrokerManager.h
Normal file
182
source/WorldServer/Broker/BrokerManager.h
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
EQ2Emulator 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.
|
||||
|
||||
EQ2Emulator 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 EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
#include "../../common/types.h"
|
||||
|
||||
// Key = (character_id, unique_id)
|
||||
using Key = std::pair<int32, int64>;
|
||||
|
||||
class Item;
|
||||
class Client;
|
||||
|
||||
struct SaleItem {
|
||||
int64 unique_id;
|
||||
int32 character_id;
|
||||
int32 house_id;
|
||||
int64 item_id;
|
||||
int64 cost_copper;
|
||||
bool for_sale;
|
||||
sint32 inv_slot_id;
|
||||
int16 slot_id;
|
||||
int16 count;
|
||||
bool from_inventory;
|
||||
string creator;
|
||||
};
|
||||
|
||||
struct SellerInfo {
|
||||
int32 character_id;
|
||||
std::string seller_name;
|
||||
int64 house_id;
|
||||
bool sale_enabled;
|
||||
bool sell_from_inventory;
|
||||
int64 coin_session;
|
||||
int64 coin_total;
|
||||
};
|
||||
|
||||
struct SellerLog {
|
||||
int64 log_id; // auto_increment PK
|
||||
std::string timestamp; // e.g. "October 20, 2005, 05:29:33 AM"
|
||||
std::string message; // the human-readable text
|
||||
};
|
||||
|
||||
class BrokerManager {
|
||||
public:
|
||||
BrokerManager();
|
||||
|
||||
//–– Player management
|
||||
void AddSeller(int32 cid,
|
||||
const std::string& name,
|
||||
int32 house_id,
|
||||
bool sale_enabled,
|
||||
bool sell_from_inventory);
|
||||
|
||||
void LoadSeller(int32 cid,
|
||||
const std::string& name,
|
||||
int32 house_id,
|
||||
bool sale_enabled,
|
||||
bool sell_from_inventory,
|
||||
int64 coin_session,
|
||||
int64 coin_total);
|
||||
|
||||
int64 ResetSellerSessionCoins(int32 cid);
|
||||
void AddSellerSessionCoins(int32 cid, uint64 session);
|
||||
void RemoveSeller(int32 character_id, bool peerCacheOnly = false);
|
||||
|
||||
//–– Item management
|
||||
void AddItem(const SaleItem& item, bool peerCacheOnly = false);
|
||||
void LoadItem(const SaleItem& item);
|
||||
|
||||
//–– Activate / deactivate sale flag
|
||||
void SetSaleStatus(int32 cid, int64 uid, bool for_sale);
|
||||
bool IsItemListed(int32 cid, int64 uid);
|
||||
void SetSalePrice(int32 cid, int64 uid, int64 price);
|
||||
int64 GetSalePrice(int32 cid, int64 uid);
|
||||
|
||||
//–– Remove quantity
|
||||
void RemoveItem(int32 cid, int64 uid, int16 quantity = 1);
|
||||
|
||||
//–– Attempt to buy (atomic DB + in-memory + broadcast)
|
||||
bool BuyItem(Client* buyer, int32 seller_cid, int64 uid, int32 quantity);
|
||||
bool IsItemForSale(int32 seller_cid, int64 uid) const;
|
||||
|
||||
//–– Called when a peer notifies that an item was sold/removed (in-memory only)
|
||||
void OnPeerRemoveItem(int32 character_id, int64 unique_id);
|
||||
|
||||
//–– Queries
|
||||
std::vector<SaleItem> GetActiveForSaleItems(int32 cid) const;
|
||||
std::optional<SaleItem> GetActiveItem(int32 cid, int64 uid) const;
|
||||
bool IsSellingItems(int32 cid, bool vaultOnly = false) const;
|
||||
std::vector<SaleItem> GetInactiveItems(int32 cid) const;
|
||||
//–– Global search API (only active_items_)
|
||||
vector<Item*>* GetItems(
|
||||
const std::string& name,
|
||||
int64 itype,
|
||||
int64 ltype,
|
||||
int64 btype,
|
||||
int64 minprice,
|
||||
int64 maxprice,
|
||||
int8 minskill,
|
||||
int8 maxskill,
|
||||
const std::string& seller,
|
||||
const std::string& adornment,
|
||||
int8 mintier,
|
||||
int8 maxtier,
|
||||
int16 minlevel,
|
||||
int16 maxlevel,
|
||||
int8 itemclass
|
||||
) const;
|
||||
|
||||
//–– UI helper: get (unique_id, cost) for active items
|
||||
std::vector<std::pair<int64,int32>> GetUniqueIDsAndCost(int32 cid) const;
|
||||
|
||||
std::optional<SellerInfo> GetSellerInfo(int32 character_id) const;
|
||||
//–– Lookup seller name
|
||||
std::string GetSellerName(int32 cid) const;
|
||||
bool IsSaleEnabled(int32 cid) const;
|
||||
bool CanSellFromInventory(int32 cid) const;
|
||||
int32 GetHouseID(int32 cid) const;
|
||||
|
||||
bool IsItemFromInventory(int32 cid, int64 uid) const;
|
||||
void LockActiveItemsForClient(Client* client) const;
|
||||
|
||||
std::string GetShopPurchaseMessage(const std::string& buyer_name, const std::string& item_desc, int16 quantity, int64 coin);
|
||||
void LogSale(int32 character_id, const std::string& buyer_name,const std::string& item_desc, int16 quantity, int64 coin);
|
||||
void LogSaleMessage(int32 character_id,const std::string& log_message);
|
||||
std::vector<SellerLog> GetSellerLog(int32 character_id) const;
|
||||
static std::string EscapeSQLString(const std::string& s) {
|
||||
std::string out;
|
||||
out.reserve(s.size() * 2);
|
||||
for (char c : s) {
|
||||
if (c == '\'') out += "''";
|
||||
else out += c;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
private:
|
||||
mutable std::shared_mutex mtx_;
|
||||
std::unordered_map<int32,SellerInfo> players_;
|
||||
std::unordered_map<
|
||||
int32,
|
||||
std::unordered_map<int64_t, SaleItem>
|
||||
> active_items_by_char_;
|
||||
std::unordered_map<
|
||||
int32,
|
||||
std::unordered_map<int64_t, SaleItem>
|
||||
> inactive_items_by_char_;
|
||||
|
||||
//–– DB sync (async writes)
|
||||
void SavePlayerToDB(const SellerInfo& p);
|
||||
void SaveItemToDB(const SaleItem& i);
|
||||
void UpdateItemInDB(const SaleItem& i);
|
||||
void DeleteItemFromDB(int32 character_id, int64 unique_id);
|
||||
void DeleteCharacterItemFromDB(int32 cid, int64 uid);
|
||||
void UpdateCharacterItemDB(int32 cid, int64 uid, int16 count);
|
||||
void DeletePlayerFromDB(int32 cid);
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -52,6 +52,7 @@
|
||||
#include "../Bots/Bot.h"
|
||||
#include "../Web/PeerManager.h"
|
||||
#include "../../common/GlobalHeaders.h"
|
||||
#include "../Broker/BrokerManager.h"
|
||||
|
||||
extern WorldDatabase database;
|
||||
extern MasterSpellList master_spell_list;
|
||||
@ -76,6 +77,7 @@ extern MasterAAList master_aa_list;
|
||||
extern MasterRaceTypeList race_types_list;
|
||||
extern Classes classes;
|
||||
extern PeerManager peer_manager;
|
||||
extern BrokerManager broker;
|
||||
|
||||
//devn00b: Fix for linux builds since we dont use stricmp we use strcasecmp
|
||||
#if defined(__GNUC__)
|
||||
@ -2193,14 +2195,35 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
LogWrite(COMMAND__ERROR, 0, "Command", "/info appearance: Unknown Index: %u", item_index);
|
||||
}
|
||||
else if(strcmp(sep->arg[0], "item") == 0 || strcmp(sep->arg[0], "merchant") == 0 || strcmp(sep->arg[0], "store") == 0 || strcmp(sep->arg[0], "buyback") == 0 || strcmp(sep->arg[0], "consignment") == 0){
|
||||
int32 item_id = atoul(sep->arg[1]);
|
||||
Item* item = master_item_list.GetItem(item_id);
|
||||
if(item){
|
||||
int64 item_id = strtoull(sep->arg[1], NULL, 0);
|
||||
Item* item = nullptr;
|
||||
|
||||
if (strcmp(sep->arg[0], "store") == 0)
|
||||
item = client->GetPlayer()->item_list.GetVaultItemFromUniqueID(item_id, true);
|
||||
else
|
||||
item = master_item_list.GetItem(item_id);
|
||||
|
||||
if(!item && client->GetMerchantTransactionID() && strcmp(sep->arg[0], "merchant") == 0) {
|
||||
Spawn* merchant = client->GetPlayer()->GetZone()->GetSpawnByID(client->GetMerchantTransactionID());
|
||||
if(merchant && merchant->GetHouseCharacterID() && merchant->GetPickupUniqueItemID()) {
|
||||
if(auto itemInfo = broker.GetActiveItem(merchant->GetHouseCharacterID(), item_id)) {
|
||||
item = master_item_list.GetItem(itemInfo->item_id);
|
||||
if(item) {
|
||||
EQ2Packet* app = item->serialize(client->GetVersion(), true, client->GetPlayer());
|
||||
client->QueuePacket(app);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!item && strcmp(sep->arg[0], "consignment") == 0) {
|
||||
client->SendSellerItemByItemUniqueId(item_id);
|
||||
}
|
||||
else if(item){
|
||||
EQ2Packet* app = item->serialize(client->GetVersion(), true, client->GetPlayer());
|
||||
client->QueuePacket(app);
|
||||
}
|
||||
else
|
||||
LogWrite(COMMAND__ERROR, 0, "Command", "/info item|merchant|store|buyback|consignment: Unknown Item ID: %u", item_id);
|
||||
LogWrite(COMMAND__ERROR, 0, "Command", "/info item|merchant|store|buyback|consignment: Unknown Item ID: %u (full arguments %s)", item_id, sep->argplus[0]);
|
||||
}
|
||||
else if (strcmp(sep->arg[0], "spell") == 0) {
|
||||
sint32 spell_id = atol(sep->arg[1]);
|
||||
@ -3900,8 +3923,12 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
|
||||
if (!spawn || !client->HasOwnerOrEditAccess() || !spawn->GetPickupItemID())
|
||||
break;
|
||||
|
||||
if(client->AddItem(spawn->GetPickupItemID(), 1)) {
|
||||
Item* tmpItem = client->GetPlayer()->item_list.GetVaultItemFromUniqueID(spawn->GetPickupUniqueItemID());
|
||||
if((tmpItem && tmpItem->generic_info.item_type == ITEM_TYPE_HOUSE_CONTAINER) || client->AddItem(spawn->GetPickupItemID(), 1)) {
|
||||
if ( tmpItem && tmpItem->generic_info.item_type == ITEM_TYPE_HOUSE_CONTAINER ) {
|
||||
tmpItem->TryUnlockItem(LockReason::LockReason_House);
|
||||
client->QueuePacket(client->GetPlayer()->SendInventoryUpdate(client->GetVersion()));
|
||||
}
|
||||
Query query;
|
||||
query.RunQuery2(Q_INSERT, "delete from spawn_instance_data where spawn_id = %u and spawn_location_id = %u and pickup_item_id = %u", spawn->GetDatabaseID(), spawn->GetSpawnLocationID(), spawn->GetPickupItemID());
|
||||
|
||||
@ -4030,12 +4057,16 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
case COMMAND_PLACE_HOUSE_ITEM: {
|
||||
if (sep && sep->IsNumber(0))
|
||||
{
|
||||
int32 uniqueid = atoi(sep->arg[0]);
|
||||
int64 uniqueid = strtoull(sep->arg[0], NULL, 0);
|
||||
|
||||
Item* item = client->GetPlayer()->item_list.GetItemFromUniqueID(uniqueid);
|
||||
//Item* item = player->GetEquipmentList()->GetItem(slot);
|
||||
Item* item = client->GetPlayer()->item_list.GetVaultItemFromUniqueID(uniqueid);
|
||||
|
||||
if (item && (item->IsHouseItem() || item->IsHouseContainer()))
|
||||
{
|
||||
if(item->IsHouseContainer() && item->details.inv_slot_id != InventorySlotType::HOUSE_VAULT) { // must be in base slot of vault in house for house containers
|
||||
client->SimpleMessage(CHANNEL_COLOR_RED, "Must be in vault to place this item.");
|
||||
break;
|
||||
}
|
||||
if (!client->HasOwnerOrEditAccess())
|
||||
{
|
||||
client->SimpleMessage(CHANNEL_COLOR_RED, "This is not your home!");
|
||||
@ -4062,7 +4093,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
Object* obj = new Object();
|
||||
Spawn* spawn = (Spawn*)obj;
|
||||
memset(&spawn->appearance, 0, sizeof(spawn->appearance));
|
||||
strcpy(spawn->appearance.name, "temp");
|
||||
strcpy(spawn->appearance.name, item->name.c_str());
|
||||
spawn->SetX(client->GetPlayer()->GetX());
|
||||
spawn->SetY(client->GetPlayer()->GetY());
|
||||
spawn->SetZ(client->GetPlayer()->GetZ());
|
||||
@ -4403,20 +4434,49 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
}
|
||||
case COMMAND_BUY_FROM_BROKER:{
|
||||
if(sep && sep->arg[1][0] && sep->IsNumber(0) && sep->IsNumber(1)){
|
||||
int32 item_id = atoul(sep->arg[0]);
|
||||
int64 item_id = strtoull(sep->arg[0], NULL, 0);
|
||||
int16 quantity = atoul(sep->arg[1]);
|
||||
Item* item = master_item_list.GetItem(item_id);
|
||||
if(item && item->generic_info.max_charges > 1)
|
||||
quantity = item->generic_info.max_charges;
|
||||
client->AddItem(item_id, quantity, AddItemType::BUY_FROM_BROKER);
|
||||
if(client->IsGMStoreSearch()) {
|
||||
Item* item = master_item_list.GetItem(item_id);
|
||||
if(item && item->generic_info.max_charges > 1)
|
||||
quantity = item->generic_info.max_charges;
|
||||
client->AddItem(item_id, quantity, AddItemType::BUY_FROM_BROKER);
|
||||
}
|
||||
else {
|
||||
client->BuySellerItemByItemUniqueId(item_id, quantity);
|
||||
LogWrite(COMMAND__ERROR, 0, "Command", "BUY_FROM_BROKER. Item ID %u, Quantity %u, full args %s.", item_id, quantity, sep->argplus[0]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COMMAND_SEARCH_STORES_PAGE:{
|
||||
LogWrite(COMMAND__ERROR, 0, "Command", "SearchStores: %s", sep && sep->arg[0] ? sep->argplus[0] : "");
|
||||
if(sep && sep->arg[0][0] && sep->IsNumber(0)){
|
||||
int32 page = atoul(sep->arg[0]);
|
||||
client->SearchStore(page);
|
||||
}
|
||||
else {
|
||||
client->SetGMStoreSearch(false);
|
||||
PacketStruct* packet = configReader.getStruct("WS_StartBroker", client->GetVersion());
|
||||
if (packet) {
|
||||
packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(client->GetPlayer()));
|
||||
//packet->setDataByName("unknown", 1);
|
||||
packet->setDataByName("unknown2", 5, 0);
|
||||
packet->setDataByName("unknown2", 20, 1);
|
||||
packet->setDataByName("unknown2", 58, 3);
|
||||
packet->setDataByName("unknown2", 40, 4);
|
||||
client->QueuePacket(packet->serialize());
|
||||
if(client->GetVersion() > 561) {
|
||||
PacketStruct* packet2 = configReader.getStruct("WS_BrokerBags", client->GetVersion());
|
||||
if (packet2) {
|
||||
packet2->setDataByName("char_id", client->GetCharacterID());
|
||||
client->QueuePacket(packet2->serialize()); //send this for now, needed to properly clear data
|
||||
safe_delete(packet2);
|
||||
}
|
||||
safe_delete(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COMMAND_SEARCH_STORES:{
|
||||
@ -4424,11 +4484,11 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
const char* values = sep->argplus[0];
|
||||
if(values){
|
||||
LogWrite(ITEM__WARNING, 0, "Item", "SearchStores: %s", values);
|
||||
|
||||
map<string, string> str_values = TranslateBrokerRequest(values);
|
||||
vector<Item*>* items = master_item_list.GetItems(str_values, client);
|
||||
if(items){
|
||||
client->SetItemSearch(items);
|
||||
client->SetItemSearch(items, str_values);
|
||||
client->SetSearchPage(0);
|
||||
client->SearchStore(0);
|
||||
}
|
||||
}
|
||||
@ -5424,6 +5484,13 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
}
|
||||
case COMMAND_ITEMSEARCH:
|
||||
case COMMAND_FROMBROKER:{
|
||||
|
||||
if(command->handler == COMMAND_ITEMSEARCH) {
|
||||
client->SetGMStoreSearch(true);
|
||||
}
|
||||
else {
|
||||
client->SetGMStoreSearch(false);
|
||||
}
|
||||
PacketStruct* packet = configReader.getStruct("WS_StartBroker", client->GetVersion());
|
||||
if (packet) {
|
||||
packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(client->GetPlayer()));
|
||||
@ -5433,13 +5500,15 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
packet->setDataByName("unknown2", 58, 3);
|
||||
packet->setDataByName("unknown2", 40, 4);
|
||||
client->QueuePacket(packet->serialize());
|
||||
PacketStruct* packet2 = configReader.getStruct("WS_BrokerBags", client->GetVersion());
|
||||
if (packet2) {
|
||||
packet2->setDataByName("char_id", client->GetCharacterID());
|
||||
client->QueuePacket(packet2->serialize()); //send this for now, needed to properly clear data
|
||||
safe_delete(packet2);
|
||||
if(client->GetVersion() > 561) {
|
||||
PacketStruct* packet2 = configReader.getStruct("WS_BrokerBags", client->GetVersion());
|
||||
if (packet2) {
|
||||
packet2->setDataByName("char_id", client->GetCharacterID());
|
||||
client->QueuePacket(packet2->serialize()); //send this for now, needed to properly clear data
|
||||
safe_delete(packet2);
|
||||
}
|
||||
safe_delete(packet);
|
||||
}
|
||||
safe_delete(packet);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -5838,6 +5907,15 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
||||
case COMMAND_SPLIT: { Command_Split(client, sep); break; }
|
||||
case COMMAND_RAIDSAY: { Command_RaidSay(client, sep); break; }
|
||||
case COMMAND_RELOAD_ZONEINFO: { Command_ReloadZoneInfo(client, sep); break; }
|
||||
case COMMAND_SLE: { Command_SetLocationEntry(client, sep); break; }
|
||||
case COMMAND_STORE_LIST_ITEM: { Command_StoreListItem(client, sep); break; }
|
||||
case COMMAND_STORE_SET_PRICE: { Command_StoreSetPrice(client, sep); break; }
|
||||
case COMMAND_STORE_SET_PRICE_LOCAL: { Command_StoreSetPriceLocal(client, sep); break; }
|
||||
case COMMAND_STORE_START_SELLING: { Command_StoreStartSelling(client, sep); break; }
|
||||
case COMMAND_STORE_STOP_SELLING: { Command_StoreStopSelling(client, sep); break; }
|
||||
case COMMAND_STORE_UNLIST_ITEM: { Command_StoreUnlistItem(client, sep); break; }
|
||||
case COMMAND_CLOSE_STORE_KEEP_SELLING: { Command_CloseStoreKeepSelling(client, sep); break; }
|
||||
case COMMAND_CANCEL_STORE: { Command_CancelStore(client, sep); break; }
|
||||
default:
|
||||
{
|
||||
LogWrite(COMMAND__WARNING, 0, "Command", "Unhandled command: %s", command->command.data.c_str());
|
||||
@ -7024,7 +7102,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
|
||||
|
||||
if(item)
|
||||
{
|
||||
if(item->details.item_locked) {
|
||||
if(item->IsItemLocked()) {
|
||||
client->SimpleMessage(CHANNEL_COLOR_RED, "You cannot destroy the item in use.");
|
||||
return;
|
||||
}
|
||||
@ -7036,11 +7114,15 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
|
||||
client->SimpleMessage(CHANNEL_COLOR_RED, "You can't destroy this item.");
|
||||
return;
|
||||
}
|
||||
if(client->GetPlayer()->item_list.IsItemInSlotType(item, InventorySlotType::HOUSE_VAULT) || client->GetPlayer()->item_list.IsItemInSlotType(item, InventorySlotType::BASE_INVENTORY)) {
|
||||
broker.RemoveItem(client->GetPlayer()->GetCharacterID(), item->details.unique_id, item->details.count);
|
||||
}
|
||||
if(item->GetItemScript() && lua_interface)
|
||||
lua_interface->RunItemScript(item->GetItemScript(), "destroyed", item, client->GetPlayer());
|
||||
|
||||
//reobtain item make sure it wasn't removed
|
||||
item = player->item_list.GetItemFromIndex(index);
|
||||
|
||||
int32 bag_id = 0;
|
||||
if(item){
|
||||
bag_id = item->details.inv_slot_id;
|
||||
@ -7049,6 +7131,8 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
|
||||
client->GetPlayer()->item_list.DestroyItem(index);
|
||||
client->GetPlayer()->UpdateInventory(bag_id);
|
||||
client->GetPlayer()->CalculateApplyWeight();
|
||||
|
||||
client->OpenShopWindow(nullptr); // update the window if it is open
|
||||
}
|
||||
}
|
||||
else if(sep->arg[4][0] && strncasecmp("move", sep->arg[0], 4) == 0 && sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4))
|
||||
@ -7058,33 +7142,34 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
|
||||
sint32 bag_id = atol(sep->arg[3]);
|
||||
int8 charges = atoi(sep->arg[4]);
|
||||
Item* item = client->GetPlayer()->item_list.GetItemFromIndex(from_index);
|
||||
|
||||
int64 unique_id = 0;
|
||||
int16 count = 0;
|
||||
if(!item) {
|
||||
client->SimpleMessage(CHANNEL_COLOR_RED, "You have no item.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(to_slot == item->details.slot_id && (bag_id < 0 || bag_id == item->details.inv_slot_id)) {
|
||||
unique_id = item->details.unique_id;
|
||||
count = item->details.count;
|
||||
if(to_slot == item->details.slot_id && (bag_id == item->details.inv_slot_id)) {
|
||||
return;
|
||||
}
|
||||
if(item->details.item_locked)
|
||||
if(item->IsItemLocked())
|
||||
{
|
||||
client->SimpleMessage(CHANNEL_COLOR_RED, "You cannot move the item in use.");
|
||||
return;
|
||||
}
|
||||
if(bag_id == -4 && !client->GetPlayer()->item_list.SharedBankAddAllowed(item))
|
||||
if(bag_id == InventorySlotType::SHARED_BANK && !client->GetPlayer()->item_list.SharedBankAddAllowed(item))
|
||||
{
|
||||
client->SimpleMessage(CHANNEL_COLOR_RED, "That item (or an item inside) cannot be shared.");
|
||||
return;
|
||||
}
|
||||
|
||||
sint32 old_inventory_id = 0;
|
||||
|
||||
if(item)
|
||||
old_inventory_id = item->details.inv_slot_id;
|
||||
|
||||
//autobank
|
||||
if (bag_id == -3 && to_slot == -1)
|
||||
if (bag_id == InventorySlotType::BANK && to_slot == -1)
|
||||
{
|
||||
if (player->HasFreeBankSlot())
|
||||
to_slot = player->FindFreeBankSlot();
|
||||
@ -7129,6 +7214,16 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
|
||||
}
|
||||
|
||||
client->GetPlayer()->CalculateApplyWeight();
|
||||
if(item) {
|
||||
if(!client->GetPlayer()->item_list.IsItemInSlotType(item, InventorySlotType::HOUSE_VAULT) &&
|
||||
!client->GetPlayer()->item_list.IsItemInSlotType(item, InventorySlotType::BASE_INVENTORY)) {
|
||||
broker.RemoveItem(client->GetPlayer()->GetCharacterID(), unique_id, charges);
|
||||
}
|
||||
}
|
||||
else {
|
||||
broker.RemoveItem(client->GetPlayer()->GetCharacterID(), unique_id, count);
|
||||
}
|
||||
client->OpenShopWindow(nullptr); // update the window if it is open
|
||||
}
|
||||
else if(sep->arg[1][0] && strncasecmp("equip", sep->arg[0], 5) == 0 && sep->IsNumber(1))
|
||||
{
|
||||
@ -7159,6 +7254,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
|
||||
client->QueuePacket(characterSheetPackets);
|
||||
|
||||
client->GetPlayer()->CalculateBonuses();
|
||||
client->OpenShopWindow(nullptr); // update the window if it is open
|
||||
}
|
||||
else if (sep->arg[1][0] && strncasecmp("unpack", sep->arg[0], 6) == 0 && sep->IsNumber(1))
|
||||
{
|
||||
@ -7168,7 +7264,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
|
||||
int16 index = atoi(sep->arg[1]);
|
||||
Item* item = client->GetPlayer()->item_list.GetItemFromIndex(index);
|
||||
if (item) {
|
||||
if(item->details.item_locked)
|
||||
if(item->IsItemLocked())
|
||||
{
|
||||
client->SimpleMessage(CHANNEL_COLOR_RED, "You cannot unpack the item in use.");
|
||||
return;
|
||||
@ -7186,6 +7282,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
|
||||
}
|
||||
client->RemoveItem(item, 1);
|
||||
|
||||
client->OpenShopWindow(nullptr); // update the window if it is open
|
||||
}
|
||||
|
||||
}
|
||||
@ -7224,6 +7321,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
|
||||
|
||||
client->UnequipItem(index, bag_id, to_slot, appearance_equip);
|
||||
client->GetPlayer()->CalculateBonuses();
|
||||
client->OpenShopWindow(nullptr); // update the window if it is open
|
||||
}
|
||||
else if(sep->arg[2][0] && strncasecmp("swap_equip", sep->arg[0], 10) == 0 && sep->IsNumber(1) && sep->IsNumber(2))
|
||||
{
|
||||
@ -7276,9 +7374,10 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
|
||||
|
||||
// Send the inventory update packet
|
||||
client->QueuePacket(player->item_list.serialize(player, client->GetVersion()));
|
||||
client->OpenShopWindow(nullptr); // update the window if it is open
|
||||
return;
|
||||
}
|
||||
else if (bag_id == -3 && to_slot == -1) {
|
||||
else if (bag_id == InventorySlotType::BANK && to_slot == -1) {
|
||||
// Auto Bank
|
||||
if (!player->item_list.GetFirstFreeBankSlot(&bag_id, &to_slot)) {
|
||||
client->SimpleMessage(CHANNEL_STATUS, "You do not have any free bank slots.");
|
||||
@ -7291,14 +7390,15 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
|
||||
player->item_list.RemoveOverflowItem(item);
|
||||
}
|
||||
client->QueuePacket(player->item_list.serialize(player, client->GetVersion()));
|
||||
client->OpenShopWindow(nullptr); // update the window if it is open
|
||||
}
|
||||
else if (bag_id == -4) {
|
||||
else if (bag_id == InventorySlotType::SHARED_BANK) {
|
||||
// Shared Bank
|
||||
if (!player->item_list.SharedBankAddAllowed(item)) {
|
||||
client->SimpleMessage(CHANNEL_STATUS, "That item (or an item inside) cannot be shared.");
|
||||
return;
|
||||
}
|
||||
Item* tmp_item = player->item_list.GetItem(-4, to_slot);
|
||||
Item* tmp_item = player->item_list.GetItem(bag_id, to_slot);
|
||||
if (tmp_item) {
|
||||
client->SimpleMessage(CHANNEL_STATUS, "You can not place an overflow item into an occupied slot");
|
||||
return;
|
||||
@ -7311,6 +7411,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
|
||||
player->item_list.RemoveOverflowItem(item);
|
||||
}
|
||||
client->QueuePacket(player->item_list.serialize(player, client->GetVersion()));
|
||||
client->OpenShopWindow(nullptr); // update the window if it is open
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -7330,6 +7431,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma
|
||||
player->item_list.RemoveOverflowItem(item);
|
||||
}
|
||||
client->QueuePacket(player->item_list.serialize(player, client->GetVersion()));
|
||||
client->OpenShopWindow(nullptr); // update the window if it is open
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -9976,14 +10078,18 @@ void Commands::Command_TradeAddItem(Client* client, Seperator* sep)
|
||||
int32 index = atoi(sep->arg[0]);
|
||||
item = client->GetPlayer()->GetPlayerItemList()->GetItemFromIndex(index);
|
||||
if (item) {
|
||||
if(item->details.item_locked || item->details.equip_slot_id) {
|
||||
if(item->IsItemLocked() || item->details.equip_slot_id) {
|
||||
client->SimpleMessage(CHANNEL_COLOR_RED, "You cannot trade an item currently in use.");
|
||||
return;
|
||||
}
|
||||
else if(item->details.inv_slot_id == -3 || item->details.inv_slot_id == -4) {
|
||||
else if(item->details.inv_slot_id == InventorySlotType::BANK || item->details.inv_slot_id == InventorySlotType::SHARED_BANK) {
|
||||
client->SimpleMessage(CHANNEL_COLOR_RED, "You cannot trade an item in the bank.");
|
||||
return;
|
||||
}
|
||||
else if(client->GetPlayer()->item_list.IsItemInSlotType(item, InventorySlotType::HOUSE_VAULT)) {
|
||||
client->SimpleMessage(CHANNEL_COLOR_RED, "You cannot trade an item in the house vault.");
|
||||
return;
|
||||
}
|
||||
|
||||
int8 result = client->GetPlayer()->trade->AddItemToTrade(client->GetPlayer(), item, atoi(sep->arg[2]), atoi(sep->arg[1]));
|
||||
if (result == 1)
|
||||
@ -11041,6 +11147,23 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) {
|
||||
else if(atoi(sep->arg[0]) == 38) {
|
||||
client->GetPlayer()->GetZone()->SendFlightPathsPackets(client);
|
||||
}
|
||||
else if(atoi(sep->arg[0]) == 39) {
|
||||
client->OpenShopWindow(nullptr, true);
|
||||
}
|
||||
else if(atoi(sep->arg[0]) == 40) {
|
||||
|
||||
PacketStruct* packet2 = configReader.getStruct("WS_HouseStoreLog", client->GetVersion());
|
||||
if (packet2) {
|
||||
packet2->setDataByName("data", sep->arg[1]);
|
||||
packet2->setDataByName("coin_gain_session", atoul(sep->arg[2]));
|
||||
packet2->setDataByName("coin_gain_alltime", atoul(sep->arg[3]));
|
||||
packet2->setDataByName("sales_log_open", atoi(sep->arg[4]));
|
||||
EQ2Packet* outapp = packet2->serialize();
|
||||
DumpPacket(outapp);
|
||||
client->QueuePacket(outapp);
|
||||
safe_delete(packet2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
PacketStruct* packet2 = configReader.getStruct("WS_ExaminePartialSpellInfo", client->GetVersion());
|
||||
@ -11503,37 +11626,8 @@ void Commands::Command_Wind(Client* client, Seperator* sep) {
|
||||
|
||||
void Commands::Command_SendMerchantWindow(Client* client, Seperator* sep, bool sell) {
|
||||
Spawn* spawn = client->GetPlayer()->GetTarget();
|
||||
if(client->GetVersion() < 561) {
|
||||
sell = false; // doesn't support in the same way as AoM just open the normal buy/sell window
|
||||
}
|
||||
if(spawn) {
|
||||
client->SetMerchantTransaction(spawn);
|
||||
if (spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(client)){
|
||||
client->SendHailCommand(spawn);
|
||||
//MerchantFactionMultiplier* multiplier = world.GetMerchantMultiplier(spawn->GetMerchantID());
|
||||
//if(!multiplier || (multiplier && client->GetPlayer()->GetFactions()->GetFactionValue(multiplier->faction_id) >= multiplier->faction_min)){
|
||||
client->SendBuyMerchantList(sell);
|
||||
if(!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY))
|
||||
client->SendSellMerchantList(sell);
|
||||
if(!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY_BACK))
|
||||
client->SendBuyBackList(sell);
|
||||
|
||||
if(client->GetVersion() > 561) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", client->GetVersion());
|
||||
if (packet) {
|
||||
packet->setDataByName("spawn_id", 0xFFFFFFFF);
|
||||
packet->setDataByName("type", 16);
|
||||
EQ2Packet* outapp = packet->serialize();
|
||||
if (outapp)
|
||||
client->QueuePacket(outapp);
|
||||
safe_delete(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR)
|
||||
client->SendRepairList();
|
||||
}
|
||||
// client->SimpleMessage(CHANNEL_COLOR_RED, "Your faction is too low to use this merchant.");
|
||||
if(spawn)
|
||||
client->SendMerchantWindow(spawn, sell);
|
||||
}
|
||||
|
||||
|
||||
@ -12946,4 +13040,301 @@ void Commands::Command_RaidSay(Client* client, Seperator* sep) {
|
||||
*/
|
||||
void Commands::Command_ReloadZoneInfo(Client* client, Seperator* sep) {
|
||||
world.ClearZoneInfoCache();
|
||||
}
|
||||
|
||||
// somewhere in your command‐handler:
|
||||
void Commands::Command_SetLocationEntry(Client* client, Seperator* sep) {
|
||||
if(client->GetCurrentZone()->GetInstanceType() == PERSONAL_HOUSE_INSTANCE) {
|
||||
client->Message(CHANNEL_COLOR_YELLOW, "Use in a non house zone.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sep->IsSet(1) || (sep->IsNumber(0))) {
|
||||
client->Message(CHANNEL_COLOR_YELLOW, "Usage: /sle <column_name> <new_value>");
|
||||
return;
|
||||
}
|
||||
|
||||
Spawn* target = client->GetPlayer()->GetTarget();
|
||||
|
||||
if(!target) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Target missing for set location entry, /sle <column_name> <new_value>");
|
||||
return;
|
||||
}
|
||||
|
||||
// GetSpawnEntryID for spawn_location_entry to set the spawnpercentage
|
||||
int32 spawnEntryID = target->GetSpawnEntryID();
|
||||
int32 spawnPlacementID = target->GetSpawnLocationPlacementID();
|
||||
int32 spawnLocationID = target->GetSpawnLocationID();
|
||||
int32 dbID = target->GetDatabaseID();
|
||||
if (spawnPlacementID == 0 || spawnLocationID == 0 || spawnEntryID == 0 || dbID == 0) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Error: no valid spawn entry selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) Whitelist the allowed columns
|
||||
static const std::unordered_set<std::string> allowed = {
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"x_offset",
|
||||
"y_offset",
|
||||
"z_offset",
|
||||
"heading",
|
||||
"pitch",
|
||||
"roll",
|
||||
"respawn",
|
||||
"respawn_offset_low",
|
||||
"respawn_offset_high",
|
||||
"duplicated_spawn",
|
||||
"expire_timer",
|
||||
"expire_offset",
|
||||
"grid_id",
|
||||
"processed",
|
||||
"instance_id",
|
||||
"lvl_override",
|
||||
"hp_override",
|
||||
"mp_override",
|
||||
"str_override",
|
||||
"sta_override",
|
||||
"wis_override",
|
||||
"int_override",
|
||||
"agi_override",
|
||||
"heat_override",
|
||||
"cold_override",
|
||||
"magic_override",
|
||||
"mental_override",
|
||||
"divine_override",
|
||||
"disease_override",
|
||||
"poison_override",
|
||||
"difficulty_override",
|
||||
"spawnpercentage",
|
||||
"condition"
|
||||
};
|
||||
|
||||
|
||||
const std::string& field = std::string(sep->arg[0]);
|
||||
if (!allowed.count(field)) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Error: column '%s' is not modifiable.", field.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string& val = std::string(sep->arg[1]);
|
||||
|
||||
Query query;
|
||||
if(field == "spawnpercentage" || field == "condition") {
|
||||
query.AddQueryAsync(0,
|
||||
&database,
|
||||
Q_UPDATE,
|
||||
// we embed the whitelisted field name directly in the format
|
||||
"UPDATE spawn_location_entry "
|
||||
"SET %s=%s "
|
||||
"WHERE id=%u and spawn_location_id=%u and spawn_id=%u ",
|
||||
field.c_str(),
|
||||
val.c_str(),
|
||||
spawnEntryID,
|
||||
spawnLocationID,
|
||||
dbID
|
||||
);
|
||||
}
|
||||
else {
|
||||
query.AddQueryAsync(0,
|
||||
&database,
|
||||
Q_UPDATE,
|
||||
// we embed the whitelisted field name directly in the format
|
||||
"UPDATE spawn_location_placement "
|
||||
"SET %s=%s "
|
||||
"WHERE id=%u and spawn_location_id=%u ",
|
||||
field.c_str(),
|
||||
val.c_str(),
|
||||
spawnPlacementID,
|
||||
spawnLocationID
|
||||
);
|
||||
}
|
||||
|
||||
client->Message(CHANNEL_COLOR_YELLOW, "Modified %s to %s for row entry id %u, spawn placement id %u, related to location id %u and spawn database id %u.",
|
||||
field.c_str(), val.c_str(), spawnEntryID, spawnPlacementID, spawnLocationID, dbID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Commands::Command_StoreListItem(Client* client, Seperator* sep) {
|
||||
if(!client->GetShopWindowStatus()) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Shop not available.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(sep && sep->arg[0]) {
|
||||
auto info = broker.GetSellerInfo(client->GetPlayer()->GetCharacterID());
|
||||
if(!info) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Player %u is not in the broker database.", client->GetPlayer()->GetCharacterID());
|
||||
}
|
||||
else if(sep->IsNumber(0)) {
|
||||
int64 unique_id = atoll(sep->arg[0]);
|
||||
if(client->GetPlayer()->item_list.CanStoreSellItem(unique_id, true)) {
|
||||
Item* item = client->GetPlayer()->item_list.GetVaultItemFromUniqueID(unique_id, true);
|
||||
if(item) {
|
||||
bool isInv = !client->GetPlayer()->item_list.IsItemInSlotType(item, InventorySlotType::HOUSE_VAULT);
|
||||
int64 cost = broker.GetSalePrice(client->GetPlayer()->GetCharacterID(), item->details.unique_id);
|
||||
client->AddItemSale(item->details.unique_id, item->details.item_id, cost, item->details.inv_slot_id, item->details.slot_id, item->details.count, isInv, true, item->creator);
|
||||
}
|
||||
else
|
||||
client->Message(CHANNEL_COLOR_RED, "Broker issue, cannot find item %u.", unique_id);
|
||||
|
||||
client->SetItemSaleStatus(unique_id, true);
|
||||
client->GetPlayer()->item_list.SetVaultItemLockUniqueID(client, unique_id, true, false);
|
||||
client->SetSellerStatus();
|
||||
}
|
||||
}
|
||||
else {
|
||||
client->Message(CHANNEL_COLOR_RED, "Invalid arguments for /store_list_item unique_id.");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
void Commands::Command_StoreSetPrice(Client* client, Seperator* sep) {
|
||||
if(!client->GetShopWindowStatus()) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Shop not available.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(sep && sep->arg[0]) {
|
||||
auto info = broker.GetSellerInfo(client->GetPlayer()->GetCharacterID());
|
||||
int64 unique_id = atoll(sep->arg[0]);
|
||||
if(!info) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Player %u is not in the broker database.", client->GetPlayer()->GetCharacterID());
|
||||
}
|
||||
else if(info->sell_from_inventory && broker.IsItemFromInventory(client->GetPlayer()->GetCharacterID(), unique_id)) {
|
||||
client->Message(CHANNEL_COLOR_RED, "You cannot change the price while selling.");
|
||||
}
|
||||
else if(info->sell_from_inventory && !broker.IsItemFromInventory(client->GetPlayer()->GetCharacterID(), unique_id) && broker.IsItemForSale(client->GetPlayer()->GetCharacterID(), unique_id)) {
|
||||
client->Message(CHANNEL_COLOR_RED, "You cannot change the price while selling.");
|
||||
}
|
||||
else if(sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4)) {
|
||||
int32 plat = atoul(sep->arg[1]);
|
||||
int32 gold = atoul(sep->arg[2]);
|
||||
int32 silver = atoul(sep->arg[3]);
|
||||
int32 copper = atoul(sep->arg[4]);
|
||||
int64 price = plat * 1000000 + gold * 10000 + silver * 100 + copper;
|
||||
|
||||
LogWrite(PLAYER__INFO, 5, "Broker",
|
||||
"--StoreSetPrice: %u (%u), cost=%u",
|
||||
client->GetPlayer()->GetCharacterID(), unique_id, price
|
||||
);
|
||||
client->SetItemSaleCost(unique_id, plat, gold, silver, copper);
|
||||
}
|
||||
else {
|
||||
client->Message(CHANNEL_COLOR_RED, "Invalid arguments for /store_set_price unique_id platinum gold silver copper.");
|
||||
}
|
||||
}
|
||||
}
|
||||
void Commands::Command_StoreSetPriceLocal(Client* client, Seperator* sep) {
|
||||
if(!client->GetShopWindowStatus()) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Shop not available.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(sep && sep->arg[0]) {
|
||||
auto info = broker.GetSellerInfo(client->GetPlayer()->GetCharacterID());
|
||||
if(!info) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Player %u is not in the broker database.", client->GetPlayer()->GetCharacterID());
|
||||
}
|
||||
else if(info->sell_from_inventory) {
|
||||
client->Message(CHANNEL_COLOR_RED, "You cannot change the price while selling.");
|
||||
}
|
||||
else if(sep->IsNumber(0) && sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4)) {
|
||||
int64 unique_id = atoll(sep->arg[0]);
|
||||
int32 plat = atoul(sep->arg[1]);
|
||||
int32 gold = atoul(sep->arg[2]);
|
||||
int32 silver = atoul(sep->arg[3]);
|
||||
int32 copper = atoul(sep->arg[4]);
|
||||
int64 price = plat * 1000000 + gold * 10000 + silver * 100 + copper;
|
||||
LogWrite(PLAYER__INFO, 5, "Broker",
|
||||
"--StoreSetLocalPrice: %u (%u), cost=%u",
|
||||
client->GetPlayer()->GetCharacterID(), unique_id, price
|
||||
);
|
||||
client->SetItemSaleCost(unique_id, plat, gold, silver, copper);
|
||||
}
|
||||
else {
|
||||
client->Message(CHANNEL_COLOR_RED, "Invalid arguments for /store_set_price_local unique_id platinum gold silver copper.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
void Commands::Command_StoreStartSelling(Client* client, Seperator* sep) {
|
||||
if(!client->GetShopWindowStatus()) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Shop not available.");
|
||||
return;
|
||||
}
|
||||
|
||||
broker.AddSeller(client->GetPlayer()->GetCharacterID(), std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), true, true);
|
||||
client->OpenShopWindow(nullptr);
|
||||
broker.LockActiveItemsForClient(client);
|
||||
}
|
||||
|
||||
void Commands::Command_StoreStopSelling(Client* client, Seperator* sep) {
|
||||
if(!client->GetShopWindowStatus()) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Shop not available.");
|
||||
return;
|
||||
}
|
||||
|
||||
broker.AddSeller(client->GetPlayer()->GetCharacterID(), std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), true, false);
|
||||
client->OpenShopWindow(nullptr);
|
||||
broker.LockActiveItemsForClient(client);
|
||||
}
|
||||
|
||||
void Commands::Command_StoreUnlistItem(Client* client, Seperator* sep) {
|
||||
if(!client->GetShopWindowStatus()) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Shop not available.");
|
||||
return;
|
||||
}
|
||||
if(sep && sep->arg[0]) {
|
||||
auto info = broker.GetSellerInfo(client->GetPlayer()->GetCharacterID());
|
||||
if(!info) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Player %u is not in the broker database.", client->GetPlayer()->GetCharacterID());
|
||||
}
|
||||
else if(sep->IsNumber(0)) {
|
||||
int64 unique_id = atoll(sep->arg[0]);
|
||||
client->SetItemSaleStatus(unique_id, false);
|
||||
client->SetSellerStatus();
|
||||
client->GetPlayer()->item_list.SetVaultItemLockUniqueID(client, unique_id, false, false);
|
||||
}
|
||||
else {
|
||||
client->Message(CHANNEL_COLOR_RED, "Invalid arguments for /store_unlist_item unique_id.");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Commands::Command_CloseStoreKeepSelling(Client* client, Seperator* sep) {
|
||||
if(!client->GetShopWindowStatus()) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Shop not available.");
|
||||
return;
|
||||
}
|
||||
auto info = broker.GetSellerInfo(client->GetPlayer()->GetCharacterID());
|
||||
client->SetShopWindowStatus(false);
|
||||
if(!info) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Player %u is not in the broker database.", client->GetPlayer()->GetCharacterID());
|
||||
}
|
||||
else {
|
||||
bool itemsSelling = broker.IsSellingItems(client->GetPlayer()->GetCharacterID());
|
||||
broker.AddSeller(client->GetPlayer()->GetCharacterID(), std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), itemsSelling, true);
|
||||
}
|
||||
broker.LockActiveItemsForClient(client);
|
||||
}
|
||||
|
||||
void Commands::Command_CancelStore(Client* client, Seperator* sep) {
|
||||
if(!client->GetShopWindowStatus()) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Shop not available.");
|
||||
return;
|
||||
}
|
||||
auto info = broker.GetSellerInfo(client->GetPlayer()->GetCharacterID());
|
||||
client->SetShopWindowStatus(false);
|
||||
if(!info) {
|
||||
client->Message(CHANNEL_COLOR_RED, "Player %u is not in the broker database.", client->GetPlayer()->GetCharacterID());
|
||||
}
|
||||
else {
|
||||
bool itemsSelling = broker.IsSellingItems(client->GetPlayer()->GetCharacterID(), true);
|
||||
broker.AddSeller(client->GetPlayer()->GetCharacterID(), std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), itemsSelling, false);
|
||||
}
|
||||
broker.LockActiveItemsForClient(client);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -467,7 +467,16 @@ public:
|
||||
void Command_RaidSay(Client* client, Seperator* sep);
|
||||
|
||||
void Command_ReloadZoneInfo(Client* client, Seperator* sep);
|
||||
|
||||
void Command_SetLocationEntry(Client* client, Seperator* sep);
|
||||
void Command_StoreListItem(Client* client, Seperator* sep);
|
||||
void Command_StoreSetPrice(Client* client, Seperator* sep);
|
||||
void Command_StoreSetPriceLocal(Client* client, Seperator* sep);
|
||||
void Command_StoreStartSelling(Client* client, Seperator* sep);
|
||||
void Command_StoreStopSelling(Client* client, Seperator* sep);
|
||||
void Command_StoreUnlistItem(Client* client, Seperator* sep);
|
||||
void Command_CloseStoreKeepSelling(Client* client, Seperator* sep);
|
||||
void Command_CancelStore(Client* client, Seperator* sep);
|
||||
|
||||
// AA Commands
|
||||
void Get_AA_Xml(Client* client, Seperator* sep);
|
||||
void Add_AA(Client* client, Seperator* sep);
|
||||
@ -984,7 +993,16 @@ private:
|
||||
|
||||
#define COMMAND_MOOD 800
|
||||
#define COMMAND_RELOAD_PLAYERSCRIPTS 801
|
||||
|
||||
#define COMMAND_SLE 802
|
||||
#define COMMAND_STORE_LIST_ITEM 803
|
||||
#define COMMAND_STORE_SET_PRICE 804
|
||||
#define COMMAND_STORE_SET_PRICE_LOCAL 805
|
||||
#define COMMAND_STORE_START_SELLING 806
|
||||
#define COMMAND_STORE_STOP_SELLING 807
|
||||
#define COMMAND_STORE_UNLIST_ITEM 808
|
||||
#define COMMAND_CLOSE_STORE_KEEP_SELLING 809
|
||||
#define COMMAND_CANCEL_STORE 810
|
||||
|
||||
#define COMMAND_MODIFY 1000 // INSERT INTO `commands`(`id`,`type`,`command`,`subcommand`,`handler`,`required_status`) VALUES ( NULL,'1','modify','','1000','200');
|
||||
#define COMMAND_MODIFY_CHARACTER 1001
|
||||
#define COMMAND_MODIFY_FACTION 1002
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef WIN32
|
||||
#include <WinSock2.h>
|
||||
#include <windows.h>
|
||||
@ -118,6 +119,9 @@ bool WorldDatabase::RemoveSpawnTemplate(int32 template_id)
|
||||
|
||||
int32 WorldDatabase::CreateSpawnFromTemplateByID(Client* client, int32 template_id)
|
||||
{
|
||||
if(client && client->GetCurrentZone() && client->GetCurrentZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE) {
|
||||
return 0;
|
||||
}
|
||||
Query query, query2, query3, query4, query5, query6;
|
||||
MYSQL_ROW row;
|
||||
int32 spawn_location_id = 0;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Entity.h"
|
||||
#include <math.h>
|
||||
#include "Items/Items.h"
|
||||
@ -149,8 +150,8 @@ void Entity::DeleteSpellEffects(bool removeClient)
|
||||
GetInfoStruct()->spell_effects[i].spell = nullptr;
|
||||
}
|
||||
}
|
||||
MMaintainedSpells.releasewritelock(__FUNCTION__, __LINE__);
|
||||
MSpellEffects.releasewritelock(__FUNCTION__, __LINE__);
|
||||
MMaintainedSpells.releasewritelock(__FUNCTION__, __LINE__);
|
||||
|
||||
map<LuaSpell*,bool>::iterator deletedPtrItrs;
|
||||
for(deletedPtrItrs = deletedPtrs.begin(); deletedPtrItrs != deletedPtrs.end(); deletedPtrItrs++) {
|
||||
@ -1091,6 +1092,11 @@ void Entity::DoRegenUpdate(){
|
||||
void Entity::AddMaintainedSpell(LuaSpell* luaspell){
|
||||
if (!luaspell)
|
||||
return;
|
||||
|
||||
if(luaspell->spell->GetSpellData()->not_maintained || luaspell->spell->GetSpellData()->duration1 == 0) {
|
||||
LogWrite(NPC__SPELLS, 5, "NPC", "AddMaintainedSpell Spell ID: %u, Concentration: %u disallowed, not_maintained true (%u) or duration is 0 (%u).", luaspell->spell->GetSpellData()->id, luaspell->spell->GetSpellData()->req_concentration, luaspell->spell->GetSpellData()->not_maintained, luaspell->spell->GetSpellData()->duration1);
|
||||
return;
|
||||
}
|
||||
|
||||
Spell* spell = luaspell->spell;
|
||||
MaintainedEffects* effect = GetFreeMaintainedSpellSlot();
|
||||
@ -1290,7 +1296,7 @@ SpellEffects* Entity::GetSpellEffectBySpellType(int8 spell_type) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
SpellEffects* Entity::GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer, sint32 type_group_spell_id, Entity* caster) {
|
||||
SpellEffects* Entity::GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer, sint32 type_group_spell_id, Entity* caster, bool notCaster) {
|
||||
SpellEffects* ret = 0;
|
||||
InfoStruct* info = GetInfoStruct();
|
||||
MSpellEffects.readlock(__FUNCTION__, __LINE__);
|
||||
@ -1301,7 +1307,7 @@ SpellEffects* Entity::GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer
|
||||
(linked_timer > 0 && info->spell_effects[i].spell->spell->GetSpellData()->linked_timer == linked_timer) ||
|
||||
(type_group_spell_id > 0 && info->spell_effects[i].spell->spell->GetSpellData()->type_group_spell_id == type_group_spell_id))
|
||||
{
|
||||
if (type_group_spell_id >= -1 && (!caster || info->spell_effects[i].caster == caster)){
|
||||
if (type_group_spell_id >= -1 && (!caster || (!notCaster && info->spell_effects[i].caster == caster) || (notCaster && info->spell_effects[i].caster != caster))){
|
||||
ret = &info->spell_effects[i];
|
||||
break;
|
||||
}
|
||||
@ -1312,12 +1318,14 @@ SpellEffects* Entity::GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer
|
||||
return ret;
|
||||
}
|
||||
|
||||
LuaSpell* Entity::HasLinkedTimerID(LuaSpell* spell, Spawn* target, bool stackWithOtherPlayers) {
|
||||
LuaSpell* Entity::HasLinkedTimerID(LuaSpell* spell, Spawn* target, bool stackWithOtherPlayers, bool checkNotCaster) {
|
||||
if(!spell->spell->GetSpellData()->linked_timer && !spell->spell->GetSpellData()->type_group_spell_id)
|
||||
return nullptr;
|
||||
LuaSpell* ret = nullptr;
|
||||
InfoStruct* info = GetInfoStruct();
|
||||
MSpellEffects.readlock(__FUNCTION__, __LINE__);
|
||||
std::vector<int32> targets = spell->GetTargets();
|
||||
|
||||
MMaintainedSpells.readlock(__FUNCTION__, __LINE__);
|
||||
//this for loop primarily handles self checks and 'friendly' checks
|
||||
for(int i = 0; i < NUM_MAINTAINED_EFFECTS; i++) {
|
||||
if(info->maintained_effects[i].spell_id != 0xFFFFFFFF)
|
||||
@ -1327,21 +1335,34 @@ LuaSpell* Entity::HasLinkedTimerID(LuaSpell* spell, Spawn* target, bool stackWit
|
||||
(spell->spell->GetSpellData()->type_group_spell_id > 0 && spell->spell->GetSpellData()->type_group_spell_id == info->maintained_effects[i].spell->spell->GetSpellData()->type_group_spell_id)) &&
|
||||
((spell->spell->GetSpellData()->friendly_spell) ||
|
||||
(!spell->spell->GetSpellData()->friendly_spell && spell->spell->GetSpellData()->type_group_spell_id >= -1 && spell->caster == info->maintained_effects[i].spell->caster) ) &&
|
||||
(target == nullptr || info->maintained_effects[i].spell->initial_target == target->GetID())) {
|
||||
(target == nullptr || info->maintained_effects[i].spell->HasTarget(target->GetID()) || info->maintained_effects[i].spell->HasAnyTarget(targets))) {
|
||||
ret = info->maintained_effects[i].spell;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
MSpellEffects.releasereadlock(__FUNCTION__, __LINE__);
|
||||
|
||||
if(!ret && !stackWithOtherPlayers && target && target->IsEntity())
|
||||
{
|
||||
SpellEffects* effect = ((Entity*)target)->GetSpellEffectWithLinkedTimer(spell->spell->GetSpellID(), spell->spell->GetSpellData()->linked_timer, spell->spell->GetSpellData()->type_group_spell_id, nullptr);
|
||||
if(effect)
|
||||
ret = effect->spell;
|
||||
MMaintainedSpells.releasereadlock(__FUNCTION__, __LINE__);
|
||||
|
||||
if(checkNotCaster && ret && ret->caster != spell->caster)
|
||||
return ret;
|
||||
else if(checkNotCaster && ret)
|
||||
ret = nullptr;
|
||||
|
||||
for (int32 id : spell->GetTargets()) {
|
||||
Spawn* tmpTarget = spell->zone->GetSpawnByID(id);
|
||||
if(!ret && !stackWithOtherPlayers && tmpTarget && tmpTarget->IsEntity())
|
||||
{
|
||||
SpellEffects* effect = ((Entity*)tmpTarget)->GetSpellEffectWithLinkedTimer(spell->spell->GetSpellID(), spell->spell->GetSpellData()->linked_timer, spell->spell->GetSpellData()->type_group_spell_id, checkNotCaster ? spell->caster : nullptr, checkNotCaster);
|
||||
if(effect) {
|
||||
ret = effect->spell;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(checkNotCaster && ret && ret->caster == spell->caster)
|
||||
ret = nullptr;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -1496,8 +1496,8 @@ public:
|
||||
SpellEffects* GetFreeSpellEffectSlot();
|
||||
SpellEffects* GetSpellEffect(int32 id, Entity* caster = 0, bool on_char_load = false);
|
||||
SpellEffects* GetSpellEffectBySpellType(int8 spell_type);
|
||||
SpellEffects* GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer = 0, sint32 type_group_spell_id = 0, Entity* caster = 0);
|
||||
LuaSpell* HasLinkedTimerID(LuaSpell* spell, Spawn* target = nullptr, bool stackWithOtherPlayers = true);
|
||||
SpellEffects* GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer = 0, sint32 type_group_spell_id = 0, Entity* caster = 0, bool notCaster = false);
|
||||
LuaSpell* HasLinkedTimerID(LuaSpell* spell, Spawn* target = nullptr, bool stackWithOtherPlayers = true, bool checkNotCaster = false);
|
||||
|
||||
//flags
|
||||
int32 GetFlags() { return info_struct.get_flags(); }
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,11 +17,13 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __EQ2_ITEMS__
|
||||
#define __EQ2_ITEMS__
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <ctime>
|
||||
#include <shared_mutex>
|
||||
#include "../../common/types.h"
|
||||
#include "../../common/DataBuffer.h"
|
||||
#include "../Commands/Commands.h"
|
||||
@ -647,6 +649,47 @@ enum ItemEffectType {
|
||||
EFFECT_CURE_TYPE_MAGIC=6,
|
||||
EFFECT_CURE_TYPE_ALL=7
|
||||
};
|
||||
|
||||
enum InventorySlotType {
|
||||
HOUSE_VAULT=-5,
|
||||
SHARED_BANK=-4,
|
||||
BANK=-3,
|
||||
OVERFLOW=-2,
|
||||
UNKNOWN_INV_SLOT_TYPE=-1,
|
||||
BASE_INVENTORY=0
|
||||
};
|
||||
|
||||
enum class LockReason : int32 {
|
||||
LockReason_None = 0,
|
||||
LockReason_House = 1u << 0,
|
||||
LockReason_Crafting = 1u << 1,
|
||||
LockReason_Shop = 1u << 2,
|
||||
};
|
||||
|
||||
inline LockReason operator|(LockReason a, LockReason b) {
|
||||
return static_cast<LockReason>(
|
||||
static_cast<uint32_t>(a) | static_cast<uint32_t>(b)
|
||||
);
|
||||
}
|
||||
inline LockReason operator&(LockReason a, LockReason b) {
|
||||
return static_cast<LockReason>(
|
||||
static_cast<uint32_t>(a) & static_cast<uint32_t>(b)
|
||||
);
|
||||
}
|
||||
inline LockReason operator~(LockReason a) {
|
||||
return static_cast<LockReason>(~static_cast<uint32_t>(a));
|
||||
}
|
||||
|
||||
enum HouseStoreItemFlags {
|
||||
HOUSE_STORE_ITEM_TEXT_RED=1,
|
||||
HOUSE_STORE_UNKNOWN_BIT2=2,
|
||||
HOUSE_STORE_UNKNOWN_BIT4=4,
|
||||
HOUSE_STORE_FOR_SALE=8,
|
||||
HOUSE_STORE_UNKNOWN_BIT16=16,
|
||||
HOUSE_STORE_VAULT_TAB=32
|
||||
// rest are also unknown
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
struct ItemStatsValues{
|
||||
sint16 str;
|
||||
@ -710,10 +753,11 @@ struct ItemCore{
|
||||
int16 count;
|
||||
int8 tier;
|
||||
int8 num_slots;
|
||||
int32 unique_id;
|
||||
int64 unique_id;
|
||||
int8 num_free_slots;
|
||||
int16 recommended_level;
|
||||
bool item_locked;
|
||||
int32 lock_flags;
|
||||
bool new_item;
|
||||
int16 new_index;
|
||||
};
|
||||
@ -934,6 +978,8 @@ public:
|
||||
#pragma pack()
|
||||
Item();
|
||||
Item(Item* in_item);
|
||||
Item(Item* in_item, int64 unique_id, std::string in_creator, std::string in_seller_name, int32 in_seller_char_id, int64 in_broker_price, int16 count, int64 in_seller_house_id);
|
||||
|
||||
~Item();
|
||||
string lowername;
|
||||
string name;
|
||||
@ -942,10 +988,15 @@ public:
|
||||
int32 sell_price;
|
||||
int32 sell_status;
|
||||
int32 max_sell_value;
|
||||
int64 broker_price;
|
||||
bool is_search_store_item;
|
||||
bool save_needed;
|
||||
int8 weapon_type;
|
||||
string adornment;
|
||||
string creator;
|
||||
string seller_name;
|
||||
int32 seller_char_id;
|
||||
int64 seller_house_id;
|
||||
int32 adorn0;
|
||||
int32 adorn1;
|
||||
int32 adorn2;
|
||||
@ -986,6 +1037,7 @@ public:
|
||||
bool crafted;
|
||||
bool tinkered;
|
||||
int8 book_language;
|
||||
mutable std::shared_mutex item_lock_mtx_;
|
||||
|
||||
void AddEffect(string effect, int8 percentage, int8 subbulletflag);
|
||||
void AddBookPage(int8 page, string page_text,int8 valign, int8 halign);
|
||||
@ -1067,6 +1119,10 @@ public:
|
||||
void AddSlot(int8 slot_id);
|
||||
void SetSlots(int32 slots);
|
||||
int16 GetIcon(int16 version);
|
||||
bool TryLockItem(LockReason reason);
|
||||
bool TryUnlockItem(LockReason reason);
|
||||
bool IsItemLocked();
|
||||
bool IsItemLockedFor(LockReason reason);
|
||||
};
|
||||
class MasterItemList{
|
||||
public:
|
||||
@ -1079,14 +1135,16 @@ public:
|
||||
Item* GetAllItemsByClassification(const char* name);
|
||||
ItemStatsValues* CalculateItemBonuses(int32 item_id, Entity* entity = 0);
|
||||
ItemStatsValues* CalculateItemBonuses(Item* desc, Entity* entity = 0, ItemStatsValues* values = 0);
|
||||
|
||||
bool ShouldAddItemBrokerType(Item* item, int64 itype);
|
||||
bool ShouldAddItemBrokerSlot(Item* item, int64 ltype);
|
||||
bool ShouldAddItemBrokerStat(Item* item, int64 btype);
|
||||
vector<Item*>* GetItems(string name, int64 itype, int64 ltype, int64 btype, int64 minprice, int64 maxprice, int8 minskill, int8 maxskill, string seller, string adornment, int8 mintier, int8 maxtier, int16 minlevel, int16 maxlevel, sint8 itemclass);
|
||||
vector<Item*>* GetItems(map<string, string> criteria, Client* client_to_map);
|
||||
void AddItem(Item* item);
|
||||
bool IsBag(int32 item_id);
|
||||
void RemoveAll();
|
||||
static int32 NextUniqueID();
|
||||
static void ResetUniqueID(int32 new_id);
|
||||
static int32 next_unique_id;
|
||||
static int64 NextUniqueID();
|
||||
int32 GetItemStatIDByName(std::string name);
|
||||
std::string GetItemStatNameByID(int32 id);
|
||||
void AddMappedItemStat(int32 id, std::string lower_case_name);
|
||||
@ -1120,10 +1178,18 @@ public:
|
||||
void MoveItem(Item* item, sint32 inv_slot, int16 slot, int8 appearance_type, bool erase_old); // erase old was true
|
||||
bool MoveItem(sint32 to_bag_id, int16 from_index, sint8 to, int8 appearance_type, int8 charges);
|
||||
void EraseItem(Item* item);
|
||||
|
||||
Item* GetItemFromUniqueID(int32 item_id, bool include_bank = false, bool lock = true);
|
||||
void SetVaultItemLockUniqueID(Client* client, int64 id, bool state, bool lock);
|
||||
bool CanStoreSellItem(int64 unique_id, bool lock);
|
||||
bool IsItemInSlotType(Item* item, InventorySlotType type, bool lockItems=true);
|
||||
|
||||
void SetVaultItemUniqueIDCount(Client* client, int64 unique_id, int16 count, bool lock = true);
|
||||
void RemoveVaultItemFromUniqueID(Client* client, int64 item_id, bool lock = true);
|
||||
Item* GetVaultItemFromUniqueID(int64 item_id, bool lock = true);
|
||||
Item* GetItemFromID(int32 item_id, int8 count = 0, bool include_bank = false, bool lock = true);
|
||||
sint32 GetAllStackCountItemFromID(int32 item_id, int8 count = 0, bool include_bank = false, bool lock = true);
|
||||
bool AssignItemToFreeSlot(Item* item);
|
||||
bool AssignItemToFreeSlot(Item* item, bool inventory_only = true);
|
||||
int16 GetNumberOfFreeSlots();
|
||||
int16 GetNumberOfItems();
|
||||
int32 GetWeight();
|
||||
@ -1132,7 +1198,7 @@ public:
|
||||
void DestroyItem(int16 index);
|
||||
Item* CanStack(Item* item, bool include_bank = false);
|
||||
vector<Item*> GetAllItemsFromID(int32 item, bool include_bank = false, bool lock = false);
|
||||
void RemoveItem(Item* item, bool delete_item = false);
|
||||
void RemoveItem(Item* item, bool delete_item = false, bool lock = true);
|
||||
bool AddItem(Item* item);
|
||||
|
||||
Item* GetItem(sint32 bag_slot, int16 slot, int8 appearance_type = 0);
|
||||
@ -1143,7 +1209,10 @@ public:
|
||||
map<int32, Item*>* GetAllItems();
|
||||
bool HasFreeBankSlot();
|
||||
int8 FindFreeBankSlot();
|
||||
|
||||
|
||||
void GetVaultItems(Client* client, int32 spawn_id, int8 maxSlots, bool isSelling = false);
|
||||
void PopulateHouseStoragePacket(Client* client, PacketStruct* packet, Item* item, int16 itemIdx, int8 storage_flags);
|
||||
|
||||
///<summary>Get the first free slot and store them in the provided variables</summary>
|
||||
///<param name='bag_id'>Will contain the bag id of the first free spot</param>
|
||||
///<param name='slot'>Will contain the slot id of the first free slot</param>
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef WIN32
|
||||
#include <WinSock2.h>
|
||||
#include <windows.h>
|
||||
@ -172,6 +173,7 @@ void WorldDatabase::LoadDataFromRow(DatabaseResult* result, Item* item)
|
||||
item->generic_info.part_of_quest_id = result->GetInt32Str("part_of_quest_id");
|
||||
item->details.recommended_level = result->GetInt16Str("recommended_level");
|
||||
item->details.item_locked = false;
|
||||
item->details.lock_flags = 0;
|
||||
item->generic_info.adventure_default_level = result->GetInt16Str("adventure_default_level");
|
||||
item->generic_info.max_charges = result->GetInt16Str("max_charges");
|
||||
item->generic_info.display_charges = result->GetInt8Str("display_charges");
|
||||
@ -541,7 +543,7 @@ int32 WorldDatabase::LoadHouseContainers(int32 item_id){
|
||||
if (item)
|
||||
{
|
||||
LogWrite(ITEM__DEBUG, 5, "Items", "\tHouse Container for item_id %u", id);
|
||||
LogWrite(ITEM__DEBUG, 5, "Items", "\tType: %i, '%i', '%u', '%i', '%i'", ITEM_TYPE_RECIPE, result.GetInt8Str("num_slots"), result.GetInt64Str("allowed_types"), result.GetInt8Str("broker_commission"), result.GetInt8Str("fence_commission"));
|
||||
LogWrite(ITEM__DEBUG, 5, "Items", "\tType: %i, '%i', '%u', '%i', '%i'", ITEM_TYPE_HOUSE_CONTAINER, result.GetInt8Str("num_slots"), result.GetInt64Str("allowed_types"), result.GetInt8Str("broker_commission"), result.GetInt8Str("fence_commission"));
|
||||
|
||||
item->SetItemType(ITEM_TYPE_HOUSE_CONTAINER);
|
||||
item->housecontainer_info->num_slots = result.GetInt8Str("num_slots");
|
||||
@ -549,6 +551,11 @@ int32 WorldDatabase::LoadHouseContainers(int32 item_id){
|
||||
item->housecontainer_info->broker_commission = result.GetInt8Str("broker_commission");
|
||||
item->housecontainer_info->fence_commission = result.GetInt8Str("fence_commission");
|
||||
|
||||
item->details.num_slots = item->housecontainer_info->num_slots;
|
||||
item->details.num_free_slots = item->housecontainer_info->num_slots;
|
||||
item->bag_info->num_slots = item->housecontainer_info->num_slots;
|
||||
item->bag_info->weight_reduction = 0;
|
||||
|
||||
total++;
|
||||
}
|
||||
else
|
||||
@ -1097,18 +1104,19 @@ void WorldDatabase::LoadItemList(int32 item_id)
|
||||
LogWrite(ITEM__INFO, 0, "Items", "Loaded %u Total Item%s (took %u seconds)", total, ( total == 1 ) ? "" : "s", Timer::GetUnixTimeStamp() - t_now);
|
||||
}
|
||||
|
||||
int32 WorldDatabase::LoadNextUniqueItemID()
|
||||
int64 WorldDatabase::LoadNextUniqueItemID()
|
||||
{
|
||||
Query query;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(id) FROM character_items");
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT NEXT VALUE FOR seq_character_items AS next_id");
|
||||
|
||||
if(result && (row = mysql_fetch_row(result)))
|
||||
{
|
||||
if(row[0])
|
||||
{
|
||||
LogWrite(ITEM__DEBUG, 0, "Items", "%s: max(id): %u", __FUNCTION__, atoul(row[0]));
|
||||
return strtoul(row[0], NULL, 0);
|
||||
int64 max_ = strtoull(row[0], NULL, 0);
|
||||
LogWrite(ITEM__DEBUG, 0, "Items", "%s: max(id): %u", __FUNCTION__, max_);
|
||||
return max_;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
@ -1119,6 +1127,31 @@ int32 WorldDatabase::LoadNextUniqueItemID()
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WorldDatabase::ResetNextUniqueItemID()
|
||||
{
|
||||
Query query;
|
||||
Query query2;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_ROW row2;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT next_not_cached_value FROM seq_character_items");
|
||||
MYSQL_RES* result2 = query2.RunQuery2(Q_SELECT, "SELECT COALESCE(MAX(id),0) + 1 FROM character_items");
|
||||
if(result && (row = mysql_fetch_row(result)) && result2 && (row2 = mysql_fetch_row(result2)))
|
||||
{
|
||||
if(row[0] && row2[0])
|
||||
{
|
||||
int64 max_cur = strtoull(row[0], NULL, 0);
|
||||
int64 max_expected = strtoull(row2[0], NULL, 0);
|
||||
string update_item = string("ALTER SEQUENCE seq_character_items RESTART WITH %llu");
|
||||
if(max_cur < max_expected)
|
||||
query.AddQueryAsync(0, this, Q_UPDATE, update_item.c_str(), max_expected);
|
||||
|
||||
LogWrite(ITEM__DEBUG, 0, "Items", "%s: max(current): %u max(expected): %u", __FUNCTION__, max_cur, max_expected);
|
||||
}
|
||||
}
|
||||
else if(!result)
|
||||
LogWrite(ITEM__ERROR, 0, "Items", "%s: Unable to reset next unique item ID.", __FUNCTION__);
|
||||
}
|
||||
|
||||
void WorldDatabase::SaveItems(Client* client)
|
||||
{
|
||||
LogWrite(ITEM__DEBUG, 3, "Items", "Save Items for Player %i", client->GetCharacterID());
|
||||
@ -1382,7 +1415,7 @@ void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Playe
|
||||
int8 remainder = item->details.count % 255;
|
||||
item->details.count = remainder;
|
||||
|
||||
if (item->details.inv_slot_id == -2)
|
||||
if (item->details.inv_slot_id == InventorySlotType::OVERFLOW)
|
||||
player->item_list.AddOverflowItem(item);
|
||||
else {
|
||||
if(!player->item_list.AddItem(item))
|
||||
@ -1398,7 +1431,7 @@ void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Playe
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (item->details.inv_slot_id == -2)
|
||||
if (item->details.inv_slot_id == InventorySlotType::OVERFLOW)
|
||||
player->item_list.AddOverflowItem(item);
|
||||
else
|
||||
player->item_list.AddItem(item);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __EQ2_ITEMS__
|
||||
#define __EQ2_ITEMS__
|
||||
#include <map>
|
||||
@ -730,9 +731,7 @@ public:
|
||||
void AddItem(Item* item);
|
||||
bool IsBag(int32 item_id);
|
||||
void RemoveAll();
|
||||
static int32 NextUniqueID();
|
||||
static void ResetUniqueID(int32 new_id);
|
||||
static int32 next_unique_id;
|
||||
static int64 NextUniqueID();
|
||||
};
|
||||
class PlayerItemList {
|
||||
public:
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __EQ2_ITEMS__
|
||||
#define __EQ2_ITEMS__
|
||||
#include <map>
|
||||
@ -824,9 +825,7 @@ public:
|
||||
void AddItem(Item* item);
|
||||
bool IsBag(int32 item_id);
|
||||
void RemoveAll();
|
||||
static int32 NextUniqueID();
|
||||
static void ResetUniqueID(int32 new_id);
|
||||
static int32 next_unique_id;
|
||||
static int64 NextUniqueID();
|
||||
};
|
||||
class PlayerItemList {
|
||||
public:
|
||||
|
@ -1,22 +1,23 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
EQ2Emulator 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.
|
||||
EQ2Emulator 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.
|
||||
|
||||
EQ2Emulator 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.
|
||||
EQ2Emulator 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 EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "LuaFunctions.h"
|
||||
#include "Spawn.h"
|
||||
#include "WorldDatabase.h"
|
||||
@ -37,6 +38,7 @@
|
||||
#include "ClientPacketFunctions.h"
|
||||
#include "Transmute.h"
|
||||
#include "Titles.h"
|
||||
#include "./Broker/BrokerManager.h"
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <sstream>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
@ -60,6 +62,7 @@ extern MasterRaceTypeList race_types_list;
|
||||
extern MasterLanguagesList master_languages_list;
|
||||
extern MasterTitlesList master_titles_list;
|
||||
extern RuleManager rule_manager;
|
||||
extern BrokerManager broker;
|
||||
|
||||
vector<string> ParseString(string strVal, char delim) {
|
||||
stringstream ss(strVal);
|
||||
@ -9646,7 +9649,6 @@ int EQ2Emu_lua_CureByType(lua_State* state) {
|
||||
}
|
||||
else {
|
||||
ZoneServer* zone = spell->zone;
|
||||
vector<int32> targets = spell->targets;
|
||||
vector<Entity*> targets_to_cure;
|
||||
for (int32 id : spell->GetTargets()) {
|
||||
target = zone->GetSpawnByID(id);
|
||||
@ -9797,6 +9799,7 @@ int EQ2Emu_lua_StartHeroicOpportunity(lua_State* state) {
|
||||
|
||||
Spawn* caster = lua_interface->GetSpawn(state);
|
||||
int8 class_id = lua_interface->GetInt8Value(state, 2);
|
||||
Spawn* targetOverride = lua_interface->GetSpawn(state, 3);
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
|
||||
if (!caster) {
|
||||
@ -9810,10 +9813,13 @@ int EQ2Emu_lua_StartHeroicOpportunity(lua_State* state) {
|
||||
}
|
||||
|
||||
Spawn* target = caster->GetTarget();
|
||||
if (!target) {
|
||||
if (!target && !targetOverride) {
|
||||
lua_interface->LogError("%s: LUA StartHeroicOpportunity command error: target is not valid", lua_interface->GetScriptName(state));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!target)
|
||||
target = targetOverride;
|
||||
|
||||
Client* client = ((Player*)caster)->GetClient();
|
||||
if (!client) {
|
||||
@ -14656,3 +14662,103 @@ int EQ2Emu_lua_GetExpRequiredByLevel(lua_State* state) {
|
||||
lua_interface->SetInt32Value(state, exp_required);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int EQ2Emu_lua_ShowShopWindow(lua_State* state) {
|
||||
|
||||
Spawn* player = lua_interface->GetSpawn(state);
|
||||
Spawn* from_spawn = lua_interface->GetSpawn(state, 2);
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if (player) {
|
||||
Client* client = 0;
|
||||
if (player->IsPlayer())
|
||||
client = ((Player*)player)->GetClient();
|
||||
if (client) {
|
||||
client->OpenShopWindow(from_spawn, true);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int EQ2Emu_lua_SetSpawnHouseScript(lua_State* state) {
|
||||
|
||||
Spawn* target = lua_interface->GetSpawn(state);
|
||||
string lua_script = lua_interface->GetStringValue(state, 2);
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if (target && target->GetDatabaseID() && !target->IsPlayer() && !target->IsBot()) {
|
||||
database.UpdateHouseSpawnScript(target->GetDatabaseID(), lua_script);
|
||||
bool scriptActive = false;
|
||||
if (lua_interface && lua_interface->GetSpawnScript(lua_script.c_str()) != 0) {
|
||||
scriptActive = true;
|
||||
target->SetSpawnScript(lua_script);
|
||||
}
|
||||
if (scriptActive) {
|
||||
target->GetZone()->CallSpawnScript(target, SPAWN_SCRIPT_SPAWN);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int EQ2Emu_lua_SendBook(lua_State* state) {
|
||||
|
||||
Spawn* target = lua_interface->GetSpawn(state);
|
||||
int32 item_id = lua_interface->GetInt32Value(state, 2);
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if (target && target->IsPlayer() && ((Player*)target)->GetClient()) {
|
||||
Item* item = master_item_list.GetItem(item_id);
|
||||
if (item) {
|
||||
((Player*)target)->GetClient()->SendShowBook(target, item->name, item->book_language, item->book_pages);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int EQ2Emu_lua_GetPickupItemID(lua_State* state) {
|
||||
Spawn* spawn = lua_interface->GetSpawn(state);
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if (spawn) {
|
||||
lua_interface->SetInt32Value(state, spawn->GetPickupItemID());
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int EQ2Emu_lua_SetHouseCharacterID(lua_State* state) {
|
||||
Spawn* spawn = lua_interface->GetSpawn(state);
|
||||
int32 character_id = lua_interface->GetInt32Value(state, 2);
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if (spawn) {
|
||||
if(!character_id) {
|
||||
PlayerHouse* ph = world.GetPlayerHouseByInstanceID(spawn->GetZone()->GetInstanceID());
|
||||
if(ph)
|
||||
spawn->SetHouseCharacterID(ph->character_id);
|
||||
}
|
||||
else {
|
||||
spawn->SetHouseCharacterID(character_id);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int EQ2Emu_lua_GetHouseCharacterID(lua_State* state) {
|
||||
Spawn* spawn = lua_interface->GetSpawn(state);
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if (spawn) {
|
||||
lua_interface->SetInt32Value(state, spawn->GetHouseCharacterID());
|
||||
}
|
||||
else {
|
||||
lua_interface->SetInt32Value(state, 0);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int EQ2Emu_lua_ShowHouseShopMerchant(lua_State* state) {
|
||||
Spawn* spawn = lua_interface->GetSpawn(state);
|
||||
Spawn* player = lua_interface->GetSpawn(state, 2);
|
||||
lua_interface->ResetFunctionStack(state);
|
||||
if (spawn && player && player->IsPlayer()) {
|
||||
if(player->GetClient())
|
||||
player->GetClient()->SendMerchantWindow(spawn, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LUA_FUNCTIONS_H
|
||||
#define LUA_FUNCTIONS_H
|
||||
|
||||
@ -684,4 +685,12 @@ int EQ2Emu_lua_GetZonePlayerFirstLevel(lua_State* state);
|
||||
int EQ2Emu_lua_GetSpellRequiredLevel(lua_State* state);
|
||||
|
||||
int EQ2Emu_lua_GetExpRequiredByLevel(lua_State* state);
|
||||
|
||||
int EQ2Emu_lua_ShowShopWindow(lua_State* state);
|
||||
int EQ2Emu_lua_SetSpawnHouseScript(lua_State* state);
|
||||
int EQ2Emu_lua_SendBook(lua_State* state);
|
||||
int EQ2Emu_lua_GetPickupItemID(lua_State* state);
|
||||
int EQ2Emu_lua_SetHouseCharacterID(lua_State* state);
|
||||
int EQ2Emu_lua_GetHouseCharacterID(lua_State* state);
|
||||
int EQ2Emu_lua_ShowHouseShopMerchant(lua_State* state);
|
||||
#endif
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -1824,6 +1824,14 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
|
||||
|
||||
lua_register(state,"GetSpellRequiredLevel", EQ2Emu_lua_GetSpellRequiredLevel);
|
||||
lua_register(state,"GetExpRequiredByLevel", EQ2Emu_lua_GetExpRequiredByLevel);
|
||||
|
||||
lua_register(state,"ShowShopWindow", EQ2Emu_lua_ShowShopWindow);
|
||||
lua_register(state,"SetSpawnHouseScript", EQ2Emu_lua_SetSpawnHouseScript);
|
||||
lua_register(state,"SendBook", EQ2Emu_lua_SendBook);
|
||||
lua_register(state,"GetPickupItemID", EQ2Emu_lua_GetPickupItemID);
|
||||
lua_register(state,"SetHouseCharacterID", EQ2Emu_lua_SetHouseCharacterID);
|
||||
lua_register(state,"GetHouseCharacterID", EQ2Emu_lua_GetHouseCharacterID);
|
||||
lua_register(state,"ShowHouseShopMerchant", EQ2Emu_lua_ShowHouseShopMerchant);
|
||||
}
|
||||
|
||||
void LuaInterface::LogError(const char* error, ...) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LUA_INTERFACE_H
|
||||
#define LUA_INTERFACE_H
|
||||
|
||||
@ -161,7 +162,16 @@ struct LuaSpell{
|
||||
std::shared_lock lock(targets_mutex);
|
||||
return std::find(targets.begin(), targets.end(), id) != targets.end();
|
||||
}
|
||||
|
||||
|
||||
bool HasAnyTarget(const std::vector<int32>& ids) const {
|
||||
std::shared_lock lock(targets_mutex);
|
||||
return std::any_of(
|
||||
ids.begin(), ids.end(),
|
||||
[this](int32 id){
|
||||
return std::find(targets.begin(), targets.end(), id) != targets.end();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
std::vector<int32> GetRemovedTargets() const {
|
||||
std::shared_lock lock(targets_mutex);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "NPC.h"
|
||||
#include "WorldDatabase.h"
|
||||
#include <math.h>
|
||||
@ -108,6 +109,8 @@ NPC::NPC(NPC* old_npc){
|
||||
SetLootDropType(old_npc->GetLootDropType());
|
||||
has_spells = old_npc->HasSpells();
|
||||
SetScaredByStrongPlayers(old_npc->IsScaredByStrongPlayers());
|
||||
if(old_npc->GetSpawnScriptSetDB() && old_npc->GetSpawnScript())
|
||||
SetSpawnScript(std::string(old_npc->GetSpawnScript()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -271,13 +271,13 @@ void Brain::AddHate(Entity* entity, sint32 hate) {
|
||||
}
|
||||
}
|
||||
|
||||
void Brain::ClearHate() {
|
||||
void Brain::ClearHate(bool lockSpawnList) {
|
||||
// Lock the hate list, we are altering the list so use a write lock
|
||||
MHateList.writelock(__FUNCTION__, __LINE__);
|
||||
|
||||
map<int32, sint32>::iterator itr;
|
||||
for (itr = m_hatelist.begin(); itr != m_hatelist.end(); itr++) {
|
||||
Spawn* spawn = m_body->GetZone()->GetSpawnByID(itr->first);
|
||||
Spawn* spawn = m_body->GetZone()->GetSpawnByID(itr->first, lockSpawnList);
|
||||
if (spawn && spawn->IsEntity())
|
||||
{
|
||||
((Entity*)spawn)->MHatedBy.lock();
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __NPC_AI_H__
|
||||
#define __NPC_AI_H__
|
||||
#include "NPC.h"
|
||||
@ -59,7 +60,7 @@ public:
|
||||
/// <param name="hate">The amount of hate to add</param>
|
||||
virtual void AddHate(Entity* entity, sint32 hate);
|
||||
/// <summary>Completely clears the hate list for this npc</summary>
|
||||
void ClearHate();
|
||||
void ClearHate(bool lockSpawnList = true);
|
||||
/// <summary>Removes the given entity from this NPC's hate list</summary>
|
||||
/// <param name="entity">Entity to remove from this NPC's hate list</param>
|
||||
void ClearHate(Entity* entity);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "World.h"
|
||||
#include "Object.h"
|
||||
#include "Spells.h"
|
||||
@ -95,5 +96,7 @@ Object* Object::Copy(){
|
||||
new_spawn->SetOmittedByDBFlag(IsOmittedByDBFlag());
|
||||
new_spawn->SetLootTier(GetLootTier());
|
||||
new_spawn->SetLootDropType(GetLootDropType());
|
||||
if(GetSpawnScriptSetDB() && GetSpawnScript())
|
||||
new_spawn->SetSpawnScript(std::string(GetSpawnScript()));
|
||||
return new_spawn;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -137,6 +137,7 @@ Player::Player(){
|
||||
active_drink_unique_id = 0;
|
||||
raidsheet_changed = false;
|
||||
hassent_raid = false;
|
||||
house_vault_slots = 0;
|
||||
}
|
||||
Player::~Player(){
|
||||
SetSaveSpellEffects(true);
|
||||
@ -953,7 +954,7 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal
|
||||
packet->setDataByName("rain2", info_struct->get_wind()); //-102.24);
|
||||
packet->setDataByName("status_points", info_struct->get_status_points());
|
||||
packet->setDataByName("guild_status", 888888);
|
||||
|
||||
packet->setDataByName("vault_slots", player->GetHouseVaultSlots());
|
||||
if (house_zone_id > 0){
|
||||
string house_name = database.GetZoneName(house_zone_id);
|
||||
if(house_name.length() > 0)
|
||||
@ -1484,7 +1485,7 @@ vector<EQ2Packet*> Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in
|
||||
SetCharSheetChanged(true);
|
||||
SetEquipment(0, equip_slot_id ? equip_slot_id : old_slot);
|
||||
}
|
||||
else if (item_list.AssignItemToFreeSlot(item)) {
|
||||
else if (item_list.AssignItemToFreeSlot(item, true)) {
|
||||
if(appearance_type)
|
||||
database.DeleteItem(GetCharacterID(), item, "APPEARANCE");
|
||||
else
|
||||
@ -1645,8 +1646,8 @@ vector<EQ2Packet*> Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((bag_id == 0 && slot < NUM_INV_SLOTS) || (bag_id == -3 && slot < NUM_BANK_SLOTS) || (bag_id == -4 && slot < NUM_SHARED_BANK_SLOTS)) {
|
||||
if (bag_id == -4 && item->CheckFlag(NO_TRADE)) {
|
||||
if ((bag_id == 0 && slot < NUM_INV_SLOTS) || (bag_id == InventorySlotType::BANK && slot < NUM_BANK_SLOTS) || (bag_id == InventorySlotType::SHARED_BANK && slot < NUM_SHARED_BANK_SLOTS) || (bag_id == InventorySlotType::HOUSE_VAULT && slot < GetHouseVaultSlots())) {
|
||||
if ((bag_id == InventorySlotType::SHARED_BANK || bag_id == InventorySlotType::HOUSE_VAULT) && !item_list.SharedBankAddAllowed(item)) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_DisplayText", version);
|
||||
if (packet) {
|
||||
packet->setDataByName("color", CHANNEL_COLOR_YELLOW);
|
||||
@ -2018,7 +2019,7 @@ vector<EQ2Packet*> Player::EquipItem(int16 index, int16 version, int8 appearance
|
||||
const char* zone_script = world.GetZoneScript(GetZone()->GetZoneID());
|
||||
if (zone_script && lua_interface)
|
||||
lua_interface->RunZoneScript(zone_script, "item_equipped", GetZone(), this, item->details.item_id, item->name.c_str(), 0, item->details.unique_id);
|
||||
int32 bag_id = item->details.inv_slot_id;
|
||||
sint32 bag_id = item->details.inv_slot_id;
|
||||
if (item->generic_info.condition == 0) {
|
||||
Client* client = GetClient();
|
||||
if (client) {
|
||||
@ -2108,7 +2109,7 @@ bool Player::AddItem(Item* item, AddItemType type) {
|
||||
safe_delete(item);
|
||||
return false;
|
||||
}
|
||||
else if (item_list.AssignItemToFreeSlot(item)) {
|
||||
else if (item_list.AssignItemToFreeSlot(item, true)) {
|
||||
item->save_needed = true;
|
||||
CalculateApplyWeight();
|
||||
return true;
|
||||
@ -2160,7 +2161,7 @@ void Player::UpdateInventory(int32 bag_id) {
|
||||
EQ2Packet* Player::MoveInventoryItem(sint32 to_bag_id, int16 from_index, int8 new_slot, int8 charges, int8 appearance_type, bool* item_deleted, int16 version) {
|
||||
|
||||
Item* item = item_list.GetItemFromIndex(from_index);
|
||||
bool isOverflow = ((item != nullptr) && (item->details.inv_slot_id == -2));
|
||||
bool isOverflow = ((item != nullptr) && (item->details.inv_slot_id == InventorySlotType::OVERFLOW));
|
||||
int8 result = item_list.MoveItem(to_bag_id, from_index, new_slot, appearance_type, charges);
|
||||
if (result == 1) {
|
||||
if(isOverflow && item->details.inv_slot_id != -2) {
|
||||
@ -3434,6 +3435,11 @@ void Player::AddMaintainedSpell(LuaSpell* luaspell){
|
||||
if(!luaspell)
|
||||
return;
|
||||
|
||||
if(luaspell->spell->GetSpellData()->not_maintained || luaspell->spell->GetSpellData()->duration1 == 0) {
|
||||
LogWrite(PLAYER__INFO, 0, "NPC", "AddMaintainedSpell Spell ID: %u, Concentration: %u disallowed, not_maintained true (%u) or duration is 0 (%u).", luaspell->spell->GetSpellData()->id, luaspell->spell->GetSpellData()->req_concentration, luaspell->spell->GetSpellData()->not_maintained, luaspell->spell->GetSpellData()->duration1);
|
||||
return;
|
||||
}
|
||||
|
||||
Spell* spell = luaspell->spell;
|
||||
MaintainedEffects* effect = GetFreeMaintainedSpellSlot();
|
||||
int32 target_type = 0;
|
||||
@ -7458,8 +7464,8 @@ void Player::SaveSpellEffects()
|
||||
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_custom_spell_display where charid=%u", GetCharacterID());
|
||||
savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_custom_spell_data where charid=%u", GetCharacterID());
|
||||
InfoStruct* info = GetInfoStruct();
|
||||
MSpellEffects.readlock(__FUNCTION__, __LINE__);
|
||||
MMaintainedSpells.readlock(__FUNCTION__, __LINE__);
|
||||
MSpellEffects.readlock(__FUNCTION__, __LINE__);
|
||||
for(int i = 0; i < 45; i++) {
|
||||
if(info->spell_effects[i].spell_id != 0xFFFFFFFF)
|
||||
{
|
||||
@ -7492,7 +7498,8 @@ void Player::SaveSpellEffects()
|
||||
SaveCustomSpellDataIndex(info->spell_effects[i].spell);
|
||||
SaveCustomSpellEffectsDisplay(info->spell_effects[i].spell);
|
||||
}
|
||||
if (i < NUM_MAINTAINED_EFFECTS && info->maintained_effects[i].spell_id != 0xFFFFFFFF){
|
||||
if (i < NUM_MAINTAINED_EFFECTS && info->maintained_effects[i].spell && info->maintained_effects[i].spell_id != 0xFFFFFFFF){
|
||||
LogWrite(PLAYER__INFO, 0, "Player", "Saving slot %u maintained effect %u", i, info->maintained_effects[i].spell_id);
|
||||
Spawn* spawn = GetZone()->GetSpawnByID(info->maintained_effects[i].spell->initial_target);
|
||||
|
||||
int32 target_char_id = 0;
|
||||
@ -7582,8 +7589,8 @@ void Player::SaveSpellEffects()
|
||||
}
|
||||
}
|
||||
}
|
||||
MMaintainedSpells.releasereadlock(__FUNCTION__, __LINE__);
|
||||
MSpellEffects.releasereadlock(__FUNCTION__, __LINE__);
|
||||
MMaintainedSpells.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
void Player::MentorTarget()
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -225,7 +225,7 @@ struct QuickBarItem{
|
||||
int16 icon_type;
|
||||
int32 id;
|
||||
int8 tier;
|
||||
int32 unique_id;
|
||||
int64 unique_id;
|
||||
EQ2_16BitString text;
|
||||
};
|
||||
|
||||
@ -1103,8 +1103,11 @@ public:
|
||||
void SetActiveFoodUniqueID(int32 unique_id, bool update_db = true);
|
||||
void SetActiveDrinkUniqueID(int32 unique_id, bool update_db = true);
|
||||
|
||||
int32 GetActiveFoodUniqueID() { return active_food_unique_id; }
|
||||
int32 GetActiveDrinkUniqueID() { return active_drink_unique_id; }
|
||||
int64 GetActiveFoodUniqueID() { return active_food_unique_id; }
|
||||
int64 GetActiveDrinkUniqueID() { return active_drink_unique_id; }
|
||||
|
||||
void SetHouseVaultSlots(int8 allowed_slots) { house_vault_slots = allowed_slots; }
|
||||
int8 GetHouseVaultSlots() { return house_vault_slots; }
|
||||
|
||||
Mutex MPlayerQuests;
|
||||
float pos_packet_speed;
|
||||
@ -1264,8 +1267,10 @@ private:
|
||||
Quest* GetAnyQuest(int32 quest_id);
|
||||
Quest* GetCompletedQuest(int32 quest_id);
|
||||
|
||||
std::atomic<int32> active_food_unique_id;
|
||||
std::atomic<int32> active_drink_unique_id;
|
||||
std::atomic<int64> active_food_unique_id;
|
||||
std::atomic<int64> active_drink_unique_id;
|
||||
|
||||
int8 house_vault_slots;
|
||||
};
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
@ -1,21 +1,21 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
EQ2Emulator 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.
|
||||
EQ2Emulator 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.
|
||||
|
||||
EQ2Emulator 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.
|
||||
EQ2Emulator 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 EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PlayerGroups.h"
|
||||
@ -34,6 +34,7 @@ extern ZoneList zone_list;
|
||||
extern RuleManager rule_manager;
|
||||
extern PeerManager peer_manager;
|
||||
extern WorldDatabase database;
|
||||
extern LuaInterface* lua_interface;
|
||||
/******************************************************** PlayerGroup ********************************************************/
|
||||
|
||||
PlayerGroup::PlayerGroup(int32 id) {
|
||||
@ -1094,6 +1095,7 @@ void PlayerGroupManager::UpdateGroupBuffs() {
|
||||
caster = *vitr;
|
||||
caster->GetMaintainedMutex()->readlock(__FUNCTION__, __LINE__);
|
||||
// go through the player's maintained spells
|
||||
bool skipSpell = false;
|
||||
me = caster->GetMaintainedSpells();
|
||||
for (i = 0; i < NUM_MAINTAINED_EFFECTS; i++) {
|
||||
if (me[i].spell_id == 0xFFFFFFFF)
|
||||
@ -1113,9 +1115,7 @@ void PlayerGroupManager::UpdateGroupBuffs() {
|
||||
|
||||
if (spell && spell->GetSpellData()->group_spell && spell->GetSpellData()->friendly_spell &&
|
||||
(spell->GetSpellData()->target_type == SPELL_TARGET_GROUP_AE || spell->GetSpellData()->target_type == SPELL_TARGET_RAID_AE)) {
|
||||
|
||||
luaspell->ClearCharTargets();
|
||||
|
||||
for (target_itr = group->GetMembers()->begin(); target_itr != group->GetMembers()->end(); target_itr++) {
|
||||
group_member = (*target_itr)->member;
|
||||
|
||||
@ -1127,8 +1127,39 @@ void PlayerGroupManager::UpdateGroupBuffs() {
|
||||
|
||||
client = (*target_itr)->client;
|
||||
|
||||
LuaSpell* conflictSpell = caster->HasLinkedTimerID(luaspell, group_member, false, true);
|
||||
if(conflictSpell && group_member && group_member->IsEntity())
|
||||
{
|
||||
if(conflictSpell->spell->GetSpellData()->min_class_skill_req > 0 && spell->GetSpellData()->min_class_skill_req > 0)
|
||||
{
|
||||
if(conflictSpell->spell->GetSpellData()->min_class_skill_req <= spell->GetSpellData()->min_class_skill_req)
|
||||
{
|
||||
if(spell->GetSpellData()->duration_until_cancel && !luaspell->num_triggers)
|
||||
{
|
||||
for (int32 id : conflictSpell->GetTargets()) {
|
||||
Spawn* tmpTarget = caster->GetZone()->GetSpawnByID(id);
|
||||
if(tmpTarget && tmpTarget->IsEntity())
|
||||
{
|
||||
((Entity*)tmpTarget)->RemoveEffectsFromLuaSpell(conflictSpell);
|
||||
caster->GetZone()->RemoveTargetFromSpell(conflictSpell, tmpTarget, false);
|
||||
caster->GetZone()->GetSpellProcess()->CheckRemoveTargetFromSpell(conflictSpell);
|
||||
lua_interface->RemoveSpawnFromSpell(conflictSpell, tmpTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is a spell that is no good, have to abort!
|
||||
caster->GetMaintainedMutex()->releasereadlock(__FUNCTION__, __LINE__);
|
||||
caster->GetZone()->GetSpellProcess()->SpellCannotStack(caster->GetZone(), client, caster, luaspell, conflictSpell);
|
||||
skipSpell = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
has_effect = false;
|
||||
|
||||
|
||||
if (group_member->GetSpellEffect(spell->GetSpellID(), caster)) {
|
||||
has_effect = true;
|
||||
}
|
||||
@ -1226,13 +1257,18 @@ void PlayerGroupManager::UpdateGroupBuffs() {
|
||||
client->QueuePacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
luaspell->SwapTargets(new_target_list);
|
||||
SpellProcess::AddSelfAndPet(luaspell, caster);
|
||||
new_target_list.clear();
|
||||
if(!skipSpell) {
|
||||
luaspell->SwapTargets(new_target_list);
|
||||
SpellProcess::AddSelfAndPet(luaspell, caster);
|
||||
new_target_list.clear();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
caster->GetMaintainedMutex()->releasereadlock(__FUNCTION__, __LINE__);
|
||||
|
||||
if(!skipSpell)
|
||||
caster->GetMaintainedMutex()->releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include "../../common/debug.h"
|
||||
#include "../../common/Log.h"
|
||||
@ -734,7 +735,12 @@ bool Recipe::PlayerHasComponentByItemID(Client* client, vector<Item*>* player_co
|
||||
cur_qty = track_req_qty;
|
||||
|
||||
track_req_qty -= cur_qty;
|
||||
itemss[i]->details.item_locked = true;
|
||||
if(!itemss[i]->TryLockItem(LockReason::LockReason_Crafting)) {
|
||||
for (int8 s = 0; s < i; s++) {
|
||||
itemss[i]->TryUnlockItem(LockReason::LockReason_Crafting);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
player_component_pair_qty->push_back({itemss[i]->details.unique_id, cur_qty});
|
||||
player_components->push_back(itemss[i]);
|
||||
if(have_qty >= required_qty)
|
||||
|
@ -1,22 +1,23 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
EQ2Emulator 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.
|
||||
EQ2Emulator 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.
|
||||
|
||||
EQ2Emulator 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.
|
||||
EQ2Emulator 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 EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Sign.h"
|
||||
|
||||
#include "../common/ConfigReader.h"
|
||||
@ -152,6 +153,8 @@ Sign* Sign::Copy(){
|
||||
new_spawn->SetLootTier(GetLootTier());
|
||||
new_spawn->SetLootDropType(GetLootDropType());
|
||||
new_spawn->SetLanguage(GetLanguage());
|
||||
if(GetSpawnScriptSetDB() && GetSpawnScript())
|
||||
new_spawn->SetSpawnScript(std::string(GetSpawnScript()));
|
||||
return new_spawn;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -120,6 +120,7 @@ Spawn::Spawn(){
|
||||
has_spawn_proximities = false;
|
||||
pickup_item_id = 0;
|
||||
pickup_unique_item_id = 0;
|
||||
house_character_id = 0;
|
||||
disable_sounds = false;
|
||||
has_quests_required = false;
|
||||
has_history_required = false;
|
||||
@ -153,6 +154,7 @@ Spawn::Spawn(){
|
||||
respawn_offset_high = 0;
|
||||
duplicated_spawn = true;
|
||||
ResetKnockedBack();
|
||||
spawn_script_setdb = false;
|
||||
}
|
||||
|
||||
Spawn::~Spawn(){
|
||||
@ -1925,8 +1927,9 @@ const char* Spawn::GetSpawnScript(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Spawn::SetSpawnScript(string name){
|
||||
void Spawn::SetSpawnScript(string name, bool db_set){
|
||||
spawn_script = name;
|
||||
spawn_script_setdb = db_set;
|
||||
}
|
||||
|
||||
void Spawn::SetPrimaryCommand(const char* name, const char* command, float distance){
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __EQ2_SPAWN__
|
||||
#define __EQ2_SPAWN__
|
||||
|
||||
@ -1089,8 +1090,9 @@ public:
|
||||
bool HasProvidedQuests(){
|
||||
return (quest_ids.size() > 0);
|
||||
}
|
||||
void SetSpawnScript(string name);
|
||||
void SetSpawnScript(string name, bool db_set = false);
|
||||
const char* GetSpawnScript();
|
||||
bool GetSpawnScriptSetDB() { return spawn_script_setdb; }
|
||||
|
||||
vector<Spawn*>* GetSpawnGroup();
|
||||
bool HasSpawnGroup();
|
||||
@ -1321,9 +1323,15 @@ public:
|
||||
{
|
||||
pickup_unique_item_id = uniqueid;
|
||||
}
|
||||
|
||||
void SetHouseCharacterID(int32 charid)
|
||||
{
|
||||
house_character_id = charid;
|
||||
}
|
||||
|
||||
int32 GetPickupItemID() { return pickup_item_id; }
|
||||
int32 GetPickupUniqueItemID() { return pickup_unique_item_id; }
|
||||
int32 GetHouseCharacterID() { return house_character_id; }
|
||||
|
||||
bool IsSoundsDisabled() { return disable_sounds; }
|
||||
void SetSoundsDisabled(bool val) { disable_sounds = val; }
|
||||
@ -1468,6 +1476,7 @@ protected:
|
||||
int32 transporter_id;
|
||||
int32 pickup_item_id;
|
||||
int32 pickup_unique_item_id;
|
||||
int32 house_character_id;
|
||||
map<int32, vector<int16>* > required_quests;
|
||||
map<int32, LUAHistory*> required_history;
|
||||
|
||||
@ -1506,6 +1515,7 @@ private:
|
||||
int tmp_action_state;
|
||||
int32 running_to;
|
||||
string spawn_script;
|
||||
bool spawn_script_setdb;
|
||||
static int32 next_id;
|
||||
ZoneServer* zone;
|
||||
int32 spawn_location_id;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "SpellProcess.h"
|
||||
#include "../common/Log.h"
|
||||
#include "Tradeskills/Tradeskills.h"
|
||||
@ -433,10 +434,15 @@ bool SpellProcess::DeleteCasterSpell(LuaSpell* spell, string reason, bool removi
|
||||
bool ret = false;
|
||||
Spawn* target = 0;
|
||||
bool target_valid = false;
|
||||
|
||||
if(spell) {
|
||||
LogWrite(SPELL__INFO, 0, "Spell", "SpellProcess::DeleteCasterSpell Spell: %s, Reason: %s, RemoveTarget: %s.",
|
||||
spell->spell->GetName(), reason.c_str(), remove_target ? "Yes" : "All");
|
||||
if(remove_target) {
|
||||
for (int32 id : spell->GetTargets()) {
|
||||
if(remove_target->GetID() == id) {
|
||||
LogWrite(SPELL__INFO, 0, "Spell", "SpellProcess::DeleteCasterSpell RemoveTarget Spell: %s, Reason: %s, Target: %s.",
|
||||
spell->spell->GetName(), reason.c_str(), remove_target->GetName());
|
||||
if(remove_target->IsEntity()){
|
||||
spell->RemoveTarget(remove_target->GetID());
|
||||
lua_interface->RemoveSpawnFromSpell(spell, remove_target);
|
||||
@ -485,13 +491,19 @@ bool SpellProcess::DeleteCasterSpell(LuaSpell* spell, string reason, bool removi
|
||||
CheckRemoveTargetFromSpell(spell, removing_all_spells, removing_all_spells);
|
||||
ZoneServer* zone = spell->zone;
|
||||
if(zone) {
|
||||
LogWrite(SPELL__DEBUG, 0, "Spell", "SpellProcess::DeleteCasterSpell RemoveTargets Spell: %s.",
|
||||
spell->spell->GetName());
|
||||
for (int32 id : spell->GetTargets()) {
|
||||
target = zone->GetSpawnByID(id);
|
||||
if(target && target->IsEntity()){
|
||||
LogWrite(SPELL__INFO, 0, "Spell", "SpellProcess::DeleteCasterSpell RemoveTargets Spell: %s, Reason: %s, CurrentTarget: %s (%u).",
|
||||
spell->spell->GetName(), reason.c_str(), target->GetName(), id);
|
||||
spell->RemoveTarget(target->GetID());
|
||||
lua_interface->RemoveSpawnFromSpell(spell, target);
|
||||
}
|
||||
else{
|
||||
LogWrite(SPELL__INFO, 0, "Spell", "SpellProcess::DeleteCasterSpell RemoveTarget Spell: %s, Reason: %s, CurrentTarget: ??Unknown??.",
|
||||
spell->spell->GetName(), reason.c_str());
|
||||
spell->RemoveTarget(spell->caster->GetID());
|
||||
lua_interface->RemoveSpawnFromSpell(spell, spell->caster);
|
||||
}
|
||||
@ -637,6 +649,7 @@ bool SpellProcess::CastInstant(Spell* spell, Entity* caster, Entity* target, boo
|
||||
lua_spell->initial_target = target->GetID();
|
||||
lua_spell->initial_target_char_id = (target && target->IsPlayer()) ? ((Player*)target)->GetCharacterID() : 0;
|
||||
GetSpellTargets(lua_spell);
|
||||
PrintTargets(lua_spell, "instant");
|
||||
|
||||
if (!lua_spell->spell->IsCopiedSpell())
|
||||
{
|
||||
@ -1019,6 +1032,23 @@ Spell* SpellProcess::GetSpell(Entity* caster){
|
||||
return spell;
|
||||
}
|
||||
|
||||
void SpellProcess::PrintTargets(LuaSpell* spell, std::string stage) {
|
||||
std::vector<int32> targets = spell->GetTargets();
|
||||
if(spell->zone) {
|
||||
for (int32_t id : targets) {
|
||||
Spawn* tmp = spell->zone->GetSpawnByID(id);
|
||||
if(tmp) {
|
||||
LogWrite(SPELL__INFO, 0, "Spell", "SpellProcess::PrintTarget(%s) Spell: %s, Spawn: %s (%u) (DBID: %u).",
|
||||
stage.c_str(), spell->spell->GetName(), tmp->GetName(), tmp->GetID(), tmp->GetDatabaseID());
|
||||
}
|
||||
else {
|
||||
LogWrite(SPELL__ERROR, 0, "Spell", "SpellProcess::PrintTarget(%s) Spell: %s SpawnID: %u - ??Unknown??.",
|
||||
stage.c_str(), spell->spell->GetName(), id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, Spawn* target, bool lock, bool harvest_spell, LuaSpell* customSpell, int16 custom_cast_time, bool in_heroic_opp)
|
||||
{
|
||||
if((customSpell != 0 || spell != 0) && caster)
|
||||
@ -1173,6 +1203,8 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
|
||||
return;
|
||||
}
|
||||
|
||||
PrintTargets(lua_spell, "cast");
|
||||
|
||||
LuaSpell* conflictSpell = caster->HasLinkedTimerID(lua_spell, target, (spell_type == SPELL_TYPE_DD || spell_type == SPELL_TYPE_DOT));
|
||||
if(conflictSpell)
|
||||
{
|
||||
@ -1185,18 +1217,17 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
|
||||
if(spell->GetSpellData()->friendly_spell)
|
||||
{
|
||||
ZoneServer* zone = caster->GetZone();
|
||||
Spawn* tmpTarget = zone->GetSpawnByID(conflictSpell->initial_target);
|
||||
if(tmpTarget && tmpTarget->IsEntity())
|
||||
{
|
||||
((Entity*)tmpTarget)->RemoveEffectsFromLuaSpell(conflictSpell);
|
||||
zone->RemoveTargetFromSpell(conflictSpell, tmpTarget, false);
|
||||
CheckRemoveTargetFromSpell(conflictSpell);
|
||||
lua_interface->RemoveSpawnFromSpell(conflictSpell, tmpTarget);
|
||||
if(client && IsReady(conflictSpell->spell, client->GetPlayer()))
|
||||
UnlockSpell(client, conflictSpell->spell);
|
||||
|
||||
for (int32 id : conflictSpell->GetTargets()) {
|
||||
Spawn* tmpTarget = zone->GetSpawnByID(id);
|
||||
if(tmpTarget && tmpTarget->IsEntity())
|
||||
{
|
||||
((Entity*)tmpTarget)->RemoveEffectsFromLuaSpell(conflictSpell);
|
||||
zone->RemoveTargetFromSpell(conflictSpell, tmpTarget, false);
|
||||
CheckRemoveTargetFromSpell(conflictSpell);
|
||||
lua_interface->RemoveSpawnFromSpell(conflictSpell, tmpTarget);
|
||||
}
|
||||
}
|
||||
DeleteSpell(lua_spell);
|
||||
return;
|
||||
}
|
||||
else if(lua_spell->spell->GetSpellData()->spell_type == SPELL_TYPE_DEBUFF)
|
||||
{
|
||||
@ -1205,11 +1236,11 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster,
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SpellCannotStack(zone, client, lua_spell->caster, lua_spell, conflictSpell);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
SpellCannotStack(zone, client, lua_spell->caster, lua_spell, conflictSpell);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1717,8 +1748,8 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her
|
||||
}
|
||||
}
|
||||
|
||||
if(client && client->GetCurrentZone() &&
|
||||
!spell->spell->GetSpellData()->friendly_spell)
|
||||
PrintTargets(spell, "castprocess");
|
||||
if(client && client->GetCurrentZone())
|
||||
{
|
||||
ZoneServer* zone = client->GetCurrentZone();
|
||||
Spawn* tmpTarget = zone->GetSpawnByID(spell->initial_target);
|
||||
@ -1726,8 +1757,30 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her
|
||||
LuaSpell* conflictSpell = spell->caster->HasLinkedTimerID(spell, tmpTarget, (spell_type == SPELL_TYPE_DD || spell_type == SPELL_TYPE_DOT));
|
||||
if(conflictSpell && tmpTarget && tmpTarget->IsEntity())
|
||||
{
|
||||
((Entity*)tmpTarget)->RemoveSpellEffect(conflictSpell);
|
||||
zone->RemoveTargetFromSpell(conflictSpell, tmpTarget);
|
||||
if(conflictSpell->spell->GetSpellData()->min_class_skill_req > 0 && spell->spell->GetSpellData()->min_class_skill_req > 0)
|
||||
{
|
||||
if(conflictSpell->spell->GetSpellData()->min_class_skill_req <= spell->spell->GetSpellData()->min_class_skill_req)
|
||||
{
|
||||
if(spell->spell->GetSpellData()->duration_until_cancel && !spell->num_triggers)
|
||||
{
|
||||
for (int32 id : conflictSpell->GetTargets()) {
|
||||
Spawn* tmpTarget = zone->GetSpawnByID(id);
|
||||
if(tmpTarget && tmpTarget->IsEntity())
|
||||
{
|
||||
((Entity*)tmpTarget)->RemoveEffectsFromLuaSpell(conflictSpell);
|
||||
zone->RemoveTargetFromSpell(conflictSpell, tmpTarget, false);
|
||||
CheckRemoveTargetFromSpell(conflictSpell);
|
||||
lua_interface->RemoveSpawnFromSpell(conflictSpell, tmpTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SpellCannotStack(zone, client, spell->caster, spell, conflictSpell);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1813,19 +1866,37 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (firstTarget && !spell->spell->GetSpellData()->not_maintained) {
|
||||
spell->caster->AddMaintainedSpell(spell);
|
||||
}
|
||||
firstTarget = false;
|
||||
|
||||
SpellEffects* effect = ((Entity*)target)->GetSpellEffect(spell->spell->GetSpellID());
|
||||
if (effect && effect->tier > spell->spell->GetSpellTier()) {
|
||||
|
||||
if(!effect)
|
||||
effect = ((Entity*)target)->GetSpellEffectWithLinkedTimer(spell->spell->GetSpellID(), spell->spell->GetSpellData()->linked_timer, spell->spell->GetSpellData()->type_group_spell_id, spell->caster, true);
|
||||
|
||||
if (effect && effect->spell != spell && effect->tier > spell->spell->GetSpellTier()) {
|
||||
if(client) {
|
||||
spell->caster->GetZone()->SendSpellFailedPacket(client, SPELL_ERROR_TAKE_EFFECT_MOREPOWERFUL);
|
||||
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "The spell did not take hold as the target already has a better spell of this type.");
|
||||
|
||||
if (!passive)
|
||||
SendFinishedCast(spell, client);
|
||||
|
||||
if(spell->zone)
|
||||
spell->zone->GetSpellProcess()->RemoveSpellScriptTimerBySpell(spell);
|
||||
DeleteActiveSpell(spell, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(effect && effect->spell != spell) {
|
||||
if(effect->spell->zone)
|
||||
effect->spell->zone->GetSpellProcess()->RemoveSpellScriptTimerBySpell(effect->spell);
|
||||
|
||||
DeleteActiveSpell(effect->spell, true);
|
||||
}
|
||||
if (firstTarget) {
|
||||
spell->caster->AddMaintainedSpell(spell);
|
||||
}
|
||||
firstTarget = false;
|
||||
((Entity*)target)->AddSpellEffect(spell);
|
||||
if(spell->spell->GetSpellData()->det_type > 0)
|
||||
((Entity*)target)->AddDetrimentalSpell(spell);
|
||||
@ -2251,6 +2322,8 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell)
|
||||
}
|
||||
else // default self cast for group/raid AE
|
||||
{
|
||||
if(caster->IsPlayer())
|
||||
GetPlayerGroupTargets((Player*)caster, caster, luaspell, true);
|
||||
target = caster;
|
||||
luaspell->initial_target = caster->GetID();
|
||||
luaspell->initial_target_char_id = (caster && caster->IsPlayer()) ? ((Player*)caster)->GetCharacterID() : 0;
|
||||
@ -2818,8 +2891,9 @@ void SpellProcess::CheckRemoveTargetFromSpell(LuaSpell* spell, bool allow_delete
|
||||
for (int32 id : spell->GetTargets()) {
|
||||
if (remove_spawn->GetID() == id) {
|
||||
found_target = true;
|
||||
|
||||
spell->RemoveTarget(id);
|
||||
LogWrite(SPELL__DEBUG, 0, "Spell", "%s CheckRemoveTargetFromSpell %s (%u).", spell->spell->GetName(), remove_spawn->GetName(), remove_spawn->GetID());
|
||||
LogWrite(SPELL__DEBUG, 0, "Spell", "SpellProcess::CheckRemoveTargetFromSpell Spell: %s, Spawn: %s (%u).", spell->spell->GetName(), remove_spawn->GetName(), remove_spawn->GetID());
|
||||
if(remove_spawn && std::find(spawnsToRemove.begin(), spawnsToRemove.end(), remove_spawn) == spawnsToRemove.end())
|
||||
spawnsToRemove.push_back(remove_spawn);
|
||||
break;
|
||||
@ -3035,6 +3109,16 @@ void SpellProcess::AddSelfAndPetToCharTargets(LuaSpell* spell, Spawn* caster, bo
|
||||
|
||||
void SpellProcess::DeleteActiveSpell(LuaSpell* spell, bool skipRemoveCurrent) {
|
||||
lua_interface->SetLuaUserDataStale(spell);
|
||||
if(spell->zone) {
|
||||
for (int32 id : spell->GetTargets()) {
|
||||
Spawn* target = spell->zone->GetSpawnByID(id);
|
||||
if(target)
|
||||
lua_interface->RemoveSpawnFromSpell(spell, target);
|
||||
}
|
||||
}
|
||||
if (spell->caster) {
|
||||
((Entity*)spell->caster)->RemoveMaintainedSpell(spell);
|
||||
}
|
||||
if(!skipRemoveCurrent) {
|
||||
lua_interface->RemoveCurrentSpell(spell->state, spell, true);
|
||||
lua_interface->DeletePendingSpell(spell);
|
||||
@ -3052,7 +3136,21 @@ bool SpellProcess::AddLuaSpellTarget(LuaSpell* lua_spell, int32 id, bool lock_sp
|
||||
lua_spell->AddTarget(id);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
|
||||
if(lua_spell->zone) {
|
||||
Spawn* target = lua_spell->zone->GetSpawnByID(id);
|
||||
if(target && target->IsPlayer()){
|
||||
lua_spell->AddCharIDTarget(((Player*)target)->GetCharacterID(), 0);
|
||||
if (((Entity*)target)->HasPet()){
|
||||
Entity* pet = ((Entity*)target)->GetPet();
|
||||
if (pet)
|
||||
lua_spell->AddCharIDTarget(((Player*)target)->GetCharacterID(), PET_TYPE_COMBAT);
|
||||
pet = ((Entity*)target)->GetCharmedPet();
|
||||
if (pet)
|
||||
lua_spell->AddCharIDTarget(((Player*)target)->GetCharacterID(), PET_TYPE_CHARMED);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __EQ2_SPELL_PROCESS__
|
||||
#define __EQ2_SPELL_PROCESS__
|
||||
#include <mutex>
|
||||
@ -182,7 +183,8 @@ public:
|
||||
/// <param name='lock'>??? not currently used</param>
|
||||
/// <param name='harvest_spell'>Is this a harvest spell?</param>
|
||||
void ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, Spawn* target = 0, bool lock = true, bool harvest_spell = false, LuaSpell* customSpell = 0, int16 custom_cast_time = 0, bool in_heroic_opp = false);
|
||||
|
||||
void PrintTargets(LuaSpell* spell, std::string stage);
|
||||
|
||||
/// <summary>Cast an EntityCommand (right click menu)</summary>
|
||||
/// <param name='zone'>The current ZoneServer</param>
|
||||
/// <param name='entity_command'>the EntityCommand to cast</param>
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
@ -253,6 +254,7 @@ void TradeskillMgr::BeginCrafting(Client* client, vector<pair<int32,int16>> comp
|
||||
|
||||
// TODO: use the vecotr to lock inventory slots
|
||||
vector<pair<int32, int16>>::iterator itr;
|
||||
vector<pair<int32, int16>>::iterator itr2;
|
||||
bool missingItem = false;
|
||||
int32 itemid = 0;
|
||||
vector<Item*> tmpItems;
|
||||
@ -266,7 +268,16 @@ void TradeskillMgr::BeginCrafting(Client* client, vector<pair<int32,int16>> comp
|
||||
break;
|
||||
}
|
||||
|
||||
item->details.item_locked = true;
|
||||
if(!item->TryLockItem(LockReason::LockReason_Crafting)) {
|
||||
for (itr = components.begin(); itr != components.end(); itr++) {
|
||||
itemid = itr->first;
|
||||
Item* item = client->GetPlayer()->item_list.GetItemFromUniqueID(itemid);
|
||||
if(item)
|
||||
item->TryUnlockItem(LockReason::LockReason_Crafting);
|
||||
}
|
||||
missingItem = true;
|
||||
break;
|
||||
}
|
||||
tmpItems.push_back(item);
|
||||
}
|
||||
|
||||
@ -280,7 +291,8 @@ void TradeskillMgr::BeginCrafting(Client* client, vector<pair<int32,int16>> comp
|
||||
vector<Item*>::iterator itemitr;
|
||||
for (itemitr = tmpItems.begin(); itemitr != tmpItems.end(); itemitr++) {
|
||||
Item* tmpItem = *itemitr;
|
||||
tmpItem->details.item_locked = false;
|
||||
if(tmpItem)
|
||||
tmpItem->TryUnlockItem(LockReason::LockReason_Crafting);
|
||||
}
|
||||
ClientPacketFunctions::StopCrafting(client);
|
||||
return;
|
||||
@ -358,13 +370,13 @@ void TradeskillMgr::StopCrafting(Client* client, bool lock) {
|
||||
item = client->GetPlayer()->item_list.GetItemFromUniqueID(itmid);
|
||||
if (item && item->details.count <= qty)
|
||||
{
|
||||
item->details.item_locked = false;
|
||||
item->TryUnlockItem(LockReason::LockReason_Crafting);
|
||||
client->GetPlayer()->item_list.RemoveItem(item);
|
||||
updateInvReq = true;
|
||||
}
|
||||
else if(item) {
|
||||
item->details.count -= qty;
|
||||
item->details.item_locked = false;
|
||||
item->TryUnlockItem(LockReason::LockReason_Crafting);
|
||||
item->save_needed = true;
|
||||
updateInvReq = true;
|
||||
}
|
||||
|
@ -1,21 +1,21 @@
|
||||
/*
|
||||
EQ2Emu: Everquest II Server Emulator
|
||||
Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emu.
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
EQ2Emu 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.
|
||||
EQ2Emulator 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.
|
||||
|
||||
EQ2Emu 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.
|
||||
EQ2Emulator 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 EQ2Emu. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "PeerManager.h"
|
||||
@ -855,4 +855,84 @@ void PeerManager::sendPeersActiveQuery(int32 character_id, bool remove) {
|
||||
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/activequery", jsonPayload);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::sendPeersAddSeller(int32 character_id, int32 house_id, std::string name, bool saleEnabled, bool invEnabled) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("character_id", character_id);
|
||||
root.put("house_id", house_id);
|
||||
root.put("name", name);
|
||||
root.put("sale_enabled", saleEnabled);
|
||||
root.put("inventory_enabled", invEnabled);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Add Seller, CharID: %u", __FUNCTION__, character_id);
|
||||
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/addseller", jsonPayload);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::sendPeersRemoveSeller(int32 character_id) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("character_id", character_id);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Remove Seller, CharID: %u", __FUNCTION__, character_id);
|
||||
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/removeseller", jsonPayload);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerManager::sendPeersAddItemSale(int32 character_id, int32 house_id, int32 itemID, int64 uniqueID, int64 price, sint32 invSlotID, int16 slotID, int16 count, bool inInventory, bool forSale, std::string itemCreator) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("character_id", character_id);
|
||||
root.put("house_id", house_id);
|
||||
root.put("item_creator", itemCreator);
|
||||
root.put("item_id", itemID);
|
||||
root.put("unique_id", uniqueID);
|
||||
root.put("price", price);
|
||||
root.put("inv_slot_id", invSlotID);
|
||||
root.put("slot_id", slotID);
|
||||
root.put("count", count);
|
||||
root.put("in_inventory", inInventory);
|
||||
root.put("for_sale", forSale);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Add Item Sale, CharID: %u, UniqueID: %u, ItemID: %u", __FUNCTION__, character_id, uniqueID, itemID);
|
||||
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/additemsale", jsonPayload);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PeerManager::sendPeersRemoveItemSale(int32 character_id, int64 uniqueID) {
|
||||
boost::property_tree::ptree root;
|
||||
root.put("character_id", character_id);
|
||||
root.put("unique_id", uniqueID);
|
||||
|
||||
std::ostringstream jsonStream;
|
||||
boost::property_tree::write_json(jsonStream, root);
|
||||
std::string jsonPayload = jsonStream.str();
|
||||
LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Remove Item Sale, CharID: %u, UniqueID: %u", __FUNCTION__, character_id, uniqueID);
|
||||
|
||||
for (const auto& [id, peer] : peers) {
|
||||
if (peer->healthCheck.status != HealthStatus::OK)
|
||||
continue;
|
||||
peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/removeitemsale", jsonPayload);
|
||||
}
|
||||
}
|
@ -1,21 +1,21 @@
|
||||
/*
|
||||
EQ2Emu: Everquest II Server Emulator
|
||||
Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emu.
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
EQ2Emu 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.
|
||||
EQ2Emulator 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.
|
||||
|
||||
EQ2Emu 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.
|
||||
EQ2Emulator 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 EQ2Emu. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PEERMANAGER_H
|
||||
@ -225,6 +225,12 @@ public:
|
||||
void sendZonePlayerList(std::vector<string>* queries, std::vector<WhoAllPeerPlayer>* peer_list, bool isGM);
|
||||
|
||||
bool GetClientGuildDetails(int32 matchCharID, GuildMember* member_details);
|
||||
|
||||
|
||||
void sendPeersAddSeller(int32 character_id, int32 house_id, std::string name, bool saleEnabled, bool invEnabled);
|
||||
void sendPeersRemoveSeller(int32 character_id);
|
||||
void sendPeersAddItemSale(int32 character_id, int32 house_id, int32 itemID, int64 uniqueID, int64 price, sint32 invSlotID, int16 slotID, int16 count, bool inInventory, bool forSale, std::string itemCreator);
|
||||
void sendPeersRemoveItemSale(int32 character_id, int64 uniqueID);
|
||||
|
||||
};
|
||||
|
||||
|
@ -1,21 +1,21 @@
|
||||
/*
|
||||
EQ2Emu: Everquest II Server Emulator
|
||||
Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emu.
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
EQ2Emu 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.
|
||||
EQ2Emulator 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.
|
||||
|
||||
EQ2Emu 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.
|
||||
EQ2Emulator 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 EQ2Emu. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "HTTPSClientPool.h"
|
||||
@ -24,6 +24,7 @@ along with EQ2Emu. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "../LoginServer.h"
|
||||
#include "../LuaInterface.h"
|
||||
#include "../Guilds/Guild.h"
|
||||
#include "../Broker/BrokerManager.h"
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
@ -44,6 +45,7 @@ extern MasterSpellList master_spell_list;
|
||||
extern MasterFactionList master_faction_list;
|
||||
extern ClientList client_list;
|
||||
extern GuildList guild_list;
|
||||
extern BrokerManager broker;
|
||||
|
||||
PeerManager peer_manager;
|
||||
HTTPSClientPool peer_https_pool;
|
||||
@ -1409,4 +1411,175 @@ void World::Web_worldhandle_activequery(const http::request<http::string_body>&
|
||||
std::string json = oss.str();
|
||||
res.body() = json;
|
||||
res.prepare_payload();
|
||||
}
|
||||
|
||||
void World::Web_worldhandle_addseller(const http::request<http::string_body>& req, http::response<http::string_body>& res) {
|
||||
res.set(http::field::content_type, "application/json; charset=utf-8");
|
||||
boost::property_tree::ptree pt, json_tree;
|
||||
|
||||
std::istringstream json_stream(req.body());
|
||||
boost::property_tree::read_json(json_stream, json_tree);
|
||||
std::string playerName("");
|
||||
int32 houseID = 0, charID = 0;
|
||||
bool saleEnabled = false, invEnabled = false, success = false;
|
||||
if (auto character_id = json_tree.get_optional<int32>("character_id")) {
|
||||
charID = character_id.get();
|
||||
}
|
||||
if (auto name = json_tree.get_optional<std::string>("name")) {
|
||||
playerName = name.get();
|
||||
}
|
||||
if (auto house_id = json_tree.get_optional<int32>("house_id")) {
|
||||
houseID = house_id.get();
|
||||
}
|
||||
if (auto sale_enabled = json_tree.get_optional<bool>("sale_enabled")) {
|
||||
saleEnabled = sale_enabled.get();
|
||||
}
|
||||
if (auto inventory_enabled = json_tree.get_optional<bool>("inventory_enabled")) {
|
||||
invEnabled = inventory_enabled.get();
|
||||
}
|
||||
|
||||
if(charID) {
|
||||
broker.AddSeller(charID, playerName, houseID, saleEnabled, invEnabled);
|
||||
success = true;
|
||||
}
|
||||
|
||||
pt.put("success", success);
|
||||
pt.put("character_id", charID);
|
||||
std::ostringstream oss;
|
||||
boost::property_tree::write_json(oss, pt);
|
||||
std::string json = oss.str();
|
||||
res.body() = json;
|
||||
res.prepare_payload();
|
||||
}
|
||||
|
||||
void World::Web_worldhandle_removeseller(const http::request<http::string_body>& req, http::response<http::string_body>& res) {
|
||||
res.set(http::field::content_type, "application/json; charset=utf-8");
|
||||
boost::property_tree::ptree pt, json_tree;
|
||||
|
||||
std::istringstream json_stream(req.body());
|
||||
boost::property_tree::read_json(json_stream, json_tree);
|
||||
int32 charID = 0;
|
||||
bool success = false;
|
||||
if (auto character_id = json_tree.get_optional<int32>("character_id")) {
|
||||
charID = character_id.get();
|
||||
}
|
||||
|
||||
if(charID) {
|
||||
broker.RemoveSeller(charID, true);
|
||||
success = true;
|
||||
}
|
||||
|
||||
pt.put("success", success);
|
||||
pt.put("character_id", charID);
|
||||
std::ostringstream oss;
|
||||
boost::property_tree::write_json(oss, pt);
|
||||
std::string json = oss.str();
|
||||
res.body() = json;
|
||||
res.prepare_payload();
|
||||
}
|
||||
|
||||
void World::Web_worldhandle_additemsale(const http::request<http::string_body>& req, http::response<http::string_body>& res) {
|
||||
res.set(http::field::content_type, "application/json; charset=utf-8");
|
||||
boost::property_tree::ptree pt, json_tree;
|
||||
|
||||
std::istringstream json_stream(req.body());
|
||||
boost::property_tree::read_json(json_stream, json_tree);
|
||||
int32 houseID = 0, charID = 0;
|
||||
bool success = false;
|
||||
int32 itemID = 0;
|
||||
int64 uniqueID = 0, inPrice = 0;
|
||||
sint32 invSlotID = 0;
|
||||
int16 slotID = 0, inCount = 0;
|
||||
bool inInventory = false;
|
||||
bool forSale = false;
|
||||
std::string itemCreator("");
|
||||
if (auto character_id = json_tree.get_optional<int32>("character_id")) {
|
||||
charID = character_id.get();
|
||||
}
|
||||
if (auto house_id = json_tree.get_optional<int32>("house_id")) {
|
||||
houseID = house_id.get();
|
||||
}
|
||||
if (auto item_id = json_tree.get_optional<int32>("item_id")) {
|
||||
itemID = item_id.get();
|
||||
}
|
||||
if (auto unique_id = json_tree.get_optional<int64>("unique_id")) {
|
||||
uniqueID = unique_id.get();
|
||||
}
|
||||
if (auto price = json_tree.get_optional<int64>("price")) {
|
||||
inPrice = price.get();
|
||||
}
|
||||
if (auto inv_slot_id = json_tree.get_optional<sint32>("inv_slot_id")) {
|
||||
invSlotID = inv_slot_id.get();
|
||||
}
|
||||
if (auto slot_id = json_tree.get_optional<int16>("slot_id")) {
|
||||
slotID = slot_id.get();
|
||||
}
|
||||
if (auto count = json_tree.get_optional<int16>("count")) {
|
||||
inCount = count.get();
|
||||
}
|
||||
if (auto in_inventory = json_tree.get_optional<bool>("in_inventory")) {
|
||||
inInventory = in_inventory.get();
|
||||
}
|
||||
if (auto for_sale = json_tree.get_optional<bool>("for_sale")) {
|
||||
forSale = for_sale.get();
|
||||
}
|
||||
if (auto item_creator = json_tree.get_optional<std::string>("item_creator")) {
|
||||
itemCreator = item_creator.get();
|
||||
}
|
||||
|
||||
|
||||
if(charID && uniqueID && itemID) {
|
||||
SaleItem it{};
|
||||
it.unique_id = uniqueID;
|
||||
it.character_id = charID;
|
||||
it.house_id = houseID;
|
||||
it.item_id = itemID;
|
||||
it.cost_copper = inPrice;
|
||||
it.for_sale = forSale;
|
||||
it.inv_slot_id = invSlotID;
|
||||
it.slot_id = slotID;
|
||||
it.count = inCount;
|
||||
it.from_inventory = inInventory;
|
||||
it.creator = itemCreator;
|
||||
broker.AddItem(it, true);
|
||||
success = true;
|
||||
}
|
||||
|
||||
pt.put("success", success);
|
||||
pt.put("character_id", charID);
|
||||
std::ostringstream oss;
|
||||
boost::property_tree::write_json(oss, pt);
|
||||
std::string json = oss.str();
|
||||
res.body() = json;
|
||||
res.prepare_payload();
|
||||
}
|
||||
|
||||
void World::Web_worldhandle_removeitemsale(const http::request<http::string_body>& req, http::response<http::string_body>& res) {
|
||||
res.set(http::field::content_type, "application/json; charset=utf-8");
|
||||
boost::property_tree::ptree pt, json_tree;
|
||||
|
||||
std::istringstream json_stream(req.body());
|
||||
boost::property_tree::read_json(json_stream, json_tree);
|
||||
int32 charID = 0;
|
||||
int64 uniqueID = 0;
|
||||
bool success = false;
|
||||
if (auto character_id = json_tree.get_optional<int32>("character_id")) {
|
||||
charID = character_id.get();
|
||||
}
|
||||
if (auto unique_id = json_tree.get_optional<int64>("unique_id")) {
|
||||
uniqueID = unique_id.get();
|
||||
}
|
||||
|
||||
if(charID && uniqueID) {
|
||||
broker.OnPeerRemoveItem(charID, uniqueID);
|
||||
success = true;
|
||||
}
|
||||
|
||||
pt.put("success", success);
|
||||
pt.put("character_id", charID);
|
||||
std::ostringstream oss;
|
||||
boost::property_tree::write_json(oss, pt);
|
||||
std::string json = oss.str();
|
||||
res.body() = json;
|
||||
res.prepare_payload();
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -167,6 +167,8 @@ Widget* Widget::Copy(){
|
||||
new_spawn->SetOpenZ(GetOpenZ());
|
||||
new_spawn->SetMultiFloorLift(multi_floor_lift);
|
||||
new_spawn->SetSoundsDisabled(IsSoundsDisabled());
|
||||
if(GetSpawnScriptSetDB() && GetSpawnScript())
|
||||
new_spawn->SetSpawnScript(std::string(GetSpawnScript()));
|
||||
return new_spawn;
|
||||
}
|
||||
|
||||
@ -417,6 +419,7 @@ void Widget::HandleUse(Client* client, string command, int8 overrideWidgetType){
|
||||
ClientPacketFunctions::SendHouseVisitWindow(client, world.GetAllPlayerHousesByHouseID(hz->id));
|
||||
|
||||
ClientPacketFunctions::SendBaseHouseWindow(client, hz, ph, id);
|
||||
client->GetPlayer()->SetTarget(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -445,6 +448,7 @@ void Widget::HandleUse(Client* client, string command, int8 overrideWidgetType){
|
||||
|
||||
ClientPacketFunctions::SendBaseHouseWindow(client, hz, ph, id);
|
||||
client->GetCurrentZone()->SendHouseItems(client);
|
||||
client->GetPlayer()->SetTarget(this);
|
||||
}
|
||||
else {
|
||||
if (hz)
|
||||
|
@ -283,6 +283,11 @@ void World::init(std::string web_ipaddr, int16 web_port, std::string cert_file,
|
||||
|
||||
world_webserver->register_route("/peerstatus", World::Web_worldhandle_peerstatus);
|
||||
world_webserver->register_route("/activequery", World::Web_worldhandle_activequery);
|
||||
|
||||
world_webserver->register_route("/addseller", World::Web_worldhandle_addseller);
|
||||
world_webserver->register_route("/removeseller", World::Web_worldhandle_removeseller);
|
||||
world_webserver->register_route("/additemsale", World::Web_worldhandle_additemsale);
|
||||
world_webserver->register_route("/removeitemsale", World::Web_worldhandle_removeitemsale);
|
||||
world_webserver->run();
|
||||
LogWrite(INIT__INFO, 0, "Init", "World Web Server is listening on %s:%u..", web_ipaddr.c_str(), web_port);
|
||||
web_success = true;
|
||||
@ -2641,6 +2646,7 @@ void World::AddPlayerHouse(int32 char_id, int32 house_id, int64 unique_id, int32
|
||||
ph->escrow_status = escrow_status;
|
||||
ph->upkeep_due = upkeep_due;
|
||||
ph->player_name = player_name;
|
||||
ph->character_id = char_id;
|
||||
ReloadHouseData(ph);
|
||||
m_playerHouses[house_id][char_id] = ph;
|
||||
}
|
||||
@ -3709,4 +3715,5 @@ void ZoneInfoMemory::LoadFromDatabaseRow(MYSQL_ROW row) {
|
||||
canGate = atoul(row[24]);
|
||||
cityZone = atoul(row[25]);
|
||||
canEvac = atoul(row[26]);
|
||||
zoneLuaScript = (row[27] != nullptr) ? row[27] : "";
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -229,6 +229,7 @@ struct PlayerHouse {
|
||||
int64 escrow_coins;
|
||||
int32 escrow_status;
|
||||
string player_name;
|
||||
int32 character_id;
|
||||
list<Deposit> deposits;
|
||||
map<string, Deposit> depositsMap;
|
||||
list<HouseHistory> history;
|
||||
@ -425,6 +426,7 @@ public:
|
||||
std::string zoneDescription;
|
||||
std::string zoneMotd;
|
||||
std::string zoneSkyFile;
|
||||
std::string zoneLuaScript;
|
||||
float underworld;
|
||||
float safeX, safeY, safeZ, safeHeading;
|
||||
int16 minimumLevel, maximumLevel, minimumVersion;
|
||||
@ -738,6 +740,10 @@ public:
|
||||
static void Web_worldhandle_setguildeventfilter(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_peerstatus(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_activequery(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_addseller(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_removeseller(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_additemsale(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
static void Web_worldhandle_removeitemsale(const http::request<http::string_body>& req, http::response<http::string_body>& res);
|
||||
|
||||
static void Web_populate_status(boost::property_tree::ptree& pt);
|
||||
|
||||
|
@ -1,21 +1,21 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
EQ2Emulator 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.
|
||||
EQ2Emulator 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.
|
||||
|
||||
EQ2Emulator 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.
|
||||
EQ2Emulator 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 EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
@ -53,6 +53,7 @@ along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "SpellProcess.h"
|
||||
#include "races.h"
|
||||
#include "Web/PeerManager.h"
|
||||
#include "Broker/BrokerManager.h"
|
||||
|
||||
extern Classes classes;
|
||||
extern Commands commands;
|
||||
@ -75,6 +76,7 @@ extern RuleManager rule_manager;
|
||||
extern MasterLanguagesList master_languages_list;
|
||||
extern ChestTrapList chest_trap_list;
|
||||
extern PeerManager peer_manager;
|
||||
BrokerManager broker;
|
||||
|
||||
//devn00b: Fix for linux builds since we dont use stricmp we use strcasecmp
|
||||
#if defined(__GNUC__)
|
||||
@ -953,23 +955,29 @@ int32 WorldDatabase::LoadAppearances(ZoneServer* zone, Client* client){
|
||||
return count;
|
||||
}
|
||||
|
||||
void WorldDatabase::LoadNPCs(ZoneServer* zone){
|
||||
void WorldDatabase::LoadNPCs(ZoneServer* zone, bool isInstanceType){
|
||||
Query query;
|
||||
MYSQL_ROW row;
|
||||
NPC* npc = 0;
|
||||
int32 id = 0;
|
||||
int32 total = 0;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT,"SELECT npc.spawn_id, s.name, npc.min_level, npc.max_level, npc.enc_level, s.race, s.model_type, npc.class_, npc.gender, s.command_primary, s.command_secondary, s.show_name, npc.min_group_size, npc.max_group_size, npc.hair_type_id, npc.facial_hair_type_id, npc.wing_type_id, npc.chest_type_id, npc.legs_type_id, npc.soga_hair_type_id, npc.soga_facial_hair_type_id, s.attackable, s.show_level, s.targetable, s.show_command_icon, s.display_hand_icon, s.hp, s.power, s.size, s.collision_radius, npc.action_state, s.visual_state, npc.mood_state, npc.initial_state, npc.activity_status, s.faction_id, s.sub_title, s.merchant_id, s.merchant_type, s.size_offset, npc.attack_type, npc.ai_strategy+0, npc.spell_list_id, npc.secondary_spell_list_id, npc.skill_list_id, npc.secondary_skill_list_id, npc.equipment_list_id, npc.str, npc.sta, npc.wis, npc.intel, npc.agi, npc.heat, npc.cold, npc.magic, npc.mental, npc.divine, npc.disease, npc.poison, npc.aggro_radius, npc.cast_percentage, npc.randomize, npc.soga_model_type, npc.heroic_flag, npc.alignment, npc.elemental, npc.arcane, npc.noxious, s.savagery, s.dissonance, npc.hide_hood, npc.emote_state, s.prefix, s.suffix, s.last_name, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, npc.water_type, npc.flying_type, s.loot_tier, s.loot_drop_type, npc.scared_by_strong_players, npc.action_state_str\n"
|
||||
"FROM spawn s\n"
|
||||
"INNER JOIN spawn_npcs npc\n"
|
||||
|
||||
std::string houseTable("");
|
||||
if(isInstanceType) {
|
||||
houseTable.append("_houses");
|
||||
}
|
||||
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT,"SELECT npc.spawn_id, s.name, npc.min_level, npc.max_level, npc.enc_level, s.race, s.model_type, npc.class_, npc.gender, s.command_primary, s.command_secondary, s.show_name, npc.min_group_size, npc.max_group_size, npc.hair_type_id, npc.facial_hair_type_id, npc.wing_type_id, npc.chest_type_id, npc.legs_type_id, npc.soga_hair_type_id, npc.soga_facial_hair_type_id, s.attackable, s.show_level, s.targetable, s.show_command_icon, s.display_hand_icon, s.hp, s.power, s.size, s.collision_radius, npc.action_state, s.visual_state, npc.mood_state, npc.initial_state, npc.activity_status, s.faction_id, s.sub_title, s.merchant_id, s.merchant_type, s.size_offset, npc.attack_type, npc.ai_strategy+0, npc.spell_list_id, npc.secondary_spell_list_id, npc.skill_list_id, npc.secondary_skill_list_id, npc.equipment_list_id, npc.str, npc.sta, npc.wis, npc.intel, npc.agi, npc.heat, npc.cold, npc.magic, npc.mental, npc.divine, npc.disease, npc.poison, npc.aggro_radius, npc.cast_percentage, npc.randomize, npc.soga_model_type, npc.heroic_flag, npc.alignment, npc.elemental, npc.arcane, npc.noxious, s.savagery, s.dissonance, npc.hide_hood, npc.emote_state, s.prefix, s.suffix, s.last_name, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, npc.water_type, npc.flying_type, s.loot_tier, s.loot_drop_type, npc.scared_by_strong_players, npc.action_state_str, s.lua_script\n"
|
||||
"FROM spawn%s s\n"
|
||||
"INNER JOIN spawn_npcs%s npc\n"
|
||||
"ON s.id = npc.spawn_id\n"
|
||||
"INNER JOIN spawn_location_entry le\n"
|
||||
"INNER JOIN spawn_location_entry%s le\n"
|
||||
"ON npc.spawn_id = le.spawn_id\n"
|
||||
"INNER JOIN spawn_location_placement lp\n"
|
||||
"INNER JOIN spawn_location_placement%s lp\n"
|
||||
"ON le.spawn_location_id = lp.spawn_location_id\n"
|
||||
"WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n"
|
||||
"GROUP BY s.id",
|
||||
zone->GetZoneID(), zone->GetInstanceID());
|
||||
houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), zone->GetZoneID(), zone->GetInstanceID());
|
||||
while(result && (row = mysql_fetch_row(result))){
|
||||
/*npc->SetAppearanceID(atoi(row[12]));
|
||||
AppearanceData* appearance = world.GetNPCAppearance(npc->GetAppearanceID());
|
||||
@ -1128,6 +1136,11 @@ void WorldDatabase::LoadNPCs(ZoneServer* zone){
|
||||
npc->GetInfoStruct()->set_action_state(action_state_str);
|
||||
}
|
||||
|
||||
if(row[87]){
|
||||
std::string lua_script = std::string(row[87]);
|
||||
npc->SetSpawnScript(lua_script, true);
|
||||
}
|
||||
|
||||
zone->AddNPC(id, npc);
|
||||
total++;
|
||||
LogWrite(NPC__DEBUG, 5, "NPC", "---Loading NPC: '%s' (%u)", npc->appearance.name, id);
|
||||
@ -1243,23 +1256,27 @@ void WorldDatabase::LoadSpiritShards(ZoneServer* zone){
|
||||
LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i Spirit Shard(s).", total);
|
||||
}
|
||||
|
||||
void WorldDatabase::LoadSigns(ZoneServer* zone){
|
||||
void WorldDatabase::LoadSigns(ZoneServer* zone, bool isInstanceType){
|
||||
Query query;
|
||||
MYSQL_ROW row;
|
||||
Sign* sign = 0;
|
||||
int32 id = 0;
|
||||
int32 total = 0;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT ss.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, ss.widget_id, ss.widget_x, ss.widget_y, ss.widget_z, s.command_primary, s.command_secondary, s.collision_radius, ss.icon, ss.type, ss.title, ss.description, ss.sign_distance, ss.zone_id, ss.zone_x, ss.zone_y, ss.zone_z, ss.zone_heading, ss.include_heading, ss.include_location, s.transport_id, s.size_offset, s.display_hand_icon, s.visual_state, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, ss.language\n"
|
||||
"FROM spawn s\n"
|
||||
"INNER JOIN spawn_signs ss\n"
|
||||
std::string houseTable("");
|
||||
if(isInstanceType) {
|
||||
houseTable.append("_houses");
|
||||
}
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT ss.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, ss.widget_id, ss.widget_x, ss.widget_y, ss.widget_z, s.command_primary, s.command_secondary, s.collision_radius, ss.icon, ss.type, ss.title, ss.description, ss.sign_distance, ss.zone_id, ss.zone_x, ss.zone_y, ss.zone_z, ss.zone_heading, ss.include_heading, ss.include_location, s.transport_id, s.size_offset, s.display_hand_icon, s.visual_state, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, ss.language, s.lua_script\n"
|
||||
"FROM spawn%s s\n"
|
||||
"INNER JOIN spawn_signs%s ss\n"
|
||||
"ON s.id = ss.spawn_id\n"
|
||||
"INNER JOIN spawn_location_entry le\n"
|
||||
"INNER JOIN spawn_location_entry%s le\n"
|
||||
"ON ss.spawn_id = le.spawn_id\n"
|
||||
"INNER JOIN spawn_location_placement lp\n"
|
||||
"INNER JOIN spawn_location_placement%s lp\n"
|
||||
"ON le.spawn_location_id = lp.spawn_location_id\n"
|
||||
"WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n"
|
||||
"GROUP BY s.id",
|
||||
zone->GetZoneID(), zone->GetInstanceID());
|
||||
houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), zone->GetZoneID(), zone->GetInstanceID());
|
||||
|
||||
while(result && (row = mysql_fetch_row(result))){
|
||||
int32 signXpackFlag = atoul(row[28]);
|
||||
@ -1330,6 +1347,11 @@ void WorldDatabase::LoadSigns(ZoneServer* zone){
|
||||
|
||||
sign->SetLanguage(atoul(row[36]));
|
||||
|
||||
if(row[37]){
|
||||
std::string lua_script = std::string(row[37]);
|
||||
sign->SetSpawnScript(lua_script, true);
|
||||
}
|
||||
|
||||
zone->AddSign(id, sign);
|
||||
total++;
|
||||
|
||||
@ -1339,23 +1361,28 @@ void WorldDatabase::LoadSigns(ZoneServer* zone){
|
||||
LogWrite(SIGN__DEBUG, 0, "Sign", "--Loaded %i Sign(s)", total);
|
||||
}
|
||||
|
||||
void WorldDatabase::LoadWidgets(ZoneServer* zone){
|
||||
void WorldDatabase::LoadWidgets(ZoneServer* zone, bool isInstanceType){
|
||||
Query query;
|
||||
MYSQL_ROW row;
|
||||
Widget* widget = 0;
|
||||
int32 id = 0;
|
||||
int32 total = 0;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT sw.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, sw.widget_id, sw.widget_x, sw.widget_y, sw.widget_z, s.command_primary, s.command_secondary, s.collision_radius, sw.include_heading, sw.include_location, sw.icon, sw.type, sw.open_heading, sw.open_y, sw.action_spawn_id, sw.open_sound_file, sw.close_sound_file, sw.open_duration, sw.closed_heading, sw.linked_spawn_id, sw.close_y, s.transport_id, s.size_offset, sw.house_id, sw.open_x, sw.open_z, sw.close_x, sw.close_z, s.display_hand_icon, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type\n"
|
||||
"FROM spawn s\n"
|
||||
"INNER JOIN spawn_widgets sw\n"
|
||||
|
||||
std::string houseTable("");
|
||||
if(isInstanceType) {
|
||||
houseTable.append("_houses");
|
||||
}
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT sw.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, sw.widget_id, sw.widget_x, sw.widget_y, sw.widget_z, s.command_primary, s.command_secondary, s.collision_radius, sw.include_heading, sw.include_location, sw.icon, sw.type, sw.open_heading, sw.open_y, sw.action_spawn_id, sw.open_sound_file, sw.close_sound_file, sw.open_duration, sw.closed_heading, sw.linked_spawn_id, sw.close_y, s.transport_id, s.size_offset, sw.house_id, sw.open_x, sw.open_z, sw.close_x, sw.close_z, s.display_hand_icon, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, s.lua_script\n"
|
||||
"FROM spawn%s s\n"
|
||||
"INNER JOIN spawn_widgets%s sw\n"
|
||||
"ON s.id = sw.spawn_id\n"
|
||||
"INNER JOIN spawn_location_entry le\n"
|
||||
"INNER JOIN spawn_location_entry%s le\n"
|
||||
"ON sw.spawn_id = le.spawn_id\n"
|
||||
"INNER JOIN spawn_location_placement lp\n"
|
||||
"INNER JOIN spawn_location_placement%s lp\n"
|
||||
"ON le.spawn_location_id = lp.spawn_location_id\n"
|
||||
"WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n"
|
||||
"GROUP BY s.id",
|
||||
zone->GetZoneID(), zone->GetInstanceID());
|
||||
houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), zone->GetZoneID(), zone->GetInstanceID());
|
||||
while(result && (row = mysql_fetch_row(result))){
|
||||
int32 widgetXpackFlag = atoul(row[33]);
|
||||
int32 widgetHolidayFlag = atoul(row[34]);
|
||||
@ -1437,7 +1464,12 @@ void WorldDatabase::LoadWidgets(ZoneServer* zone){
|
||||
widget->SetLootTier(atoul(row[39]));
|
||||
|
||||
widget->SetLootDropType(atoul(row[40]));
|
||||
|
||||
|
||||
if(row[41]){
|
||||
std::string lua_script = std::string(row[41]);
|
||||
widget->SetSpawnScript(lua_script, true);
|
||||
}
|
||||
|
||||
zone->AddWidget(id, widget);
|
||||
total++;
|
||||
|
||||
@ -1447,23 +1479,28 @@ void WorldDatabase::LoadWidgets(ZoneServer* zone){
|
||||
LogWrite(WIDGET__DEBUG, 0, "Widget", "--Loaded %i Widget(s)", total);
|
||||
}
|
||||
|
||||
void WorldDatabase::LoadObjects(ZoneServer* zone){
|
||||
void WorldDatabase::LoadObjects(ZoneServer* zone, bool isInstanceType){
|
||||
Query query;
|
||||
MYSQL_ROW row;
|
||||
Object* object = 0;
|
||||
int32 id = 0;
|
||||
int32 total = 0;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT so.spawn_id, s.name, s.race, s.model_type, s.command_primary, s.command_secondary, s.targetable, s.size, s.show_name, s.visual_state, s.attackable, s.show_level, s.show_command_icon, s.display_hand_icon, s.faction_id, s.collision_radius, s.transport_id, s.size_offset, so.device_id, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type\n"
|
||||
"FROM spawn s\n"
|
||||
"INNER JOIN spawn_objects so\n"
|
||||
|
||||
std::string houseTable("");
|
||||
if(isInstanceType) {
|
||||
houseTable.append("_houses");
|
||||
}
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT so.spawn_id, s.name, s.race, s.model_type, s.command_primary, s.command_secondary, s.targetable, s.size, s.show_name, s.visual_state, s.attackable, s.show_level, s.show_command_icon, s.display_hand_icon, s.faction_id, s.collision_radius, s.transport_id, s.size_offset, so.device_id, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, s.lua_script\n"
|
||||
"FROM spawn%s s\n"
|
||||
"INNER JOIN spawn_objects%s so\n"
|
||||
"ON s.id = so.spawn_id\n"
|
||||
"INNER JOIN spawn_location_entry le\n"
|
||||
"INNER JOIN spawn_location_entry%s le\n"
|
||||
"ON so.spawn_id = le.spawn_id\n"
|
||||
"INNER JOIN spawn_location_placement lp\n"
|
||||
"INNER JOIN spawn_location_placement%s lp\n"
|
||||
"ON le.spawn_location_id = lp.spawn_location_id\n"
|
||||
"WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n"
|
||||
"GROUP BY s.id",
|
||||
zone->GetZoneID(), zone->GetInstanceID());
|
||||
"GROUP BY s.id",
|
||||
houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), zone->GetZoneID(), zone->GetInstanceID());
|
||||
|
||||
while(result && (row = mysql_fetch_row(result))){
|
||||
|
||||
@ -1519,6 +1556,11 @@ void WorldDatabase::LoadObjects(ZoneServer* zone){
|
||||
|
||||
object->SetLootDropType(atoul(row[26]));
|
||||
|
||||
if(row[27]){
|
||||
std::string lua_script = std::string(row[27]);
|
||||
object->SetSpawnScript(lua_script, true);
|
||||
}
|
||||
|
||||
zone->AddObject(id, object);
|
||||
total++;
|
||||
|
||||
@ -1528,23 +1570,28 @@ void WorldDatabase::LoadObjects(ZoneServer* zone){
|
||||
LogWrite(OBJECT__DEBUG, 0, "Object", "--Loaded %i Object(s)", total);
|
||||
}
|
||||
|
||||
void WorldDatabase::LoadGroundSpawns(ZoneServer* zone){
|
||||
void WorldDatabase::LoadGroundSpawns(ZoneServer* zone, bool isInstanceType){
|
||||
Query query;
|
||||
MYSQL_ROW row;
|
||||
GroundSpawn* spawn = 0;
|
||||
int32 id = 0;
|
||||
int32 total = 0;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT sg.spawn_id, s.name, s.race, s.model_type, s.command_primary, s.command_secondary, s.targetable, s.size, s.show_name, s.visual_state, s.attackable, s.show_level, s.show_command_icon, s.display_hand_icon, s.faction_id, s.collision_radius, sg.number_harvests, sg.num_attempts_per_harvest, sg.groundspawn_id, sg.collection_skill, s.size_offset, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.aaxp_rewards, s.loot_tier, s.loot_drop_type\n"
|
||||
"FROM spawn s\n"
|
||||
"INNER JOIN spawn_ground sg\n"
|
||||
|
||||
std::string houseTable("");
|
||||
if(isInstanceType) {
|
||||
houseTable.append("_houses");
|
||||
}
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT sg.spawn_id, s.name, s.race, s.model_type, s.command_primary, s.command_secondary, s.targetable, s.size, s.show_name, s.visual_state, s.attackable, s.show_level, s.show_command_icon, s.display_hand_icon, s.faction_id, s.collision_radius, sg.number_harvests, sg.num_attempts_per_harvest, sg.groundspawn_id, sg.collection_skill, s.size_offset, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, s.lua_script\n"
|
||||
"FROM spawn%s s\n"
|
||||
"INNER JOIN spawn_ground%s sg\n"
|
||||
"ON s.id = sg.spawn_id\n"
|
||||
"INNER JOIN spawn_location_entry le\n"
|
||||
"INNER JOIN spawn_location_entry%s le\n"
|
||||
"ON sg.spawn_id = le.spawn_id\n"
|
||||
"INNER JOIN spawn_location_placement lp\n"
|
||||
"INNER JOIN spawn_location_placement%s lp\n"
|
||||
"ON le.spawn_location_id = lp.spawn_location_id\n"
|
||||
"WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n"
|
||||
"GROUP BY s.id",
|
||||
zone->GetZoneID(), zone->GetInstanceID());
|
||||
houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), zone->GetZoneID(), zone->GetInstanceID());
|
||||
while(result && (row = mysql_fetch_row(result))){
|
||||
|
||||
int32 gsXpackFlag = atoul(row[21]);
|
||||
@ -1601,6 +1648,11 @@ void WorldDatabase::LoadGroundSpawns(ZoneServer* zone){
|
||||
|
||||
spawn->SetLootDropType(atoul(row[26]));
|
||||
|
||||
if(row[27]){
|
||||
std::string lua_script = std::string(row[27]);
|
||||
spawn->SetSpawnScript(lua_script, true);
|
||||
}
|
||||
|
||||
zone->AddGroundSpawn(id, spawn);
|
||||
total++;
|
||||
LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---Loading GroundSpawn: '%s' (%u)", spawn->appearance.name, id);
|
||||
@ -3551,6 +3603,8 @@ void WorldDatabase::LoadSpawns(ZoneServer* zone)
|
||||
|
||||
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter LoadSpawns");
|
||||
|
||||
int8 isInstanceType = (zone->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE);
|
||||
|
||||
if(zone->GetInstanceID() == 0) {
|
||||
npcs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_npcs sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC);
|
||||
objects = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_objects so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT);
|
||||
@ -3561,6 +3615,19 @@ void WorldDatabase::LoadSpawns(ZoneServer* zone)
|
||||
spawn_group_associations = LoadSpawnLocationGroupAssociations(zone);
|
||||
spawn_group_chances = LoadSpawnGroupChances(zone);
|
||||
}
|
||||
else if(isInstanceType) {
|
||||
npcs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement_houses slp, spawn_location_name_houses sln, spawn_location_entry_houses sle, spawn_npcs_houses sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC);
|
||||
objects = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement_houses slp, spawn_location_name_houses sln, spawn_location_entry_houses sle, spawn_objects_houses so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT);
|
||||
widgets = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement_houses slp, spawn_location_name_houses sln, spawn_location_entry_houses sle, spawn_widgets_houses sw where sw.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_WIDGET);
|
||||
signs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement_houses slp, spawn_location_name_houses sln, spawn_location_entry_houses sle, spawn_signs_houses ss where ss.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_SIGN);
|
||||
ground_spawns = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement_houses slp, spawn_location_name_houses sln, spawn_location_entry_houses sle, spawn_ground_houses sg where sg.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_GROUNDSPAWN);
|
||||
|
||||
npcs += ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_npcs sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC);
|
||||
objects += ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_objects so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT);
|
||||
widgets += ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_widgets sw where sw.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_WIDGET);
|
||||
signs += ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_signs ss where ss.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_SIGN);
|
||||
ground_spawns += ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_ground sg where sg.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_GROUNDSPAWN);
|
||||
}
|
||||
else {
|
||||
npcs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_npcs sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC);
|
||||
objects = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_objects so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT);
|
||||
@ -3570,17 +3637,22 @@ void WorldDatabase::LoadSpawns(ZoneServer* zone)
|
||||
spawn_groups = LoadSpawnLocationGroups(zone);
|
||||
spawn_group_associations = LoadSpawnLocationGroupAssociations(zone);
|
||||
spawn_group_chances = LoadSpawnGroupChances(zone);
|
||||
|
||||
}
|
||||
LogWrite(SPAWN__INFO, 0, "Spawn", "Loaded for zone '%s' (%u):\n\t%u NPC(s), %u Object(s), %u Widget(s)\n\t%u Sign(s), %u Ground Spawn(s), %u Spawn Group(s)\n\t%u Spawn Group Association(s), %u Spawn Group Chance(s)", zone->GetZoneName(), zone->GetZoneID(), npcs, objects, widgets, signs, ground_spawns, spawn_groups, spawn_group_associations, spawn_group_chances);
|
||||
LogWrite(SPAWN__INFO, 0, "Spawn", "Loaded for zone '%s' (%u):\n\t%u NPC(s), %u Object(s), %u Widget(s)\n\t%u Sign(s), %u Ground Spawn(s), %u Spawn Group(s)\n\t%u Spawn Group Association(s), %u Spawn Group Chance(s), %u InstanceHouse", zone->GetZoneName(), zone->GetZoneID(), npcs, objects, widgets, signs, ground_spawns, spawn_groups, spawn_group_associations, spawn_group_chances, isInstanceType);
|
||||
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit LoadSpawns");
|
||||
|
||||
}
|
||||
|
||||
bool WorldDatabase::UpdateSpawnLocationSpawns(Spawn* spawn) {
|
||||
Query query;
|
||||
query.RunQuery2(Q_UPDATE, "update spawn_location_placement set x=%f, y=%f, z=%f, heading=%f, x_offset=%f, y_offset=%f, z_offset=%f, respawn=%u, expire_timer=%u, expire_offset=%u, grid_id=%u, pitch=%f, roll=%f where id = %u",
|
||||
spawn->GetX(), spawn->GetY(), spawn->GetZ(), spawn->GetHeading(), spawn->GetXOffset(), spawn->GetYOffset(), spawn->GetZOffset(), spawn->GetRespawnTime(), spawn->GetExpireTime(), spawn->GetExpireOffsetTime(), spawn->GetLocation(), spawn->GetPitch(), spawn->GetRoll(), spawn->GetSpawnLocationPlacementID());
|
||||
int8 isInstanceType = (spawn && spawn->GetZone() && spawn->GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE);
|
||||
std::string houseTable("");
|
||||
if(isInstanceType) {
|
||||
houseTable.append("_houses");
|
||||
}
|
||||
|
||||
query.RunQuery2(Q_UPDATE, "update spawn_location_placement%s set x=%f, y=%f, z=%f, heading=%f, x_offset=%f, y_offset=%f, z_offset=%f, respawn=%u, expire_timer=%u, expire_offset=%u, grid_id=%u, pitch=%f, roll=%f where id = %u",
|
||||
houseTable.c_str(), spawn->GetX(), spawn->GetY(), spawn->GetZ(), spawn->GetHeading(), spawn->GetXOffset(), spawn->GetYOffset(), spawn->GetZOffset(), spawn->GetRespawnTime(), spawn->GetExpireTime(), spawn->GetExpireOffsetTime(), spawn->GetLocation(), spawn->GetPitch(), spawn->GetRoll(), spawn->GetSpawnLocationPlacementID());
|
||||
if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF) {
|
||||
LogWrite(WORLD__ERROR, 0, "World", "Error in UpdateSpawnLocationSpawns query '%s': %s", query.GetQuery(), query.GetError());
|
||||
return false;
|
||||
@ -3588,10 +3660,10 @@ bool WorldDatabase::UpdateSpawnLocationSpawns(Spawn* spawn) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorldDatabase::UpdateSpawnWidget(int32 widget_id, char* queryString) {
|
||||
bool WorldDatabase::UpdateSpawnWidget(int32 widget_id, char* queryString,bool is_house) {
|
||||
Query query;
|
||||
query.RunQuery2(Q_UPDATE, "update spawn_widgets set %s where widget_id = %u",
|
||||
queryString, widget_id);
|
||||
query.RunQuery2(Q_UPDATE, "update spawn_widgets%s set %s where widget_id = %u",
|
||||
is_house ? "_houses" : "", queryString, widget_id);
|
||||
if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF) {
|
||||
LogWrite(WORLD__ERROR, 0, "World", "Error in UpdateSpawnWidget query '%s': %s", query.GetQuery(), query.GetError());
|
||||
return false;
|
||||
@ -3666,11 +3738,11 @@ void WorldDatabase::LoadRevivePoints(vector<RevivePoint*>* revive_points, int32
|
||||
}
|
||||
}
|
||||
|
||||
int32 WorldDatabase::GetNextSpawnIDInZone(int32 zone_id)
|
||||
int32 WorldDatabase::GetNextSpawnIDInZone(int32 zone_id, bool isInstanceType)
|
||||
{
|
||||
Query query;
|
||||
int32 ret = 0;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT MAX(id) FROM spawn where id LIKE '%i____'", zone_id);
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT MAX(id) FROM spawn%s where id LIKE '%i____'", isInstanceType ? "_houses" : "", zone_id);
|
||||
if(result && mysql_num_rows(result) > 0) {
|
||||
MYSQL_ROW row;
|
||||
row = mysql_fetch_row(result);
|
||||
@ -3692,14 +3764,18 @@ bool WorldDatabase::SaveSpawnInfo(Spawn* spawn){
|
||||
string suffix = getSafeEscapeString(spawn->GetSuffixTitle());
|
||||
string prefix = getSafeEscapeString(spawn->GetPrefixTitle());
|
||||
string last_name = getSafeEscapeString(spawn->GetLastName());
|
||||
|
||||
int8 isInstanceType = (spawn->GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE);
|
||||
std::string houseTable("");
|
||||
if(isInstanceType) {
|
||||
houseTable.append("_houses");
|
||||
}
|
||||
if(spawn->GetDatabaseID() == 0){
|
||||
|
||||
int32 new_spawn_id = GetNextSpawnIDInZone(spawn->GetZone()->GetZoneID(), isInstanceType);
|
||||
|
||||
int8 isInstanceType = (spawn->GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE);
|
||||
|
||||
int32 new_spawn_id = GetNextSpawnIDInZone(spawn->GetZone()->GetZoneID());
|
||||
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn (id, name, race, model_type, size, targetable, show_name, command_primary, command_secondary, visual_state, attackable, show_level, show_command_icon, display_hand_icon, faction_id, collision_radius, hp, power, prefix, suffix, last_name, is_instanced_spawn, merchant_min_level, merchant_max_level) values(%u, '%s', %i, %i, %i, %i, %i, %u, %u, %i, %i, %i, %i, %i, %u, %i, %u, %u, '%s', '%s', '%s', %u, %u, %u)",
|
||||
new_spawn_id, name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->GetSize(), spawn->appearance.targetable, spawn->appearance.display_name, spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, 0, spawn->appearance.pos.collision_radius, spawn->GetTotalHP(), spawn->GetTotalPower(), prefix.c_str(), suffix.c_str(), last_name.c_str(), isInstanceType, spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel());
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn%s (id, name, race, model_type, size, targetable, show_name, command_primary, command_secondary, visual_state, attackable, show_level, show_command_icon, display_hand_icon, faction_id, collision_radius, hp, power, prefix, suffix, last_name, is_instanced_spawn, merchant_min_level, merchant_max_level) values(%u, '%s', %i, %i, %i, %i, %i, %u, %u, %i, %i, %i, %i, %i, %u, %i, %u, %u, '%s', '%s', '%s', %u, %u, %u)",
|
||||
houseTable.c_str(), new_spawn_id, name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->GetSize(), spawn->appearance.targetable, spawn->appearance.display_name, spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, 0, spawn->appearance.pos.collision_radius, spawn->GetTotalHP(), spawn->GetTotalPower(), prefix.c_str(), suffix.c_str(), last_name.c_str(), isInstanceType, spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel());
|
||||
|
||||
if( new_spawn_id > 0 )
|
||||
spawn->SetDatabaseID(new_spawn_id); // use the new zone_id range
|
||||
@ -3709,40 +3785,40 @@ bool WorldDatabase::SaveSpawnInfo(Spawn* spawn){
|
||||
return false; // else, hang your head in shame as you are an utter failure
|
||||
|
||||
if(spawn->IsNPC()){
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_npcs (spawn_id, min_level, max_level, enc_level, class_, gender, min_group_size, max_group_size, hair_type_id, facial_hair_type_id, wing_type_id, chest_type_id, legs_type_id, soga_hair_type_id, soga_facial_hair_type_id, soga_model_type, heroic_flag, action_state, mood_state, initial_state, activity_status, hide_hood, emote_state) values(%u, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i)",
|
||||
spawn->GetDatabaseID(), spawn->GetLevel(), spawn->GetLevel(), spawn->GetDifficulty(), spawn->GetAdventureClass(), spawn->GetGender(), 0, 0, ((NPC*)spawn)->features.hair_type, ((NPC*)spawn)->features.hair_face_type,
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_npcs%s (spawn_id, min_level, max_level, enc_level, class_, gender, min_group_size, max_group_size, hair_type_id, facial_hair_type_id, wing_type_id, chest_type_id, legs_type_id, soga_hair_type_id, soga_facial_hair_type_id, soga_model_type, heroic_flag, action_state, mood_state, initial_state, activity_status, hide_hood, emote_state) values(%u, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i)",
|
||||
houseTable.c_str(), spawn->GetDatabaseID(), spawn->GetLevel(), spawn->GetLevel(), spawn->GetDifficulty(), spawn->GetAdventureClass(), spawn->GetGender(), 0, 0, ((NPC*)spawn)->features.hair_type, ((NPC*)spawn)->features.hair_face_type,
|
||||
((NPC*)spawn)->features.wing_type, ((NPC*)spawn)->features.chest_type, ((NPC*)spawn)->features.legs_type, ((NPC*)spawn)->features.soga_hair_type, ((NPC*)spawn)->features.soga_hair_face_type, spawn->appearance.soga_model_type, spawn->appearance.heroic_flag, spawn->GetActionState(), spawn->GetMoodState(), spawn->GetInitialState(), spawn->GetActivityStatus(), spawn->appearance.hide_hood, spawn->appearance.emote_state);
|
||||
}
|
||||
else if(spawn->IsObject()){
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_objects (spawn_id) values(%u)", spawn->GetDatabaseID());
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_objects%s (spawn_id) values(%u)", houseTable.c_str(), spawn->GetDatabaseID());
|
||||
}
|
||||
else if(spawn->IsWidget()){
|
||||
Widget* widget = (Widget*)spawn;
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_widgets (spawn_id, widget_id) values(%u, %u)", spawn->GetDatabaseID(), widget->GetWidgetID());
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_widgets%s (spawn_id, widget_id) values(%u, %u)", houseTable.c_str(), spawn->GetDatabaseID(), widget->GetWidgetID());
|
||||
}
|
||||
else if(spawn->IsSign()){
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_signs (spawn_id, description) values(%u, 'change me')", spawn->GetDatabaseID());
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_signs%s (spawn_id, description) values(%u, 'change me')", houseTable.c_str(), spawn->GetDatabaseID());
|
||||
|
||||
}
|
||||
else if (spawn->IsGroundSpawn()) {
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_ground (spawn_id) values(%u)", spawn->GetDatabaseID());
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_ground%s (spawn_id) values(%u)", houseTable.c_str(), spawn->GetDatabaseID());
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(spawn->IsNPC()){
|
||||
query.RunQuery2(Q_UPDATE, "update spawn_npcs, spawn set name='%s', min_level=%i, max_level=%i, enc_level=%i, race=%i, model_type=%i, class_=%i, gender=%i, show_name=%i, attackable=%i, show_level=%i, targetable=%i, show_command_icon=%i, display_hand_icon=%i, hair_type_id=%i, facial_hair_type_id=%i, wing_type_id=%i, chest_type_id=%i, legs_type_id=%i, soga_hair_type_id=%i, soga_facial_hair_type_id=%i, soga_model_type=%i, size=%i, hp=%u, heroic_flag=%i, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, action_state=%i, mood_state=%i, initial_state=%i, activity_status=%i, alignment=%i, faction_id=%u, hide_hood=%i, emote_state=%i, suffix ='%s', prefix='%s', last_name='%s', merchant_min_level = %u, merchant_max_level = %u where spawn_npcs.spawn_id = spawn.id and spawn.id = %u",
|
||||
name.c_str(), spawn->GetLevel(), spawn->GetLevel(), spawn->GetDifficulty(), spawn->GetRace(), spawn->GetModelType(),
|
||||
query.RunQuery2(Q_UPDATE, "update spawn_npcs%s, spawn set name='%s', min_level=%i, max_level=%i, enc_level=%i, race=%i, model_type=%i, class_=%i, gender=%i, show_name=%i, attackable=%i, show_level=%i, targetable=%i, show_command_icon=%i, display_hand_icon=%i, hair_type_id=%i, facial_hair_type_id=%i, wing_type_id=%i, chest_type_id=%i, legs_type_id=%i, soga_hair_type_id=%i, soga_facial_hair_type_id=%i, soga_model_type=%i, size=%i, hp=%u, heroic_flag=%i, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, action_state=%i, mood_state=%i, initial_state=%i, activity_status=%i, alignment=%i, faction_id=%u, hide_hood=%i, emote_state=%i, suffix ='%s', prefix='%s', last_name='%s', merchant_min_level = %u, merchant_max_level = %u where spawn_npcs%s.spawn_id = spawn.id and spawn.id = %u",
|
||||
houseTable.c_str(), name.c_str(), spawn->GetLevel(), spawn->GetLevel(), spawn->GetDifficulty(), spawn->GetRace(), spawn->GetModelType(),
|
||||
spawn->GetAdventureClass(), spawn->GetGender(), spawn->appearance.display_name, spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.targetable, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, ((NPC*)spawn)->features.hair_type,
|
||||
((NPC*)spawn)->features.hair_face_type, ((NPC*)spawn)->features.wing_type, ((NPC*)spawn)->features.chest_type, ((NPC*)spawn)->features.legs_type, ((NPC*)spawn)->features.soga_hair_type, ((NPC*)spawn)->features.soga_hair_face_type, spawn->appearance.soga_model_type, spawn->GetSize(),
|
||||
spawn->GetTotalHPBase(), spawn->appearance.heroic_flag, spawn->GetTotalPowerBase(), spawn->GetCollisionRadius(), spawn->GetPrimaryCommandListID(),
|
||||
spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->GetActionState(), spawn->GetMoodState(), spawn->GetInitialState(),
|
||||
spawn->GetActivityStatus(), ((NPC*)spawn)->GetAlignment(), spawn->GetFactionID(), spawn->appearance.hide_hood, spawn->appearance.emote_state,
|
||||
suffix.c_str(), prefix.c_str(), last_name.c_str(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), spawn->GetDatabaseID());
|
||||
suffix.c_str(), prefix.c_str(), last_name.c_str(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), houseTable.c_str(), spawn->GetDatabaseID());
|
||||
}
|
||||
else if(spawn->IsObject()){
|
||||
query.RunQuery2(Q_UPDATE, "update spawn_objects, spawn set name='%s', model_type=%i, show_name=%i, targetable=%i, size=%i, command_primary=%u, command_secondary=%u, visual_state=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, collision_radius=%i, hp = %u, power = %u, device_id = %i, merchant_min_level = %u, merchant_max_level = %u where spawn_objects.spawn_id = spawn.id and spawn.id = %u",
|
||||
name.c_str(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.targetable, spawn->GetSize(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon,
|
||||
spawn->GetCollisionRadius(), spawn->GetTotalHP(), spawn->GetTotalPower(), ((Object*)spawn)->GetDeviceID(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), spawn->GetDatabaseID());
|
||||
query.RunQuery2(Q_UPDATE, "update spawn_objects%s, spawn set name='%s', model_type=%i, show_name=%i, targetable=%i, size=%i, command_primary=%u, command_secondary=%u, visual_state=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, collision_radius=%i, hp = %u, power = %u, device_id = %i, merchant_min_level = %u, merchant_max_level = %u where spawn_objects%s.spawn_id = spawn.id and spawn.id = %u",
|
||||
houseTable.c_str(), name.c_str(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.targetable, spawn->GetSize(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon,
|
||||
spawn->GetCollisionRadius(), spawn->GetTotalHP(), spawn->GetTotalPower(), ((Object*)spawn)->GetDeviceID(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), houseTable.c_str(), spawn->GetDatabaseID());
|
||||
}
|
||||
else if(spawn->IsWidget()){
|
||||
Widget* widget = (Widget*)spawn;
|
||||
@ -3750,25 +3826,25 @@ bool WorldDatabase::SaveSpawnInfo(Spawn* spawn){
|
||||
char* closeSound = 0;
|
||||
if (widget->GetOpenSound() != NULL) openSound = (char*)widget->GetOpenSound(); else openSound = (char*)string("0").c_str();
|
||||
if (widget->GetCloseSound() != NULL) closeSound = (char*)widget->GetCloseSound(); else closeSound = (char*)string("0").c_str();
|
||||
query.RunQuery2(Q_UPDATE, "update spawn_widgets, spawn set name='%s', race=%i, model_type=%i, show_name=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, size=%i, hp=%u, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, faction_id=%u, suffix ='%s', prefix='%s', last_name='%s',widget_id = %u,widget_x = %f,widget_y = %f,widget_z = %f,include_heading = %u,include_location = %u,icon = %u,type='%s',open_heading = %f,closed_heading = %f,open_x = %f,open_y = %f,open_z = %f,action_spawn_id = %u,open_sound_file='%s',close_sound_file='%s',open_duration = %u,close_x = %f,close_y=%f,close_z=%f,linked_spawn_id = %u,house_id = %u, merchant_min_level = %u, merchant_max_level = %u where spawn_widgets.spawn_id = spawn.id and spawn.id = %u",
|
||||
name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, spawn->GetSize(),
|
||||
query.RunQuery2(Q_UPDATE, "update spawn_widgets%s, spawn set name='%s', race=%i, model_type=%i, show_name=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, size=%i, hp=%u, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, faction_id=%u, suffix ='%s', prefix='%s', last_name='%s',widget_id = %u,widget_x = %f,widget_y = %f,widget_z = %f,include_heading = %u,include_location = %u,icon = %u,type='%s',open_heading = %f,closed_heading = %f,open_x = %f,open_y = %f,open_z = %f,action_spawn_id = %u,open_sound_file='%s',close_sound_file='%s',open_duration = %u,close_x = %f,close_y=%f,close_z=%f,linked_spawn_id = %u,house_id = %u, merchant_min_level = %u, merchant_max_level = %u where spawn_widgets%s.spawn_id = spawn.id and spawn.id = %u",
|
||||
houseTable.c_str(), name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, spawn->GetSize(),
|
||||
spawn->GetTotalHP(), spawn->GetTotalPower(), spawn->GetCollisionRadius(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->GetFactionID(),
|
||||
suffix.c_str(), prefix.c_str(), last_name.c_str(), widget->GetWidgetID(), widget->GetX(), widget->GetY(), widget->GetZ(), widget->GetIncludeHeading(), widget->GetIncludeLocation(), widget->GetIconValue(), Widget::GetWidgetTypeNameByTypeID(widget->GetWidgetType()).c_str(),
|
||||
widget->GetOpenHeading(), widget->GetClosedHeading(), widget->GetOpenX(), widget->GetOpenY(), widget->GetOpenZ(),
|
||||
widget->GetActionSpawnID(), openSound, closeSound,widget->GetOpenDuration(),
|
||||
widget->GetCloseX(),widget->GetCloseY(),widget->GetCloseZ(),widget->GetLinkedSpawnID(),widget->GetHouseID(),
|
||||
spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), spawn->GetDatabaseID());
|
||||
spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), houseTable.c_str(), spawn->GetDatabaseID());
|
||||
}
|
||||
else if(spawn->IsSign()){
|
||||
Sign* sign = (Sign*)spawn;
|
||||
query.RunQuery2(Q_UPDATE, "update spawn_signs, spawn set name='%s', race=%i, model_type=%i, show_name=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, size=%i, hp=%u, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, faction_id=%u, suffix ='%s', prefix='%s', last_name='%s', `type`='%s', zone_id = %u, widget_id = %u, title='%s', widget_x = %f, widget_y = %f, widget_z = %f, icon = %u, description='%s', sign_distance = %f, zone_x = %f, zone_y = %f, zone_z = %f, zone_heading = %f, include_heading = %u, include_location = %u, merchant_min_level = %u, merchant_max_level = %u, language = %u where spawn_signs.spawn_id = spawn.id and spawn.id = %u",
|
||||
name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, spawn->GetSize(),
|
||||
query.RunQuery2(Q_UPDATE, "update spawn_signs%s, spawn set name='%s', race=%i, model_type=%i, show_name=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, size=%i, hp=%u, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, faction_id=%u, suffix ='%s', prefix='%s', last_name='%s', `type`='%s', zone_id = %u, widget_id = %u, title='%s', widget_x = %f, widget_y = %f, widget_z = %f, icon = %u, description='%s', sign_distance = %f, zone_x = %f, zone_y = %f, zone_z = %f, zone_heading = %f, include_heading = %u, include_location = %u, merchant_min_level = %u, merchant_max_level = %u, language = %u where spawn_signs%s.spawn_id = spawn.id and spawn.id = %u",
|
||||
houseTable.c_str(), name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, spawn->GetSize(),
|
||||
spawn->GetTotalHP(), spawn->GetTotalPower(), spawn->GetCollisionRadius(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->GetFactionID(),
|
||||
suffix.c_str(), prefix.c_str(), last_name.c_str(), sign->GetSignType() == SIGN_TYPE_GENERIC ? "Generic" : "Zone", sign->GetSignZoneID(),
|
||||
sign->GetWidgetID(), sign->GetSignTitle(), sign->GetWidgetX(), sign->GetWidgetY(), sign->GetWidgetZ(),
|
||||
sign->GetIconValue(), sign->GetSignDescription(), sign->GetSignDistance(), sign->GetSignZoneX(),
|
||||
sign->GetSignZoneY(), sign->GetSignZoneZ(), sign->GetSignZoneHeading(), sign->GetIncludeHeading(),
|
||||
sign->GetIncludeLocation(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), sign->GetLanguage(), spawn->GetDatabaseID());
|
||||
sign->GetIncludeLocation(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), sign->GetLanguage(), houseTable.c_str(), spawn->GetDatabaseID());
|
||||
}
|
||||
}
|
||||
if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
|
||||
@ -3794,7 +3870,7 @@ int32 WorldDatabase::SaveCombinedSpawnLocation(ZoneServer* zone, Spawn* in_spawn
|
||||
spawnLocationID = spawn_location_id;
|
||||
if(!name)
|
||||
name = "Combine SpawnGroup Generated";
|
||||
if(!CreateNewSpawnLocation(spawn_location_id, name)){
|
||||
if(!CreateNewSpawnLocation(spawn_location_id, name, (zone->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE))){
|
||||
safe_delete(spawns);
|
||||
return 0;
|
||||
}
|
||||
@ -3841,21 +3917,28 @@ bool WorldDatabase::SaveSpawnEntry(Spawn* spawn, const char* spawn_location_name
|
||||
Query query;
|
||||
Query query2;
|
||||
int32 count = 0;
|
||||
|
||||
int8 isInstanceType = (spawn->GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE);
|
||||
std::string houseTable("");
|
||||
if(isInstanceType) {
|
||||
houseTable.append("_houses");
|
||||
}
|
||||
|
||||
if(create_spawnlocation){
|
||||
count = GetSpawnLocationCount(spawn->GetSpawnLocationID());
|
||||
count = GetSpawnLocationCount(spawn->GetSpawnLocationID(), spawn);
|
||||
if(count == 0){
|
||||
if(!CreateNewSpawnLocation(spawn->GetSpawnLocationID(), spawn_location_name))
|
||||
if(!CreateNewSpawnLocation(spawn->GetSpawnLocationID(), spawn_location_name, isInstanceType))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_location_entry (spawn_id, spawn_location_id, spawnpercentage) values(%u, %u, %i)", spawn->GetDatabaseID(), spawn->GetSpawnLocationID(), percent);
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_location_entry%s (spawn_id, spawn_location_id, spawnpercentage) values(%u, %u, %i)", houseTable.c_str(), spawn->GetDatabaseID(), spawn->GetSpawnLocationID(), percent);
|
||||
|
||||
if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
|
||||
LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in SaveSpawnEntry query '%s': %s", query.GetQuery(), query.GetError());
|
||||
return false;
|
||||
}
|
||||
if(save_zonespawn){
|
||||
query2.RunQuery2(Q_INSERT, "insert into spawn_location_placement (zone_id, instance_id, spawn_location_id, x, y, z, x_offset, y_offset, z_offset, heading, grid_id) values(%u, %u, %u, %f, %f, %f, %f, %f, %f, %f, %u)", spawn->GetZone()->GetZoneID(), spawn->GetZone()->GetInstanceID(), spawn->GetSpawnLocationID(), spawn->GetX(), spawn->GetY(), spawn->GetZ(),x_offset, y_offset, z_offset, spawn->GetHeading(), spawn->GetLocation());
|
||||
query2.RunQuery2(Q_INSERT, "insert into spawn_location_placement%s (zone_id, instance_id, spawn_location_id, x, y, z, x_offset, y_offset, z_offset, heading, grid_id) values(%u, %u, %u, %f, %f, %f, %f, %f, %f, %f, %u)", houseTable.c_str(), spawn->GetZone()->GetZoneID(), spawn->GetZone()->GetInstanceID(), spawn->GetSpawnLocationID(), spawn->GetX(), spawn->GetY(), spawn->GetZ(),x_offset, y_offset, z_offset, spawn->GetHeading(), spawn->GetLocation());
|
||||
if(query2.GetErrorNumber() && query2.GetError() && query2.GetErrorNumber() < 0xFFFFFFFF){
|
||||
LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in SaveSpawnEntry query '%s': %s", query2.GetQuery(), query2.GetError());
|
||||
return false;
|
||||
@ -3901,12 +3984,12 @@ float WorldDatabase::GetSpawnLocationPlacementOffsetZ(int32 location_id) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool WorldDatabase::CreateNewSpawnLocation(int32 id, const char* name){
|
||||
bool WorldDatabase::CreateNewSpawnLocation(int32 id, const char* name, bool isHouseType){
|
||||
Query query;
|
||||
if(!name)
|
||||
name = "Unknown Spawn Location Name";
|
||||
string str_name = getSafeEscapeString(name);
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_location_name (id, name) values(%u, '%s')", id, str_name.c_str());
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_location_name%s (id, name) values(%u, '%s')", isHouseType ? "_houses" : "", id, str_name.c_str());
|
||||
if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
|
||||
LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in CreateNewSpawnLocation query '%s': %s", query.GetQuery(), query.GetError());
|
||||
return false;
|
||||
@ -3918,10 +4001,16 @@ int32 WorldDatabase::GetSpawnLocationCount(int32 location, Spawn* spawn){
|
||||
Query query;
|
||||
int32 ret = 0;
|
||||
MYSQL_RES* result = 0;
|
||||
|
||||
int8 isInstanceType = (spawn && spawn->GetZone() && spawn->GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE);
|
||||
std::string houseTable("");
|
||||
if(isInstanceType) {
|
||||
houseTable.append("_houses");
|
||||
}
|
||||
if(spawn)
|
||||
result = query.RunQuery2(Q_SELECT, "SELECT count(id) FROM spawn_location_entry where spawn_location_id=%u and spawn_id=%u", location, spawn->GetDatabaseID());
|
||||
result = query.RunQuery2(Q_SELECT, "SELECT count(id) FROM spawn_location_entry%s where spawn_location_id=%u and spawn_id=%u", houseTable.c_str(), location, spawn->GetDatabaseID());
|
||||
else
|
||||
result = query.RunQuery2(Q_SELECT, "SELECT count(id) FROM spawn_location_entry where spawn_location_id=%u", location);
|
||||
result = query.RunQuery2(Q_SELECT, "SELECT count(id) FROM spawn_location_entry%s where spawn_location_id=%u", houseTable.c_str(), location);
|
||||
if(result && mysql_num_rows(result) > 0){
|
||||
MYSQL_ROW row;
|
||||
while(result && (row = mysql_fetch_row(result)) && row[0]){
|
||||
@ -3931,10 +4020,10 @@ int32 WorldDatabase::GetSpawnLocationCount(int32 location, Spawn* spawn){
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32 WorldDatabase::GetNextSpawnLocation(){
|
||||
int32 WorldDatabase::GetNextSpawnLocation(bool isInstanceType){
|
||||
Query query;
|
||||
int32 ret = 0;
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(id) FROM spawn_location_name");
|
||||
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(id) FROM spawn_location_name%s", isInstanceType ? "_houses" : "");
|
||||
if(result && mysql_num_rows(result) > 0){
|
||||
MYSQL_ROW row;
|
||||
while(result && (row = mysql_fetch_row(result)) && row[0]){
|
||||
@ -3950,11 +4039,17 @@ bool WorldDatabase::RemoveSpawnFromSpawnLocation(Spawn* spawn){
|
||||
Query query2;
|
||||
int32 count = GetSpawnLocationCount(spawn->GetSpawnLocationID(), spawn);
|
||||
|
||||
query.RunQuery2(Q_DELETE, "delete FROM spawn_location_placement where spawn_location_id=%u", spawn->GetSpawnLocationID());
|
||||
int8 isInstanceType = (spawn && spawn->GetZone() && spawn->GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE);
|
||||
std::string houseTable("");
|
||||
if(isInstanceType) {
|
||||
houseTable.append("_houses");
|
||||
}
|
||||
|
||||
query.RunQuery2(Q_DELETE, "delete FROM spawn_location_placement%s where spawn_location_id=%u", houseTable.c_str(), spawn->GetSpawnLocationID());
|
||||
if(count == 1)
|
||||
query.RunQuery2(Q_DELETE, "delete FROM spawn_location_name where id=%u", spawn->GetSpawnLocationID());
|
||||
query.RunQuery2(Q_DELETE, "delete FROM spawn_location_name%s where id=%u", houseTable.c_str(), spawn->GetSpawnLocationID());
|
||||
|
||||
query2.RunQuery2(Q_DELETE, "delete FROM spawn_location_entry where spawn_id=%u and spawn_location_id = %u", spawn->GetDatabaseID(), spawn->GetSpawnLocationID());
|
||||
query2.RunQuery2(Q_DELETE, "delete FROM spawn_location_entry%s where spawn_id=%u and spawn_location_id = %u", houseTable.c_str(), spawn->GetDatabaseID(), spawn->GetSpawnLocationID());
|
||||
if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
|
||||
LogWrite(WORLD__ERROR, 0, "World", "Error in RemoveSpawnFromSpawnLocation query '%s': %s", query.GetQuery(), query.GetError());
|
||||
return false;
|
||||
@ -5288,7 +5383,8 @@ bool WorldDatabase::DeleteCharacter(int32 account_id, int32 character_id){
|
||||
query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_custom_spell_dataindex where charid=%u", character_id);
|
||||
query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_custom_spell_display where charid=%u", character_id);
|
||||
query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_custom_spell_data where charid=%u", character_id);
|
||||
|
||||
broker.RemoveSeller(character_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -7129,13 +7225,17 @@ bool WorldDatabase::LoadNPC(ZoneServer* zone, int32 spawn_id) {
|
||||
NPC* npc = nullptr;
|
||||
int32 id = 0;
|
||||
DatabaseResult result;
|
||||
|
||||
database_new.Select(&result, "SELECT npc.spawn_id, s.name, npc.min_level, npc.max_level, npc.enc_level, s.race, s.model_type, npc.class_, npc.gender, s.command_primary, s.command_secondary, s.show_name, npc.min_group_size, npc.max_group_size, npc.hair_type_id, npc.facial_hair_type_id, npc.wing_type_id, npc.chest_type_id, npc.legs_type_id, npc.soga_hair_type_id, npc.soga_facial_hair_type_id, s.attackable, s.show_level, s.targetable, s.show_command_icon, s.display_hand_icon, s.hp, s.power, s.size, s.collision_radius, npc.action_state, s.visual_state, npc.mood_state, npc.initial_state, npc.activity_status, s.faction_id, s.sub_title, s.merchant_id, s.merchant_type, s.size_offset, npc.attack_type, npc.ai_strategy+0, npc.spell_list_id, npc.secondary_spell_list_id, npc.skill_list_id, npc.secondary_skill_list_id, npc.equipment_list_id, npc.str, npc.sta, npc.wis, npc.intel, npc.agi, npc.heat, npc.cold, npc.magic, npc.mental, npc.divine, npc.disease, npc.poison, npc.aggro_radius, npc.cast_percentage, npc.randomize, npc.soga_model_type, npc.heroic_flag, npc.alignment, npc.elemental, npc.arcane, npc.noxious, s.savagery, s.dissonance, npc.hide_hood, npc.emote_state, s.prefix, s.suffix, s.last_name, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, npc.scared_by_strong_players, npc.action_state_str\n"
|
||||
"FROM spawn s\n"
|
||||
"INNER JOIN spawn_npcs npc\n"
|
||||
int8 isInstanceType = (zone->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE);
|
||||
std::string houseTable("");
|
||||
if(isInstanceType) {
|
||||
houseTable.append("_houses");
|
||||
}
|
||||
database_new.Select(&result, "SELECT npc.spawn_id, s.name, npc.min_level, npc.max_level, npc.enc_level, s.race, s.model_type, npc.class_, npc.gender, s.command_primary, s.command_secondary, s.show_name, npc.min_group_size, npc.max_group_size, npc.hair_type_id, npc.facial_hair_type_id, npc.wing_type_id, npc.chest_type_id, npc.legs_type_id, npc.soga_hair_type_id, npc.soga_facial_hair_type_id, s.attackable, s.show_level, s.targetable, s.show_command_icon, s.display_hand_icon, s.hp, s.power, s.size, s.collision_radius, npc.action_state, s.visual_state, npc.mood_state, npc.initial_state, npc.activity_status, s.faction_id, s.sub_title, s.merchant_id, s.merchant_type, s.size_offset, npc.attack_type, npc.ai_strategy+0, npc.spell_list_id, npc.secondary_spell_list_id, npc.skill_list_id, npc.secondary_skill_list_id, npc.equipment_list_id, npc.str, npc.sta, npc.wis, npc.intel, npc.agi, npc.heat, npc.cold, npc.magic, npc.mental, npc.divine, npc.disease, npc.poison, npc.aggro_radius, npc.cast_percentage, npc.randomize, npc.soga_model_type, npc.heroic_flag, npc.alignment, npc.elemental, npc.arcane, npc.noxious, s.savagery, s.dissonance, npc.hide_hood, npc.emote_state, s.prefix, s.suffix, s.last_name, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, npc.scared_by_strong_players, npc.action_state_str, s.lua_script\n"
|
||||
"FROM spawn%s s\n"
|
||||
"INNER JOIN spawn_npcs%s npc\n"
|
||||
"ON npc.spawn_id = s.id\n"
|
||||
"WHERE s.id = %u",
|
||||
spawn_id);
|
||||
houseTable.c_str(), houseTable.c_str(), spawn_id);
|
||||
|
||||
if (result.GetNumRows() > 0 && result.Next()) {
|
||||
id = result.GetInt32(0);
|
||||
@ -7273,6 +7373,11 @@ bool WorldDatabase::LoadNPC(ZoneServer* zone, int32 spawn_id) {
|
||||
npc->GetInfoStruct()->set_action_state(action_state_str);
|
||||
}
|
||||
|
||||
if(!result.IsNull(83)){
|
||||
std::string lua_script = std::string(result.GetString(83));
|
||||
npc->SetSpawnScript(lua_script, true);
|
||||
}
|
||||
|
||||
zone->AddNPC(id, npc);
|
||||
|
||||
//skipped spells/skills/equipment as it is all loaded, the following rely on a spawn to load
|
||||
@ -7761,9 +7866,6 @@ void WorldDatabase::GetHouseSpawnInstanceData(ZoneServer* zone, Spawn* spawn)
|
||||
if (!spawn)
|
||||
return;
|
||||
|
||||
if (zone->house_object_database_lookup.count(spawn->GetModelType()) < 1)
|
||||
zone->house_object_database_lookup.Put(spawn->GetModelType(), spawn->GetDatabaseID());
|
||||
|
||||
DatabaseResult result;
|
||||
|
||||
database_new.Select(&result, "SELECT pickup_item_id, pickup_unique_item_id\n"
|
||||
@ -8851,7 +8953,6 @@ void WorldDatabase::SaveSignMark(int32 char_id, int32 sign_id, char* char_name,
|
||||
LogWrite(SIGN__DEBUG, 0, "Sign", "ERROR in WorldDatabase::SaveSignMark");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
string WorldDatabase::GetSignMark(int32 char_id, int32 sign_id, char* char_name) {
|
||||
@ -8884,4 +8985,121 @@ int32 WorldDatabase::GetMysqlExpCurve(int level) {
|
||||
}
|
||||
//return 1 so we dont break shit later divide by 0 and all that.
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool WorldDatabase::RemoveBrokerItem(int32 cid, int64 uid, int32 quantity) {
|
||||
if (quantity <= 0)
|
||||
return false;
|
||||
|
||||
string update = string("UPDATE broker_items SET count = count - %u WHERE character_id = %u AND unique_id = %llu AND count >= %u");
|
||||
Query q;
|
||||
q.RunQuery2(
|
||||
Q_UPDATE,
|
||||
update.c_str(), quantity, cid, uid, quantity
|
||||
);
|
||||
|
||||
if (q.GetErrorNumber() && q.GetError() && q.GetErrorNumber() < 0xFFFFFFFF) {
|
||||
LogWrite(WORLD__ERROR, 0, "Broker",
|
||||
"DB error removing %u from item %llu for char %u: %s",
|
||||
quantity, uid, cid, q.GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// exactly one row updated ⇒ we had enough stock (and trigger will
|
||||
// delete it if count hit zero)
|
||||
return (q.GetAffectedRows() == 1);
|
||||
}
|
||||
|
||||
int32 WorldDatabase::LoadBrokerSellers(BrokerManager &broker) {
|
||||
Query query;
|
||||
MYSQL_ROW row;
|
||||
int32 count = 0;
|
||||
|
||||
MYSQL_RES * result = query.RunQuery2(
|
||||
Q_SELECT,
|
||||
"SELECT character_id, seller_name, house_id, sale_enabled, sell_from_inventory, coin_session, coin_total FROM broker_sellers"
|
||||
);
|
||||
|
||||
while (result && (row = mysql_fetch_row(result))) {
|
||||
int32 cid = atoul(row[0]);
|
||||
std::string name = row[1] ? row[1] : "";
|
||||
int32 hid = strtoull(row[2], NULL, 0);
|
||||
bool en = atoi(row[3]) != 0;
|
||||
bool inv = atoi(row[4]) != 0;
|
||||
int64 coin_session = strtoull(row[5], NULL, 0);
|
||||
int64 coin_total = strtoull(row[6], NULL, 0);
|
||||
|
||||
broker.LoadSeller(cid, name, hid, true, inv, coin_session, coin_total);
|
||||
++count;
|
||||
|
||||
LogWrite(PLAYER__ERROR, 5, "Broker",
|
||||
"--Loaded broker player: %u (%s), house=%u, sale=%u, inv=%u",
|
||||
cid, name.c_str(), hid, en, inv
|
||||
);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int32 WorldDatabase::LoadBrokerItems(BrokerManager &broker) {
|
||||
Query query;
|
||||
MYSQL_ROW row;
|
||||
int32 count = 0;
|
||||
|
||||
MYSQL_RES * result = query.RunQuery2(
|
||||
Q_SELECT,
|
||||
"SELECT unique_id, character_id, house_id, item_id,"
|
||||
" cost_copper, for_sale, inv_slot_id, slot_id, count, from_inventory, creator "
|
||||
" FROM broker_items"
|
||||
);
|
||||
|
||||
while (result && (row = mysql_fetch_row(result))) {
|
||||
SaleItem it{};
|
||||
it.unique_id = strtoull(row[0], NULL, 0);
|
||||
it.character_id = atoi(row[1]);
|
||||
it.house_id = atoi(row[2]);
|
||||
it.item_id = strtoull(row[3], NULL, 0);
|
||||
it.cost_copper = atoi(row[4]);
|
||||
it.for_sale = atoi(row[5]) != 0;
|
||||
it.inv_slot_id = atoi(row[6]);
|
||||
it.slot_id = atoi(row[7]);
|
||||
it.count = atoi(row[8]);
|
||||
it.from_inventory = atoul(row[9]);
|
||||
if(row[10])
|
||||
it.creator = std::string(row[10]);
|
||||
|
||||
broker.LoadItem(it);
|
||||
++count;
|
||||
|
||||
LogWrite(PLAYER__ERROR, 5, "Broker",
|
||||
"--Loaded broker item: uid=%llu, char=%u, item=%llu,"
|
||||
" price=%u, count=%u",
|
||||
it.unique_id, it.character_id,
|
||||
it.item_id, it.cost_copper,
|
||||
it.count
|
||||
);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int32 WorldDatabase::LoadBrokerData(BrokerManager &broker) {
|
||||
int32 p = LoadBrokerSellers(broker);
|
||||
int32 i = LoadBrokerItems(broker);
|
||||
return p + i;
|
||||
}
|
||||
|
||||
bool WorldDatabase::UpdateHouseSpawnScript(int32 dbid, std::string scriptName) {
|
||||
string update = string("UPDATE spawn_houses SET lua_script='%s' where id=%u");
|
||||
Query q;
|
||||
q.RunQuery2(
|
||||
Q_UPDATE,
|
||||
update.c_str(), getSafeEscapeString(scriptName.c_str()).c_str(), dbid
|
||||
);
|
||||
|
||||
if (q.GetErrorNumber() && q.GetError() && q.GetErrorNumber() < 0xFFFFFFFF) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (q.GetAffectedRows() == 1);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef EQ2WORLD_EMU_DATABASE_H
|
||||
#define EQ2WORLD_EMU_DATABASE_H
|
||||
|
||||
@ -50,6 +51,7 @@
|
||||
#include "Rules/Rules.h"
|
||||
#include "Languages.h"
|
||||
#include "World.h"
|
||||
#include "Broker/BrokerManager.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -175,13 +177,13 @@ public:
|
||||
void SaveWorldTime(WorldTime* time);
|
||||
|
||||
bool SaveSpawnInfo(Spawn* spawn);
|
||||
int32 GetNextSpawnIDInZone(int32 zone_id);
|
||||
int32 GetNextSpawnIDInZone(int32 zone_id, bool isInstanceType = false);
|
||||
bool SaveSpawnEntry(Spawn* spawn, const char* spawn_location_name, int8 percent, float x_offset, float y_offset, float z_offset, bool save_zonespawn = true, bool create_spawnlocation = true);
|
||||
float GetSpawnLocationPlacementOffsetX(int32 location_id);
|
||||
float GetSpawnLocationPlacementOffsetY(int32 location_id);
|
||||
float GetSpawnLocationPlacementOffsetZ(int32 location_id);
|
||||
int32 GetNextSpawnLocation();
|
||||
bool CreateNewSpawnLocation(int32 id, const char* name);
|
||||
int32 GetNextSpawnLocation(bool isInstanceType = false);
|
||||
bool CreateNewSpawnLocation(int32 id, const char* name, bool isHouseType = false);
|
||||
bool RemoveSpawnFromSpawnLocation(Spawn* spawn);
|
||||
int32 GetSpawnLocationCount(int32 location, Spawn* spawn = 0);
|
||||
vector<string>* GetSpawnNameList(const char* in_name);
|
||||
@ -240,7 +242,7 @@ public:
|
||||
void UpdateStartingSkillbar(int32 char_id, int8 class_id, int8 race_id);
|
||||
void UpdateStartingTitles(int32 char_id, int8 class_id, int8 race_id, int8 gender_id);
|
||||
bool UpdateSpawnLocationSpawns(Spawn* spawn);
|
||||
bool UpdateSpawnWidget(int32 widget_id, char* query);
|
||||
bool UpdateSpawnWidget(int32 widget_id, char* query, bool is_house = false);
|
||||
bool CheckVersionTable();
|
||||
void LoadFactionAlliances();
|
||||
void LoadFactionList();
|
||||
@ -275,16 +277,16 @@ public:
|
||||
void LoadGroundSpawnItems(ZoneServer* zone);
|
||||
void LoadSpawns(ZoneServer* zone);
|
||||
int8 GetAppearanceType(string type);
|
||||
void LoadNPCs(ZoneServer* zone);
|
||||
void LoadNPCs(ZoneServer* zone, bool isInstanceType = false);
|
||||
void LoadSpiritShards(ZoneServer* zone);
|
||||
int32 LoadAppearances(ZoneServer* zone, Client* client = 0);
|
||||
int32 LoadNPCSpells();
|
||||
int32 LoadNPCSkills(ZoneServer* zone);
|
||||
int32 LoadNPCEquipment(ZoneServer* zone);
|
||||
void LoadObjects(ZoneServer* zone);
|
||||
void LoadGroundSpawns(ZoneServer* zone);
|
||||
void LoadWidgets(ZoneServer* zone);
|
||||
void LoadSigns(ZoneServer* zone);
|
||||
void LoadObjects(ZoneServer* zone, bool isInstanceType = false);
|
||||
void LoadGroundSpawns(ZoneServer* zone, bool isInstanceType = false);
|
||||
void LoadWidgets(ZoneServer* zone, bool isInstanceType = false);
|
||||
void LoadSigns(ZoneServer* zone, bool isInstanceType = false);
|
||||
void ReloadItemList(int32 item_id = 0);
|
||||
void LoadItemList(int32 item_id = 0);
|
||||
int32 LoadItemStats(int32 item_id = 0);
|
||||
@ -293,7 +295,8 @@ public:
|
||||
int32 LoadItemLevelOverride(int32 item_id = 0);
|
||||
int32 LoadItemEffects(int32 item_id = 0);
|
||||
int32 LoadBookPages(int32 item_id = 0);
|
||||
int32 LoadNextUniqueItemID();
|
||||
int64 LoadNextUniqueItemID();
|
||||
void ResetNextUniqueItemID();
|
||||
int32 LoadSkillItems(int32 item_id = 0);
|
||||
int32 LoadRangeWeapons(int32 item_id = 0);
|
||||
int32 LoadThrownWeapons(int32 item_id = 0);
|
||||
@ -662,6 +665,17 @@ public:
|
||||
void LoadCharacterSpellEffects(int32 char_id, Client *client, int8 db_spell_type);
|
||||
|
||||
int32 GetMysqlExpCurve(int level);
|
||||
|
||||
bool RemoveBrokerItem(int32 cid, int64 uid, int32 quantity);
|
||||
int32 LoadBrokerSellers(BrokerManager &broker);
|
||||
int32 LoadBrokerItems(BrokerManager &broker);
|
||||
int32 LoadBrokerData(BrokerManager &broker);
|
||||
|
||||
void ClearSellerSession(int32 character_id);
|
||||
void AddToSellerSession(int32 character_id, int64 amount);
|
||||
int64 GetSellerSession(int32 character_id);
|
||||
|
||||
bool UpdateHouseSpawnScript(int32 dbid, std::string scriptName);
|
||||
private:
|
||||
DatabaseNew database_new;
|
||||
std::map<int32, string> zone_names;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -94,6 +94,7 @@
|
||||
#include "AltAdvancement/AltAdvancement.h"
|
||||
#include "Bots/Bot.h"
|
||||
#include "VisualStates.h"
|
||||
#include "Broker/BrokerManager.h"
|
||||
|
||||
extern WorldDatabase database;
|
||||
extern const char* ZONE_NAME;
|
||||
@ -130,6 +131,7 @@ extern MasterRecipeBookList master_recipebook_list;
|
||||
extern VisualStates visual_states;
|
||||
extern PeerManager peer_manager;
|
||||
extern HTTPSClientPool peer_https_pool;
|
||||
extern BrokerManager broker;
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -242,6 +244,10 @@ Client::Client(EQStream* ieqs) : underworld_cooldown_timer(5000), pos_update(125
|
||||
recipe_xor_packet = nullptr;
|
||||
recipe_packet_count = 0;
|
||||
recipe_orig_packet_size = 0;
|
||||
gm_store_search = false;
|
||||
SetShopWindowStatus(false);
|
||||
search_page = 0;
|
||||
firstlogin_transmit = false;
|
||||
}
|
||||
|
||||
Client::~Client() {
|
||||
@ -306,7 +312,7 @@ void Client::RemoveClientFromZone() {
|
||||
zone_list.RemoveClientFromMap(player->GetName(), this);
|
||||
|
||||
safe_delete(camp_timer);
|
||||
safe_delete(search_items);
|
||||
ClearItemSearch();
|
||||
safe_delete(current_rez.expire_timer);
|
||||
safe_delete(pending_last_name);
|
||||
safe_delete_array(incoming_paperdoll.image_bytes);
|
||||
@ -838,8 +844,10 @@ void Client::SendCharInfo() {
|
||||
if (player->GetHP() < player->GetTotalHP() || player->GetPower() < player->GetTotalPower())
|
||||
GetCurrentZone()->AddDamagedSpawn(player);
|
||||
|
||||
if (firstlogin)
|
||||
if (firstlogin) {
|
||||
firstlogin_transmit = true;
|
||||
firstlogin = false;
|
||||
}
|
||||
|
||||
player->ClearProcs();
|
||||
items = player->GetEquippedItemList();
|
||||
@ -910,6 +918,8 @@ void Client::SendCharInfo() {
|
||||
GetPlayer()->SetSaveSpellEffects(false);
|
||||
GetPlayer()->SetCharSheetChanged(true);
|
||||
GetPlayer()->SetReturningFromLD(false);
|
||||
|
||||
broker.LockActiveItemsForClient(this);
|
||||
}
|
||||
|
||||
void Client::SendZoneSpawns() {
|
||||
@ -1491,7 +1501,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
|
||||
spawn->position_changed = true;
|
||||
|
||||
_snprintf(query, 256, "open_heading=%f,include_heading=1", newHeading);
|
||||
if (database.UpdateSpawnWidget(widget->GetWidgetID(), query))
|
||||
if (database.UpdateSpawnWidget(widget->GetWidgetID(), query, GetCurrentZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE))
|
||||
SimpleMessage(CHANNEL_COLOR_YELLOW, "Successfully saved widget open heading information.");
|
||||
}
|
||||
else
|
||||
@ -1508,7 +1518,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
|
||||
|
||||
spawn->position_changed = true;
|
||||
_snprintf(query, 256, "closed_heading=%f,include_heading=1", newHeading);
|
||||
if (database.UpdateSpawnWidget(widget->GetWidgetID(), query))
|
||||
if (database.UpdateSpawnWidget(widget->GetWidgetID(), query, GetCurrentZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE))
|
||||
SimpleMessage(CHANNEL_COLOR_YELLOW, "Successfully saved widget close heading information.");
|
||||
|
||||
if (spawn->GetSpawnLocationID())
|
||||
@ -1921,6 +1931,26 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
|
||||
if (GetPlayer()->IsDeletedSpawn()) {
|
||||
GetPlayer()->SetDeletedSpawn(false);
|
||||
}
|
||||
|
||||
if(firstlogin_transmit) {
|
||||
if (auto info = broker.GetSellerInfo(GetPlayer()->GetCharacterID())) {
|
||||
auto logs = broker.GetSellerLog(GetPlayer()->GetCharacterID());
|
||||
for (auto const & log : logs) {
|
||||
SendHouseSaleLog(log.message,
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
}
|
||||
}
|
||||
firstlogin_transmit = false;
|
||||
}
|
||||
if(HasOwnerOrEditAccess()) { // we are in their own house
|
||||
int64 coin_session = database.GetSellerSession(GetPlayer()->GetCharacterID());
|
||||
if(coin_session) {
|
||||
OpenShopWindow(nullptr, false, 1);
|
||||
}
|
||||
}
|
||||
|
||||
ResetZoningCoords();
|
||||
SetReadyForUpdates();
|
||||
GetPlayer()->SendSpawnChanges(true);
|
||||
@ -2130,9 +2160,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
|
||||
EQ2_CommandString remote(app->pBuffer, app->size);
|
||||
|
||||
LogWrite(PACKET__DEBUG, 1, "Packet", "RemoteCmdMsg Packet dump:");
|
||||
#if EQDEBUG >= 9
|
||||
DumpPacket(app);
|
||||
#endif
|
||||
commands.Process(remote.handler, &remote.command, this);
|
||||
}
|
||||
else //bad client, disconnect
|
||||
@ -2448,10 +2476,12 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
|
||||
int32 spawn_index = 0;
|
||||
|
||||
if (GetVersion() <= 561) {
|
||||
spawn_index = packet->getType_int32_ByName("house_id");
|
||||
house_id = packet->getType_int32_ByName("house_id");
|
||||
spawn_index = GetPlayer()->GetTarget() ? GetPlayer()->GetTarget()->GetID() : 0;
|
||||
}
|
||||
else {
|
||||
house_id = packet->getType_int64_ByName("house_id");
|
||||
spawn_index = packet->getType_int32_ByName("spawn_id");
|
||||
}
|
||||
ZoneChangeDetails zone_details;
|
||||
if (GetHouseZoneServer(&zone_details, spawn_index, house_id)) {
|
||||
@ -2904,8 +2934,8 @@ bool Client::HandleLootItem(Spawn* entity, Item* item, Spawn* target, bool overr
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lootingPlayer->item_list.HasFreeSlot() || lootingPlayer->item_list.CanStack(item)) {
|
||||
if (lootingPlayer->item_list.AssignItemToFreeSlot(item)) {
|
||||
if (lootingPlayer->item_list.HasFreeSlot() || lootingPlayer->item_list.CanStack(item, false)) {
|
||||
if (lootingPlayer->item_list.AssignItemToFreeSlot(item, true)) {
|
||||
|
||||
if (item->CheckFlag2(HEIRLOOM)) { // TODO: RAID Support
|
||||
GroupMemberInfo* gmi = lootingClient->GetPlayer()->GetGroupMemberInfo();
|
||||
@ -3396,6 +3426,16 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
|
||||
item = GetPlayer()->GetAppearanceEquipmentList()->GetItemFromUniqueID(id);
|
||||
if (!item)
|
||||
item = master_item_list.GetItem(id);
|
||||
if (!item && HasOwnerOrEditAccess())
|
||||
item = GetPlayer()->item_list.GetVaultItemFromUniqueID(id, true);
|
||||
if(!item && GetMerchantTransactionID()) {
|
||||
Spawn* merchant = GetPlayer()->GetZone()->GetSpawnByID(GetMerchantTransactionID());
|
||||
if(merchant && merchant->GetHouseCharacterID() && merchant->GetPickupUniqueItemID()) {
|
||||
if(auto itemInfo = broker.GetActiveItem(merchant->GetHouseCharacterID(), id)) {
|
||||
item = master_item_list.GetItem(itemInfo->item_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item) {// && sent_item_details.count(id) == 0){
|
||||
MItemDetails.writelock(__FUNCTION__, __LINE__);
|
||||
sent_item_details[id] = true;
|
||||
@ -3407,7 +3447,7 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
|
||||
delete item;
|
||||
}
|
||||
else {
|
||||
LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest: Unknown Item ID = %u", id);
|
||||
LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest#0: Unknown Item ID = %u", id);
|
||||
DumpPacket(app);
|
||||
}
|
||||
}
|
||||
@ -3430,6 +3470,16 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
|
||||
item = GetPlayer()->GetAppearanceEquipmentList()->GetItemFromUniqueID(unique_id);
|
||||
if (!item)
|
||||
item = master_item_list.GetItem(id);
|
||||
|
||||
if(!item && GetMerchantTransactionID()) {
|
||||
Spawn* merchant = GetPlayer()->GetZone()->GetSpawnByID(GetMerchantTransactionID());
|
||||
if(merchant && merchant->GetHouseCharacterID() && merchant->GetPickupUniqueItemID()) {
|
||||
if(auto itemInfo = broker.GetActiveItem(merchant->GetHouseCharacterID(), id)) {
|
||||
item = master_item_list.GetItem(itemInfo->item_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item) {
|
||||
MItemDetails.writelock(__FUNCTION__, __LINE__);
|
||||
sent_item_details[id] = true;
|
||||
@ -3438,7 +3488,7 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
|
||||
QueuePacket(app);
|
||||
}
|
||||
else {
|
||||
LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest: Unknown Item ID = %u", id);
|
||||
LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest#1: Unknown Item ID = %u", id);
|
||||
DumpPacket(app);
|
||||
}
|
||||
}
|
||||
@ -3462,6 +3512,16 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
|
||||
//int16 unknown5 = request->getType_sint16_ByName("unknown5");
|
||||
//printf("Type: (%i) Unknown_0: (%u) Unknown_1: (%u) Unknown2: (%i) Unique ID: (%u) Unknown5: (%i) Item ID: (%u)\n",type,unknown_0,unknown_1,unknown2,unique_id,unknown5,id);
|
||||
Item* item = master_item_list.GetItem(id);
|
||||
|
||||
if(!item && GetMerchantTransactionID()) {
|
||||
Spawn* merchant = GetPlayer()->GetZone()->GetSpawnByID(GetMerchantTransactionID());
|
||||
if(merchant && merchant->GetHouseCharacterID() && merchant->GetPickupUniqueItemID()) {
|
||||
if(auto itemInfo = broker.GetActiveItem(merchant->GetHouseCharacterID(), id)) {
|
||||
item = master_item_list.GetItem(itemInfo->item_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item) {
|
||||
//only display popup for non merchant links
|
||||
EQ2Packet* app = item->serialize(GetVersion(), (request->getType_int8_ByName("show_popup") != 0), GetPlayer(), true, 0, 0, GetVersion() > 561 ? true : false);
|
||||
@ -3469,7 +3529,7 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) {
|
||||
QueuePacket(app);
|
||||
}
|
||||
else {
|
||||
LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest: Unknown Item ID = %u", id);
|
||||
LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest#2: Unknown Item ID = %u", id);
|
||||
DumpPacket(app);
|
||||
}
|
||||
}
|
||||
@ -3798,7 +3858,7 @@ bool Client::Process(bool zone_process) {
|
||||
if (temp_placement_timer.Check()) {
|
||||
if (GetTempPlacementSpawn() && GetPlayer()->WasSentSpawn(GetTempPlacementSpawn()->GetID()) && !hasSentTempPlacementSpawn) {
|
||||
int8 placement = 0;
|
||||
int32 uniqueID = GetPlacementUniqueItemID();
|
||||
int64 uniqueID = GetPlacementUniqueItemID();
|
||||
Item* uniqueItem = GetPlayer()->item_list.GetItemFromUniqueID(uniqueID);
|
||||
if (uniqueItem && uniqueItem->houseitem_info)
|
||||
placement = uniqueItem->houseitem_info->house_location;
|
||||
@ -8107,7 +8167,9 @@ bool Client::AddItem(Item* item, bool* item_deleted, AddItemType type) {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
OpenShopWindow(nullptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -8301,6 +8363,9 @@ void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) {
|
||||
Guild* guild = GetPlayer()->GetGuild();
|
||||
if (spawn && spawn->GetMerchantID() > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY)) &&
|
||||
spawn->IsClientInMerchantLevelRange(this)) {
|
||||
if((spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO))
|
||||
return;
|
||||
|
||||
int32 total_sell_price = 0;
|
||||
int32 total_status_sell_price = 0; //for status
|
||||
float multiplier = CalculateBuyMultiplier(spawn->GetMerchantID());
|
||||
@ -8316,11 +8381,15 @@ void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) {
|
||||
if (!item)
|
||||
item = player->item_list.GetItemFromID(item_id);
|
||||
if (item && master_item) {
|
||||
if(item->details.inv_slot_id == -3 || item->details.inv_slot_id == -4) {
|
||||
if(item->details.inv_slot_id == InventorySlotType::BANK || item->details.inv_slot_id == InventorySlotType::SHARED_BANK) {
|
||||
SimpleMessage(CHANNEL_COLOR_RED, "You cannot sell an item in the bank.");
|
||||
return;
|
||||
}
|
||||
if (item->details.item_locked || item->details.equip_slot_id)
|
||||
if(GetPlayer()->item_list.IsItemInSlotType(item, InventorySlotType::HOUSE_VAULT)) {
|
||||
SimpleMessage(CHANNEL_COLOR_RED, "You cannot sell an item in the house vault.");
|
||||
return;
|
||||
}
|
||||
if (item->IsItemLocked() || item->details.equip_slot_id)
|
||||
{
|
||||
SimpleMessage(CHANNEL_COLOR_RED, "You cannot sell the item in use.");
|
||||
return;
|
||||
@ -8418,6 +8487,9 @@ void Client::BuyBack(int32 item_id, int16 quantity) {
|
||||
Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
|
||||
if (spawn && spawn->GetMerchantID() > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY_BACK)) &&
|
||||
spawn->IsClientInMerchantLevelRange(this)) {
|
||||
if((spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO))
|
||||
return;
|
||||
|
||||
deque<BuyBackItem*>::iterator itr;
|
||||
BuyBackItem* buyback = 0;
|
||||
BuyBackItem* closest = 0;
|
||||
@ -8446,7 +8518,7 @@ void Client::BuyBack(int32 item_id, int16 quantity) {
|
||||
sint64 dispFlags = 0;
|
||||
if (item && item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "buyback_display_flags", item, player, nullptr, &dispFlags) && (dispFlags & DISPLAY_FLAG_NO_BUY))
|
||||
SimpleMessage(CHANNEL_NARRATIVE, "You do not meet all the requirements to buy this item.");
|
||||
else if (!player->item_list.HasFreeSlot() && !player->item_list.CanStack(item))
|
||||
else if (!player->item_list.HasFreeSlot() && !player->item_list.CanStack(item, false))
|
||||
SimpleMessage(CHANNEL_COLOR_RED, "You do not have any slots available for this item.");
|
||||
else if (player->RemoveCoins(closest->quantity * closest->price)) {
|
||||
bool removed = false;
|
||||
@ -8489,9 +8561,15 @@ void Client::BuyBack(int32 item_id, int16 quantity) {
|
||||
|
||||
void Client::BuyItem(int32 item_id, int16 quantity) {
|
||||
// Get the merchant we are buying from
|
||||
LogWrite(CCLIENT__ERROR, 0, "Client", "buy item %u quantity %u", item_id, quantity);
|
||||
|
||||
Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
|
||||
// Make sure the spawn has a merchant list
|
||||
if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) {
|
||||
if(spawn && spawn->GetHouseCharacterID()) {
|
||||
broker.BuyItem(this, spawn->GetHouseCharacterID(), item_id, quantity);
|
||||
SendMerchantWindow(spawn, false);
|
||||
}
|
||||
else if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) {
|
||||
int32 total_buy_price = 0;
|
||||
float multiplier = CalculateBuyMultiplier(spawn->GetMerchantID());
|
||||
int32 sell_price = 0;
|
||||
@ -8538,7 +8616,7 @@ void Client::BuyItem(int32 item_id, int16 quantity) {
|
||||
total_buy_price = sell_price * quantity;
|
||||
item = new Item(master_item);
|
||||
item->details.count = quantity;
|
||||
if (!player->item_list.HasFreeSlot() && !player->item_list.CanStack(item)) {
|
||||
if (!player->item_list.HasFreeSlot() && !player->item_list.CanStack(item, false)) {
|
||||
SimpleMessage(CHANNEL_COLOR_RED, "You do not have any slots available for this item.");
|
||||
lua_interface->SetLuaUserDataStale(item);
|
||||
safe_delete(item);
|
||||
@ -8683,6 +8761,9 @@ void Client::BuyItem(int32 item_id, int16 quantity) {
|
||||
void Client::RepairItem(int32 item_id) {
|
||||
Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
|
||||
if (spawn && (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR)) {
|
||||
if((spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO))
|
||||
return;
|
||||
|
||||
Item* item = player->item_list.GetItemFromID(item_id);
|
||||
if (!item)
|
||||
item = player->GetEquipmentList()->GetItemFromItemID(item_id);
|
||||
@ -8721,6 +8802,9 @@ void Client::RepairItem(int32 item_id) {
|
||||
void Client::RepairAllItems() {
|
||||
Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
|
||||
if (spawn && (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR)) {
|
||||
if((spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO))
|
||||
return;
|
||||
|
||||
vector<Item*>* repairable_items = GetRepairableItems();
|
||||
if (repairable_items && repairable_items->size() > 0) {
|
||||
vector<Item*>::iterator itr;
|
||||
@ -8869,6 +8953,9 @@ void Client::SendAchievementUpdate(bool first_login) {
|
||||
void Client::SendBuyMerchantList(bool sell) {
|
||||
Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID());
|
||||
if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) {
|
||||
if((spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO))
|
||||
return;
|
||||
|
||||
vector<MerchantItemInfo>* items = world.GetMerchantItemList(spawn->GetMerchantID(), spawn->GetMerchantType(), player);
|
||||
if (items) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
|
||||
@ -9028,8 +9115,8 @@ void Client::SendSellMerchantList(bool sell) {
|
||||
for (test_itr = items->begin(); test_itr != items->end(); test_itr++) {
|
||||
bool isbagwithitems = false;
|
||||
|
||||
if(test_itr->second && (test_itr->second->details.inv_slot_id == -3 || test_itr->second->details.inv_slot_id == -4))
|
||||
continue; // omit bank/shared-bank
|
||||
if(test_itr->second && (test_itr->second->details.inv_slot_id < 0))
|
||||
continue; // omit bank/shared-bank/vault/anything with negative inventory slot id
|
||||
|
||||
if (test_itr->second && test_itr->second->IsBag() && (test_itr->second->details.num_slots - test_itr->second->details.num_free_slots != test_itr->second->details.num_slots))
|
||||
isbagwithitems = true;
|
||||
@ -9479,7 +9566,7 @@ void Client::SendGuildCreateWindow() {
|
||||
}
|
||||
}
|
||||
|
||||
void Client::AddBuyBack(int32 unique_id, int32 item_id, int16 quantity, int32 price, bool save_needed) {
|
||||
void Client::AddBuyBack(int64 unique_id, int32 item_id, int16 quantity, int32 price, bool save_needed) {
|
||||
BuyBackItem* item = new BuyBackItem;
|
||||
item->item_id = item_id;
|
||||
item->unique_id = unique_id;
|
||||
@ -10406,19 +10493,88 @@ void Client::AddSendNewSpells(vector<Spell*>* spells) {
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SetItemSearch(vector<Item*>* items) {
|
||||
void Client::SetItemSearch(vector<Item*>* items, map<string, string> values) {
|
||||
if (items) {
|
||||
safe_delete(search_items);
|
||||
search_items = items;
|
||||
ClearItemSearch();
|
||||
{
|
||||
std::lock_guard<std::mutex> L(item_search_mtx_);
|
||||
search_items = items;
|
||||
search_values = values;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
vector<Item*>* Client::GetSearchItems() {
|
||||
return search_items;
|
||||
void Client::ClearItemSearch() {
|
||||
std::lock_guard<std::mutex> L(item_search_mtx_);
|
||||
if(search_items) {
|
||||
for (auto it = search_items->begin(); it != search_items->end(); /* no increment here */) {
|
||||
Item* item = *it;
|
||||
if (item->is_search_store_item) {
|
||||
safe_delete(item);
|
||||
// erase returns the next iterator
|
||||
it = search_items->erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
safe_delete(search_items);
|
||||
}
|
||||
|
||||
void Client::SendSellerItemByItemUniqueId(int64 unique_id) {
|
||||
std::lock_guard<std::mutex> L(item_search_mtx_);
|
||||
if (!search_items) return;
|
||||
|
||||
for (Item* item : *search_items) {
|
||||
if (item && item->details.unique_id == unique_id) {
|
||||
EQ2Packet* app = item->serialize(GetVersion(), true, GetPlayer());
|
||||
QueuePacket(app);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Client::BuySellerItemByItemUniqueId(int64 unique_id, int16 quantity) {
|
||||
int32 seller_id = 0;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> L(item_search_mtx_);
|
||||
if (!search_items) return;
|
||||
|
||||
for (Item* item : *search_items) {
|
||||
if (item && item->details.unique_id == unique_id) {
|
||||
seller_id = item->seller_char_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(seller_id) {
|
||||
if(broker.BuyItem(this, seller_id, unique_id, quantity)) {
|
||||
vector<Item*>* items = master_item_list.GetItems(search_values, this);
|
||||
if(items){
|
||||
SetItemSearch(items, search_values);
|
||||
SearchStore(search_page);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Message(CHANNEL_COLOR_RED, "Could not find seller %u when attempting to buy unique id %u.", seller_id, unique_id);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SetSellerStatus() {
|
||||
bool sellInv = broker.CanSellFromInventory(GetPlayer()->GetCharacterID());
|
||||
bool itemsSelling = broker.IsSellingItems(GetPlayer()->GetCharacterID());
|
||||
broker.AddSeller(GetPlayer()->GetCharacterID(), std::string(GetPlayer()->GetName()),
|
||||
GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), true, sellInv);
|
||||
}
|
||||
|
||||
void Client::SearchStore(int32 page) {
|
||||
std::lock_guard<std::mutex> L(item_search_mtx_);
|
||||
SetSearchPage(page);
|
||||
if (search_items) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_BrokerItems", GetVersion());
|
||||
if (packet) {
|
||||
@ -10447,27 +10603,49 @@ void Client::SearchStore(int32 page) {
|
||||
std::string teststr("test ");
|
||||
teststr.append(std::to_string(i));
|
||||
packet->setArrayDataByName("string_one", teststr.c_str(), i);
|
||||
packet->setArrayDataByName("string_two", "testtwo", i);
|
||||
packet->setArrayDataByName("seller_name", "EQ2EMuDev", i);
|
||||
packet->setArrayDataByName("item_id", item->details.item_id, i);
|
||||
packet->setArrayDataByName("item_id2", item->details.item_id, i);
|
||||
if(IsGMStoreSearch()) {
|
||||
packet->setArrayDataByName("seller_name", "EQ2EMuDev", i);
|
||||
packet->setArrayDataByName("item_id", item->details.item_id, i);
|
||||
packet->setArrayDataByName("item_id2", item->details.item_id, i);
|
||||
packet->setArrayDataByName("sell_price", item->sell_price, i);
|
||||
if (item->stack_count == 0)
|
||||
packet->setArrayDataByName("quantity", 1, i);
|
||||
else
|
||||
packet->setArrayDataByName("quantity", item->stack_count, i);
|
||||
packet->setArrayDataByName("string_two", "testtwo", i);
|
||||
}
|
||||
else {
|
||||
packet->setArrayDataByName("seller_name", item->seller_name.c_str(), i);
|
||||
packet->setArrayDataByName("item_id", item->details.unique_id, i);
|
||||
packet->setArrayDataByName("item_id2", item->details.unique_id, i);
|
||||
packet->setArrayDataByName("sell_price", item->broker_price, i);
|
||||
packet->setArrayDataByName("quantity", item->details.count, i);
|
||||
if(item->seller_house_id) {
|
||||
HouseZone* hz = world.GetHouseZone(item->seller_house_id);
|
||||
if(hz && item->seller_name.size() > 0) {
|
||||
string name;
|
||||
name = item->seller_name;
|
||||
name.append("'s ");
|
||||
name.append(hz->name);
|
||||
packet->setArrayDataByName("string_two", name.c_str(), i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packet->setArrayDataByName("icon", item->GetIcon(GetVersion()), i);
|
||||
//packet->setArrayDataByName("unknown2b", i, i);
|
||||
packet->setArrayDataByName("item_seller_id", 1, i);
|
||||
if (item->stack_count == 0)
|
||||
packet->setArrayDataByName("quantity", 1, i);
|
||||
else
|
||||
packet->setArrayDataByName("quantity", item->stack_count, i);
|
||||
packet->setArrayDataByName("stack_size", item->stack_count, i);
|
||||
|
||||
packet->setArrayDataByName("sell_price", item->sell_price, i);
|
||||
|
||||
|
||||
std::string tmpStr("");
|
||||
tmpStr.append(item->name.c_str());
|
||||
tmpStr.append(" (");
|
||||
tmpStr.append(std::to_string(item->details.item_id));
|
||||
tmpStr.append(")");
|
||||
if(IsGMStoreSearch()) {
|
||||
tmpStr.append(" (");
|
||||
tmpStr.append(std::to_string(item->details.item_id));
|
||||
tmpStr.append(")");
|
||||
}
|
||||
|
||||
packet->setArrayDataByName("item_name", tmpStr.c_str(), i);
|
||||
packet->setArrayDataByName("req_level", item->generic_info.adventure_default_level, i);
|
||||
@ -12035,6 +12213,9 @@ bool Client::HandleNewLogin(int32 account_id, int32 access_code)
|
||||
new_client_login = NewLoginState::LOGIN_ALLOWED;
|
||||
}
|
||||
|
||||
// vault slots
|
||||
RefreshVaultSlotCount();
|
||||
|
||||
const char* zone_script = world.GetZoneScript(GetCurrentZone()->GetZoneID());
|
||||
if (zone_script && lua_interface)
|
||||
lua_interface->RunZoneScript(zone_script, "new_client", GetCurrentZone(), GetPlayer());
|
||||
@ -12337,6 +12518,126 @@ void Client::SendDefaultCommand(Spawn* spawn, const char* command, float distanc
|
||||
}
|
||||
}
|
||||
|
||||
void Client::RefreshVaultSlotCount() {
|
||||
std::vector<PlayerHouse*> houses = world.GetAllPlayerHouses(GetCharacterID());
|
||||
if (!houses.empty()) {
|
||||
int8 bestSlots = 0;
|
||||
int32 bestZoneID = 0;
|
||||
|
||||
for (PlayerHouse* ph : houses) {
|
||||
if (!ph)
|
||||
continue;
|
||||
HouseZone* hz = world.GetHouseZone(ph->house_id);
|
||||
if (!hz)
|
||||
continue;
|
||||
|
||||
if (hz->vault_slots > bestSlots) {
|
||||
bestSlots = hz->vault_slots;
|
||||
bestZoneID = ph->house_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestZoneID != 0) {
|
||||
GetPlayer()->GetPlayerInfo()->SetHouseZone(bestZoneID);
|
||||
GetPlayer()->SetHouseVaultSlots(bestSlots);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
**
|
||||
October 18, 2005, 07:17:51 PM Rocawne buys Elemental Vestment (Adept I) for 1 Platinum, 0 Gold, 0 Silver, 0 Copper
|
||||
October 19, 2005, 02:57:20 AM Radian buys A Rusting Gear for 5 Gold, 75 Silver, 0 Copper
|
||||
|
||||
but then they stop outputting coin that equals 0
|
||||
|
||||
October 20, 2005, 05:29:33 AM Kilea buys Rules of the Sandscrawler Clan - Page 7 for 9 Gold, 75 Silver
|
||||
October 21, 2005, 12:29:41 AM Meldo buys Oozing Wound (Apprentice IV) for 10 Gold
|
||||
|
||||
February 14, 2006, 02:59:32 PM Tom buys 13 pu-erh tea leafes for 4 Gold, 55 Silver
|
||||
**/
|
||||
void Client::SendHouseSaleLog(std::string message, int64 coin_session, int64 coin_total, int8 flag) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_HouseStoreLog", GetVersion());
|
||||
if (packet) {
|
||||
packet->setDataByName("data", message.c_str());
|
||||
packet->setDataByName("coin_gain_session", coin_session);
|
||||
packet->setDataByName("coin_gain_alltime", coin_total);
|
||||
packet->setDataByName("sales_log_open", flag);
|
||||
EQ2Packet* outapp = packet->serialize();
|
||||
QueuePacket(outapp);
|
||||
safe_delete(packet);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SetItemSaleStatus(int64 unique_id, bool status) {
|
||||
broker.SetSaleStatus(GetPlayer()->GetCharacterID(), unique_id, status);
|
||||
OpenShopWindow(nullptr);
|
||||
}
|
||||
|
||||
void Client::OpenShopWindow(Spawn* interaction, bool sendAlways, int8 saleLogOnly) {
|
||||
if (GetCurrentZone()->GetInstanceType() == PERSONAL_HOUSE_INSTANCE && !HasOwnerOrEditAccess()) {
|
||||
SimpleMessage(CHANNEL_COLOR_RED, "This is not your home!");
|
||||
return;
|
||||
}
|
||||
|
||||
auto info = broker.GetSellerInfo(GetPlayer()->GetCharacterID());
|
||||
bool wasOpen = false;
|
||||
|
||||
if(!saleLogOnly) {
|
||||
wasOpen = GetShopWindowStatus();
|
||||
|
||||
if(sendAlways)
|
||||
SetShopWindowStatus(true);
|
||||
|
||||
if(!GetShopWindowStatus() && !sendAlways)
|
||||
return; // don't send window is not open
|
||||
|
||||
if(!info)
|
||||
broker.AddSeller(GetPlayer()->GetCharacterID(), std::string(GetPlayer()->GetName()), GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), true, false);
|
||||
|
||||
GetPlayer()->item_list.GetVaultItems(this, interaction ? interaction->GetID() : GetPlayer()->GetID(), GetPlayer()->GetHouseVaultSlots(), info ? info->sell_from_inventory : false);
|
||||
|
||||
broker.LockActiveItemsForClient(this);
|
||||
}
|
||||
|
||||
if(!wasOpen && info) {
|
||||
int64 coin_session = database.GetSellerSession(GetPlayer()->GetCharacterID());
|
||||
if(coin_session) {
|
||||
broker.ResetSellerSessionCoins(GetPlayer()->GetCharacterID());
|
||||
std::string msg = FormatCoinReceiveMessage(coin_session, "consigned sales");
|
||||
broker.LogSaleMessage(GetPlayer()->GetCharacterID(), msg);
|
||||
GetPlayer()->AddCoins(coin_session);
|
||||
SimpleMessage(CHANNEL_NARRATIVE, msg.c_str());
|
||||
SendHouseSaleLog(msg, info ? coin_session : 0, info ? info->coin_total : 0, saleLogOnly);
|
||||
}
|
||||
else {
|
||||
SendHouseSaleLog("", 0, info ? info->coin_total : 0, saleLogOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SetItemSaleCost(int64 unique_id, int32 platinum, int32 gold, int32 silver, int32 copper) {
|
||||
int64 cost = platinum * 1000000 + gold * 10000 + silver * 100 + copper;
|
||||
broker.SetSalePrice(GetPlayer()->GetCharacterID(), unique_id, cost);
|
||||
OpenShopWindow(nullptr);
|
||||
}
|
||||
|
||||
void Client::AddItemSale(int64 unique_id, int32 item_id, int64 price, int32 inv_slot_id, int16 slot_id, int16 count, bool inInventory, bool forSale, std::string itemCreator) {
|
||||
SaleItem it{};
|
||||
it.unique_id = unique_id;
|
||||
it.character_id = GetPlayer()->GetCharacterID();
|
||||
it.house_id = GetPlayer()->GetPlayerInfo()->GetHouseZoneID();
|
||||
it.item_id = item_id;
|
||||
it.cost_copper = price;
|
||||
it.for_sale = forSale;
|
||||
it.inv_slot_id = inv_slot_id;
|
||||
it.slot_id = slot_id;
|
||||
it.count = count;
|
||||
it.from_inventory = inInventory;
|
||||
it.creator = itemCreator;
|
||||
broker.AddItem(it);
|
||||
}
|
||||
bool Client::HandleHouseEntityCommands(Spawn* spawn, int32 spawnid, string command)
|
||||
{
|
||||
if (GetCurrentZone()->GetInstanceType() != PERSONAL_HOUSE_INSTANCE)
|
||||
@ -12369,26 +12670,12 @@ bool Client::PopulateHouseSpawn(PacketStruct* place_object)
|
||||
{
|
||||
Spawn* tmp = GetTempPlacementSpawn();
|
||||
|
||||
int32 spawn_group_id = database.GetNextSpawnLocation();
|
||||
int32 spawn_group_id = database.GetNextSpawnLocation(true);
|
||||
tmp->SetSpawnLocationID(spawn_group_id);
|
||||
|
||||
float newHeading = place_object->getType_float_ByName("heading") + 180;
|
||||
|
||||
int32 spawnDBID = 0;
|
||||
if (GetCurrentZone()->house_object_database_lookup.count(tmp->GetModelType()) > 0)
|
||||
{
|
||||
spawnDBID = GetCurrentZone()->house_object_database_lookup.Get(tmp->GetModelType());
|
||||
tmp->SetDatabaseID(spawnDBID);
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnDBID = database.FindHouseInstanceSpawn(tmp);
|
||||
if (spawnDBID)
|
||||
{
|
||||
GetCurrentZone()->house_object_database_lookup.Put(tmp->GetModelType(), spawnDBID);
|
||||
tmp->SetDatabaseID(spawnDBID);
|
||||
}
|
||||
}
|
||||
|
||||
tmp->SetX(place_object->getType_float_ByName("x"));
|
||||
tmp->SetY(place_object->getType_float_ByName("y"));
|
||||
@ -12404,7 +12691,6 @@ bool Client::PopulateHouseSpawn(PacketStruct* place_object)
|
||||
|
||||
if (!spawnDBID)
|
||||
{
|
||||
GetCurrentZone()->house_object_database_lookup.Put(tmp->GetModelType(), tmp->GetDatabaseID());
|
||||
// we need to copy as to not delete the ZoneServer object_list entry this on house item pickup
|
||||
GetCurrentZone()->AddObject(tmp->GetDatabaseID(), ((Object*)tmp)->Copy());
|
||||
}
|
||||
@ -12423,33 +12709,41 @@ bool Client::PopulateHouseSpawnFinalize()
|
||||
GetCurrentZone()->AddSpawn(tmp);
|
||||
GetCurrentZone()->SendSpawnChanges(tmp, this);
|
||||
SetTempPlacementSpawn(nullptr);
|
||||
int32 uniqueID = GetPlacementUniqueItemID();
|
||||
Item* uniqueItem = GetPlayer()->item_list.GetItemFromUniqueID(uniqueID);
|
||||
int64 uniqueID = GetPlacementUniqueItemID();
|
||||
Item* uniqueItem = GetPlayer()->item_list.GetVaultItemFromUniqueID(uniqueID);
|
||||
if(!uniqueItem) {
|
||||
Message(CHANNEL_COLOR_RED, "Missing unique item!");
|
||||
return false;
|
||||
}
|
||||
if(!uniqueItem->TryLockItem(LockReason::LockReason_House)) {
|
||||
Message(CHANNEL_COLOR_RED, "Item could not be locked for house placement!");
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
QueuePacket(GetPlayer()->SendInventoryUpdate(GetVersion()));
|
||||
}
|
||||
|
||||
tmp->SetPickupItemID(uniqueItem->details.item_id);
|
||||
tmp->SetPickupUniqueItemID(uniqueID);
|
||||
|
||||
if (uniqueItem)
|
||||
if (GetCurrentZone()->GetInstanceType() == PERSONAL_HOUSE_INSTANCE)
|
||||
{
|
||||
if (GetCurrentZone()->GetInstanceType() == PERSONAL_HOUSE_INSTANCE)
|
||||
{
|
||||
Query query;
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_instance_data set spawn_id = %u, spawn_location_id = %u, pickup_item_id = %u, pickup_unique_item_id = %u", tmp->GetDatabaseID(), tmp->GetSpawnLocationID(), tmp->GetPickupItemID(), uniqueID);
|
||||
}
|
||||
|
||||
if (uniqueItem->GetItemScript() &&
|
||||
lua_interface->RunItemScript(uniqueItem->GetItemScript(), "placed", uniqueItem, GetPlayer(), tmp))
|
||||
{
|
||||
uniqueItem = GetPlayer()->item_list.GetItemFromUniqueID(uniqueID);
|
||||
}
|
||||
|
||||
if (uniqueItem) {
|
||||
database.DeleteItem(GetCharacterID(), uniqueItem, 0);
|
||||
GetPlayer()->item_list.RemoveItem(uniqueItem, true);
|
||||
QueuePacket(GetPlayer()->SendInventoryUpdate(GetVersion()));
|
||||
}
|
||||
|
||||
SetPlacementUniqueItemID(0);
|
||||
Query query;
|
||||
query.RunQuery2(Q_INSERT, "insert into spawn_instance_data set spawn_id = %u, spawn_location_id = %u, pickup_item_id = %u, pickup_unique_item_id = %u", tmp->GetDatabaseID(), tmp->GetSpawnLocationID(), tmp->GetPickupItemID(), uniqueID);
|
||||
}
|
||||
|
||||
if (uniqueItem->GetItemScript() &&
|
||||
lua_interface->RunItemScript(uniqueItem->GetItemScript(), "placed", uniqueItem, GetPlayer(), tmp))
|
||||
{
|
||||
uniqueItem = GetPlayer()->item_list.GetItemFromUniqueID(uniqueID);
|
||||
}
|
||||
|
||||
if (uniqueItem && uniqueItem->generic_info.item_type != ITEM_TYPE_HOUSE_CONTAINER) {
|
||||
database.DeleteItem(GetCharacterID(), uniqueItem, 0);
|
||||
GetPlayer()->item_list.RemoveItem(uniqueItem, true);
|
||||
QueuePacket(GetPlayer()->SendInventoryUpdate(GetVersion()));
|
||||
}
|
||||
|
||||
SetPlacementUniqueItemID(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -13648,6 +13942,18 @@ bool Client::GetHouseZoneServer(ZoneChangeDetails* zone_details, int32 spawn_id,
|
||||
hz = world.GetHouseZone(ph->house_id);
|
||||
}
|
||||
}
|
||||
if(!ph) {
|
||||
Spawn* houseWidget = GetPlayer()->GetSpawnByIndex(house_id);
|
||||
if (houseWidget && houseWidget->IsWidget() && ((Widget*)houseWidget)->GetHouseID()) {
|
||||
hz = world.GetHouseZone(((Widget*)houseWidget)->GetHouseID());
|
||||
if (hz) {
|
||||
ph = world.GetPlayerHouseByHouseID(GetPlayer()->GetCharacterID(), hz->id);
|
||||
}
|
||||
else {
|
||||
Message(CHANNEL_COLOR_YELLOW, "HouseWidget#2 spawn index %u house zone could not be found.", spawn_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ph && hz) {
|
||||
if (zone_list.GetZoneByInstance(zone_details, ph->instance_id, hz->zone_id)) {
|
||||
@ -13770,4 +14076,144 @@ bool Client::SendDialogChoice(int32 spawnID, const std::string& windowTextPrompt
|
||||
safe_delete(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Client::SendMerchantWindow(Spawn* spawn, bool sell) {
|
||||
if(GetVersion() < 561) {
|
||||
sell = false; // doesn't support in the same way as AoM just open the normal buy/sell window
|
||||
}
|
||||
if(spawn) {
|
||||
SetMerchantTransaction(spawn);
|
||||
if (spawn->GetHouseCharacterID() > 0) {
|
||||
if (auto info = broker.GetSellerInfo(spawn->GetHouseCharacterID())) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
|
||||
if (packet) {
|
||||
packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(spawn));
|
||||
int32 i = 0;
|
||||
int tmp_level = 0;
|
||||
sint8 item_difficulty = 0;
|
||||
auto items = broker.GetActiveForSaleItems(spawn->GetHouseCharacterID());
|
||||
int32 itemCount = 0;
|
||||
for (auto const& itm : items) {
|
||||
if(itm.inv_slot_id != spawn->GetPickupUniqueItemID())
|
||||
continue;
|
||||
if (!itm.for_sale) {
|
||||
continue;
|
||||
}
|
||||
itemCount++;
|
||||
}
|
||||
packet->setArrayLengthByName("num_items", itemCount);
|
||||
|
||||
for (auto const& itm : items) {
|
||||
if(itm.inv_slot_id != spawn->GetPickupUniqueItemID())
|
||||
continue;
|
||||
if (!itm.for_sale) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Item* item = master_item_list.GetItem(itm.item_id);
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
packet->setArrayDataByName("item_name", item->name.c_str(), i);
|
||||
packet->setArrayDataByName("item_id", itm.unique_id, i);
|
||||
//packet->setArrayDataByName("unique_item_id", item->details.item_id, i);
|
||||
packet->setArrayDataByName("stack_size", itm.count, i);
|
||||
packet->setArrayDataByName("icon", item->GetIcon(GetVersion()), i);
|
||||
if (item->generic_info.adventure_default_level > 0)
|
||||
tmp_level = item->generic_info.adventure_default_level;
|
||||
else
|
||||
tmp_level = item->generic_info.tradeskill_default_level;
|
||||
packet->setArrayDataByName("level", tmp_level, i);
|
||||
if (rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, DisplayItemTiers)->GetBool()) {
|
||||
packet->setArrayDataByName("tier", item->details.tier, i);
|
||||
}
|
||||
packet->setArrayDataByName("item_id2", item->details.item_id, i);
|
||||
item_difficulty = player->GetArrowColor(tmp_level);
|
||||
if (item_difficulty != ARROW_COLOR_WHITE && item_difficulty != ARROW_COLOR_RED && item_difficulty != ARROW_COLOR_GRAY)
|
||||
item_difficulty = ARROW_COLOR_WHITE;
|
||||
|
||||
sint64 overrideValue = 0;
|
||||
if (item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "item_difficulty", item, player, nullptr, &overrideValue))
|
||||
item_difficulty = (sint8)overrideValue;
|
||||
|
||||
item_difficulty -= 6;
|
||||
if (item_difficulty < 0)
|
||||
item_difficulty *= -1;
|
||||
|
||||
packet->setArrayDataByName("item_difficulty", item_difficulty, i);
|
||||
packet->setArrayDataByName("quantity", 1, i);
|
||||
packet->setArrayDataByName("unknown5", 255, i);
|
||||
packet->setArrayDataByName("stack_size2", itm.count, i);
|
||||
|
||||
sint64 dispFlags = 0;
|
||||
if (item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "buy_display_flags", item, player, nullptr, &dispFlags))
|
||||
packet->setArrayDataByName("display_flags", (int8)dispFlags, i);
|
||||
|
||||
std::string overrideValueStr;
|
||||
// classic client isn't properly tracking this field, DoF we don't have it identified yet, but no field to cause any issues (can add later if identified)
|
||||
if (GetVersion() >= 546 && item->GetItemScript() && lua_interface && lua_interface->RunItemScriptWithReturnString(item->GetItemScript(), "item_description", item, player, &overrideValueStr))
|
||||
packet->setArrayDataByName("description", overrideValueStr.c_str(), i);
|
||||
|
||||
packet->setArrayDataByName("price", itm.cost_copper, i);
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (GetVersion() < 561) {
|
||||
//buy is 0 so dont need to set it
|
||||
if (sell)
|
||||
packet->setDataByName("type", 1);
|
||||
}
|
||||
else if (GetVersion() == 561) {
|
||||
packet->setDataByName("type", 2);
|
||||
}
|
||||
else {
|
||||
if (sell)
|
||||
packet->setDataByName("type", 130);
|
||||
else
|
||||
packet->setDataByName("type", 2);
|
||||
}
|
||||
EQ2Packet* outapp = packet->serialize();
|
||||
QueuePacket(outapp);
|
||||
safe_delete(packet);
|
||||
if(GetVersion() > 561) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
|
||||
if (packet) {
|
||||
packet->setDataByName("spawn_id", 0xFFFFFFFF);
|
||||
packet->setDataByName("type", 16);
|
||||
EQ2Packet* outapp = packet->serialize();
|
||||
if (outapp)
|
||||
QueuePacket(outapp);
|
||||
safe_delete(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)){
|
||||
SendHailCommand(spawn);
|
||||
//MerchantFactionMultiplier* multiplier = world.GetMerchantMultiplier(spawn->GetMerchantID());
|
||||
//if(!multiplier || (multiplier && GetPlayer()->GetFactions()->GetFactionValue(multiplier->faction_id) >= multiplier->faction_min)){
|
||||
SendBuyMerchantList(sell);
|
||||
if(!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY))
|
||||
SendSellMerchantList(sell);
|
||||
if(!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY_BACK))
|
||||
SendBuyBackList(sell);
|
||||
|
||||
if(GetVersion() > 561) {
|
||||
PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion());
|
||||
if (packet) {
|
||||
packet->setDataByName("spawn_id", 0xFFFFFFFF);
|
||||
packet->setDataByName("type", 16);
|
||||
EQ2Packet* outapp = packet->serialize();
|
||||
if (outapp)
|
||||
QueuePacket(outapp);
|
||||
safe_delete(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR)
|
||||
SendRepairList();
|
||||
}
|
||||
}
|
@ -1,22 +1,23 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
EQ2Emulator 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.
|
||||
EQ2Emulator 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.
|
||||
|
||||
EQ2Emulator 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.
|
||||
EQ2Emulator 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 EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CLIENT_H
|
||||
#define CLIENT_H
|
||||
|
||||
@ -77,7 +78,7 @@ struct QueuedQuest {
|
||||
|
||||
struct BuyBackItem {
|
||||
int32 item_id;
|
||||
int32 unique_id;
|
||||
int64 unique_id;
|
||||
int16 quantity;
|
||||
int32 price;
|
||||
bool save_needed;
|
||||
@ -340,6 +341,7 @@ public:
|
||||
inline int32 GetAccountID() { return account_id; }
|
||||
inline const char* GetAccountName() { return account_name; }
|
||||
inline sint16 GetAdminStatus() { return admin_status; }
|
||||
inline bool IsGMStoreSearch() { return gm_store_search; }
|
||||
inline int16 GetVersion() { return version; }
|
||||
void SetNameCRC(int32 val) { name_crc = val; }
|
||||
int32 GetNameCRC() { return name_crc; }
|
||||
@ -348,8 +350,8 @@ public:
|
||||
void SetVersion(int16 new_version) { version = new_version; }
|
||||
void SetAccountID(int32 in_accountid) { account_id = in_accountid; }
|
||||
void SetCharacterID(int32 in_characterid) { character_id = in_characterid; }
|
||||
void SetAdminStatus(sint16 in_status) { admin_status = in_status; }
|
||||
|
||||
void SetAdminStatus(sint16 in_status) { admin_status = in_status; SetGMStoreSearch(false); }
|
||||
void SetGMStoreSearch(bool setting_val) { gm_store_search = setting_val; }
|
||||
|
||||
void DetermineCharacterUpdates();
|
||||
|
||||
@ -429,7 +431,7 @@ public:
|
||||
void BuyBack(int32 item_id, int16 quantity);
|
||||
void RepairItem(int32 item_id);
|
||||
void RepairAllItems();
|
||||
void AddBuyBack(int32 unique_id, int32 item_id, int16 quantity, int32 price, bool save_needed = true);
|
||||
void AddBuyBack(int64 unique_id, int32 item_id, int16 quantity, int32 price, bool save_needed = true);
|
||||
deque<BuyBackItem*>* GetBuyBacks();
|
||||
vector<Item*>* GetRepairableItems();
|
||||
vector<Item*>* GetItemsByEffectType(ItemEffectType type, ItemEffectType secondary_effect = NO_EFFECT_TYPE);
|
||||
@ -452,9 +454,13 @@ public:
|
||||
void SendNewAdventureSpells();
|
||||
void SendNewTradeskillSpells();
|
||||
string GetCoinMessage(int32 total_coins);
|
||||
void SetItemSearch(vector<Item*>* items);
|
||||
vector<Item*>* GetSearchItems();
|
||||
void SetItemSearch(vector<Item*>* items, map<string, string> values);
|
||||
void ClearItemSearch();
|
||||
|
||||
void SearchStore(int32 page);
|
||||
void SendSellerItemByItemUniqueId(int64 unique_id);
|
||||
void BuySellerItemByItemUniqueId(int64 unique_id, int16 quantity);
|
||||
void SetSellerStatus();
|
||||
void SetPlayer(Player* new_player);
|
||||
|
||||
void AddPendingQuestAcceptReward(Quest* quest);
|
||||
@ -564,12 +570,20 @@ public:
|
||||
|
||||
Spawn* GetTempPlacementSpawn() { return tempPlacementSpawn; }
|
||||
|
||||
void SetPlacementUniqueItemID(int32 id) { placement_unique_item_id = id; }
|
||||
int32 GetPlacementUniqueItemID() { return placement_unique_item_id; }
|
||||
void SetPlacementUniqueItemID(int64 id) { placement_unique_item_id = id; }
|
||||
int64 GetPlacementUniqueItemID() { return placement_unique_item_id; }
|
||||
|
||||
void SetHasOwnerOrEditAccess(bool val) { hasOwnerOrEditAccess = val; }
|
||||
bool HasOwnerOrEditAccess() { return hasOwnerOrEditAccess; }
|
||||
|
||||
void RefreshVaultSlotCount();
|
||||
void SendHouseSaleLog(std::string message, int64 coin_session, int64 coin_total, int8 flag);
|
||||
void SetItemSaleStatus(int64 unique_id, bool status);
|
||||
void OpenShopWindow(Spawn* interaction, bool sendAlways = false, int8 saleLogOnly = 0);
|
||||
void SetShopWindowStatus(bool status) { shop_window_open = status; }
|
||||
bool GetShopWindowStatus() { return shop_window_open; }// false for disabled , true for enabled
|
||||
void SetItemSaleCost(int64 unique_id, int32 platinum, int32 gold, int32 silver, int32 copper);
|
||||
void AddItemSale(int64 unique_id, int32 item_id, int64 price, int32 inv_slot_id, int16 slot_id, int16 count, bool inInventory, bool forSale, std::string itemCreator);
|
||||
|
||||
bool HandleHouseEntityCommands(Spawn* spawn, int32 spawnid, string command);
|
||||
// find an appropriate spawn to use for the house object, save spawn location/entry data to DB
|
||||
bool PopulateHouseSpawn(PacketStruct* place_object);
|
||||
@ -725,6 +739,10 @@ public:
|
||||
|
||||
void SetLastTellName(std::string tellName) { last_tell_name = tellName; }
|
||||
std::string GetLastTellName() { return last_tell_name; }
|
||||
|
||||
void SetSearchPage(int32 page) { search_page = page; }
|
||||
|
||||
void SendMerchantWindow(Spawn* spawn, bool sell);
|
||||
DialogManager dialog_manager;
|
||||
private:
|
||||
void AddRecipeToPlayerPack(Recipe* recipe, PacketStruct* packet, int16* i);
|
||||
@ -745,6 +763,8 @@ private:
|
||||
Mutex MQuestQueue;
|
||||
Mutex MDeletePlayer;
|
||||
vector<Item*>* search_items;
|
||||
map<string, string> search_values;
|
||||
int32 search_page;
|
||||
int32 waypoint_id = 0;
|
||||
map<string, WaypointInfo> waypoints;
|
||||
int32 transport_spawn_id;
|
||||
@ -775,6 +795,7 @@ private:
|
||||
int32 account_id;
|
||||
int32 character_id;
|
||||
sint16 admin_status; // -2 Banned, -1 Suspended, 0 User, etc.
|
||||
bool gm_store_search;
|
||||
char account_name[64];
|
||||
char zone_name[64];
|
||||
int32 zoneID;
|
||||
@ -811,8 +832,9 @@ private:
|
||||
float zoning_y;
|
||||
float zoning_z;
|
||||
float zoning_h;
|
||||
bool firstlogin;
|
||||
|
||||
std::atomic<bool> firstlogin;
|
||||
std::atomic<bool> firstlogin_transmit;
|
||||
|
||||
enum NewLoginState { LOGIN_NONE, LOGIN_DELAYED, LOGIN_ALLOWED, LOGIN_INITIAL_LOAD, LOGIN_SEND };
|
||||
NewLoginState new_client_login; // 1 = delayed state, 2 = let client in
|
||||
Timer underworld_cooldown_timer;
|
||||
@ -857,7 +879,7 @@ private:
|
||||
int32 delayedAccessKey;
|
||||
Timer delayTimer;
|
||||
Spawn* tempPlacementSpawn;
|
||||
int32 placement_unique_item_id;
|
||||
int64 placement_unique_item_id;
|
||||
bool hasOwnerOrEditAccess;
|
||||
bool hasSentTempPlacementSpawn;
|
||||
|
||||
@ -891,6 +913,10 @@ private:
|
||||
int recipe_orig_packet_size;
|
||||
|
||||
std::string last_tell_name;
|
||||
|
||||
mutable std::mutex item_search_mtx_;
|
||||
std::atomic<bool> shop_window_open;
|
||||
map<string, string> str_values;
|
||||
};
|
||||
|
||||
class ClientList {
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../common/debug.h"
|
||||
#include "../common/Log.h"
|
||||
|
||||
@ -101,6 +102,7 @@ LuaInterface* lua_interface = new LuaInterface();
|
||||
#include "Titles.h"
|
||||
#include "Languages.h"
|
||||
#include "Achievements/Achievements.h"
|
||||
#include "./Broker/BrokerManager.h"
|
||||
|
||||
volatile bool RunLoops = true;
|
||||
sint32 numclients = 0;
|
||||
@ -118,7 +120,6 @@ extern Variables variables;
|
||||
extern PeerManager peer_manager;
|
||||
extern HTTPSClientPool peer_https_pool;
|
||||
ConfigReader configReader;
|
||||
int32 MasterItemList::next_unique_id = 0;
|
||||
int last_signal = 0;
|
||||
RuleManager rule_manager;
|
||||
MasterTitlesList master_titles_list;
|
||||
@ -126,7 +127,7 @@ MasterLanguagesList master_languages_list;
|
||||
ChestTrapList chest_trap_list;
|
||||
extern MasterAchievementList master_achievement_list;
|
||||
extern map<int16, int16> EQOpcodeVersions;
|
||||
|
||||
extern BrokerManager broker;
|
||||
|
||||
ThreadReturnType ItemLoad (void* tmp);
|
||||
ThreadReturnType AchievmentLoad (void* tmp);
|
||||
@ -283,7 +284,6 @@ int main(int argc, char** argv) {
|
||||
// JA: Load all Item info
|
||||
LogWrite(ITEM__INFO, 0, "Items", "Loading Items...");
|
||||
database.LoadItemList();
|
||||
MasterItemList::ResetUniqueID(database.LoadNextUniqueItemID());
|
||||
|
||||
LogWrite(SPELL__INFO, 0, "Spells", "Loading Spells...");
|
||||
database.LoadSpells();
|
||||
@ -311,9 +311,6 @@ int main(int argc, char** argv) {
|
||||
database.LoadMerchantInformation();
|
||||
}
|
||||
|
||||
LogWrite(GUILD__INFO, 0, "Guilds", "Loading Guilds...");
|
||||
database.LoadGuilds();
|
||||
|
||||
LogWrite(TRADESKILL__INFO, 0, "Recipes", "Loading Recipe Books...");
|
||||
database.LoadRecipeBooks();
|
||||
LogWrite(TRADESKILL__INFO, 0, "Recipes", "Loading Recipes...");
|
||||
@ -344,7 +341,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
LogWrite(WORLD__INFO, 0, "World", "Loading House Zone Data...");
|
||||
database.LoadHouseZones();
|
||||
database.LoadPlayerHouses();
|
||||
|
||||
LogWrite(WORLD__INFO, 0, "World", "Loading Heroic OP Data...");
|
||||
database.LoadHOStarters();
|
||||
@ -368,10 +364,21 @@ int main(int argc, char** argv) {
|
||||
Sleep(10);
|
||||
LogWrite(WORLD__INFO, 0, "World", "Load threads finished.");
|
||||
}
|
||||
|
||||
LogWrite(GUILD__INFO, 0, "Guilds", "Loading Guilds...");
|
||||
database.LoadGuilds();
|
||||
|
||||
LogWrite(WORLD__INFO, 0, "World", "Loading Player House Data...");
|
||||
database.LoadPlayerHouses();
|
||||
|
||||
LogWrite(WORLD__INFO, 0, "World", "Total World startup time: %u seconds.", Timer::GetUnixTimeStamp() - t_total);
|
||||
int ret_code = 0;
|
||||
if (eqsf.Open(net.GetWorldPort())) {
|
||||
world.world_loaded = true; // need this set ahead so peering starts sending data also
|
||||
|
||||
LogWrite(WORLD__INFO, 0, "World", "Loading Broker Data...");
|
||||
database.LoadBrokerData(broker);
|
||||
|
||||
if (strlen(net.GetWorldAddress()) == 0)
|
||||
LogWrite(NET__INFO, 0, "Net", "World server listening on port %i", net.GetWorldPort());
|
||||
else
|
||||
@ -380,7 +387,6 @@ int main(int argc, char** argv) {
|
||||
if(strlen(net.GetInternalWorldAddress())>0)
|
||||
LogWrite(NET__INFO, 0, "Net", "World server listening on: %s:%i", net.GetInternalWorldAddress(), net.GetWorldPort());
|
||||
|
||||
world.world_loaded = true;
|
||||
world.world_uptime = getCurrentTimestamp();
|
||||
#ifdef WIN32
|
||||
_beginthread(StartPeerPoll, 0, NULL);
|
||||
@ -558,8 +564,8 @@ ThreadReturnType ItemLoad (void* tmp)
|
||||
|
||||
LogWrite(ITEM__INFO, 0, "Items", "Loading Items...");
|
||||
db.LoadItemList();
|
||||
MasterItemList::ResetUniqueID(db.LoadNextUniqueItemID());
|
||||
|
||||
db.ResetNextUniqueItemID();
|
||||
|
||||
// Relies on the item list so needs to be in the item thread
|
||||
LogWrite(COLLECTION__INFO, 0, "Collect", "Loading Collections...");
|
||||
db.LoadCollections();
|
||||
|
@ -31,6 +31,7 @@ using namespace std;
|
||||
#include "Commands/Commands.h"
|
||||
#include "Zone/pathfinder_interface.h"
|
||||
#include "NPC_AI.h"
|
||||
#include "Broker/BrokerManager.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <WinSock2.h>
|
||||
@ -116,7 +117,7 @@ extern Chat chat;
|
||||
extern MasterRaceTypeList race_types_list;
|
||||
extern MasterSpellList master_spell_list; // temp - remove later
|
||||
extern MasterSkillList master_skill_list;
|
||||
|
||||
extern BrokerManager broker;
|
||||
|
||||
int32 MinInstanceID = 1000;
|
||||
|
||||
@ -1641,9 +1642,30 @@ bool ZoneServer::Process()
|
||||
LogWrite(WIDGET__INFO, 0, "Widget", "-Loading Widget data...");
|
||||
database.LoadWidgets(this);
|
||||
LogWrite(WIDGET__INFO, 0, "Widget", "-Load Widget data complete!");
|
||||
|
||||
|
||||
LogWrite(GROUNDSPAWN__INFO, 0, "GSpawn", "-Loading Groundspawn data...");
|
||||
database.LoadGroundSpawns(this);
|
||||
LogWrite(GROUNDSPAWN__INFO, 0, "GSpawn", "-Load Groundspawn data complete!");
|
||||
|
||||
if(GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE) {
|
||||
LogWrite(NPC__INFO, 0, "NPC", "-Loading House NPC data...");
|
||||
database.LoadNPCs(this, true);
|
||||
LogWrite(NPC__INFO, 0, "NPC", "-Load House NPC data complete!");
|
||||
|
||||
LogWrite(OBJECT__INFO, 0, "Object", "-Loading House Object data...");
|
||||
database.LoadObjects(this, true);
|
||||
LogWrite(OBJECT__INFO, 0, "Object", "-Load House Object data complete!");
|
||||
|
||||
LogWrite(SIGN__INFO, 0, "Sign", "-Loading House Sign data...");
|
||||
database.LoadSigns(this, true);
|
||||
LogWrite(SIGN__INFO, 0, "Sign", "-Load House Sign data complete!");
|
||||
|
||||
LogWrite(GROUNDSPAWN__INFO, 0, "GSpawn", "-Loading House Groundspawn data...");
|
||||
database.LoadGroundSpawns(this, true);
|
||||
LogWrite(GROUNDSPAWN__INFO, 0, "GSpawn", "-Load House Groundspawn data complete!");
|
||||
}
|
||||
|
||||
LogWrite(GROUNDSPAWN__INFO, 0, "GSpawn", "-Load Groundspawn entries...");
|
||||
database.LoadGroundSpawnEntries(this);
|
||||
LogWrite(GROUNDSPAWN__INFO, 0, "GSpawn", "-Load Groundspawn data complete!");
|
||||
|
||||
@ -2542,23 +2564,25 @@ Spawn* ZoneServer::ProcessSpawnLocation(SpawnLocation* spawnlocation, map<int32,
|
||||
|
||||
int32 spawnTime = 1;
|
||||
|
||||
if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_NPC)
|
||||
spawnTime = database.CheckSpawnRemoveInfo(instNPCs,spawnlocation->entities[i]->spawn_location_id);
|
||||
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_OBJECT)
|
||||
spawnTime = database.CheckSpawnRemoveInfo(instObjSpawns,spawnlocation->entities[i]->spawn_location_id);
|
||||
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_WIDGET)
|
||||
spawnTime = database.CheckSpawnRemoveInfo(instWidgetSpawns,spawnlocation->entities[i]->spawn_location_id);
|
||||
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_SIGN)
|
||||
spawnTime = database.CheckSpawnRemoveInfo(instSignSpawns,spawnlocation->entities[i]->spawn_location_id);
|
||||
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_GROUNDSPAWN)
|
||||
spawnTime = database.CheckSpawnRemoveInfo(instGroundSpawns,spawnlocation->entities[i]->spawn_location_id);
|
||||
|
||||
if(spawnTime == 0) { // don't respawn
|
||||
return nullptr;
|
||||
}
|
||||
else if(spawnTime > 1) { // if not 1, respawn after time
|
||||
AddRespawn(spawnlocation->entities[i]->spawn_location_id, spawnTime);
|
||||
return nullptr;
|
||||
if(GetInstanceType() != PERSONAL_HOUSE_INSTANCE) {
|
||||
if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_NPC)
|
||||
spawnTime = database.CheckSpawnRemoveInfo(instNPCs,spawnlocation->entities[i]->spawn_location_id);
|
||||
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_OBJECT)
|
||||
spawnTime = database.CheckSpawnRemoveInfo(instObjSpawns,spawnlocation->entities[i]->spawn_location_id);
|
||||
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_WIDGET)
|
||||
spawnTime = database.CheckSpawnRemoveInfo(instWidgetSpawns,spawnlocation->entities[i]->spawn_location_id);
|
||||
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_SIGN)
|
||||
spawnTime = database.CheckSpawnRemoveInfo(instSignSpawns,spawnlocation->entities[i]->spawn_location_id);
|
||||
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_GROUNDSPAWN)
|
||||
spawnTime = database.CheckSpawnRemoveInfo(instGroundSpawns,spawnlocation->entities[i]->spawn_location_id);
|
||||
|
||||
if(spawnTime == 0) { // don't respawn
|
||||
return nullptr;
|
||||
}
|
||||
else if(spawnTime > 1) { // if not 1, respawn after time
|
||||
AddRespawn(spawnlocation->entities[i]->spawn_location_id, spawnTime);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (spawnlocation->conditional > 0) {
|
||||
@ -2642,19 +2666,19 @@ Spawn* ZoneServer::ProcessInstanceSpawnLocation(SpawnLocation* spawnlocation, ma
|
||||
if(spawnlocation->entities[i]->spawn_percentage >= rand_number)
|
||||
{
|
||||
if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_NPC &&
|
||||
(spawnTime = database.CheckSpawnRemoveInfo(instNPCs,spawnlocation->entities[i]->spawn_location_id)) > 0)
|
||||
(GetInstanceType() == PERSONAL_HOUSE_INSTANCE || (spawnTime = database.CheckSpawnRemoveInfo(instNPCs,spawnlocation->entities[i]->spawn_location_id)) > 0))
|
||||
spawn = AddNPCSpawn(spawnlocation, spawnlocation->entities[i]);
|
||||
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_GROUNDSPAWN &&
|
||||
(spawnTime = database.CheckSpawnRemoveInfo(instGroundSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0)
|
||||
(GetInstanceType() == PERSONAL_HOUSE_INSTANCE || (spawnTime = database.CheckSpawnRemoveInfo(instGroundSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0))
|
||||
spawn = AddGroundSpawn(spawnlocation, spawnlocation->entities[i]);
|
||||
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_OBJECT &&
|
||||
(spawnTime = database.CheckSpawnRemoveInfo(instObjSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0)
|
||||
(GetInstanceType() == PERSONAL_HOUSE_INSTANCE || (spawnTime = database.CheckSpawnRemoveInfo(instObjSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0))
|
||||
spawn = AddObjectSpawn(spawnlocation, spawnlocation->entities[i]);
|
||||
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_WIDGET &&
|
||||
(spawnTime = database.CheckSpawnRemoveInfo(instWidgetSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0)
|
||||
(GetInstanceType() == PERSONAL_HOUSE_INSTANCE || (spawnTime = database.CheckSpawnRemoveInfo(instWidgetSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0))
|
||||
spawn = AddWidgetSpawn(spawnlocation, spawnlocation->entities[i]);
|
||||
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_SIGN &&
|
||||
(spawnTime = database.CheckSpawnRemoveInfo(instSignSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0)
|
||||
(GetInstanceType() == PERSONAL_HOUSE_INSTANCE || (spawnTime = database.CheckSpawnRemoveInfo(instSignSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0))
|
||||
spawn = AddSignSpawn(spawnlocation, spawnlocation->entities[i]);
|
||||
|
||||
if(spawn && spawn->IsOmittedByDBFlag())
|
||||
@ -2669,26 +2693,28 @@ Spawn* ZoneServer::ProcessInstanceSpawnLocation(SpawnLocation* spawnlocation, ma
|
||||
database.GetHouseSpawnInstanceData(this, spawn);
|
||||
|
||||
const char* script = 0;
|
||||
|
||||
for(int x=0;x<3;x++)
|
||||
{
|
||||
switch(x)
|
||||
|
||||
if(spawn && !spawn->GetSpawnScript()) {
|
||||
for(int x=0;x<3;x++)
|
||||
{
|
||||
case 0:
|
||||
script = world.GetSpawnEntryScript(spawnlocation->entities[i]->spawn_entry_id);
|
||||
break;
|
||||
case 1:
|
||||
script = world.GetSpawnLocationScript(spawnlocation->entities[i]->spawn_location_id);
|
||||
break;
|
||||
case 2:
|
||||
script = world.GetSpawnScript(spawnlocation->entities[i]->spawn_id);
|
||||
break;
|
||||
}
|
||||
switch(x)
|
||||
{
|
||||
case 0:
|
||||
script = world.GetSpawnEntryScript(spawnlocation->entities[i]->spawn_entry_id);
|
||||
break;
|
||||
case 1:
|
||||
script = world.GetSpawnLocationScript(spawnlocation->entities[i]->spawn_location_id);
|
||||
break;
|
||||
case 2:
|
||||
script = world.GetSpawnScript(spawnlocation->entities[i]->spawn_id);
|
||||
break;
|
||||
}
|
||||
|
||||
if(spawn && script && lua_interface->GetSpawnScript(script) != 0)
|
||||
{
|
||||
spawn->SetSpawnScript(string(script));
|
||||
break;
|
||||
if(script && lua_interface->GetSpawnScript(script) != 0)
|
||||
{
|
||||
spawn->SetSpawnScript(string(script));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3610,7 +3636,10 @@ void ZoneServer::RemoveClient(Client* client)
|
||||
|
||||
bool dismissPets = false;
|
||||
if(client)
|
||||
{
|
||||
{
|
||||
if(client->GetPlayer() && client->GetPlayer()->GetCharacterID())
|
||||
broker.AddSeller(client->GetPlayer()->GetCharacterID(), std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), true, false);
|
||||
|
||||
if (client->GetPlayer())
|
||||
client_list.RemovePlayerFromInvisHistory(client->GetPlayer()->GetID());
|
||||
|
||||
@ -3705,6 +3734,8 @@ void ZoneServer::RemoveClientImmediately(Client* client) {
|
||||
|
||||
if(client)
|
||||
{
|
||||
if(client->GetPlayer() && client->GetPlayer()->GetCharacterID())
|
||||
broker.AddSeller(client->GetPlayer()->GetCharacterID(), std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), true, false);
|
||||
if(client->GetPlayer()) {
|
||||
if((client->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) > 0) {
|
||||
client->GetPlayer()->SetActivityStatus(client->GetPlayer()->GetActivityStatus() - ACTIVITY_STATUS_LINKDEAD);
|
||||
@ -5421,7 +5452,7 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
|
||||
spellProcess->RemoveSpellFromQueue((Player*)dead, true);
|
||||
|
||||
if (dead->IsNPC())
|
||||
((NPC*)dead)->Brain()->ClearHate();
|
||||
((NPC*)dead)->Brain()->ClearHate(!spawnListLocked);
|
||||
|
||||
safe_delete(encounter);
|
||||
|
||||
@ -5729,9 +5760,13 @@ void ZoneServer::SendInterruptPacket(Spawn* interrupted, LuaSpell* spell, bool f
|
||||
packet = configReader.getStruct(fizzle ? "WS_SpellFizzle" : "WS_Interrupt", client->GetVersion());
|
||||
if(packet){
|
||||
packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(interrupted));
|
||||
packet->setArrayLengthByName("num_targets", spell->targets.size());
|
||||
for (int32 i = 0; i < spell->targets.size(); i++)
|
||||
packet->setArrayDataByName("target_id", client->GetPlayer()->GetIDWithPlayerSpawn(client->GetPlayer()->GetZone()->GetSpawnByID(spell->targets[i])), i);
|
||||
std::vector<int32> targets = spell->GetTargets(); // snapshot under lock
|
||||
int i = 0;
|
||||
packet->setArrayLengthByName("num_targets", targets.size());
|
||||
for (int32 id : targets) {
|
||||
packet->setArrayDataByName("target_id", client->GetPlayer()->GetIDWithPlayerSpawn(client->GetPlayer()->GetZone()->GetSpawnByID(id)), i);
|
||||
i++;
|
||||
}
|
||||
packet->setDataByName("spell_id", spell->spell->GetSpellID());
|
||||
outapp = packet->serialize();
|
||||
client->QueuePacket(outapp);
|
||||
@ -5772,15 +5807,18 @@ void ZoneServer::SendCastSpellPacket(LuaSpell* spell, Entity* caster, int32 spel
|
||||
}
|
||||
|
||||
packet->setDataByName("spawn_id", caster_id);
|
||||
packet->setArrayLengthByName("num_targets", spell->targets.size());
|
||||
for (int32 i = 0; i < spell->targets.size(); i++) {
|
||||
int32 target_id = client->GetPlayer()->GetIDWithPlayerSpawn(spell->caster->GetZone()->GetSpawnByID(spell->targets[i]));
|
||||
std::vector<int32> targets = spell->GetTargets(); // snapshot under lock
|
||||
int i = 0;
|
||||
packet->setArrayLengthByName("num_targets", targets.size());
|
||||
for (int32 id : targets) {
|
||||
int32 target_id = client->GetPlayer()->GetIDWithPlayerSpawn(spell->caster->GetZone()->GetSpawnByID(id));
|
||||
if(target_id) {
|
||||
packet->setArrayDataByName("target", target_id, i);
|
||||
}
|
||||
else {
|
||||
packet->setArrayDataByName("target", 0xFFFFFFFF, i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
int32 visual_to_use = spell_visual_override > 0 ? spell_visual_override : spell->spell->GetSpellData()->spell_visual;
|
||||
@ -8989,25 +9027,27 @@ void ZoneServer::SetSpawnScript(SpawnEntry* entry, Spawn* spawn)
|
||||
|
||||
const char* script = 0;
|
||||
|
||||
for (int x = 0; x < 3; x++)
|
||||
{
|
||||
switch (x)
|
||||
if(spawn && !spawn->GetSpawnScript()) {
|
||||
for (int x = 0; x < 3; x++)
|
||||
{
|
||||
case 0:
|
||||
script = world.GetSpawnEntryScript(entry->spawn_entry_id);
|
||||
break;
|
||||
case 1:
|
||||
script = world.GetSpawnLocationScript(entry->spawn_location_id);
|
||||
break;
|
||||
case 2:
|
||||
script = world.GetSpawnScript(entry->spawn_id);
|
||||
break;
|
||||
}
|
||||
switch (x)
|
||||
{
|
||||
case 0:
|
||||
script = world.GetSpawnEntryScript(entry->spawn_entry_id);
|
||||
break;
|
||||
case 1:
|
||||
script = world.GetSpawnLocationScript(entry->spawn_location_id);
|
||||
break;
|
||||
case 2:
|
||||
script = world.GetSpawnScript(entry->spawn_id);
|
||||
break;
|
||||
}
|
||||
|
||||
if (script && lua_interface && lua_interface->GetSpawnScript(script) != 0)
|
||||
{
|
||||
spawn->SetSpawnScript(string(script));
|
||||
break;
|
||||
if (script && lua_interface && lua_interface->GetSpawnScript(script) != 0)
|
||||
{
|
||||
spawn->SetSpawnScript(string(script));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9252,6 +9292,8 @@ void ZoneServer::RemoveClientsFromZone(ZoneServer* zone) {
|
||||
MClientList.readlock(__FUNCTION__, __LINE__);
|
||||
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
||||
Client* client = *itr;
|
||||
if(client->GetPlayer() && client->GetPlayer()->GetCharacterID())
|
||||
broker.AddSeller(client->GetPlayer()->GetCharacterID(), std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), true, false);
|
||||
if(client->GetCurrentZone() == zone) {
|
||||
client->SetCurrentZone(nullptr);
|
||||
}
|
||||
@ -9265,7 +9307,7 @@ void ZoneServer::RemoveClientsFromZone(ZoneServer* zone) {
|
||||
void ZoneServer::SendSubSpawnUpdates(SUBSPAWN_TYPES subtype) {
|
||||
std::map<int32, Spawn*>::iterator subitr;
|
||||
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
||||
for(subitr = subspawn_list[subtype].begin(); subitr != subspawn_list[subtype].end(); subitr++) {
|
||||
for(subitr = subspawn_list[subtype].begin(); subitr != subspawn_list[subtype].end(); subitr++) {
|
||||
subitr->second->changed = true;
|
||||
subitr->second->info_changed = true;
|
||||
AddChangedSpawn(subitr->second);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
/*
|
||||
EQ2Emulator: Everquest II Server Emulator
|
||||
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
||||
Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net)
|
||||
|
||||
This file is part of EQ2Emulator.
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ZONESERVER_H
|
||||
#define ZONESERVER_H
|
||||
|
||||
@ -170,7 +171,7 @@ struct TrackedSpawn {
|
||||
struct HouseItem {
|
||||
int32 spawn_id;
|
||||
int32 item_id;
|
||||
int32 unique_id;
|
||||
int64 unique_id;
|
||||
Item* item;
|
||||
};
|
||||
|
||||
@ -701,8 +702,6 @@ public:
|
||||
Spawn* GetSpawnFromUniqueItemID(int32 unique_id);
|
||||
void SendHouseItems(Client* client);
|
||||
|
||||
MutexMap<int32, int32> house_object_database_lookup; // 1st int32 = model type, 2nd int32 = spawn id
|
||||
|
||||
int32 GetWatchdogTime() { return watchdogTimestamp; }
|
||||
void SetWatchdogTime(int32 time) { watchdogTimestamp = time; }
|
||||
void CancelThreads();
|
||||
|
@ -970,4 +970,4 @@ std::tuple<int64, int64, int64, int64> convertTimestampDuration(int64 total_mill
|
||||
|
||||
// Return the result as a tuple
|
||||
return std::make_tuple(days.count(), hours.count(), minutes.count(), seconds.count());
|
||||
}
|
||||
}
|
@ -27,6 +27,9 @@
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <tuple>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef ERRBUF_SIZE
|
||||
#define ERRBUF_SIZE 1024
|
||||
@ -122,6 +125,44 @@ static bool IsPrivateAddress(uint32_t ip)
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::string FormatCoinReceiveMessage(int64 total_copper, const std::string& reason) {
|
||||
// breakdown into denominations
|
||||
int64 platinum = total_copper / 1'000'000;
|
||||
int64 rem = total_copper % 1'000'000;
|
||||
int64 gold = rem / 10'000;
|
||||
rem %= 10'000;
|
||||
int64 silver = rem / 100;
|
||||
int64 copper = rem % 100;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "You received ";
|
||||
|
||||
bool first = true;
|
||||
if (platinum > 0) {
|
||||
oss << platinum << " platinum";
|
||||
first = false;
|
||||
}
|
||||
if (gold > 0) {
|
||||
if (!first) oss << ", ";
|
||||
oss << gold << " gold";
|
||||
first = false;
|
||||
}
|
||||
if (silver > 0) {
|
||||
if (!first) oss << ", ";
|
||||
oss << silver << " silver";
|
||||
first = false;
|
||||
}
|
||||
// if nothing else or there's copper, show copper
|
||||
if (copper > 0 || first) {
|
||||
if (!first) oss << ", ";
|
||||
oss << copper << " copper";
|
||||
}
|
||||
|
||||
oss << " earned through " << reason;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
|
||||
template<class Type> void AddData(Type input, string* datastring){
|
||||
if(datastring)
|
||||
datastring->append((char*)&input, sizeof(input));
|
||||
|
Loading…
x
Reference in New Issue
Block a user