feat: middle click objects in the inventory to open their interface

makes carrying around shop objects easier

might need to restrict the allowed interaction types more, as some may break due to the source entity being the player
This commit is contained in:
Kae 2024-03-27 16:00:13 +11:00
parent c484fab32d
commit 888cde79ef
9 changed files with 102 additions and 38 deletions

View File

@ -20,6 +20,8 @@
#include "StarJsonExtra.hpp"
#include "StarStatistics.hpp"
#include "StarAugmentItem.hpp"
#include "StarObjectItem.hpp"
#include "StarInteractionTypes.hpp"
namespace Star {
@ -94,6 +96,27 @@ InventoryPane::InventoryPane(MainInterface* parent, PlayerPtr player, ContainerI
rightClickCallback(slot);
};
auto middleClickCallback = [this](String const& bagType, Widget* widget) {
if (!m_player->inWorld())
return;
auto itemGrid = convert<ItemGridWidget>(widget);
InventorySlot inventorySlot = BagSlot(bagType, itemGrid->selectedIndex());
auto inventory = m_player->inventory();
if (auto sourceItem = as<ObjectItem>(itemGrid->selectedItem())) {
if (auto actionTypeName = sourceItem->instanceValue("interactAction")) {
auto actionType = InteractActionTypeNames.getLeft(actionTypeName.toString());
if (actionType >= InteractActionType::OpenCraftingInterface && actionType <= InteractActionType::ScriptPane) {
auto actionData = sourceItem->instanceValue("interactData", Json());
if (actionData.isType(Json::Type::Object))
actionData = actionData.set("openWithInventory", false);
InteractAction action(actionType, m_player->entityId(), actionData);
m_player->interact(action);
}
}
}
};
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);
@ -102,6 +125,7 @@ InventoryPane::InventoryPane(MainInterface* parent, PlayerPtr player, ContainerI
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(strf("{}.middle", itemGrid), bind(middleClickCallback, name, _1));
}
invWindowReader.registerCallback("close", [=](Widget*) {

View File

@ -199,6 +199,9 @@ bool MainInterface::escapeDialogOpen() const {
void MainInterface::openCraftingWindow(Json const& config, EntityId sourceEntityId) {
if (m_craftingWindow && m_paneManager.isDisplayed(m_craftingWindow)) {
m_paneManager.dismissPane(m_craftingWindow);
bool fromPlayer = false;
if (auto player = m_client->mainPlayer())
fromPlayer = player->inWorld() && player->entityId() == sourceEntityId;
if (m_craftingWindow->sourceEntityId() == sourceEntityId) {
m_craftingWindow.reset();
return;
@ -215,21 +218,27 @@ void MainInterface::openCraftingWindow(Json const& config, EntityId sourceEntity
void MainInterface::openMerchantWindow(Json const& config, EntityId sourceEntityId) {
if (m_merchantWindow && m_paneManager.isDisplayed(m_merchantWindow)) {
m_paneManager.dismissPane(m_merchantWindow);
if (m_merchantWindow->sourceEntityId() == sourceEntityId) {
bool fromPlayer = false;
if (auto player = m_client->mainPlayer())
fromPlayer = player->inWorld() && player->entityId() == sourceEntityId;
if (!fromPlayer && m_merchantWindow->sourceEntityId() == sourceEntityId) {
m_merchantWindow.reset();
return;
}
}
bool openWithInventory = config.getBool("openWithInventory", true);
m_merchantWindow = make_shared<MerchantPane>(m_client->worldClient(), m_client->mainPlayer(), config, sourceEntityId);
m_paneManager.displayPane(PaneLayer::Window,
m_merchantWindow,
[this](PanePtr const&) {
[this, openWithInventory](PanePtr const&) {
if (auto player = m_client->mainPlayer())
player->clearSwap();
m_paneManager.dismissRegisteredPane(MainInterfacePanes::Inventory);
if (openWithInventory)
m_paneManager.dismissRegisteredPane(MainInterfacePanes::Inventory);
});
m_paneManager.displayRegisteredPane(MainInterfacePanes::Inventory);
if (openWithInventory)
m_paneManager.displayRegisteredPane(MainInterfacePanes::Inventory);
m_paneManager.bringPaneAdjacent(m_paneManager.registeredPane(MainInterfacePanes::Inventory),
m_merchantWindow, Root::singleton().assets()->json("/interface.config:bringAdjacentWindowGap").toFloat());

View File

@ -379,7 +379,7 @@ CollisionKind MaterialDatabase::materialCollisionKind(MaterialId materialId) con
}
bool MaterialDatabase::canPlaceInLayer(MaterialId materialId, TileLayer layer) const {
return layer == TileLayer::Foreground || !getMaterialInfo(materialId)->foregroundOnly;
return layer != TileLayer::Background || !getMaterialInfo(materialId)->foregroundOnly;
}
ItemDescriptor MaterialDatabase::materialItemDrop(MaterialId materialId) const {

View File

@ -1650,14 +1650,14 @@ void UniverseServer::acceptConnection(UniverseConnection connection, Maybe<HostA
WriteLocker clientsLocker(m_clientsLock);
if (auto clashId = getClientForUuid(clientConnect->playerUuid)) {
if (administrator) {
doDisconnection(*clashId, String("Duplicate Uuid joined and is Administrator so has priority."));
doDisconnection(*clashId, "Duplicate Uuid joined and is Administrator so has priority.");
} else {
connectionFail("Duplicate player UUID");
return;
}
}
if (m_clients.size() + 1 > m_maxPlayers) {
if (m_clients.size() + 1 > m_maxPlayers && !administrator) {
connectionFail("Max player connections");
return;
}
@ -1734,8 +1734,14 @@ void UniverseServer::acceptConnection(UniverseConnection connection, Maybe<HostA
clientWarpPlayer(clientId, WarpAlias::OwnShip);
}
} else {
Logger::info("UniverseServer: Spawning player at ship");
clientWarpPlayer(clientId, WarpAlias::OwnShip);
Maybe<String> defaultReviveWarp = assets->json("/universe_server.config").optString("defaultReviveWarp");
if (defaultReviveWarp) {
Logger::info("UniverseServer: Spawning player at default warp");
clientWarpPlayer(clientId, parseWarpAction(*defaultReviveWarp));
} else {
Logger::info("UniverseServer: Spawning player at ship");
clientWarpPlayer(clientId, WarpAlias::OwnShip);
}
}
clientFlyShip(clientId, clientContext->shipCoordinate().location(), clientContext->shipLocation());

View File

@ -106,7 +106,9 @@ Vec2I ItemGridWidget::positionOfSlot(size_t slotNumber) {
bool ItemGridWidget::sendEvent(InputEvent const& event) {
if (m_visible) {
if (auto mouseButton = event.ptr<MouseButtonDownEvent>()) {
if (mouseButton->mouseButton == MouseButton::Left || (m_rightClickCallback && mouseButton->mouseButton == MouseButton::Right)) {
if (mouseButton->mouseButton == MouseButton::Left
|| (m_rightClickCallback && mouseButton->mouseButton == MouseButton::Right)
|| (m_middleClickCallback && mouseButton->mouseButton == MouseButton::Middle)) {
Vec2I mousePos = *context()->mousePosition(event);
for (size_t i = 0; i < (m_bag->size() - m_bagOffset) && i < unsigned(m_dimensions[0] * m_dimensions[1]); ++i) {
Vec2I loc = locOfItemSlot(i);
@ -116,6 +118,8 @@ bool ItemGridWidget::sendEvent(InputEvent const& event) {
m_selectedIndex = i;
if (mouseButton->mouseButton == MouseButton::Right)
m_rightClickCallback(this);
else if (mouseButton->mouseButton == MouseButton::Middle)
m_middleClickCallback(this);
else
m_callback(this);
return true;
@ -145,6 +149,10 @@ void ItemGridWidget::setRightClickCallback(WidgetCallbackFunc callback) {
m_rightClickCallback = callback;
}
void ItemGridWidget::setMiddleClickCallback(WidgetCallbackFunc callback) {
m_middleClickCallback = callback;
}
void ItemGridWidget::setItemBag(ItemBagConstPtr bag) {
m_bag = bag;

View File

@ -41,6 +41,7 @@ public:
bool sendEvent(InputEvent const& event) override;
void setCallback(WidgetCallbackFunc callback);
void setRightClickCallback(WidgetCallbackFunc callback);
void setMiddleClickCallback(WidgetCallbackFunc callback);
void setItemBag(ItemBagConstPtr bag);
void setProgress(float progress);
@ -93,6 +94,7 @@ private:
unsigned m_selectedIndex;
WidgetCallbackFunc m_callback;
WidgetCallbackFunc m_rightClickCallback;
WidgetCallbackFunc m_middleClickCallback;
};
}

View File

@ -67,12 +67,16 @@ void ItemSlotWidget::update(float dt) {
bool ItemSlotWidget::sendEvent(InputEvent const& event) {
if (m_visible) {
if (auto mouseButton = event.ptr<MouseButtonDownEvent>()) {
if (mouseButton->mouseButton == MouseButton::Left || (m_rightClickCallback && mouseButton->mouseButton == MouseButton::Right)) {
if (mouseButton->mouseButton == MouseButton::Left
|| (m_rightClickCallback && mouseButton->mouseButton == MouseButton::Right)
|| (m_middleClickCallback && mouseButton->mouseButton == MouseButton::Middle)) {
Vec2I mousePos = *context()->mousePosition(event);
RectI itemArea = m_itemDraggableArea.translated(screenPosition());
if (itemArea.contains(mousePos)) {
if (mouseButton->mouseButton == MouseButton::Right)
m_rightClickCallback(this);
else if (mouseButton->mouseButton == MouseButton::Middle)
m_middleClickCallback(this);
else
m_callback(this);
return true;
@ -92,6 +96,10 @@ void ItemSlotWidget::setRightClickCallback(WidgetCallbackFunc callback) {
m_rightClickCallback = callback;
}
void ItemSlotWidget::setMiddleClickCallback(WidgetCallbackFunc callback) {
m_middleClickCallback = callback;
}
void ItemSlotWidget::setItem(ItemPtr const& item) {
m_item = item;
}

View File

@ -19,6 +19,7 @@ public:
bool sendEvent(InputEvent const& event) override;
void setCallback(WidgetCallbackFunc callback);
void setRightClickCallback(WidgetCallbackFunc callback);
void setMiddleClickCallback(WidgetCallbackFunc callback);
void setItem(ItemPtr const& item);
ItemPtr item() const;
void setProgress(float progress);
@ -61,6 +62,7 @@ private:
WidgetCallbackFunc m_callback;
WidgetCallbackFunc m_rightClickCallback;
WidgetCallbackFunc m_middleClickCallback;
float m_progress;
ProgressWidgetPtr m_durabilityBar;

View File

@ -471,23 +471,27 @@ WidgetConstructResult WidgetParser::itemSlotHandler(String const& name, Json con
String backingImage = config.getString("backingImage", "");
String callback = config.getString("callback", name);
String rightClickCallback;
if (callback.equals("null"))
rightClickCallback = callback;
else
rightClickCallback = callback + ".right";
String rightClickCallback = callback.equals("null") ? callback : callback + ".right";
rightClickCallback = config.getString("rightClickCallback", rightClickCallback);
String middleClickCallback = callback.equals("null") ? callback : callback + ".middle";
middleClickCallback = config.getString("middleClickCallback", middleClickCallback);
auto itemSlot = make_shared<ItemSlotWidget>(ItemPtr(), backingImage);
if (!m_callbacks.contains(callback))
throw WidgetParserException::format("Failed to find itemSlot callback named: '{}'", callback);
itemSlot->setCallback(m_callbacks.get(callback));
if (auto leftClickCallback = m_callbacks.ptr(callback))
itemSlot->setCallback(*leftClickCallback);
else
throw WidgetParserException::format("Failed to find ItemSlot callback named: '{}'", callback);
if (!m_callbacks.contains(rightClickCallback))
throw WidgetParserException::format("Failed to find itemslot rightClickCallback named: '{}'", rightClickCallback);
if (auto callback = m_callbacks.ptr(rightClickCallback))
itemSlot->setRightClickCallback(*callback);
else
throw WidgetParserException::format("Failed to find ItemSlot rightClickCallback named: '{}'", rightClickCallback);
if (auto callback = m_callbacks.ptr(middleClickCallback))
itemSlot->setMiddleClickCallback(*callback);
itemSlot->setRightClickCallback(m_callbacks.get(rightClickCallback));
itemSlot->setBackingImageAffinity(config.getBool("showBackingImageWhenFull", false), config.getBool("showBackingImageWhenEmpty", true));
itemSlot->showDurability(config.getBool("showDurability", false));
itemSlot->showCount(config.getBool("showCount", true));
@ -515,33 +519,34 @@ WidgetConstructResult WidgetParser::itemGridHandler(String const& name, Json con
} catch (MapException const& e) {
throw WidgetParserException::format("Malformed gui json, missing a required value in the map. {}", outputException(e, false));
}
String backingImage = config.getString("backingImage", "");
String callback = config.getString("callback", name);
String rightClickCallback;
if (callback.equals("null"))
rightClickCallback = callback;
else
rightClickCallback = callback + ".right";
String rightClickCallback = callback.equals("null") ? callback : callback + ".right";
rightClickCallback = config.getString("rightClickCallback", rightClickCallback);
String middleClickCallback = callback.equals("null") ? callback : callback + ".middle";
middleClickCallback = config.getString("middleClickCallback", middleClickCallback);
unsigned slotOffset = config.getInt("slotOffset", 0);
String backingImage = config.getString("backingImage", "");
auto itemGrid = make_shared<ItemGridWidget>(ItemBagConstPtr(), dimensions, rowSpacing, columnSpacing, backingImage, slotOffset);
if (!m_callbacks.contains(callback))
throw WidgetParserException::format("Failed to find itemgrid callback named: '{}'", callback);
itemGrid->setCallback(m_callbacks.get(callback));
itemGrid->setBackingImageAffinity(
config.getBool("showBackingImageWhenFull", false), config.getBool("showBackingImageWhenEmpty", true));
config.getBool("showBackingImageWhenFull", false), config.getBool("showBackingImageWhenEmpty", true));
itemGrid->showDurability(config.getBool("showDurability", false));
if (!m_callbacks.contains(rightClickCallback))
throw WidgetParserException::format("Failed to find itemgrid rightClickCallback named: '{}'", rightClickCallback);
if (auto leftClickCallback = m_callbacks.ptr(callback))
itemGrid->setCallback(*leftClickCallback);
else
throw WidgetParserException::format("Failed to find ItemGrid callback named: '{}'", callback);
if (auto callback = m_callbacks.ptr(rightClickCallback))
itemGrid->setRightClickCallback(*callback);
else
throw WidgetParserException::format("Failed to find ItemGrid rightClickCallback named: '{}'", rightClickCallback);
itemGrid->setRightClickCallback(m_callbacks.get(rightClickCallback));
if (auto callback = m_callbacks.ptr(middleClickCallback))
itemGrid->setMiddleClickCallback(*callback);
common(itemGrid, config);