osb/source/frontend/StarInventory.cpp
Kai Blaschke 431a9c00a5
Fixed a huge amount of Clang warnings
On Linux and macOS, using Clang to compile OpenStarbound produces about 400 MB worth of warnings during the build, making the compiler output unreadable and slowing the build down considerably.

99% of the warnings were unqualified uses of std::move and std::forward, which are now all properly qualified.

Fixed a few other minor warnings about non-virtual destructors and some uses of std::move preventing copy elision on temporary objects.

Most remaining warnings are now unused parameters.
2024-02-19 16:55:19 +01:00

458 lines
16 KiB
C++

#include "StarInventory.hpp"
#include "StarGuiReader.hpp"
#include "StarItemTooltip.hpp"
#include "StarSimpleTooltip.hpp"
#include "StarRoot.hpp"
#include "StarUniverseClient.hpp"
#include "StarItemGridWidget.hpp"
#include "StarButtonWidget.hpp"
#include "StarPortraitWidget.hpp"
#include "StarPaneManager.hpp"
#include "StarLabelWidget.hpp"
#include "StarImageWidget.hpp"
#include "StarPlayerInventory.hpp"
#include "StarPlayerCompanions.hpp"
#include "StarWorldClient.hpp"
#include "StarAssets.hpp"
#include "StarItem.hpp"
#include "StarMainInterface.hpp"
#include "StarMerchantInterface.hpp"
#include "StarJsonExtra.hpp"
#include "StarStatistics.hpp"
#include "StarAugmentItem.hpp"
namespace Star {
InventoryPane::InventoryPane(MainInterface* parent, PlayerPtr player, ContainerInteractorPtr containerInteractor) {
m_parent = parent;
m_player = std::move(player);
m_containerInteractor = std::move(containerInteractor);
GuiReader invWindowReader;
auto config = Root::singleton().assets()->json("/interface/windowconfig/playerinventory.config");
auto leftClickCallback = [this](String const& bagType, Widget* widget) {
auto itemGrid = convert<ItemGridWidget>(widget);
InventorySlot inventorySlot = BagSlot(bagType, itemGrid->selectedIndex());
auto inventory = m_player->inventory();
if (context()->shiftHeld()) {
if (auto sourceItem = itemGrid->selectedItem()) {
if (auto activeMerchantPane = m_parent->activeMerchantPane()) {
auto remainder = activeMerchantPane->addItems(inventory->takeSlot(inventorySlot));
if (remainder && !remainder->empty())
inventory->setItem(inventorySlot, remainder);
} else if (m_containerInteractor->containerOpen()) {
inventory->takeSlot(inventorySlot);
m_containerInteractor->addToContainer(sourceItem);
m_containerSource = inventorySlot;
m_expectingSwap = true;
}
}
} else {
inventory->shiftSwap(inventorySlot);
}
};
auto rightClickCallback = [this](InventorySlot slot) {
auto inventory = m_player->inventory();
if (ItemPtr slotItem = inventory->itemsAt(slot)) {
auto swapItem = inventory->swapSlotItem();
if (!swapItem || swapItem->empty() || swapItem->couldStack(slotItem)) {
uint64_t count = swapItem ? swapItem->couldStack(slotItem) : slotItem->maxStack();
if (context()->shiftHeld())
count = max(1, min<int>(count, slotItem->count() / 2));
else
count = 1;
if (auto taken = slotItem->take(count)) {
if (swapItem)
swapItem->stackWith(taken);
else
inventory->setSwapSlotItem(taken);
}
} else if (auto augment = as<AugmentItem>(swapItem)) {
if (auto augmented = augment->applyTo(slotItem))
inventory->setItem(slot, augmented);
}
}
else if (auto swapSlot = inventory->swapSlotItem()) {
if (auto es = slot.ptr<EquipmentSlot>()) {
if (inventory->itemAllowedAsEquipment(swapSlot, *es))
inventory->setItem(slot, swapSlot->take(1));
} else if (slot.is<TrashSlot>()) {
inventory->setItem(slot, swapSlot->take(1));
} else if (auto bs = slot.ptr<BagSlot>()) {
if (inventory->itemAllowedInBag(swapSlot, bs->first))
inventory->setItem(slot, swapSlot->take(1));
}
}
};
auto bagGridCallback = [rightClickCallback](String const& bagType, Widget* widget) {
auto slot = BagSlot(bagType, convert<ItemGridWidget>(widget)->selectedIndex());
rightClickCallback(slot);
};
Json itemBagConfig = config.get("bagConfig");
auto bagOrder = itemBagConfig.toObject().keys().sorted([&itemBagConfig](String const& a, String const& b) {
return itemBagConfig.get(a).getInt("order", 0) < itemBagConfig.get(b).getInt("order", 0);
});
for (auto name : bagOrder) {
auto itemGrid = itemBagConfig.get(name).getString("itemGrid");
invWindowReader.registerCallback(itemGrid, bind(leftClickCallback, name, _1));
invWindowReader.registerCallback(strf("{}.right", itemGrid), bind(bagGridCallback, name, _1));
}
invWindowReader.registerCallback("close", [=](Widget*) {
dismiss();
});
invWindowReader.registerCallback("sort", [=](Widget*) {
m_player->inventory()->condenseBagStacks(m_selectedTab);
m_player->inventory()->sortBag(m_selectedTab);
// Don't show sorted items as new items
m_itemGrids[m_selectedTab]->updateItemState();
m_itemGrids[m_selectedTab]->clearChangedSlots();
});
invWindowReader.registerCallback("gridModeSelector", [=](Widget* widget) {
auto selected = convert<ButtonWidget>(widget)->data().toString();
selectTab(m_tabButtonData.keyOf(selected));
});
auto registerSlotCallbacks = [&](String name, InventorySlot slot) {
invWindowReader.registerCallback(name, [=](Widget* paneObj) {
if (as<ItemSlotWidget>(paneObj))
m_player->inventory()->shiftSwap(slot);
else
throw GuiException("Invalid object type, expected ItemSlotWidget");
});
invWindowReader.registerCallback(name + ".right", [=](Widget* paneObj) {
if (as<ItemSlotWidget>(paneObj))
rightClickCallback(slot);
else
throw GuiException("Invalid object type, expected ItemSlotWidget");
});
};
for (auto const p : EquipmentSlotNames)
registerSlotCallbacks(p.second, p.first);
registerSlotCallbacks("trash", TrashSlot());
invWindowReader.construct(config.get("paneLayout"), this);
m_trashSlot = fetchChild<ItemSlotWidget>("trash");
m_trashBurn = GameTimer(config.get("trashBurnTimeout").toFloat());
m_disabledTechOverlays.append(fetchChild<ImageWidget>("techHeadDisabled"));
m_disabledTechOverlays.append(fetchChild<ImageWidget>("techBodyDisabled"));
m_disabledTechOverlays.append(fetchChild<ImageWidget>("techLegsDisabled"));
for (auto const p : EquipmentSlotNames) {
if (auto itemSlot = fetchChild<ItemSlotWidget>(p.second))
itemSlot->setItem(m_player->inventory()->itemsAt(p.first));
}
for (auto name : bagOrder) {
auto itemTab = itemBagConfig.get(name);
m_itemGrids[name] = fetchChild<ItemGridWidget>(itemTab.getString("itemGrid"));
m_itemGrids[name]->setItemBag(m_player->inventory()->bagContents(name));
m_itemGrids[name]->hide();
m_newItemMarkers[name] = fetchChild<Widget>(itemTab.getString("newItemMarker"));
m_tabButtonData[name] = itemTab.getString("tabButtonData");
}
selectTab(bagOrder[0]);
auto centralPortrait = fetchChild<PortraitWidget>("portrait");
centralPortrait->setEntity(m_player);
auto portrait = make_shared<PortraitWidget>(m_player, PortraitMode::Bust);
portrait->setIconMode();
setTitle(portrait, m_player->name(), config.getString("subtitle"));
m_expectingSwap = false;
if (auto item = m_player->inventory()->swapSlotItem())
m_currentSwapSlotItem = item->descriptor();
m_pickUpSounds = jsonToStringList(config.get("sounds").get("pickup"));
m_putDownSounds = jsonToStringList(config.get("sounds").get("putdown"));
m_someUpSounds = jsonToStringList(config.get("sounds").get("someup"));
m_someDownSounds = jsonToStringList(config.get("sounds").get("somedown"));
}
void InventoryPane::displayed() {
Pane::displayed();
m_expectingSwap = false;
for (auto grid : m_itemGrids)
grid.second->updateItemState();
m_itemGrids[m_selectedTab]->indicateChangedSlots();
}
PanePtr InventoryPane::createTooltip(Vec2I const& screenPosition) {
ItemPtr item;
if (auto child = getChildAt(screenPosition)) {
if (auto itemSlot = as<ItemSlotWidget>(child)) {
item = itemSlot->item();
if (!item) {
auto widgetData = itemSlot->data();
if (widgetData && widgetData.type() == Json::Type::Object) {
if (auto text = widgetData.optString("tooltipText"))
return SimpleTooltipBuilder::buildTooltip(*text);
}
}
}
if (auto itemGrid = as<ItemGridWidget>(child))
item = itemGrid->itemAt(screenPosition);
}
if (item)
return ItemTooltipBuilder::buildItemTooltip(item, m_player);
auto techDatabase = Root::singleton().techDatabase();
for (auto const& p : TechTypeNames) {
if (auto techIcon = fetchChild<ImageWidget>(strf("tech{}", p.second))) {
if (techIcon->screenBoundRect().contains(screenPosition)) {
if (auto techModule = m_player->techs()->equippedTechs().maybe(p.first))
return SimpleTooltipBuilder::buildTooltip(techDatabase->tech(*techModule).description);
}
}
}
return {};
}
bool InventoryPane::giveContainerResult(ContainerResult result) {
if (!m_expectingSwap)
return false;
for (auto& item : result) {
auto inv = m_player->inventory();
m_player->triggerPickupEvents(item);
auto remainder = inv->stackWith(m_containerSource, item);
if (remainder && !remainder->empty())
m_player->giveItem(remainder);
}
m_expectingSwap = false;
return true;
}
void InventoryPane::updateItems() {
for (auto& p : m_itemGrids)
p.second->updateItemState();
}
bool InventoryPane::containsNewItems() const {
for (auto& p : m_itemGrids) {
if (p.second->slotsChanged())
return true;
}
return false;
}
void InventoryPane::clearChangedSlots() {
for (auto& p : m_itemGrids) {
p.second->updateItemState();
p.second->clearChangedSlots();
}
}
void InventoryPane::update(float dt) {
auto inventory = m_player->inventory();
auto context = Widget::context();
HashSet<ItemPtr> customBarItems;
for (uint8_t i = 0; i < inventory->customBarIndexes(); ++i) {
if (auto primarySlot = inventory->customBarPrimarySlot(i)) {
if (auto primaryItem = inventory->itemsAt(*primarySlot))
customBarItems.add(primaryItem);
}
if (auto secondarySlot = inventory->customBarSecondarySlot(i)) {
if (auto secondaryItem = inventory->itemsAt(*secondarySlot))
customBarItems.add(secondaryItem);
}
}
m_trashSlot->setItem(inventory->itemsAt(TrashSlot()));
m_trashSlot->showLinkIndicator(customBarItems.contains(m_trashSlot->item()));
if (auto trashItem = m_trashSlot->item()) {
if (m_trashBurn.tick(dt) && trashItem->count() > 0) {
m_player->statistics()->recordEvent("trashItem", JsonObject{
{"itemName", trashItem->name()},
{"count", trashItem->count()},
{"category", trashItem->category()}
});
trashItem->take(trashItem->count());
}
} else {
m_trashBurn.reset();
}
m_trashSlot->setProgress(m_trashBurn.timer / m_trashBurn.time);
for (auto const& p : EquipmentSlotNames) {
if (auto itemSlot = fetchChild<ItemSlotWidget>(p.second)) {
itemSlot->setItem(inventory->itemsAt(p.first));
itemSlot->showLinkIndicator(customBarItems.contains(itemSlot->item()));
}
}
auto techDatabase = Root::singleton().techDatabase();
for (auto const& p : TechTypeNames) {
if (auto techIcon = fetchChild<ImageWidget>(strf("tech{}", p.second))) {
if (auto techModule = m_player->techs()->equippedTechs().maybe(p.first))
techIcon->setImage(techDatabase->tech(*techModule).icon);
else
techIcon->setImage("");
}
}
if (ItemPtr swapSlot = inventory->swapSlotItem()) {
if (!PlayerInventory::itemAllowedInBag(swapSlot, m_selectedTab)) {
for (auto& pair : m_itemGrids) {
if (pair.first != m_selectedTab && PlayerInventory::itemAllowedInBag(swapSlot, pair.first)) {
selectTab(pair.first);
break;
}
}
}
}
for (auto p : m_itemGrids) {
p.second->updateItemState();
for (size_t i = 0; i < p.second->itemSlots(); ++i) {
auto itemWidget = p.second->itemWidgetAt(i);
itemWidget->showLinkIndicator(customBarItems.contains(itemWidget->item()));
}
}
m_itemGrids[m_selectedTab]->clearChangedSlots();
for (auto& pair : m_newItemMarkers) {
if (m_itemGrids[pair.first]->slotsChanged())
pair.second->show();
else
pair.second->hide();
}
for (auto& techOverlay : m_disabledTechOverlays)
techOverlay->setVisibility(m_player->techOverridden());
auto healthLabel = fetchChild<LabelWidget>("healthtext");
healthLabel->setText(toString(m_player->maxHealth()));
auto energyLabel = fetchChild<LabelWidget>("energytext");
energyLabel->setText(toString(m_player->maxEnergy()));
auto weaponLabel = fetchChild<LabelWidget>("weapontext");
weaponLabel->setText(strf("{}%", ceil(m_player->powerMultiplier() * 100)));
auto defenseLabel = fetchChild<LabelWidget>("defensetext");
if (m_player->protection() == 0)
defenseLabel->setText("--");
else
defenseLabel->setText(toString(ceil(m_player->protection())));
auto moneyLabel = fetchChild<LabelWidget>("lblMoney");
moneyLabel->setText(toString(m_player->currency("money")));
if (m_player->currency("essence") > 0) {
fetchChild<ImageWidget>("imgEssenceIcon")->show();
auto essenceLabel = fetchChild<LabelWidget>("lblEssence");
essenceLabel->show();
essenceLabel->setText(toString(m_player->currency("essence")));
} else {
fetchChild<ImageWidget>("imgEssenceIcon")->hide();
fetchChild<LabelWidget>("lblEssence")->hide();
}
auto config = Root::singleton().assets()->json("/interface/windowconfig/playerinventory.config");
auto pets = m_player->companions()->getCompanions("pets");
if (pets.size() > 0) {
auto pet = pets.first();
auto companionImage = fetchChild<ImageWidget>("companionSlot");
companionImage->setVisibility(true);
companionImage->setDrawables(pet->portrait());
auto nameLabel = fetchChild<LabelWidget>("companionName");
if (auto name = pet->name()) {
nameLabel->setText(pet->name()->toUpper());
} else {
nameLabel->setText(config.getString("defaultPetNameLabel"));
}
auto attackLabel = fetchChild<LabelWidget>("companionAttackStat");
if (auto attack = pet->stat("attack")) {
attackLabel->setText(strf("{:.0f}", *attack));
} else {
attackLabel->setText("");
}
auto defenseLabel = fetchChild<LabelWidget>("companionDefenseStat");
if (auto defense = pet->stat("defense")) {
defenseLabel->setText(strf("{:.0f}", *defense));
} else {
defenseLabel->setText("");
}
if (containsChild("companionHealthBar")) {
auto healthBar = fetchChild<ProgressWidget>("companionHealthBar");
Maybe<float> health = pet->resource("health");
Maybe<float> healthMax = pet->resourceMax("health");
if (health && healthMax) {
healthBar->setCurrentProgressLevel(*health);
healthBar->setMaxProgressLevel(*healthMax);
} else {
healthBar->setCurrentProgressLevel(0);
healthBar->setMaxProgressLevel(1);
}
}
} else {
fetchChild<ImageWidget>("companionSlot")->setVisibility(false);
fetchChild<LabelWidget>("companionName")->setText(config.getString("defaultPetNameLabel"));
fetchChild<LabelWidget>("companionAttackStat")->setText("");
fetchChild<LabelWidget>("companionDefenseStat")->setText("");
if (containsChild("companionHealthBar")) {
auto healthBar = fetchChild<ProgressWidget>("companionHealthBar");
healthBar->setCurrentProgressLevel(0);
healthBar->setMaxProgressLevel(1);
}
}
if (auto item = inventory->swapSlotItem()) {
if (!m_currentSwapSlotItem || !item->matches(*m_currentSwapSlotItem, true))
context->playAudio(RandomSource().randFrom(m_pickUpSounds));
else if (item->count() > m_currentSwapSlotItem->count())
context->playAudio(RandomSource().randFrom(m_someUpSounds));
else if (item->count() < m_currentSwapSlotItem->count())
context->playAudio(RandomSource().randFrom(m_someDownSounds));
m_currentSwapSlotItem = item->descriptor();
} else {
if (m_currentSwapSlotItem)
context->playAudio(RandomSource().randFrom(m_putDownSounds));
m_currentSwapSlotItem = {};
}
m_title = m_player->name();
Pane::update(dt);
}
void InventoryPane::selectTab(String const& selected) {
for (auto grid : m_itemGrids)
grid.second->hide();
m_selectedTab = selected;
m_itemGrids[m_selectedTab]->show();
m_itemGrids[m_selectedTab]->indicateChangedSlots();
auto tabs = fetchChild<ButtonGroupWidget>("gridModeSelector");
for (auto button : tabs->buttons())
if (button->data().toString().equalsIgnoreCase(m_tabButtonData[selected]))
tabs->select(tabs->id(button));
}
}