2024-10-25 00:31:08 -05:00

2528 lines
71 KiB
C++

#include "StdAfx.h"
#ifdef ENABLE_IKASHOP_RENEWAL
#include <fstream>
#include <ranges>
#include <msl/msl.h>
#include "../GameLib/ItemManager.h"
#include "../EterPack/EterPackManager.h"
#include "Packet.h"
#include "PythonNetworkStream.h"
#include "PythonTextTail.h"
#include "PythonPlayer.h"
#include "PythonIkarusShop.h"
#include "PythonCharacterManager.h"
#include "PythonSystem.h"
#ifdef min
#undef min
#undef max
#endif
#include <rapidjson/rapidjson.h>
#include <rapidjson/filereadstream.h>
#include <rapidjson/filewritestream.h>
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
#include <fmt\fmt.h>
#include "JsonHelper.h"
using namespace ikashop::python;
DWORD ikashop::GetItemHash(const TShopPlayerItem& item)
{
// checking type - skipping non equip items
CItemData* table = nullptr;
if(!CItemManager::instance().GetItemDataPointer(item.vnum, &table))
return 0;
switch(table->GetType())
{
case CItemData::ITEM_TYPE_ARMOR:
case CItemData::ITEM_TYPE_WEAPON:
case CItemData::ITEM_TYPE_COSTUME:
case CItemData::ITEM_TYPE_BELT:
break;
default:
return 0;
}
// copying values
auto copy {item};
// resetting non hashable values
copy.expiration = {};
copy.id = {};
copy.owner = {};
copy.pos = {};
copy.window = {};
// returning memory hash
return GetCRC32(reinterpret_cast<const char*>(&copy), sizeof(copy));
}
#ifdef ENABLE_IKASHOP_ENTITIES
namespace ikashop
{
#ifdef EXTEND_IKASHOP_ULTIMATE
ShopInstance::EffectID ShopInstance::_FindEffectCRC(const SignString& name) {
auto it = s_RegisterEffectMap.find(name);
if (it != s_RegisterEffectMap.end())
return it->second;
EffectID crc = 0;
if (!CEffectManager::Instance().RegisterEffect2(name.c_str(), &crc, false)) {
crc = 0;
TraceError("CANNOT REGISTER EFFECT FILE : %s", name.c_str());
}
s_RegisterEffectMap[name] = crc;
return crc;
}
void ShopInstance::LoadShopSkinSettings()
{
static constexpr char skinSettings[] = "ikashop/ultimate/shop_skins.txt";
static bool s_loaded = false;
if(s_loaded)
return;
s_loaded = true;
// getting file data
CMappedFile file{};
const void* data = nullptr;
if(!CEterPackManager::instance().Get(file, skinSettings, &data))
{
TraceError("CANNOT OPEN %s", skinSettings);
return;
}
// getting text file parser
CMemoryTextFileLoader text;
text.Bind(file.Size(), data);
for(int i=0; i < text.GetLineCount(); i++)
{
// skipping empty lines and comments
auto line = msl::trim(text.GetLineString(i));
if(line.empty() || line[0] == '#')
continue;
// checking tokens
auto tokens = msl::string_split(line, "\t");
if(tokens.size() < 3)
{
TraceError("%s: ERROR ON LINE %d", skinSettings, i);
continue;
}
// extracting index & folder
auto& indexstr = tokens[0];
if(!std::all_of(indexstr.begin(), indexstr.end(), [](auto&& ch){ return std::isdigit(ch); }))
{
TraceError("%s: ERROR ON LINE %d", skinSettings, i);
continue;
}
int index = std::stoi(indexstr.c_str());
auto& folder = tokens[1];
auto& msm = tokens[2];
_LoadShopSkin(index, folder, msm);
}
}
void ShopInstance::_LoadShopSkin(int index, const std::string& folder, const std::string& msm)
{
if(s_SkinMap.contains(index))
{
TraceError("SHOP SKIN INDEX %d ALREADY EXISTS!", index);
return;
}
// getting msm
auto msmfile = fmt::format("{}/{}.msm", folder, msm);
CTextFileLoader loader;
if(!loader.Load(msmfile.c_str()))
{
TraceError("SHOP SKIN INDEX %d FAILED TO LOAD MSM [%s]!", index, msmfile.c_str());
return;
}
std::string model;
if(!loader.GetTokenString("basemodelfilename", &model))
{
TraceError("SHOP SKIN INDEX %d FAILED TO LOAD MSM [%s] CANNOT FIND basemodelfilename!", index, msmfile.c_str());
return;
}
// registering skin info
auto& skin = s_SkinMap[index];
skin.folder = folder;
skin.model = model;
// extracting effects
DWORD datacount = 0;
if (loader.SetChildNode("attachingdata") && loader.GetTokenDoubleWord("attachingdatacount", &datacount))
{
for (DWORD i = 0; i < datacount; ++i, loader.SetParentNode())
{
if (!loader.SetChildNode("attachingdata", i))
continue;
DWORD type = 0;
if (!loader.GetTokenDoubleWord("attachingdatatype", &type))
continue;
if(type != NRaceData::ATTACHING_DATA_TYPE_EFFECT)
continue;
std::string script;
if(!loader.GetTokenString("effectscriptname", &script))
continue;
std::string bone{};
loader.GetTokenString("attachingbonename", &bone);
D3DXVECTOR3 pos{};
loader.GetTokenVector3("effectposition", &pos);
D3DXVECTOR3 rot{};
loader.GetTokenVector3("effectrotation", &rot);
auto& effect = skin.effects.emplace_back();
effect.script = script;
effect.bone = bone;
effect.position = pos;
effect.rotation = rot;
}
}
}
#endif
ShopInstance::ShopInstance(SkinType Skin) {
m_VirtualID = 0;
m_Skin = Skin;
#ifdef EXTEND_IKASHOP_ULTIMATE
LoadShopSkinSettings();
#endif
}
void ShopInstance::SetName(const SignString& str) {
static CGraphicText* font = nullptr;
if (!font)
font = static_cast<CGraphicText*>(DefaultFont_GetResource());
m_Text.SetValue(str.c_str());
m_Text.SetTextPointer(font);
m_Text.SetOutline(true);
m_Text.SetHorizonalAlign(CGraphicTextInstance::EHorizontalAlign::HORIZONTAL_ALIGN_CENTER);
m_Text.SetColor(0xFF7AE75D);
m_Text.Update();
}
const ShopInstance::SignString& ShopInstance::GetName() {
return m_Text.GetValueStringReference();
}
void ShopInstance::Show(float x, float y, float z) {
std::string model = "ikashop/lite/shop.gr2";
#ifdef EXTEND_IKASHOP_ULTIMATE
std::string animation{};
auto iter = s_SkinMap.find(m_Skin);
if (iter != s_SkinMap.end()) {
model = iter->second.model;
animation = fmt::format("{}/wait.gr2", iter->second.folder);
}
else if(m_Skin == 999)
{
model = "ikashop/ultimate/auction_deco/auction.gr2";
}
#endif
m_GraphicThing.Clear();
m_GraphicThing.ReserveModelThing(1);
m_GraphicThing.ReserveModelInstance(1);
m_GraphicThing.RegisterModelThing(
0, (CGraphicThing*)CResourceManager::Instance().GetResourcePointer(model.c_str()));
m_GraphicThing.SetModelInstance(0, 0, 0);
#ifdef EXTEND_IKASHOP_ULTIMATE
if (!animation.empty()) {
m_GraphicThing.RegisterMotionThing(
0, (CGraphicThing*)CResourceManager::Instance().GetResourcePointer(animation.c_str()));
m_GraphicThing.SetMotion(0);
}
#endif
m_GraphicThing.SetPosition(x, -y, z);
m_GraphicThing.Show();
m_GraphicThing.Update();
m_GraphicThing.Transform();
m_GraphicThing.Deform();
m_TextHeight = m_GraphicThing.GetHeight() + 10;
if (!IsSkin())
m_TextHeight -= 50;
#ifdef EXTEND_IKASHOP_ULTIMATE
auto _AttachEffect = [&](const std::string& script, const std::string& bone, const auto& position, const auto& rotation){
auto id = _FindEffectCRC(script);
// searching bone index
BoneIndex boneIndex = -1;
if (!bone.empty())
if (!m_GraphicThing.FindBoneIndex(0, bone.c_str(), &boneIndex))
boneIndex = -1;
// creating effect and registering bone and id
EffectID effectId = CEffectManager::Instance().GetEmptyIndex();
CEffectManager::Instance().CreateEffectInstance(effectId, id);
auto& attachedEffect = m_AttachedEffect.emplace_back();
attachedEffect.id = effectId;
attachedEffect.bone = boneIndex;
attachedEffect.position = position;
attachedEffect.rotation = rotation;
};
// finding effect file & bone name
if (iter != s_SkinMap.end())
{
for(auto& effect : iter->second.effects)
_AttachEffect(effect.script, effect.bone, effect.position, effect.rotation);
}
else if(m_Skin == 999)
{
static const D3DXVECTOR3 position{0.0f, 0.0f, -120.0f};
static const D3DXVECTOR3 rotation{};
_AttachEffect("d:/ymir work/effect/npc2/p_shop_05.mse", "", position, rotation);
}
#endif
}
void ShopInstance::Clear() {
m_GraphicThing.Clear();
m_VirtualID = 0;
m_Skin = 0;
#ifdef EXTEND_IKASHOP_ULTIMATE
for (auto& attach : m_AttachedEffect)
CEffectManager::Instance().DestroyEffectInstance(attach.id);
m_AttachedEffect.clear();
#endif
}
void ShopInstance::Render() {
m_GraphicThing.Render();
}
void ShopInstance::RenderText() {
if (m_GraphicThing.isShow())
m_Text.Render();
}
void ShopInstance::BlendRender() {
m_GraphicThing.BlendRender();
}
void ShopInstance::Update() {
m_GraphicThing.Deform();
m_GraphicThing.Update();
#ifdef EXTEND_IKASHOP_ULTIMATE
// updating effects
auto& effectManager = CEffectManager::Instance();
auto& wolrdMatrix = m_GraphicThing.GetWorldMatrix();
for (auto& attachedEffect : m_AttachedEffect)
{
effectManager.SelectEffectInstance(attachedEffect.id);
static D3DXMATRIX resultMatrix{};
D3DXMatrixIdentity(&resultMatrix);
// moving to bone matrix
if (attachedEffect.bone != -1)
{
D3DXMATRIX* boneMatrixPtr;
if (m_GraphicThing.GetBoneMatrix(0, attachedEffect.bone, &boneMatrixPtr))
resultMatrix *= *boneMatrixPtr;
}
// moving to worldMatrix
resultMatrix *= wolrdMatrix;
// moving to attach position
resultMatrix._41 += attachedEffect.position.x;
resultMatrix._42 += attachedEffect.position.y;
resultMatrix._43 += attachedEffect.position.z;
// rotating to attach rotation
auto& rotation = attachedEffect.rotation;
if (rotation.x != 0.0f || rotation.y != 0.0f || rotation.z != 0.0f)
{
static D3DXMATRIX rotationMatrix{};
D3DXMatrixIdentity(&rotationMatrix);
D3DXMatrixRotationYawPitchRoll(&rotationMatrix, rotation.x, rotation.y, rotation.z);
resultMatrix *= rotationMatrix;
}
effectManager.SetEffectInstanceGlobalMatrix(resultMatrix);
}
#endif
// update and render text tail
CPythonGraphic& rpyGraphic = CPythonGraphic::Instance();
rpyGraphic.Identity();
float x = 0, y = 0, z = 0;
const D3DXVECTOR3& c_rv3Position = m_GraphicThing.GetPosition();
rpyGraphic.ProjectPosition(c_rv3Position.x, c_rv3Position.y, c_rv3Position.z + m_TextHeight, &x, &y, &z);
x = floorf(x);
y = floorf(y);
z *= CPythonGraphic::Instance().GetOrthoDepth() * -1.0f;
z += 15.0f;
m_Text.SetPosition(x, y, z);
}
bool ShopInstance::IsPickable() {
return m_GraphicThing.isShow();
}
bool ShopInstance::IsPicked() {
float u, v, t;
return m_GraphicThing.Intersect(&u, &v, &t);
}
#ifdef EXTEND_IKASHOP_ULTIMATE
void ShopInstance::HideAllEffects()
{
if (m_shownEffects)
{
m_shownEffects = false;
for (auto& effect : m_AttachedEffect)
{
CEffectManager::instance().SelectEffectInstance(effect.id);
CEffectManager::instance().ApplyAlwaysHidden();
}
}
}
void ShopInstance::ShowAllEffects()
{
if (!m_shownEffects)
{
m_shownEffects = true;
for (auto& effect : m_AttachedEffect)
{
CEffectManager::instance().SelectEffectInstance(effect.id);
CEffectManager::instance().ReleaseAlwaysHidden();
}
}
}
#endif
}
#endif
CPythonIkarusShop::CPythonIkarusShop()
{
m_businessBoard = nullptr;
#ifdef ENABLE_IKASHOP_ENTITIES
m_bIsShowName = false;
#endif
// checking folder
DWORD attr = ::GetFileAttributesA("shop");
if (attr == INVALID_FILE_ATTRIBUTES || attr != FILE_ATTRIBUTE_DIRECTORY)
::CreateDirectoryA("shop", 0);
LoadFilterHistory();
#ifdef EXTEND_IKASHOP_PRO
LoadPriceHistory();
#endif
}
void CPythonIkarusShop::SetSearchShopBoard(PyObject* poWindow)
{
m_searchShopBoard = poWindow;
}
//starts
void CPythonIkarusShop::SetBusinessBoard(PyObject* poWindow)
{
m_businessBoard = poWindow;
}
PyObject* CPythonIkarusShop::GetBusinessBoard()
{
return m_businessBoard;
}
#ifdef EXTEND_IKASHOP_ULTIMATE
void CPythonIkarusShop::SetShopSkinBoard(PyObject* poWindow)
{
m_shopSkinBoard = poWindow;
}
PyObject* CPythonIkarusShop::GetShopSkinBoard()
{
return m_shopSkinBoard;
}
#endif
bool CPythonIkarusShop::CallOnBusinessBoard(const std::string& func, PyObject* args)
{
if(args == nullptr)
args = PyTuple_New(0);
bool ret = false;
if(m_businessBoard != nullptr)
PyCallClassMemberFunc(m_businessBoard, func.c_str(), args, &ret);
return ret;
}
bool CPythonIkarusShop::CallOnSearchShopBoard(const std::string& func, PyObject* args)
{
if (args == nullptr)
args = PyTuple_New(0);
bool ret = false;
if (m_searchShopBoard != nullptr)
PyCallClassMemberFunc(m_searchShopBoard, func.c_str(), args, &ret);
return ret;
}
#ifdef EXTEND_IKASHOP_ULTIMATE
void CPythonIkarusShop::OpenShop(const ikashop::TShopInfo& shop, const std::vector<ikashop::TShopItemView>& vec)
#else
void CPythonIkarusShop::OpenShop(const ikashop::TShopInfo& shop, const std::vector<ikashop::TShopItem>& vec)
#endif
{
// making shop dict
PythonSafeDict shopDict;
shopDict.set("id", shop.ownerid);
shopDict.set("duration", shop.duration);
shopDict.set("name", shop.name);
#ifdef EXTEND_IKASHOP_ULTIMATE
shopDict.set("lock", shop.lock_index);
shopDict.set("items", ExtractPythonShopItemViewList(vec));
#else
shopDict.set("items", ExtractPythonShopItemList(vec));
#endif
CallOnSearchShopBoard("OpenShopGuest", Py_BuildValue("(O)", shopDict.weakRef()));
}
#ifdef EXTEND_IKASHOP_ULTIMATE
void CPythonIkarusShop::OpenShopOwner(const ikashop::TShopInfo& shop, const std::vector<ikashop::TShopItemView>& vec)
#else
void CPythonIkarusShop::OpenShopOwner(const ikashop::TShopInfo& shop, const std::vector<ikashop::TShopItem>& vec)
#endif
{
// making shop dict
PythonSafeDict shopDict;
shopDict.set("duration", shop.duration);
#ifdef EXTEND_IKASHOP_ULTIMATE
shopDict.set("lock", shop.lock_index);
shopDict.set("items", ExtractPythonShopItemViewList(vec));
shopDict.set("decoration", shop.decoration);
shopDict.set("decoration_time", shop.decoration_time);
#else
shopDict.set("items", ExtractPythonShopItemList(vec));
#endif
CallOnBusinessBoard("OpenShopOwner", Py_BuildValue("(O)", shopDict.weakRef()));
}
void CPythonIkarusShop::OpenShopOwnerNoShop()
{
CallOnBusinessBoard("OpenShopOwnerEmpty" );
}
void CPythonIkarusShop::ShopClose()
{
CallOnBusinessBoard("ShopClose" );
}
void CPythonIkarusShop::ShopExpiredGuesting(DWORD ownerid)
{
CallOnSearchShopBoard("ShopExpiredGuesting", Py_BuildValue("(i)", ownerid));
}
void CPythonIkarusShop::ShopRemoveItem(bool owner, DWORD itemid)
{
if(owner)
CallOnBusinessBoard("ShopOwnerRemoveItem", Py_BuildValue("(i)", itemid));
else
CallOnSearchShopBoard("ShopGuestRemoveItem", Py_BuildValue("(i)", itemid));
}
void CPythonIkarusShop::ShopGuestEditItem(DWORD itemid, const ikashop::TPriceInfo& price)
{
ikashop::python::PythonSafeDict dict;
dict.set("price", price.yang);
#ifdef ENABLE_CHEQUE_SYSTEM
dict.set("cheque", price.cheque);
#endif
CallOnSearchShopBoard("ShopGuestEditItem", Py_BuildValue("(iO)", itemid, dict.weakRef()));
}
void CPythonIkarusShop::ShopOwnerEditItem(DWORD itemid, const ikashop::TPriceInfo& price)
{
ikashop::python::PythonSafeDict dict;
dict.set("price", price.yang);
#ifdef ENABLE_CHEQUE_SYSTEM
dict.set("cheque", price.cheque);
#endif
CallOnBusinessBoard("ShopOwnerEditItem", Py_BuildValue("(iO)", itemid, dict.weakRef()));
}
void CPythonIkarusShop::ShopFilterResult(const std::vector<ikashop::TSearchResultInfo>& vec)
{
static auto extractOne = [](const ikashop::TSearchResultInfo& data){
#ifdef EXTEND_IKASHOP_ULTIMATE
auto dict = ExtractPythonDictShopItemView(data);
#else
auto dict = ExtractPythonDictShopItem(data);
#endif
dict.set("is_auction", data.auction);
dict.set("seller_name", data.seller_name);
dict.set("duration", data.duration);
return dict;
};
auto itemList = ExtractPythonListFromContainer(vec, extractOne);
CallOnSearchShopBoard("SetSearchResultItems", Py_BuildValue("(O)", itemList.weakRef()));
}
void CPythonIkarusShop::OfferListReceived(const std::vector<ikashop::TOfferListElement>& offers)
{
static auto extractOffer = [](auto&& offer){
auto item = ExtractPythonDictShopItem(offer.item);
#ifdef EXTEND_IKASHOP_ULTIMATE
item.set("priceavg", offer.priceavg);
#endif
PythonSafeDict offerDict;
offerDict.set("name", offer.shopname);
offerDict.set("id", offer.offerid);
offerDict.set("buyer_name", offer.buyername);
offerDict.set("ownerid", offer.ownerid);
offerDict.set("itemid", offer.itemid);
offerDict.set("price", offer.price.yang);
offerDict.set("notified", offer.notified);
offerDict.set("accepted", offer.accepted);
offerDict.set("item", item);
offerDict.set("incoming", offer.incoming);
offerDict.set("datetime", offer.datetime);
return offerDict;
};
auto offerList = ExtractPythonListFromContainer(offers, extractOffer);
CallOnBusinessBoard("SetOffers", Py_BuildValue("(O)", offerList.weakRef()));
}
void CPythonIkarusShop::SafeboxRefresh(const ikashop::TValutesInfo& valute, const std::vector<ikashop::TShopPlayerItem>& items)
{
auto itemList = ExtractPythonPlayerItemList(items);
CallOnBusinessBoard("SetupSafebox", Py_BuildValue("(KO)", valute.yang, itemList.weakRef()));
}
void CPythonIkarusShop::SafeboxRemoveItem(DWORD itemid)
{
CallOnBusinessBoard("SafeboxRemoveItem", Py_BuildValue("(i)", itemid));
}
void CPythonIkarusShop::SafeboxAddItem(const ikashop::TShopPlayerItem& item)
{
auto itemDict = ikashop::python::ExtractPythonDictPlayerItem(item);
CallOnBusinessBoard("SafeboxAddItem", Py_BuildValue("(O)", itemDict.weakRef()));
}
void CPythonIkarusShop::AuctionList(const std::vector<ikashop::TAuctionListElement>& auctions)
{
static auto extractAuctionListElement = [](auto&& element){
auto auctionDict = ExtractPythonDictShopItem(element.auction);
#ifdef EXTEND_IKASHOP_ULTIMATE
auctionDict.set("priceavg", element.priceavg);
#endif
auctionDict.set("seller_name", element.auction.ownername);
auctionDict.set("duration", element.auction.duration);
auctionDict.set("price", element.actual_best.yang);
auctionDict.set("is_auction", true);
return auctionDict;
};
auto auctionList = ExtractPythonListFromContainer(auctions, extractAuctionListElement);
CallOnSearchShopBoard("SetAuctionList", Py_BuildValue("(O)", auctionList.weakRef()));
}
#ifdef EXTEND_IKASHOP_ULTIMATE
void CPythonIkarusShop::MyAuctionOpen(const ikashop::TAuctionInfo& auction, const std::vector<ikashop::TAuctionOfferInfo>& offers, long long priceavg)
#else
void CPythonIkarusShop::MyAuctionOpen(const ikashop::TAuctionInfo& auction, const std::vector<ikashop::TAuctionOfferInfo>& offers)
#endif
{
static auto extractAutionOffer = [](auto&& offer){
PythonSafeDict offerDict;
offerDict.set("buyerid", offer.buyerid);
offerDict.set("buyername", offer.buyername);
offerDict.set("offerid", offer.ownerid);
offerDict.set("price", offer.price.yang);
offerDict.set("datetime", offer.datetime);
return offerDict;
};
auto auctionDict = ExtractPythonDictShopItem(auction);
auctionDict.set("duration", auction.duration);
auctionDict.set("offers", ExtractPythonListFromContainer(offers, extractAutionOffer));
#ifdef EXTEND_IKASHOP_ULTIMATE
auctionDict.set("priceavg", priceavg);
#endif
CallOnBusinessBoard("SetupAcutionOwner", Py_BuildValue("(O)", auctionDict.weakRef()));
}
#ifdef EXTEND_IKASHOP_ULTIMATE
void CPythonIkarusShop::AuctionOpen(const ikashop::TAuctionInfo& auction, const std::vector<ikashop::TAuctionOfferInfo>& offers, long long priceavg)
#else
void CPythonIkarusShop::AuctionOpen(const ikashop::TAuctionInfo& auction, const std::vector<ikashop::TAuctionOfferInfo>& offers)
#endif
{
static auto extractAutionOffer = [](auto&& offer) {
PythonSafeDict offerDict;
offerDict.set("buyerid", offer.buyerid);
offerDict.set("buyername", offer.buyername);
offerDict.set("offerid", offer.ownerid);
offerDict.set("price", offer.price.yang);
offerDict.set("datetime", offer.datetime);
return offerDict;
};
auto auctionDict = ExtractPythonDictShopItem(auction);
auctionDict.set("ownername", auction.ownername);
auctionDict.set("duration", auction.duration);
auctionDict.set("offers", ExtractPythonListFromContainer(offers, extractAutionOffer));
#ifdef EXTEND_IKASHOP_ULTIMATE
auctionDict.set("priceavg", priceavg);
#endif
CallOnSearchShopBoard("OpenAuctionGuest", Py_BuildValue("(O)", auctionDict.weakRef()));
}
void CPythonIkarusShop::OpenMyAuctionNoAuction()
{
CallOnBusinessBoard("OpenCreateAuctionQuestion");
}
//filter caching methods
void CPythonIkarusShop::LoadFilterHistory()
{
// opening stream
std::ifstream stream("shop/search_history.json", std::ios::in | std::ios::ate | std::ios::binary);
if(stream.is_open() == false)
return;
// reading size
auto fsize = stream.tellg();
// resetting reading point
stream.seekg(0);
// making string receiving file content
std::string jsonString;
jsonString.resize(fsize);
// reading file content inside string
stream.read(jsonString.data(), fsize);
stream.close();
// making document
rapidjson::Document jsonDoc;
jsonDoc.Parse(jsonString.c_str());
// checking for parser errors
if(jsonDoc.HasParseError()){
TraceError("PARSER ERROR PARSING SEARCH HISTORY FILE: %d", (int) jsonDoc.GetParseError());
return;
}
// checking list object
if(jsonDoc.IsArray() == false){
TraceError("INVALID SEARCH HISTORY FILE: document.IsArray() == false");
return;
}
// reserving dimension
m_filterHistory.clear();
m_filterHistory.reserve(jsonDoc.Size());
// iterating over array
for(auto& member : jsonDoc.GetArray())
{
auto& history = m_filterHistory.emplace_back();
// decoding info
history.count = jsonHelper::getValue<int>(member, "count");
// decoding datetime
history.datetime = jsonHelper::getValue<int64_t>(member, "datetime");
// decoding filter name
auto name = jsonHelper::getValue<std::string>(member, "item_name");
snprintf(history.name, sizeof(history.name), "%s", name.c_str());
// decoding filter
history.type = jsonHelper::getValue<int>(member, "type");
history.subtype = jsonHelper::getValue<int>(member, "subtype");
history.levelstart = jsonHelper::getValue<int>(member, "level_start");
history.levelend = jsonHelper::getValue<int>(member, "level_end");
history.pricestart.yang = jsonHelper::getValue<int64_t>(member, "price_start");
history.pricend.yang = jsonHelper::getValue<int64_t>(member, "price_end");
history.sashGrade = jsonHelper::getValue<int>(member, "sash");
history.alchemyGrade = jsonHelper::getValue<int>(member, "alchemy");
for(int i=0; i < ITEM_ATTRIBUTE_SLOT_NORM_NUM; i++){
auto& attr = history.attrs[i];
attr.bType = jsonHelper::getValue<int>(member, fmt::format("attrtype{}", i));
attr.sValue = jsonHelper::getValue<int>(member, fmt::format("attrvalue{}", i));
}
}
}
void CPythonIkarusShop::SaveFilterHistory()
{
// creating document
rapidjson::Document doc;
doc.SetArray();
// reserving array size
doc.GetArray().Reserve(m_filterHistory.size(), doc.GetAllocator());
// populating array
for(auto& history : m_filterHistory)
{
rapidjson::Value member;
member.SetObject();
// encoding info
jsonHelper::insertValue(member, "count", static_cast<int>(history.count), doc.GetAllocator());
// encoding datetime
jsonHelper::insertValue(member, "datetime", static_cast<int64_t>(history.datetime), doc.GetAllocator());
// encoding filter
jsonHelper::insertValue(member, "item_name", std::string(history.name), doc.GetAllocator());
jsonHelper::insertValue(member, "type", static_cast<int>(history.type), doc.GetAllocator());
jsonHelper::insertValue(member, "subtype", static_cast<int>(history.subtype), doc.GetAllocator());
jsonHelper::insertValue(member, "level_start", static_cast<int>(history.levelstart), doc.GetAllocator());
jsonHelper::insertValue(member, "level_end", static_cast<int>(history.levelend), doc.GetAllocator());
jsonHelper::insertValue(member, "price_start", static_cast<int64_t>(history.pricestart.yang), doc.GetAllocator());
jsonHelper::insertValue(member, "price_end", static_cast<int64_t>(history.pricend.yang), doc.GetAllocator());
jsonHelper::insertValue(member, "alchemy", static_cast<int>(history.alchemyGrade), doc.GetAllocator());
jsonHelper::insertValue(member, "sash", static_cast<int>(history.sashGrade), doc.GetAllocator());
for (int i =0; i < ITEM_ATTRIBUTE_SLOT_NORM_NUM; i++){
auto& attr = history.attrs[i];
jsonHelper::insertValue(member, fmt::format("attrtype{}", i), static_cast<int>(attr.bType), doc.GetAllocator());
jsonHelper::insertValue(member, fmt::format("attrvalue{}", i), static_cast<int>(attr.sValue), doc.GetAllocator());
}
// pushing element inside the array
doc.PushBack(member, doc.GetAllocator());
}
// making json string
rapidjson::StringBuffer buffer;
buffer.Clear();
rapidjson::Writer<rapidjson::StringBuffer> StringWriter(buffer);
doc.Accept(StringWriter);
// opening stream
std::ofstream stream("shop/search_history.json", std::ios::out | std::ios::binary);
if (!stream.is_open()){
TraceError("CANNOT SAVE SEARCH HISTORY FILE!");
return;
}
// completing stream data
stream.write(buffer.GetString(), buffer.GetSize());
stream.close();
}
void CPythonIkarusShop::AddNewFilterToHistory(TFilterHistoryElement& element)
{
m_filterHistory.emplace_back(element);
while (m_filterHistory.size() > ikashop::OFFLINESHOP_MAX_FILTER_HISTORY_SIZE)
PopOldestFilterHistoryElement();
SaveFilterHistory();
}
void CPythonIkarusShop::PopOldestFilterHistoryElement()
{
if(!m_filterHistory.empty())
m_filterHistory.erase(std::min_element(m_filterHistory.begin(), m_filterHistory.end()));
}
#ifdef EXTEND_IKASHOP_PRO
void CPythonIkarusShop::LoadPriceHistory()
{
static auto loadFileIntoMap = [](const std::string& filename, auto& map){
// opening stream
std::ifstream stream(filename, std::ios::in | std::ios::ate | std::ios::binary);
if (stream.is_open() == false)
return;
// reading size
auto fsize = stream.tellg();
// resetting reading point
stream.seekg(0);
// making string receiving file content
std::string jsonString;
jsonString.resize(fsize);
// reading file content inside string
stream.read(jsonString.data(), fsize);
stream.close();
// making document
rapidjson::Document jsonDoc;
jsonDoc.Parse(jsonString.c_str());
// checking for parser errors
if (jsonDoc.HasParseError()) {
TraceError("PARSER ERROR PARSING PRICE HISTORY FILE: %d", (int)jsonDoc.GetParseError());
return;
}
// checking list object
if (jsonDoc.IsArray() == false) {
TraceError("INVALID PRICE HISTORY FILE: document.IsArray() == false");
return;
}
// reserving dimension
map.clear();
// getting current timestamp to check expiration
const auto currentTime = std::time(nullptr);
// iterating over array
for (auto& member : jsonDoc.GetArray())
{
auto key = static_cast<DWORD>(jsonHelper::getValue<int>(member, "key"));
auto expiration = jsonHelper::getValue<int64_t>(member, "expiration");
auto price = jsonHelper::getValue<int64_t>(member, "price");
auto count = jsonHelper::getValue<int>(member, "count");
#ifdef ENABLE_CHEQUE_SYSTEM
auto cheque = jsonHelper::getValue<int>(member, "cheque");
#endif
// checking expiration
if(expiration < currentTime)
continue;
auto& registerPrice = map[key];
registerPrice.expiration = expiration;
registerPrice.price = price;
registerPrice.count = static_cast<DWORD>(std::max(1, count));
#ifdef ENABLE_CHEQUE_SYSTEM
registerPrice.cheque = cheque;
#endif
}
};
loadFileIntoMap("shop/price_history_vnum.json", m_priceByVnum);
loadFileIntoMap("shop/price_history_hash.json", m_priceByHash);
}
void CPythonIkarusShop::SavePriceHistory()
{
static auto saveMapToFile = [](const std::string& file, const auto& map){
// creating document
rapidjson::Document doc;
doc.SetArray();
// reserving array size
doc.GetArray().Reserve(map.size(), doc.GetAllocator());
// populating array
for (auto& [key, element] : map)
{
rapidjson::Value member;
member.SetObject();
// encoding info
jsonHelper::insertValue(member, "key", static_cast<int>(key), doc.GetAllocator());
jsonHelper::insertValue(member, "expiration", element.expiration, doc.GetAllocator());
jsonHelper::insertValue(member, "price", element.price, doc.GetAllocator());
jsonHelper::insertValue(member, "count", static_cast<int>(element.count), doc.GetAllocator());
#ifdef ENABLE_CHEQUE_SYSTEM
jsonHelper::insertValue(member, "cheque", element.cheque, doc.GetAllocator());
#endif
// pushing element inside the array
doc.PushBack(member, doc.GetAllocator());
}
// making json string
rapidjson::StringBuffer buffer;
buffer.Clear();
rapidjson::Writer<rapidjson::StringBuffer> StringWriter(buffer);
doc.Accept(StringWriter);
// opening stream
std::ofstream stream(file, std::ios::out | std::ios::binary);
if (!stream.is_open()) {
TraceError("CANNOT SAVE PRICE HISTORY FILE!");
return;
}
// completing stream data
stream.write(buffer.GetString(), buffer.GetSize());
stream.close();
};
saveMapToFile("shop/price_history_vnum.json", m_priceByVnum);
saveMapToFile("shop/price_history_hash.json", m_priceByHash);
}
void CPythonIkarusShop::RegisterPriceHistory(DWORD vnum, DWORD count, DWORD hash, const ikashop::TPriceInfo& price)
{
// calculating info
const auto expiration = static_cast<int64_t>(std::time(nullptr)) + PRICE_HISTORY_EXPIRATION;
const auto total = price.yang;
#ifdef ENABLE_CHEQUE_SYSTEM
const auto cheque = price.cheque;
#endif
// registering by vnum
m_priceByVnum[vnum] = {expiration, total, count
#ifdef ENABLE_CHEQUE_SYSTEM
, cheque
#endif
};
// registering by hash
if(hash != 0)
m_priceByHash[hash] = {expiration, total, count
#ifdef ENABLE_CHEQUE_SYSTEM
, cheque
#endif
};
// moving data to file
SavePriceHistory();
}
std::optional<ikashop::TPriceInfo> CPythonIkarusShop::GetPriceHistory(DWORD vnum, DWORD hash, DWORD count)
{
// converting yang amount to cheque + yang
static auto composePrice = [](auto price, auto registerCount, auto count
#ifdef ENABLE_CHEQUE_SYSTEM
, auto cheque
#endif
) -> ikashop::TPriceInfo {
// checking if count matches
if(registerCount == count)
return { price
#ifdef ENABLE_CHEQUE_SYSTEM
, cheque
#endif
};
// adjusting price using count
auto total = price;
#ifdef ENABLE_CHEQUE_SYSTEM
total += static_cast<long long>(cheque) * YANG_PER_CHEQUE;
#endif
total *= count;
total /= registerCount;
#ifdef ENABLE_CHEQUE_SYSTEM
return { total % YANG_PER_CHEQUE, static_cast<int>(total / YANG_PER_CHEQUE)};
#endif
return { total };
};
// searching by hash
if(hash != 0)
{
auto iter = m_priceByHash.find(hash);
if(iter != m_priceByHash.end())
return composePrice(iter->second.price, iter->second.count, count
#ifdef ENABLE_CHEQUE_SYSTEM
, iter->second.cheque
#endif
);
}
// searching by vnum
auto iter = m_priceByVnum.find(vnum);
if(iter != m_priceByVnum.end())
return composePrice(iter->second.price, iter->second.count, count
#ifdef ENABLE_CHEQUE_SYSTEM
, iter->second.cheque
#endif
);
// not found
return std::nullopt;
}
#endif
PythonSafeList CPythonIkarusShop::GetFilterHistory()
{
static auto extractFilter = [](const TFilterHistoryElement& filter){
PythonGarbageCollector collector;
PythonSafeList attrlist;
for (auto& attr : filter.attrs)
attrlist.append(collector.makeValue("(ii)", attr.bType, attr.sValue));
PythonSafeDict filterDict;
filterDict.set("datetime", filter.datetime);
filterDict.set("count", filter.count);
filterDict.set("type", filter.type);
filterDict.set("subtype", filter.subtype);
filterDict.set("name", filter.name);
filterDict.set("price_min", filter.pricestart.yang);
filterDict.set("price_max", filter.pricend.yang);
filterDict.set("level_min", filter.levelstart);
filterDict.set("level_max", filter.levelend);
filterDict.set("alchemy", filter.alchemyGrade);
filterDict.set("sash", filter.sashGrade);
filterDict.set("level_max", filter.levelend);
filterDict.set("attrs", attrlist);
return filterDict;
};
return ExtractPythonListFromContainer(m_filterHistory, extractFilter);
}
std::vector<std::string> CPythonIkarusShop::GetItemNames()
{
// getting item map
std::map<DWORD, std::string> nameMap;
CItemManager::instance().GetItemsNameMap(nameMap);
// making vector
std::vector<std::string> vec;
vec.reserve(nameMap.size());
for(auto& [vnum, name] : nameMap)
vec.emplace_back(name);
return vec;
}
void CPythonIkarusShop::ShopBuilding_AddItem(int win, int slot)
{
CallOnBusinessBoard("InsertItemInSlot" , Py_BuildValue("(ii)", win, slot));
}
void CPythonIkarusShop::AuctionBuilding_AddItem(int win, int slot)
{
CallOnBusinessBoard("AuctionInsertItemInSlot" , Py_BuildValue("(ii)", win, slot));
}
#ifdef EXTEND_IKASHOP_PRO
void CPythonIkarusShop::RefreshBoardCounters(int safebox, int auction, int offers, int notifications)
#else
void CPythonIkarusShop::RefreshBoardCounters(int safebox, int auction, int offers)
#endif
{
CallOnBusinessBoard("SetSafeboxCounter", Py_BuildValue("(i)", safebox));
CallOnBusinessBoard("SetAuctionCounter", Py_BuildValue("(i)", auction));
CallOnBusinessBoard("SetOffersCounter", Py_BuildValue("(i)", offers));
#ifdef EXTEND_IKASHOP_PRO
CallOnBusinessBoard("SetNotificationCounter", Py_BuildValue("(i)", notifications));
#endif
}
#ifdef EXTEND_IKASHOP_PRO
void CPythonIkarusShop::RegisterNotification(const ikashop::TNotificationInfo& info)
{
for(auto it = m_notifications.begin(); it != m_notifications.end(); it++)
{
if(it->datetime < info.datetime)
{
m_notifications.emplace(it, info);
return;
}
}
m_notifications.emplace_back(info);
}
void CPythonIkarusShop::LoadNotifications()
{
m_notifications.clear();
// opening stream
auto filename = fmt::format("shop/notifications_{}.json", CPythonPlayer::instance().GetName());
std::ifstream stream(filename, std::ios::in | std::ios::ate | std::ios::binary);
if (stream.is_open() == false)
return;
// reading size
auto fsize = stream.tellg();
// resetting reading point
stream.seekg(0);
// making string receiving file content
std::string jsonString;
jsonString.resize(fsize);
// reading file content inside string
stream.read(jsonString.data(), fsize);
stream.close();
// making document
rapidjson::Document jsonDoc;
jsonDoc.Parse(jsonString.c_str());
// checking for parser errors
if (jsonDoc.HasParseError()) {
TraceError("PARSER ERROR PARSING NOTIFICATION FILE: %d", (int)jsonDoc.GetParseError());
return;
}
// checking list object
if (jsonDoc.IsArray() == false) {
TraceError("INVALID NOTIFICATION FILE: document.IsArray() == false");
return;
}
// getting current timestamp to check expiration
const auto currentTime = std::time(nullptr);
// iterating over array
for (auto& member : jsonDoc.GetArray())
{
ikashop::TNotificationInfo info{};
info.seenflag = true;
info.type = static_cast<ikashop::ENotificationType>(jsonHelper::getValue<int>(member, "type"));
info.what = static_cast<DWORD>(jsonHelper::getValue<int>(member, "what"));
info.datetime = jsonHelper::getValue<int64_t>(member, "datetime");
auto who = jsonHelper::getValue<std::string>(member, "who");
std::strncpy(info.who, who.c_str(), sizeof(info.who));
auto format = jsonHelper::getValue<std::string>(member, "format");
std::strncpy(info.format, format.c_str(), sizeof(info.format));
// checking expiration
if (currentTime > info.datetime && currentTime - info.datetime > NOTIFICATION_EXPIRATION)
continue;
m_notifications.emplace_back(info);
}
}
void CPythonIkarusShop::SaveNotifications()
{
// creating document
rapidjson::Document doc;
doc.SetArray();
// reserving array size
doc.GetArray().Reserve(m_notifications.size(), doc.GetAllocator());
// populating array
for (auto& info : m_notifications)
{
rapidjson::Value member;
member.SetObject();
// encoding info
jsonHelper::insertValue(member, "type", static_cast<int>(info.type), doc.GetAllocator());
jsonHelper::insertValue(member, "who", info.who, doc.GetAllocator());
jsonHelper::insertValue(member, "what", static_cast<int>(info.what), doc.GetAllocator());
jsonHelper::insertValue(member, "format", info.format, doc.GetAllocator());
jsonHelper::insertValue(member, "datetime", static_cast<int64_t>(info.datetime), doc.GetAllocator());
// pushing element inside the array
doc.PushBack(member, doc.GetAllocator());
}
// making json string
rapidjson::StringBuffer buffer;
buffer.Clear();
rapidjson::Writer<rapidjson::StringBuffer> StringWriter(buffer);
doc.Accept(StringWriter);
// opening stream
auto filename = fmt::format("shop/notifications_{}.json", CPythonPlayer::instance().GetName());
std::ofstream stream(filename, std::ios::out | std::ios::binary);
if (!stream.is_open()) {
TraceError("CANNOT SAVE NOTIFICATION FILE!");
return;
}
// completing stream data
stream.write(buffer.GetString(), buffer.GetSize());
stream.close();
}
const std::deque<ikashop::TNotificationInfo>& CPythonIkarusShop::GetNotifications()
{
return m_notifications;
}
void CPythonIkarusShop::SetNotificationList(const std::vector<ikashop::TNotificationInfo>& notifications)
{
auto list = ExtractPythonListFromContainer(notifications, ExtractPythonDictNotification);
CallOnBusinessBoard("SetNotifications", Py_BuildValue("(O)", list.weakRef()));
}
#endif
#ifdef EXTEND_IKASHOP_ULTIMATE
void CPythonIkarusShop::PriceAverageResponse(long long average)
{
CallOnBusinessBoard("SetPriceAverage", Py_BuildValue("(K)", average));
}
#endif
#ifdef ENABLE_IKASHOP_ENTITIES
std::string GetEntityNameFormat(const char* name
#ifdef EXTEND_IKASHOP_ULTIMATE
, bool isAuction
#endif
)
{
// getting localeinfo module
auto pyModule = PythonReference::steal(PyImport_ImportModule("localeInfo"));
if (!pyModule)
return name;
// getting attr name
#ifdef EXTEND_IKASHOP_ULTIMATE
const std::string attr = isAuction ? "IKASHOP_AUCTION_GUEST_NAME_FORMAT" : "IKASHOP_SHOP_NAME_FORMAT";
#else
const std::string attr = "IKASHOP_SHOP_NAME_FORMAT";
#endif
// checking module has attr
if(!PyObject_HasAttrString(pyModule, attr.c_str()))
return name;
// getting module attribute
auto pyAttr = PythonReference::steal(PyObject_GetAttrString(pyModule, attr.c_str()));
if(!pyAttr)
return name;
// checking attribute is string
if(!PyString_Check(pyAttr))
return name;
// getting string from attr
const auto* format = PyString_AsString(pyAttr);
return fmt::format(fmt::runtime(format), name);
}
void CPythonIkarusShop::InsertEntity(DWORD vid, int type, const char* name, long x, long y, long z)
{
auto namestr = GetEntityNameFormat(name
#ifdef EXTEND_IKASHOP_ULTIMATE
, type == 999
#endif
);
auto& shop = m_shopInstances.emplace_back(std::make_unique<ikashop::ShopInstance>(type));
shop->SetVID(vid);
shop->SetShopType(type);
shop->SetName(namestr);
CPythonBackground&rkBgMgr=CPythonBackground::Instance();
rkBgMgr.GlobalPositionToLocalPosition(x, y);
z = CPythonBackground::Instance().GetHeight(x, y) + 10.0f;
shop->Show(x,y,z);
}
void CPythonIkarusShop::RemoveEntity(DWORD vid)
{
for (auto it = m_shopInstances.begin(); it != m_shopInstances.end(); it++)
{
ikashop::ShopInstance& shop = *(*it);
if (shop.GetVID() == vid)
{
shop.Clear();
m_shopInstances.erase(it);
return;
}
}
}
void CPythonIkarusShop::RenderEntities()
{
#ifdef EXTEND_IKASHOP_PRO
auto mainInstance = CPythonCharacterManager::instance().GetMainInstancePtr();
#endif
CPythonGraphic::Instance().SetDiffuseOperation();
for (auto & iter : m_shopInstances)
{
#ifdef EXTEND_IKASHOP_PRO
if (!mainInstance || mainInstance->GetDistance(iter->GetThingInstancePtr()->GetPosition()) > CPythonSystem::instance().GetShopRangeView()) {
# ifdef EXTEND_IKASHOP_ULTIMATE
iter->HideAllEffects();
# endif
continue;
}
#endif
#ifdef EXTEND_IKASHOP_ULTIMATE
iter->ShowAllEffects();
#endif
iter->Render();
iter->BlendRender();
}
if (CPythonSystem::Instance().IsShowSalesText()) {
auto& graphic = CPythonGraphic::Instance();
graphic.PushState();
graphic.SetInterfaceRenderState();
for (auto& instance : m_shopInstances){
#ifdef EXTEND_IKASHOP_PRO
if (!mainInstance || mainInstance->GetDistance(instance->GetThingInstancePtr()->GetPosition()) > CPythonSystem::instance().GetShopRangeView())
continue;
#endif
instance->RenderText();
}
graphic.PopState();
graphic.SetGameRenderState();
}
}
void CPythonIkarusShop::UpdateEntities()
{
for (auto & iter : m_shopInstances)
iter->Update();
}
bool CPythonIkarusShop::GetShowNameFlag()
{
return m_bIsShowName;
}
void CPythonIkarusShop::SetShowNameFlag(bool flag)
{
m_bIsShowName = flag;
}
void CPythonIkarusShop::DeleteEntities()
{
for (auto& iter : m_shopInstances)
iter->Clear();
m_shopInstances.clear();
}
bool CPythonIkarusShop::CheckPickedShopEntity(DWORD* ret)
{
for (auto& instance : m_shopInstances) {
if (instance->IsPickable()) {
if (instance->IsPicked()) {
if (ret)
*ret = instance->GetVID();
return true;
}
}
}
return false;
}
#endif
void CPythonIkarusShop::DeleteSearchResult(DWORD itemid)
{
CallOnSearchShopBoard("DeleteSearchResultItem", Py_BuildValue("(i)", itemid));
}
void CPythonIkarusShop::PopupMessage(const std::string& message)
{
CallOnBusinessBoard("ServerPopupMessage", Py_BuildValue("(s)", message.c_str()));
}
CPythonIkarusShop::~CPythonIkarusShop()
{
}
PyObject* ikashopSendShopCreate(PyObject* poSelf, PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusShopCreateShop();
return Py_BuildNone();
}
PyObject* ikashopSendForceCloseShop(PyObject* poSelf , PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusShopForceCloseShop();
return Py_BuildNone();
}
PyObject* ikashopSendOpenShop(PyObject* poSelf , PyObject* poArgs)
{
uint32_t id = 0;
if(!PyTuple_GetUnsignedInteger(poArgs, 0, &id))
return Py_BadArgument();
CPythonNetworkStream::Instance().SendIkarusShopOpenShop(id);
return Py_BuildNone();
}
PyObject* ikashopSendOpenShopOwner(PyObject* poSelf , PyObject* poArgs)
{
CPythonNetworkStream::Instance().SendIkarusShopOpenShopOwner();
return Py_BuildNone();
}
PyObject* ikashopSendBuyItem(PyObject* poSelf, PyObject* poArgs)
{
uint32_t ownerid = 0;
if(!PyTuple_GetUnsignedInteger(poArgs, 0, &ownerid))
return Py_BadArgument();
uint32_t itemid = 0;
if (!PyTuple_GetUnsignedInteger(poArgs, 1, &itemid))
return Py_BadArgument();
long long seenprice = 0;
if (!PyTuple_GetLongLong(poArgs, 2, &seenprice))
return Py_BadArgument();
CPythonNetworkStream::instance().SendIkarusShopBuyItem(ownerid, itemid, false, seenprice);
return Py_BuildNone();
}
PyObject* ikashopSendAddItem(PyObject* poSelf , PyObject* poArgs)
{
BYTE window = 0;
if(!PyTuple_GetInteger(poArgs, 0, &window))
return Py_BadArgument();
WORD slot = 0;
if(!PyTuple_GetInteger(poArgs, 1, &slot))
return Py_BadArgument();
int destpos = 0;
if (!PyTuple_GetInteger(poArgs, 2, &destpos))
return Py_BadArgument();
long long yang=0;
if(!PyTuple_GetLongLong(poArgs, 3, &yang))
return Py_BadArgument();
#ifdef ENABLE_CHEQUE_SYSTEM
int cheque = 0;
if(!PyTuple_GetInteger(poArgs, 4, &cheque))
return Py_BadArgument();
#endif
ikashop::TShopItemInfo info;
info.pos = {window, slot};
info.price.yang = yang;
info.destpos = destpos;
#ifdef ENABLE_CHEQUE_SYSTEM
info.price.cheque = cheque;
#endif
CPythonNetworkStream::instance().SendIkarusShopAddItem(info);
return Py_BuildNone();
}
PyObject* ikashopSendRemoveItem(PyObject* poSelf , PyObject* poArgs)
{
uint32_t itemid = 0;
if(!PyTuple_GetUnsignedInteger(poArgs, 0, &itemid))
return Py_BadArgument();
CPythonNetworkStream::instance().SendIkarusShopRemoveItem(itemid);
return Py_BuildNone();
}
PyObject* ikashopSendEditItem(PyObject* poSelf , PyObject* poArgs)
{
uint32_t itemid=0;
if(!PyTuple_GetUnsignedInteger(poArgs, 0, &itemid))
return Py_BadArgument();
long long yang=0;
if(!PyTuple_GetLongLong(poArgs, 1, &yang))
return Py_BadArgument();
#ifdef ENABLE_CHEQUE_SYSTEM
int cheque = 0;
if(!PyTuple_GetInteger(poArgs, 2, &cheque))
return Py_BadArgument();
#endif
ikashop::TPriceInfo price;
price.yang = yang;
#ifdef ENABLE_CHEQUE_SYSTEM
price.cheque = cheque;
#endif
CPythonNetworkStream::instance().SendOfflineShopEditItem(itemid, price);
return Py_BuildNone();
}
PyObject* ikashopSendFilterRequest(PyObject* poSelf , PyObject* poArgs)
{
char* name = nullptr;
ikashop::TFilterInfo info;
if(!PyTuple_GetString(poArgs, 0, &name))
return Py_BadArgument();
if(!PyTuple_GetInteger(poArgs, 1, &info.type))
return Py_BadArgument();
if(!PyTuple_GetInteger(poArgs, 2, &info.subtype))
return Py_BadArgument();
PyObject* prices = nullptr;
if(!PyTuple_GetObject(poArgs, 3, &prices))
return Py_BadArgument();
PyObject* levels = nullptr;
if(!PyTuple_GetObject(poArgs, 4, &levels))
return Py_BadArgument();
PyObject* attrs=nullptr;
if(!PyTuple_GetObject(poArgs, 5, &attrs))
return Py_BadArgument();
PyObject* specials=nullptr;
if(!PyTuple_GetObject(poArgs, 6, &specials))
return Py_BadArgument();
// extracting prices
if(!PyTuple_GetLongLong(prices, 0, &info.pricestart.yang))
return Py_BadArgument();
if(!PyTuple_GetLongLong(prices, 1, &info.pricend.yang))
return Py_BadArgument();
// extracting levels
if(!PyTuple_GetInteger(levels, 0, &info.levelstart))
return Py_BadArgument();
if(!PyTuple_GetInteger(levels, 1, &info.levelend))
return Py_BadArgument();
// extracting attrs
for (int i = 0; i < ITEM_ATTRIBUTE_SLOT_NORM_NUM; i++)
{
PyObject* attribute = nullptr;
if(!PyTuple_GetObject(attrs, i, &attribute))
return Py_BadArgument();
if(!PyTuple_GetInteger(attribute, 0, &info.attrs[i].bType))
return Py_BadArgument();
int value = 0;
if(!PyTuple_GetInteger(attribute, 1, &value))
return Py_BadArgument();
info.attrs[i].sValue = static_cast<short>(value);
}
// extracting specials
if (!PyTuple_GetInteger(specials, 0, &info.sashGrade))
return Py_BadArgument();
if (!PyTuple_GetInteger(specials, 1, &info.alchemyGrade))
return Py_BadArgument();
// copying name
strncpy(info.name, name, sizeof(info.name));
CPythonNetworkStream::instance().SendIkarusShopFilterRequest(info);
return Py_BuildNone();
}
PyObject* ikashopSendRandomSearchFillRequest(PyObject* poSelf, PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusShopSearchFillRequest();
return Py_BuildNone();
}
PyObject* ikashopSendOfferCreate(PyObject* poSelf , PyObject* poArgs)
{
ikashop::TOfferInfo offer;
if(!PyTuple_GetUnsignedLong(poArgs, 0, &offer.ownerid))
return Py_BadArgument();
if (!PyTuple_GetUnsignedLong(poArgs, 1, &offer.itemid))
return Py_BadArgument();
if (!PyTuple_GetLongLong(poArgs, 2, &offer.price.yang))
return Py_BadArgument();
#ifdef ENABLE_CHEQUE_SYSTEM
if(!PyTuple_GetInteger(poArgs, 3, &offer.price.cheque))
return Py_BadArgument();
#endif
CPythonNetworkStream::instance().SendIkarusShopOfferCreate(offer);
return Py_BuildNone();
}
PyObject* ikashopSendOfferAccept(PyObject* poSelf , PyObject* poArgs)
{
DWORD offerid=0;
if(!PyTuple_GetUnsignedLong(poArgs, 0, &offerid))
return Py_BadArgument();
CPythonNetworkStream::instance().SendIkarusShopOfferAccept(offerid);
return Py_BuildNone();
}
PyObject* ikashopSendOfferCancel(PyObject* poSelf , PyObject* poArgs)
{
DWORD offerid = 0;
if (!PyTuple_GetUnsignedLong(poArgs, 0, &offerid))
return Py_BadArgument();
DWORD ownerid = 0;
if (!PyTuple_GetUnsignedLong(poArgs, 1, &ownerid))
return Py_BadArgument();
CPythonNetworkStream::instance().SendIkarusShopOfferCancel(offerid, ownerid);
return Py_BuildNone();
}
PyObject* ikashopSendOfferListRequest(PyObject* poSelf , PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusShopOfferListRequest();
return Py_BuildNone();
}
PyObject* ikashopSendSafeboxOpen(PyObject* poSelf , PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusShopSafeboxOpen();
return Py_BuildNone();
}
PyObject* ikashopSendSafeboxGetItem(PyObject* poSelf , PyObject* poArgs)
{
DWORD itemid = 0;
if (!PyTuple_GetUnsignedLong(poArgs, 0, &itemid))
return Py_BadArgument();
CPythonNetworkStream::instance().SendIkarusShopSafeboxGetItem(itemid);
return Py_BuildNone();
}
PyObject* ikashopSendSafeboxGetValutes(PyObject* poSelf , PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusShopSafeboxGetValutes();
return Py_BuildNone();
}
PyObject* ikashopSendSafeboxClose(PyObject* poSelf , PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusShopSafeboxClose();
return Py_BuildNone();
}
PyObject* ikashopSendOffersListClose(PyObject* poSelf, PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusOffersListClose();
return Py_BuildNone();
}
PyObject* ikashopSendCloseMyShopBoard(PyObject* poSelf, PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusMyShopCloseBoard();
return Py_BuildNone();
}
PyObject* ikashopSendCloseShopGuestBoard(PyObject* poSelf, PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusCloseShopGuestBoard();
return Py_BuildNone();
}
PyObject* ikashopSendAuctionListRequest(PyObject* poSelf, PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusShopAuctionListRequest();
return Py_BuildNone();
}
PyObject* ikashopSendAuctionOpenAuction(PyObject* poSelf, PyObject* poArgs)
{
DWORD ownerid = 0;
if (!PyTuple_GetUnsignedLong(poArgs, 0, &ownerid))
return Py_BadArgument();
CPythonNetworkStream::instance().SendIkarusShopAuctionOpen(ownerid);
return Py_BuildNone();
}
PyObject* ikashopSendAuctionAddOffer(PyObject* poSelf, PyObject* poArgs)
{
DWORD ownerid = 0;
if (!PyTuple_GetUnsignedLong(poArgs, 0, &ownerid))
return Py_BadArgument();
ikashop::TPriceInfo price;
if(!PyTuple_GetLongLong(poArgs, 1, &price.yang))
return Py_BadArgument();
#ifdef ENABLE_CHEQUE_SYSTEM
if(!PyTuple_GetInteger(poArgs, 2, &price.cheque))
return Py_BadArgument();
#endif
CPythonNetworkStream::instance().SendIkarusShopAuctionAddOffer(ownerid, price);
return Py_BuildNone();
}
PyObject* ikashopSendAuctionExitFrom(PyObject* poSelf, PyObject* poArgs)
{
DWORD ownerid = 0;
if (!PyTuple_GetUnsignedLong(poArgs, 0, &ownerid))
return Py_BadArgument();
CPythonNetworkStream::instance().SendIkarusShopAuctionExitFrom(ownerid);
return Py_BuildNone();
}
PyObject* ikashopSendAuctionCreate(PyObject* poSelf, PyObject* poArgs)
{
TItemPos pos;
if(!PyTuple_GetInteger(poArgs, 0, &pos.window_type))
return Py_BadArgument();
if(!PyTuple_GetInteger(poArgs, 1, &pos.cell))
return Py_BadArgument();
ikashop::TPriceInfo price;
if(!PyTuple_GetLongLong(poArgs, 2, &price.yang))
return Py_BadArgument();
CPythonNetworkStream::instance().SendIkarusShopAuctionCreate(pos, price);
return Py_BuildNone();
}
PyObject* ikashopSendMyAuctionOpen(PyObject* poSelf, PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusShopMyAuctionOpen();
return Py_BuildNone();
}
PyObject* ikashopSendMyAuctionClose(PyObject* poSelf, PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusShopMyAuctionClose();
return Py_BuildNone();
}
PyObject* ikashopSendMyAuctionCancel(PyObject* poSelf, PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusShopMyAuctionCancel();
return Py_BuildNone();
}
PyObject* SendCloseMyShopBoard(PyObject* poSelf, PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusMyShopCloseBoard();
return Py_BuildNone();
}
PyObject* ikashopGetFilterHistory(PyObject* poSelf , PyObject* poArgs)
{
auto list = CPythonIkarusShop::instance().GetFilterHistory();
return list.incRef();
}
PyObject* ikashopRegisterFilterHistory(PyObject* poSelf , PyObject* poArgs)
{
CPythonIkarusShop::TFilterHistoryElement element;
CPythonIkarusShop::NowDatetime(element.datetime);
if(!PyTuple_GetUnsignedLong(poArgs, 0, &element.count))
return Py_BadArgument();
char* name=nullptr;
if(!PyTuple_GetString(poArgs, 1, &name))
return Py_BadArgument();
strncpy(element.name, name, sizeof(element.name));
if(!PyTuple_GetInteger(poArgs, 2, &element.type))
return Py_BadArgument();
if(!PyTuple_GetInteger(poArgs, 3, &element.subtype))
return Py_BadArgument();
PyObject* prices = nullptr;
if (!PyTuple_GetObject(poArgs, 4, &prices))
return Py_BadArgument();
PyObject* levels = nullptr;
if (!PyTuple_GetObject(poArgs, 5, &levels))
return Py_BadArgument();
PyObject* attrs = nullptr;
if (!PyTuple_GetObject(poArgs, 6, &attrs))
return Py_BadArgument();
PyObject* specials = nullptr;
if (!PyTuple_GetObject(poArgs, 7, &specials))
return Py_BadArgument();
// extracting prices
if(!PyTuple_GetLongLong(prices, 0, &element.pricestart.yang))
return Py_BadArgument();
if(!PyTuple_GetLongLong(prices, 1, &element.pricend.yang))
return Py_BadArgument();
// extracting levels
if(!PyTuple_GetInteger(levels, 0, &element.levelstart))
return Py_BadArgument();
if(!PyTuple_GetInteger(levels, 1, &element.levelend))
return Py_BadArgument();
// extracting attributes
for (int i = 0; i < ITEM_ATTRIBUTE_SLOT_NORM_NUM; i++)
{
PyObject* attribute = nullptr;
if(!PyTuple_GetObject(attrs, i, &attribute))
return Py_BadArgument();
int value = 0;
if(!PyTuple_GetInteger(attribute, 0, &element.attrs[i].bType))
return Py_BadArgument();
if(!PyTuple_GetInteger(attribute, 1, &value))
return Py_BadArgument();
element.attrs[i].sValue = static_cast<short>(value);
}
// extracting specials
if (!PyTuple_GetInteger(specials, 0, &element.sashGrade))
return Py_BadArgument();
if (!PyTuple_GetInteger(specials, 1, &element.alchemyGrade))
return Py_BadArgument();
CPythonIkarusShop::instance().AddNewFilterToHistory(element);
return Py_BuildNone();
}
PyObject* ikashopGetItemNames(PyObject* poSelf, PyObject* poArgs)
{
auto vec = CPythonIkarusShop::instance().GetItemNames();
// converting map to dict
PythonSafeList list;
for(auto& name : vec)
list.append(name);
return list.incRef();
}
PyObject* ikashopSetBusinessBoard(PyObject* poSelf , PyObject* poArgs)
{
PyObject* board = nullptr;
if(!PyTuple_GetObject(poArgs, 0, &board))
return Py_BadArgument();
if(board == Py_None)
board = nullptr;
CPythonIkarusShop::instance().SetBusinessBoard(board);
return Py_BuildNone();
}
PyObject* ikashopSetSearchShopBoard(PyObject* poSelf , PyObject* poArgs)
{
PyObject* board = nullptr;
if (!PyTuple_GetObject(poArgs, 0, &board))
return Py_BadArgument();
if (board == Py_None)
board = nullptr;
CPythonIkarusShop::instance().SetSearchShopBoard(board);
return Py_BuildNone();
}
PyObject* ikashopGetBusinessBoard(PyObject* poSelf , PyObject* poArgs)
{
PyObject* poInterface = CPythonIkarusShop::instance().GetBusinessBoard();
if(!poInterface)
return Py_BuildNone();
Py_IncRef(poInterface);
return poInterface;
}
#ifdef EXTEND_IKASHOP_ULTIMATE
PyObject* ikashopSetShopSkinBoard(PyObject* poSelf, PyObject* poArgs)
{
PyObject* board = nullptr;
if (!PyTuple_GetObject(poArgs, 0, &board))
return Py_BadArgument();
if (board == Py_None)
board = nullptr;
CPythonIkarusShop::instance().SetShopSkinBoard(board);
return Py_BuildNone();
}
PyObject* ikashopGetShopSkinBoard(PyObject* poSelf, PyObject* poArgs)
{
PyObject* poInterface = CPythonIkarusShop::instance().GetShopSkinBoard();
if (!poInterface)
return Py_BuildNone();
Py_IncRef(poInterface);
return poInterface;
}
#endif
PyObject* ikashopShopBuilding_AddItem(PyObject* poSelf, PyObject* poArgs)
{
int win = 0;
if(!PyTuple_GetInteger(poArgs, 0, &win))
return Py_BadArgument();
int slot = 0;
if(!PyTuple_GetInteger(poArgs, 1, &slot))
return Py_BadArgument();
CPythonIkarusShop::instance().ShopBuilding_AddItem(win, slot);
return Py_BuildNone();
}
PyObject* ikashopAuctionBuilding_AddItem(PyObject* poSelf, PyObject* poArgs)
{
int win = 0;
if (!PyTuple_GetInteger(poArgs, 0, &win))
return Py_BadArgument();
int slot = 0;
if (!PyTuple_GetInteger(poArgs, 1, &slot))
return Py_BadArgument();
CPythonIkarusShop::instance().AuctionBuilding_AddItem(win, slot);
return Py_BuildNone();
}
#ifdef ENABLE_IKASHOP_ENTITIES
PyObject* ikashopHideShopNames(PyObject* poSelf, PyObject* poArgs)
{
CPythonIkarusShop::instance().SetShowNameFlag(false);
return Py_BuildNone();
}
PyObject* ikashopShowShopNames(PyObject* poSelf, PyObject* poArgs)
{
CPythonIkarusShop::instance().SetShowNameFlag(true);
return Py_BuildNone();
}
#ifdef EXTEND_IKASHOP_PRO
PyObject* ikashopSendMoveShopEntity(PyObject* poSelf, PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusShopMoveShopEntity();
return Py_BuildNone();
}
#endif
#endif
PyObject* ikashopGetFirstEmptyCell(PyObject* poSelf, PyObject* poArgs)
{
int size = 0;
if (!PyTuple_GetInteger(poArgs, 0, &size))
return Py_BadArgument();
PyObject* items = nullptr;
if (!PyTuple_GetObject(poArgs, 1, &items) || !PyList_Check(items))
return Py_BadArgument();
constexpr auto width = ikashop::OFFLINESHOP_GRID_WIDTH;
constexpr auto height = ikashop::OFFLINESHOP_GRID_HEIGHT;
#ifdef EXTEND_IKASHOP_ULTIMATE
constexpr auto pageSize = ikashop::OFFLINESHOP_PAGE_SIZE;
ikashop::CShopGrid<width, height> grid[ikashop::OFFLINESHOP_PAGE_COUNT]{};
#else
ikashop::CShopGrid<width, height> grid{};
#endif
for(int i=0; i < PyList_Size(items); i++)
{
auto info = PyList_GetItem(items, i);
if(!PyTuple_Check(info) || PyTuple_Size(info) != 2)
return Py_BadArgument();
DWORD vnum = 0;
if(!PyTuple_GetUnsignedLong(info, 0, &vnum))
return Py_BadArgument();
int cell = 0;
if(!PyTuple_GetInteger(info, 1, &cell))
return Py_BadArgument();
CItemData* data = nullptr;
if(!CItemManager::instance().GetItemDataPointer(vnum, &data))
return Py_BadArgument();
#ifdef EXTEND_IKASHOP_ULTIMATE
const auto page = cell / pageSize;
const auto localCell = cell % pageSize;
if(page < 0 || page >= ikashop::OFFLINESHOP_PAGE_COUNT)
return Py_BuildNone();
grid[page].RegisterItem(localCell, data->GetSize());
#else
grid.RegisterItem(cell, data->GetSize());
#endif
}
#ifdef EXTEND_IKASHOP_ULTIMATE
std::optional<int> cell;
int page = 0;
for(; page < ikashop::OFFLINESHOP_PAGE_COUNT; page++)
if((cell = grid[page].FindSpace(size)))
break;
#else
const auto cell = grid.FindSpace(size);
#endif
if(!cell)
return Py_BuildNone();
#ifdef EXTEND_IKASHOP_ULTIMATE
const auto global = pageSize * page + *cell;
return PyInt_FromLong(global);
#else
return PyInt_FromLong(*cell);
#endif
}
PyObject* ikashopCheckShopSpace(PyObject* poSelf, PyObject* poArgs)
{
PyObject* items = nullptr;
if (!PyTuple_GetObject(poArgs, 0, &items) || !PyList_Check(items))
return Py_BadArgument();
DWORD vnum = 0;
if (!PyTuple_GetUnsignedLong(poArgs, 1, &vnum))
return Py_BadArgument();
int cell = 0;
if (!PyTuple_GetInteger(poArgs, 2, &cell))
return Py_BadArgument();
#ifdef EXTEND_IKASHOP_ULTIMATE
int lockIndex = 0;
if(!PyTuple_GetInteger(poArgs, 3, &lockIndex))
return Py_BadArgument();
#endif
CItemData* itemData = nullptr;
if(!CItemManager::instance().GetItemDataPointer(vnum, &itemData))
return PyBool_FromLong(false);
constexpr auto width = ikashop::OFFLINESHOP_GRID_WIDTH;
constexpr auto height = ikashop::OFFLINESHOP_GRID_HEIGHT;
#ifdef EXTEND_IKASHOP_ULTIMATE
constexpr auto pageSize = ikashop::OFFLINESHOP_PAGE_SIZE;
ikashop::CShopGrid<width, height> grid[ikashop::OFFLINESHOP_PAGE_COUNT]{};
#else
ikashop::CShopGrid<width, height> grid{};
#endif
// filling grids with items
for(int i=0; i < PyList_Size(items); i++)
{
auto info = PyList_GetItem(items, i);
if(!PyTuple_Check(info) || PyTuple_Size(info) != 2)
return Py_BadArgument();
DWORD vnum = 0;
if(!PyTuple_GetUnsignedLong(info, 0, &vnum))
return Py_BadArgument();
int cell = 0;
if(!PyTuple_GetInteger(info, 1, &cell))
return Py_BadArgument();
CItemData* data = nullptr;
if(!CItemManager::instance().GetItemDataPointer(vnum, &data))
return Py_BadArgument();
#ifdef EXTEND_IKASHOP_ULTIMATE
const auto page = cell / pageSize;
const auto localCell = cell % pageSize;
if(page < 0 || page >= ikashop::OFFLINESHOP_PAGE_COUNT)
return Py_BuildNone();
grid[page].RegisterItem(localCell, data->GetSize());
#else
grid.RegisterItem(cell, data->GetSize());
#endif
}
#ifdef EXTEND_IKASHOP_ULTIMATE
// locking cells
for(int i=lockIndex; i < pageSize * ikashop::OFFLINESHOP_PAGE_COUNT; i++)
{
const auto page = i / pageSize;
const auto local = i % pageSize;
grid[page].RegisterItem(local, 1);
}
// checking space
const auto page = cell / pageSize;
const auto local = cell % pageSize;
return PyBool_FromLong(grid[page].CheckSpace(local, itemData->GetSize()));
#else
return PyBool_FromLong(grid.CheckSpace(cell, itemData->GetSize()));
#endif
}
PyObject* ikashopGetItemHash(PyObject* poSelf, PyObject* poArgs)
{
// extracting data
DWORD vnum = 0;
if (!PyTuple_GetUnsignedLong(poArgs, 0, &vnum))
return Py_BadArgument();
DWORD count = 0;
if (!PyTuple_GetUnsignedLong(poArgs, 1, &count))
return Py_BadArgument();
PyObject* sockets = nullptr;
if (!PyTuple_GetObject(poArgs, 2, &sockets))
return Py_BadArgument();
PyObject* attrs = nullptr;
if (!PyTuple_GetObject(poArgs, 3, &attrs))
return Py_BadArgument();
PyObject* attribute = nullptr;
int attrvalue = 0;
ikashop::TShopPlayerItem item{};
item.vnum = vnum;
item.count = count;
for (int i = 0; i < ITEM_SOCKET_SLOT_MAX_NUM; i++)
if (!PyTuple_GetLong(sockets, i, &item.alSockets[i]))
return Py_BadArgument();
for (int i = 0; i < ITEM_ATTRIBUTE_SLOT_MAX_NUM; i++)
{
if (!PyTuple_GetObject(attrs, i, &attribute) || !PyTuple_GetInteger(attribute, 0, &item.aAttr[i].bType) || !PyTuple_GetInteger(attribute, 1, &attrvalue))
return Py_BadArgument();
item.aAttr[i].sValue = static_cast<short>(attrvalue);
}
return PyInt_FromSize_t(static_cast<size_t>(ikashop::GetItemHash(item)));
}
#ifdef EXTEND_IKASHOP_PRO
PyObject* ikashopGetItemPriceCache(PyObject* poSelf, PyObject* poArgs)
{
// extracting data
DWORD vnum = 0;
if(!PyTuple_GetUnsignedLong(poArgs, 0, &vnum))
return Py_BadArgument();
DWORD hash = 0;
if(!PyTuple_GetUnsignedLong(poArgs, 1, &hash))
return Py_BadArgument();
DWORD count = 0;
if(!PyTuple_GetUnsignedLong(poArgs, 2, &count))
return Py_BadArgument();
// getting price
auto price = CPythonIkarusShop::instance().GetPriceHistory(vnum, hash, count);
if(!price)
return Py_BuildNone();
// making return value
PythonSafeDict dict;
dict.set("value", price->yang);
#ifdef ENABLE_CHEQUE_SYSTEM
dict.set("cheque", price->cheque);
#endif
return dict.incRef();
}
PyObject* ikashopRegisterItemPriceCache(PyObject* poSelf, PyObject* poArgs)
{
// extracting data
DWORD vnum = 0;
if (!PyTuple_GetUnsignedLong(poArgs, 0, &vnum))
return Py_BadArgument();
DWORD hash = 0;
if (!PyTuple_GetUnsignedLong(poArgs, 1, &hash))
return Py_BadArgument();
DWORD count = 0;
if (!PyTuple_GetUnsignedLong(poArgs, 2, &count))
return Py_BadArgument();
ikashop::TPriceInfo price;
if (!PyTuple_GetLongLong(poArgs, 3, &price.yang))
return Py_BadArgument();
#ifdef ENABLE_CHEQUE_SYSTEM
if (!PyTuple_GetInteger(poArgs, 4, &price.cheque))
return Py_BadArgument();
#endif
CPythonIkarusShop::Instance().RegisterPriceHistory(vnum, count, hash, price);
return Py_BuildNone();
}
PyObject* ikashopLoadNotifications(PyObject* poSelf, PyObject* poArgs)
{
CPythonIkarusShop::instance().LoadNotifications();
return Py_BuildNone();
}
PyObject* ikashopSendNotificationListRequest(PyObject* poSelf, PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusNotificationListRequest();
return Py_BuildNone();
}
PyObject* ikashopSendNotificationListClose(PyObject* poSelf, PyObject* poArgs)
{
CPythonNetworkStream::instance().SendIkarusNotificationListClose();
return Py_BuildNone();
}
PyObject* ikashopRegisterNotification(PyObject* poSelf, PyObject* poArgs)
{
ikashop::TNotificationInfo info{};
info.seenflag = true;
int type = 0;
if(!PyTuple_GetInteger(poArgs, 0, &type))
return Py_BadArgument();
info.type = static_cast<ikashop::ENotificationType>(type);
char* who = nullptr;
if (!PyTuple_GetString(poArgs, 1, &who))
return Py_BadArgument();
std::strncpy(info.who, who, sizeof(info.who));
if (!PyTuple_GetUnsignedLong(poArgs, 2, &info.what))
return Py_BadArgument();
char* format = nullptr;
if (!PyTuple_GetString(poArgs, 3, &format))
return Py_BadArgument();
std::strncpy(info.format, format, sizeof(info.format));
if (!PyTuple_GetLongLong(poArgs, 4, &info.datetime))
return Py_BadArgument();
bool save = false;
if(!PyTuple_GetBoolean(poArgs, 5, &save))
return Py_BadArgument();
CPythonIkarusShop::instance().RegisterNotification(info);
if(save)
CPythonIkarusShop::instance().SaveNotifications();
return Py_BuildNone();
}
PyObject* ikashopGetNotifications(PyObject* poSelf, PyObject* poArgs)
{
auto& notifications = CPythonIkarusShop::instance().GetNotifications();
auto list = ExtractPythonListFromContainer(notifications, ExtractPythonDictNotification);
return list.incRef();
}
#endif
#ifdef EXTEND_IKASHOP_ULTIMATE
PyObject* ikashopSendPriceAverageRequest(PyObject* poSelf, PyObject* poArgs)
{
DWORD vnum = 0;
if (!PyTuple_GetUnsignedLong(poArgs, 0, &vnum))
return Py_BadArgument();
DWORD count = 0;
if (!PyTuple_GetUnsignedLong(poArgs, 1, &count))
return Py_BadArgument();
CPythonNetworkStream::instance().SendPriceAverageRequest(vnum, count);
return Py_BuildNone();
}
PyObject* ikashopSendShopDecorationUse(PyObject* poSelf, PyObject* poArgs)
{
int index = 0;
if(!PyTuple_GetInteger(poArgs, 0, &index))
return Py_BadArgument();
CPythonNetworkStream::instance().SendShopDecorationUsage(index);
return Py_BuildNone();
}
PyObject* ikashopSendShopMoveItem(PyObject* poSelf, PyObject* poArgs)
{
int srcPos = 0;
if (!PyTuple_GetInteger(poArgs, 0, &srcPos))
return Py_BadArgument();
int destPos = 0;
if (!PyTuple_GetInteger(poArgs, 1, &destPos))
return Py_BadArgument();
CPythonNetworkStream::instance().SendShopMoveItem(srcPos, destPos);
return Py_BuildNone();
}
#endif
void initikashop()
{
static PyMethodDef s_methods[] =
{
//packets exchanging
{ "SendShopCreate", ikashopSendShopCreate, METH_VARARGS },
{ "SendForceCloseShop", ikashopSendForceCloseShop, METH_VARARGS },
{ "SendOpenShop", ikashopSendOpenShop, METH_VARARGS },
{ "SendOpenShopOwner", ikashopSendOpenShopOwner, METH_VARARGS },
{ "SendBuyItem", ikashopSendBuyItem, METH_VARARGS },
{ "SendAddItem", ikashopSendAddItem, METH_VARARGS },
{ "SendRemoveItem", ikashopSendRemoveItem, METH_VARARGS },
{ "SendEditItem", ikashopSendEditItem, METH_VARARGS },
{ "SendFilterRequest", ikashopSendFilterRequest, METH_VARARGS },
{ "SendRandomSearchFillRequest", ikashopSendRandomSearchFillRequest, METH_VARARGS },
{ "SendOfferCreate", ikashopSendOfferCreate, METH_VARARGS },
{ "SendOfferAccept", ikashopSendOfferAccept, METH_VARARGS },
{ "SendOfferCancel", ikashopSendOfferCancel, METH_VARARGS },
{ "SendOfferListRequest", ikashopSendOfferListRequest, METH_VARARGS },
{ "SendSafeboxOpen", ikashopSendSafeboxOpen, METH_VARARGS },
{ "SendSafeboxGetItem", ikashopSendSafeboxGetItem, METH_VARARGS },
{ "SendSafeboxGetValutes", ikashopSendSafeboxGetValutes, METH_VARARGS },
{ "SendSafeboxClose", ikashopSendSafeboxClose, METH_VARARGS },
{ "SendOffersListClose", ikashopSendOffersListClose, METH_VARARGS },
{ "SendCloseMyShopBoard", ikashopSendCloseMyShopBoard, METH_VARARGS },
{ "SendCloseShopGuestBoard", ikashopSendCloseShopGuestBoard, METH_VARARGS },
//AUCTION
{ "SendAuctionListRequest", ikashopSendAuctionListRequest, METH_VARARGS },
{ "SendAuctionOpenAuction", ikashopSendAuctionOpenAuction, METH_VARARGS },
{ "SendAuctionAddOffer", ikashopSendAuctionAddOffer, METH_VARARGS },
{ "SendAuctionExitFrom", ikashopSendAuctionExitFrom, METH_VARARGS },
{ "SendAuctionCreate", ikashopSendAuctionCreate, METH_VARARGS },
{ "SendMyAuctionOpen", ikashopSendMyAuctionOpen, METH_VARARGS },
{ "SendMyAuctionClose", ikashopSendMyAuctionClose, METH_VARARGS },
{ "SendMyAuctionCancel", ikashopSendMyAuctionCancel, METH_VARARGS },
//interfaces methods
{ "GetFilterHistory", ikashopGetFilterHistory, METH_VARARGS },
{ "RegisterFilterHistory", ikashopRegisterFilterHistory, METH_VARARGS },
{ "GetItemNames", ikashopGetItemNames, METH_VARARGS },
{ "SetSearchShopBoard", ikashopSetSearchShopBoard, METH_VARARGS },
{ "SetBusinessBoard", ikashopSetBusinessBoard, METH_VARARGS },
{ "GetBusinessBoard", ikashopGetBusinessBoard, METH_VARARGS },
#ifdef EXTEND_IKASHOP_ULTIMATE
{ "SetShopSkinBoard", ikashopSetShopSkinBoard, METH_VARARGS },
{ "GetShopSkinBoard", ikashopGetShopSkinBoard, METH_VARARGS },
#endif
//create shop methods
{ "AddItemOnShop", ikashopShopBuilding_AddItem, METH_VARARGS },
{ "AddItemOnAuction", ikashopAuctionBuilding_AddItem, METH_VARARGS },
#ifdef ENABLE_IKASHOP_ENTITIES
{ "HideShopNames", ikashopHideShopNames, METH_VARARGS },
{ "ShowShopNames", ikashopShowShopNames, METH_VARARGS },
#ifdef EXTEND_IKASHOP_PRO
{ "SendMoveShopEntity", ikashopSendMoveShopEntity, METH_VARARGS },
#endif
#endif
// helpers
{ "GetFirstEmptyCell", ikashopGetFirstEmptyCell, METH_VARARGS },
{ "CheckShopSpace", ikashopCheckShopSpace, METH_VARARGS },
{ "GetItemHash", ikashopGetItemHash, METH_VARARGS },
#ifdef EXTEND_IKASHOP_PRO
// price cache
{ "GetItemPriceCache", ikashopGetItemPriceCache, METH_VARARGS },
{ "RegisterItemPriceCache", ikashopRegisterItemPriceCache, METH_VARARGS },
{ "LoadNotifications", ikashopLoadNotifications, METH_VARARGS },
{ "SendNotificationListRequest", ikashopSendNotificationListRequest, METH_VARARGS },
{ "SendNotificationListClose", ikashopSendNotificationListClose, METH_VARARGS },
{ "RegisterNotification", ikashopRegisterNotification, METH_VARARGS },
{ "GetNotifications", ikashopGetNotifications, METH_VARARGS },
#endif
#ifdef EXTEND_IKASHOP_ULTIMATE
{ "SendPriceAverageRequest", ikashopSendPriceAverageRequest, METH_VARARGS },
{ "SendShopDecorationUse", ikashopSendShopDecorationUse, METH_VARARGS },
{ "SendShopMoveItem", ikashopSendShopMoveItem, METH_VARARGS },
#endif
{ NULL, NULL, NULL },
};
PyObject * poModule = Py_InitModule("ikashop", s_methods);
PyModule_AddIntConstant(poModule, "FILTER_ATTRIBUTE_NUM", ITEM_ATTRIBUTE_SLOT_NORM_NUM);
PyModule_AddIntConstant(poModule, "FILTER_NAME_MAX_LEN", CItemData::ITEM_NAME_MAX_LEN);
PyModule_AddIntConstant(poModule, "SHOP_GRID_WIDTH", ikashop::OFFLINESHOP_GRID_WIDTH);
PyModule_AddIntConstant(poModule, "SHOP_GRID_HEIGHT", ikashop::OFFLINESHOP_GRID_HEIGHT);
PyModule_AddIntConstant(poModule, "SAFEBOX_GRID_WIDTH", ikashop::SAFEBOX_GRID_WIDTH);
PyModule_AddIntConstant(poModule, "SAFEBOX_GRID_HEIGHT", ikashop::SAFEBOX_GRID_HEIGHT);
PyModule_AddIntConstant(poModule, "SAFEBOX_PAGE_COUNT", ikashop::SAFEBOX_PAGE_COUNT);
#ifdef ENABLE_CHEQUE_SYSTEM
PyModule_AddIntConstant(poModule, "YANG_PER_CHEQUE", YANG_PER_CHEQUE);
#endif
#ifdef EXTEND_IKASHOP_ULTIMATE
PyModule_AddIntConstant(poModule, "OFFSHOP_LOCK_INDEX_INIT", CPythonIkarusShop::OFFSHOP_LOCK_INDEX_INIT);
#endif
#ifdef EXTEND_IKASHOP_PRO
PyModule_AddIntConstant(poModule, "NOTIFICATION_SELLER_SELLER_SOLD_ITEM", static_cast<int>(ikashop::ENotificationType::SELLER_SOLD_ITEM));
PyModule_AddIntConstant(poModule, "NOTIFICATION_SELLER_NEW_OFFER_ON_ITEM", static_cast<int>(ikashop::ENotificationType::SELLER_NEW_OFFER_ON_ITEM));
PyModule_AddIntConstant(poModule, "NOTIFICATION_SELLER_SHOP_EXPIRED", static_cast<int>(ikashop::ENotificationType::SELLER_SHOP_EXPIRED));
PyModule_AddIntConstant(poModule, "NOTIFICATION_SELLER_ITEM_EXPIRED", static_cast<int>(ikashop::ENotificationType::SELLER_ITEM_EXPIRED));
PyModule_AddIntConstant(poModule, "NOTIFICATION_SELLER_AUCTION_NEW_OFFER", static_cast<int>(ikashop::ENotificationType::SELLER_AUCTION_NEW_OFFER));
PyModule_AddIntConstant(poModule, "NOTIFICATION_SELLER_AUCTION_EXPIRED", static_cast<int>(ikashop::ENotificationType::SELLER_AUCTION_EXPIRED));
PyModule_AddIntConstant(poModule, "NOTIFICATION_SELLER_AUCTION_SOLD", static_cast<int>(ikashop::ENotificationType::SELLER_AUCTION_SOLD));
PyModule_AddIntConstant(poModule, "NOTIFICATION_SELLER_OFFER_CANCELLED", static_cast<int>(ikashop::ENotificationType::SELLER_OFFER_CANCELLED));
PyModule_AddIntConstant(poModule, "NOTIFICATION_BUYER_OFFER_DENIED", static_cast<int>(ikashop::ENotificationType::BUYER_OFFER_DENIED));
PyModule_AddIntConstant(poModule, "NOTIFICATION_BUYER_OFFER_ACCEPTED", static_cast<int>(ikashop::ENotificationType::BUYER_OFFER_ACCEPTED));
PyModule_AddIntConstant(poModule, "NOTIFICATION_BUYER_OFFER_EXPIRED", static_cast<int>(ikashop::ENotificationType::BUYER_OFFER_EXPIRED));
PyModule_AddIntConstant(poModule, "NOTIFICATION_BUYER_AUCTION_BUY", static_cast<int>(ikashop::ENotificationType::BUYER_AUCTION_BUY));
PyModule_AddIntConstant(poModule, "NOTIFICATION_BUYER_AUCTION_RAISE", static_cast<int>(ikashop::ENotificationType::BUYER_AUCTION_RAISE));
PyModule_AddIntConstant(poModule, "NOTIFICATION_BUYER_AUCTION_LOST", static_cast<int>(ikashop::ENotificationType::BUYER_AUCTION_LOST));
#ifdef EXTEND_IKASHOP_ULTIMATE
PyModule_AddIntConstant(poModule, "NOTIFICATION_SELLER_DECORATION_EXPIRED", static_cast<int>(ikashop::ENotificationType::SELLER_DECORATION_EXPIRED));
#endif
#endif
}
#endif