Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/frontend/StarMerchantInterface.cpp
diff options
context:
space:
mode:
authorKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
committerKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
commit6352e8e3196f78388b6c771073f9e03eaa612673 (patch)
treee23772f79a7fbc41bc9108951e9e136857484bf4 /source/frontend/StarMerchantInterface.cpp
parent6741a057e5639280d85d0f88ba26f000baa58f61 (diff)
everything everywhere
all at once
Diffstat (limited to 'source/frontend/StarMerchantInterface.cpp')
-rw-r--r--source/frontend/StarMerchantInterface.cpp391
1 files changed, 391 insertions, 0 deletions
diff --git a/source/frontend/StarMerchantInterface.cpp b/source/frontend/StarMerchantInterface.cpp
new file mode 100644
index 0000000..7059cb8
--- /dev/null
+++ b/source/frontend/StarMerchantInterface.cpp
@@ -0,0 +1,391 @@
+#include "StarMerchantInterface.hpp"
+#include "StarJsonExtra.hpp"
+#include "StarGuiReader.hpp"
+#include "StarLexicalCast.hpp"
+#include "StarRoot.hpp"
+#include "StarItemTooltip.hpp"
+#include "StarPlayer.hpp"
+#include "StarWorldClient.hpp"
+#include "StarButtonWidget.hpp"
+#include "StarLabelWidget.hpp"
+#include "StarTextBoxWidget.hpp"
+#include "StarImageWidget.hpp"
+#include "StarItemGridWidget.hpp"
+#include "StarListWidget.hpp"
+#include "StarTabSet.hpp"
+#include "StarAssets.hpp"
+#include "StarItemDatabase.hpp"
+#include "StarPlayerInventory.hpp"
+#include "StarItemBag.hpp"
+#include "StarQuestManager.hpp"
+
+namespace Star {
+
+MerchantPane::MerchantPane(
+ WorldClientPtr worldClient, PlayerPtr player, Json const& settings, EntityId sourceEntityId) {
+ m_worldClient = move(worldClient);
+ m_player = move(player);
+ m_sourceEntityId = sourceEntityId;
+
+ auto assets = Root::singleton().assets();
+ auto baseConfig = settings.get("config", "/interface/windowconfig/merchant.config");
+ m_settings = jsonMerge(assets->fetchJson(baseConfig), settings);
+
+ m_refreshTimer = GameTimer(assets->json("/merchant.config:autoRefreshRate").toFloat());
+
+ m_buyFactor = m_settings.getFloat("buyFactor", assets->json("/merchant.config:defaultBuyFactor").toFloat());
+ m_sellFactor = m_settings.getFloat("sellFactor", assets->json("/merchant.config:defaultSellFactor").toFloat());
+
+ m_itemBag = make_shared<ItemBag>(m_settings.getUInt("sellContainerSize"));
+
+ GuiReader reader;
+ reader.registerCallback("spinCount.up", [=](Widget*) {
+ if (m_selectedIndex != NPos) {
+ if (m_buyCount < maxBuyCount())
+ m_buyCount++;
+ else
+ m_buyCount = 1;
+ } else {
+ m_buyCount = 0;
+ }
+ countChanged();
+ });
+
+ reader.registerCallback("spinCount.down", [=](Widget*) {
+ if (m_selectedIndex != NPos) {
+ if (m_buyCount > 1)
+ m_buyCount--;
+ else
+ m_buyCount = std::max(maxBuyCount(), 1);
+ } else {
+ m_buyCount = 0;
+ }
+ countChanged();
+ });
+
+ reader.registerCallback("countChanged", [=](Widget*) { countChanged(); });
+ reader.registerCallback("parseCountText", [=](Widget*) { countTextChanged(); });
+
+ reader.registerCallback("buy", [=](Widget*) { buy(); });
+
+ reader.registerCallback("sell", [=](Widget*) { sell(); });
+
+ reader.registerCallback("close", [=](Widget*) { dismiss(); });
+
+ reader.registerCallback("itemGrid",
+ [=](Widget*) {
+ swapSlot();
+ updateSellTotal();
+ });
+
+ Json paneLayout = m_settings.get("paneLayout");
+ paneLayout = jsonMerge(paneLayout, m_settings.get("paneLayoutOverride", {}));
+ reader.construct(paneLayout, this);
+
+ m_tabSet = findChild<TabSetWidget>("buySellTabs");
+ m_tabSet->setCallback([this](Widget*) {
+ auto bgResult = getBG();
+ if (m_tabSet->selectedTab() == 0)
+ bgResult.body = m_settings.getString("buyBody");
+ else
+ bgResult.body = m_settings.getString("sellBody");
+ setBG(bgResult);
+ });
+ m_itemGuiList = findChild<ListWidget>("itemList");
+ m_countTextBox = findChild<TextBoxWidget>("tbCount");
+ m_buyTotalLabel = findChild<LabelWidget>("lblBuyTotal");
+ m_buyButton = findChild<ButtonWidget>("btnBuy");
+ m_sellTotalLabel = findChild<LabelWidget>("lblSellTotal");
+ m_sellButton = findChild<ButtonWidget>("btnSell");
+
+ m_itemGrid = findChild<ItemGridWidget>("itemGrid");
+ m_itemGrid->setItemBag(m_itemBag);
+
+ buildItemList();
+
+ updateSelection();
+
+ updateSellTotal();
+}
+
+void MerchantPane::displayed() {
+ Pane::displayed();
+}
+
+void MerchantPane::dismissed() {
+ Pane::dismissed();
+
+ for (auto unsold : m_itemBag->takeAll())
+ m_player->giveItem(unsold);
+
+ m_worldClient->sendEntityMessage(m_sourceEntityId, "onMerchantClosed");
+}
+
+PanePtr MerchantPane::createTooltip(Vec2I const& screenPosition) {
+ if (m_tabSet->selectedTab() == 0) {
+ for (size_t i = 0; i < m_itemGuiList->numChildren(); ++i) {
+ auto entry = m_itemGuiList->itemAt(i);
+ if (entry->getChildAt(screenPosition)) {
+ auto itemConfig = m_itemList.get(i);
+ ItemPtr item = Root::singleton().itemDatabase()->item(ItemDescriptor(itemConfig.get("item")));
+ return ItemTooltipBuilder::buildItemTooltip(item, m_player);
+ }
+ }
+ } else {
+ if (auto item = m_itemGrid->itemAt(screenPosition))
+ return ItemTooltipBuilder::buildItemTooltip(item, m_player);
+ }
+ return {};
+}
+
+void MerchantPane::update() {
+ Pane::update();
+
+ if (!m_worldClient->playerCanReachEntity(m_sourceEntityId))
+ dismiss();
+
+ if (m_refreshTimer.wrapTick()) {
+ for (size_t i = 0; i < m_itemList.size(); ++i) {
+ auto itemConfig = m_itemList.get(i);
+ auto itemWidget = m_itemGuiList->itemAt(i);
+ setupWidget(itemWidget, itemConfig);
+ }
+ updateBuyTotal();
+ }
+
+ updateSelection();
+
+ m_itemGrid->updateAllItemSlots();
+}
+
+EntityId MerchantPane::sourceEntityId() const {
+ return m_sourceEntityId;
+}
+
+ItemPtr MerchantPane::addItems(ItemPtr const& items) {
+ if (m_tabSet->selectedTab() == 1) {
+ auto remainder = m_itemBag->addItems(items);
+ updateSellTotal();
+ return remainder;
+ } else {
+ return items;
+ }
+}
+
+void MerchantPane::swapSlot() {
+ ItemPtr source = m_player->inventory()->swapSlotItem();
+ auto inv = m_player->inventory();
+ if (context()->shiftHeld()) {
+ if (m_itemGrid->selectedItem()) {
+ auto remainder = inv->addItems(m_itemBag->takeItems(m_itemGrid->selectedIndex()));
+ if (remainder && !remainder->empty())
+ m_itemBag->setItem(m_itemGrid->selectedIndex(), remainder);
+ }
+ } else {
+ if (auto heldItem = m_player->inventory()->swapSlotItem())
+ inv->setSwapSlotItem(m_itemBag->swapItems(m_itemGrid->selectedIndex(), heldItem));
+ else
+ inv->setSwapSlotItem(m_itemBag->takeItems(m_itemGrid->selectedIndex()));
+ }
+}
+
+void MerchantPane::buildItemList() {
+ m_itemGuiList->clear();
+ m_itemList = m_settings.getArray("items");
+
+ auto itemDatabase = Root::singleton().itemDatabase();
+ filter(m_itemList, [&](Json const& itemConfig) {
+ if (!itemDatabase->hasItem(ItemDescriptor(itemConfig.get("item")).name()))
+ return false;
+
+ if (auto prerequisite = itemConfig.optString("prerequisiteQuest")) {
+ if (!m_player->questManager()->hasCompleted(*prerequisite))
+ return false;
+ }
+
+ if (auto quests = itemConfig.optArray("exclusiveQuests")) {
+ for (auto quest : *quests) {
+ if (m_player->questManager()->hasQuest(quest.toString()))
+ return false;
+ }
+ }
+
+ if (auto prerequisite = itemConfig.optUInt("prerequisiteShipLevel")) {
+ if (m_player->shipUpgrades().shipLevel < *prerequisite)
+ return false;
+ }
+
+ if (auto maxLevel = itemConfig.optUInt("maxShipLevel")) {
+ if (m_player->shipUpgrades().shipLevel > *maxLevel)
+ return false;
+ }
+
+ return true;
+ });
+
+ for (auto itemConfig : m_itemList) {
+ auto widget = m_itemGuiList->addItem();
+ setupWidget(widget, itemConfig);
+ }
+}
+
+void MerchantPane::setupWidget(WidgetPtr const& widget, Json const& itemConfig) {
+ auto& root = Root::singleton();
+ auto assets = root.assets();
+ ItemPtr item = root.itemDatabase()->item(ItemDescriptor(itemConfig.get("item")));
+
+ String name = item->friendlyName();
+ if (item->count() > 1)
+ name = strf("%s (x%s)", name, item->count());
+
+ auto itemName = widget->fetchChild<LabelWidget>("itemName");
+ itemName->setText(name);
+
+ unsigned price = ceil(itemConfig.getInt("price", item->price()) * m_buyFactor);
+ widget->setLabel("priceLabel", strf("%s", price));
+ widget->setData(price);
+
+ bool unavailable = price > m_player->currency("money");
+ auto unavailableoverlay = widget->fetchChild<ImageWidget>("unavailableoverlay");
+ if (unavailable) {
+ itemName->setColor(Color::Gray);
+ unavailableoverlay->show();
+ } else {
+ itemName->setColor(Color::White);
+ unavailableoverlay->hide();
+ }
+
+ widget->fetchChild<ItemSlotWidget>("itemIcon")->setItem(item);
+ widget->show();
+}
+
+void MerchantPane::updateSelection() {
+ if (m_selectedIndex != m_itemGuiList->selectedItem()) {
+ m_selectedIndex = m_itemGuiList->selectedItem();
+
+ if (m_selectedIndex != NPos) {
+ auto itemConfig = m_itemList.get(m_selectedIndex);
+ m_selectedItem = Root::singleton().itemDatabase()->item(ItemDescriptor(itemConfig.get("item")));
+ findChild<ButtonWidget>("spinCount.up")->enable();
+ findChild<ButtonWidget>("spinCount.down")->enable();
+ m_countTextBox->setColor(Color::White);
+ m_buyCount = 1;
+ } else {
+ findChild<ButtonWidget>("spinCount.up")->disable();
+ findChild<ButtonWidget>("spinCount.down")->disable();
+ m_countTextBox->setColor(Color::Gray);
+ m_buyCount = 0;
+ }
+
+ countChanged();
+ }
+}
+
+void MerchantPane::updateBuyTotal() {
+ if (auto selected = m_itemGuiList->selectedWidget())
+ m_buyTotal = selected->data().toUInt() * m_buyCount;
+ else
+ m_buyTotal = 0;
+
+ m_buyTotalLabel->setText(strf("%s", m_buyTotal));
+
+ if (m_selectedIndex != NPos && m_buyCount > 0)
+ m_buyButton->enable();
+ else
+ m_buyButton->disable();
+
+ if (m_buyTotal > (int)m_player->inventory()->currency("money")) {
+ m_buyTotalLabel->setColor(Color::Red);
+ m_buyButton->disable();
+ } else {
+ m_buyTotalLabel->setColor(Color::White);
+ }
+}
+
+void MerchantPane::buy() {
+ if (m_buyTotal > 0 && m_player->inventory()->consumeCurrency("money", m_buyTotal)) {
+ auto countRemaining = m_buyCount;
+ while (countRemaining > 0) {
+ auto buyItem = m_selectedItem->clone();
+ buyItem->setCount(m_selectedItem->count() * countRemaining);
+ countRemaining -= buyItem->count();
+ m_player->giveItem(buyItem);
+ }
+
+ auto reportItem = m_selectedItem->clone();
+ reportItem->setCount(reportItem->count() * m_buyCount, true);
+ auto buySummary = JsonObject{{"item", reportItem->descriptor().toJson()}, {"total", m_buyTotal}};
+ m_worldClient->sendEntityMessage(m_sourceEntityId, "onBuy", {buySummary});
+
+ auto& guiContext = GuiContext::singleton();
+ guiContext.playAudio(Root::singleton().assets()->json("/merchant.config:buySound").toString());
+
+ buildItemList();
+
+ updateBuyTotal();
+ }
+}
+
+void MerchantPane::updateSellTotal() {
+ m_sellTotal = 0;
+ for (auto item : m_itemBag->items()) {
+ if (item)
+ m_sellTotal += round(item->price() * m_sellFactor);
+ }
+ m_sellTotalLabel->setText(strf("%s", m_sellTotal));
+ if (m_sellTotal > 0)
+ m_sellButton->enable();
+ else
+ m_sellButton->disable();
+}
+
+void MerchantPane::sell() {
+ if (m_sellTotal > 0) {
+ auto sellSummary = JsonObject{{"items", m_itemBag->toJson()}, {"total", m_sellTotal}};
+ m_worldClient->sendEntityMessage(m_sourceEntityId, "onSell", {sellSummary});
+
+ m_player->inventory()->addCurrency("money", m_sellTotal);
+ m_itemBag->clearItems();
+ updateSellTotal();
+
+ auto& guiContext = GuiContext::singleton();
+ guiContext.playAudio(Root::singleton().assets()->json("/merchant.config:sellSound").toString());
+ }
+}
+
+int MerchantPane::maxBuyCount() {
+ if (auto selected = m_itemGuiList->selectedWidget()) {
+ auto assets = Root::singleton().assets();
+ auto unitPrice = selected->data().toUInt();
+ if (unitPrice == 0)
+ return 1000;
+ return min(1000, (int)floor(m_player->currency("money") / unitPrice));
+ } else {
+ return 0;
+ }
+}
+
+void MerchantPane::countChanged() {
+ m_countTextBox->setText(strf("x%s", m_buyCount));
+ updateBuyTotal();
+}
+
+void MerchantPane::countTextChanged() {
+ if (m_selectedIndex == NPos) {
+ m_buyCount = 0;
+ countChanged();
+ } else {
+ try {
+ auto countString = m_countTextBox->getText().replace("x", "");
+ if (countString.size()) {
+ m_buyCount = clamp<int>(lexicalCast<int>(countString), 1, maxBuyCount());
+ countChanged();
+ }
+ } catch (BadLexicalCast const&) {
+ m_buyCount = 1;
+ countChanged();
+ }
+ }
+}
+
+}