Reset script panes on character swap

This commit is contained in:
Kae 2023-07-29 02:12:03 +10:00
parent 35fc2679de
commit 224ad2c2c0
13 changed files with 131 additions and 31 deletions

View File

@ -510,10 +510,23 @@ void ClientApplication::changeState(MainAppState newState) {
m_universeClient->setLuaCallbacks("input", LuaBindings::makeInputCallbacks()); m_universeClient->setLuaCallbacks("input", LuaBindings::makeInputCallbacks());
m_universeClient->setLuaCallbacks("voice", LuaBindings::makeVoiceCallbacks()); m_universeClient->setLuaCallbacks("voice", LuaBindings::makeVoiceCallbacks());
m_universeClient->playerReloadCallback() = [&]() { auto heldScriptPanes = make_shared<List<MainInterface::ScriptPaneInfo>>();
if (auto paneManager = m_mainInterface->paneManager()) {
if (auto inventory = paneManager->registeredPane<InventoryPane>(MainInterfacePanes::Inventory)) m_universeClient->playerReloadPreCallback() = [&, heldScriptPanes](bool resetInterface) {
inventory->clearChangedSlots(); if (!resetInterface)
return;
m_mainInterface->takeScriptPanes(*heldScriptPanes);
};
m_universeClient->playerReloadCallback() = [&, heldScriptPanes](bool resetInterface) {
auto paneManager = m_mainInterface->paneManager();
if (auto inventory = paneManager->registeredPane<InventoryPane>(MainInterfacePanes::Inventory))
inventory->clearChangedSlots();
if (resetInterface) {
m_mainInterface->reviveScriptPanes(*heldScriptPanes);
heldScriptPanes->clear();
} }
}; };

View File

@ -14,7 +14,7 @@
namespace Star { namespace Star {
BaseScriptPane::BaseScriptPane(Json config) : Pane() { BaseScriptPane::BaseScriptPane(Json config) : Pane(), m_rawConfig(config) {
auto& root = Root::singleton(); auto& root = Root::singleton();
auto assets = root.assets(); auto assets = root.assets();
@ -97,6 +97,9 @@ bool BaseScriptPane::sendEvent(InputEvent const& event) {
return Pane::sendEvent(event); return Pane::sendEvent(event);
} }
Json const& BaseScriptPane::config() const { return m_config; }
Json const& BaseScriptPane::rawConfig() const { return m_rawConfig; }
PanePtr BaseScriptPane::createTooltip(Vec2I const& screenPosition) { PanePtr BaseScriptPane::createTooltip(Vec2I const& screenPosition) {
auto result = m_script.invoke<Json>("createTooltip", screenPosition); auto result = m_script.invoke<Json>("createTooltip", screenPosition);
if (result && !result.value().isNull()) { if (result && !result.value().isNull()) {

View File

@ -26,11 +26,15 @@ public:
bool sendEvent(InputEvent const& event) override; bool sendEvent(InputEvent const& event) override;
Json const& config() const;
Json const& rawConfig() const;
PanePtr createTooltip(Vec2I const& screenPosition) override; PanePtr createTooltip(Vec2I const& screenPosition) override;
Maybe<String> cursorOverride(Vec2I const& screenPosition) override; Maybe<String> cursorOverride(Vec2I const& screenPosition) override;
protected: protected:
virtual GuiReaderPtr reader(); virtual GuiReaderPtr reader();
Json m_config; Json m_config;
Json m_rawConfig;
GuiReaderPtr m_reader; GuiReaderPtr m_reader;

View File

@ -498,22 +498,8 @@ void MainInterface::handleInteractAction(InteractAction interactAction) {
m_paneManager.dismissPane(m_interactionScriptPanes[sourceEntity]); m_paneManager.dismissPane(m_interactionScriptPanes[sourceEntity]);
ScriptPanePtr scriptPane = make_shared<ScriptPane>(m_client, interactAction.data, sourceEntity); ScriptPanePtr scriptPane = make_shared<ScriptPane>(m_client, interactAction.data, sourceEntity);
// keep any number of script panes open with null source entities displayScriptPane(scriptPane, sourceEntity);
if (sourceEntity != NullEntityId)
m_interactionScriptPanes[sourceEntity] = scriptPane;
if (scriptPane->openWithInventory()) {
m_paneManager.displayPane(PaneLayer::Window, scriptPane, [this](PanePtr const&) {
if (auto player = m_client->mainPlayer())
player->clearSwap();
m_paneManager.dismissRegisteredPane(MainInterfacePanes::Inventory);
});
m_paneManager.displayRegisteredPane(MainInterfacePanes::Inventory);
m_paneManager.bringPaneAdjacent(m_paneManager.registeredPane(MainInterfacePanes::Inventory),
scriptPane, Root::singleton().assets()->json("/interface.config:bringAdjacentWindowGap").toFloat());
} else {
m_paneManager.displayPane(PaneLayer::Window, scriptPane);
}
} else if (interactAction.type == InteractActionType::Message) { } else if (interactAction.type == InteractActionType::Message) {
m_client->mainPlayer()->receiveMessage(connectionForEntity(interactAction.entityId), m_client->mainPlayer()->receiveMessage(connectionForEntity(interactAction.entityId),
interactAction.data.getString("messageType"), interactAction.data.getArray("messageArgs")); interactAction.data.getString("messageType"), interactAction.data.getArray("messageArgs"));
@ -952,6 +938,38 @@ CanvasWidgetPtr MainInterface::fetchCanvas(String const& canvasName, bool ignore
return canvas; return canvas;
} }
// For when the player swaps characters. We need to completely reload ScriptPanes,
// because a lot of ScriptPanes do not expect the character to suddenly change and may break or spill data over.
void MainInterface::takeScriptPanes(List<ScriptPaneInfo>& out) {
m_paneManager.dismissWhere([&](PanePtr const& pane) {
if (auto scriptPane = as<ScriptPane>(pane)) {
if (scriptPane->isDismissed())
return false;
auto sourceEntityId = scriptPane->sourceEntityId();
m_interactionScriptPanes.remove(sourceEntityId);
auto& info = out.emplaceAppend();
info.scriptPane = scriptPane;
info.config = scriptPane->rawConfig();
info.sourceEntityId = sourceEntityId;
info.visible = scriptPane->visibility();
info.position = scriptPane->relativePosition();
return true;
}
return false;
});
}
void MainInterface::reviveScriptPanes(List<ScriptPaneInfo>& panes) {
for (auto& info : panes) { // this is evil and stupid
info.scriptPane->~ScriptPane();
new(info.scriptPane.get()) ScriptPane(m_client, info.config, info.sourceEntityId);
info.scriptPane->setVisibility(info.visible);
displayScriptPane(info.scriptPane, info.sourceEntityId);
info.scriptPane->setPosition(info.position);
}
}
PanePtr MainInterface::createEscapeDialog() { PanePtr MainInterface::createEscapeDialog() {
auto assets = Root::singleton().assets(); auto assets = Root::singleton().assets();
@ -1542,4 +1560,23 @@ bool MainInterface::overlayClick(Vec2I const& mousePos, MouseButton mouseButton)
return false; return false;
} }
void MainInterface::displayScriptPane(ScriptPanePtr& scriptPane, EntityId sourceEntity) {
// keep any number of script panes open with null source entities
if (sourceEntity != NullEntityId)
m_interactionScriptPanes[sourceEntity] = scriptPane;
if (scriptPane->openWithInventory()) {
m_paneManager.displayPane(PaneLayer::Window, scriptPane, [this](PanePtr const&) {
if (auto player = m_client->mainPlayer())
player->clearSwap();
m_paneManager.dismissRegisteredPane(MainInterfacePanes::Inventory);
});
m_paneManager.displayRegisteredPane(MainInterfacePanes::Inventory);
m_paneManager.bringPaneAdjacent(m_paneManager.registeredPane(MainInterfacePanes::Inventory),
scriptPane, Root::singleton().assets()->json("/interface.config:bringAdjacentWindowGap").toFloat());
} else {
m_paneManager.displayPane(PaneLayer::Window, scriptPane);
}
}
} }

View File

@ -119,6 +119,16 @@ public:
CanvasWidgetPtr fetchCanvas(String const& canvasName, bool ignoreInterfaceScale = false); CanvasWidgetPtr fetchCanvas(String const& canvasName, bool ignoreInterfaceScale = false);
struct ScriptPaneInfo {
ScriptPanePtr scriptPane;
Json config;
EntityId sourceEntityId;
bool visible;
Vec2I position;
};
void takeScriptPanes(List<ScriptPaneInfo>& out);
void reviveScriptPanes(List<ScriptPaneInfo>& panes);
private: private:
PanePtr createEscapeDialog(); PanePtr createEscapeDialog();
@ -142,6 +152,8 @@ private:
bool overlayClick(Vec2I const& mousePos, MouseButton mouseButton); bool overlayClick(Vec2I const& mousePos, MouseButton mouseButton);
void displayScriptPane(ScriptPanePtr& scriptPane, EntityId sourceEntity);
GuiContext* m_guiContext; GuiContext* m_guiContext;
MainInterfaceConfigConstPtr m_config; MainInterfaceConfigConstPtr m_config;
InterfaceCursor m_cursor; InterfaceCursor m_cursor;

View File

@ -88,4 +88,8 @@ bool ScriptPane::openWithInventory() const {
return m_config.getBool("openWithInventory", false); return m_config.getBool("openWithInventory", false);
} }
EntityId ScriptPane::sourceEntityId() const {
return m_sourceEntityId;
}
} }

View File

@ -22,6 +22,8 @@ public:
bool openWithInventory() const; bool openWithInventory() const;
EntityId sourceEntityId() const;
LuaCallbacks makePaneCallbacks() override; LuaCallbacks makePaneCallbacks() override;
private: private:
UniverseClientPtr m_client; UniverseClientPtr m_client;

View File

@ -477,7 +477,7 @@ void UniverseClient::stopLua() {
m_scriptContexts.clear(); m_scriptContexts.clear();
} }
bool UniverseClient::reloadPlayer(Json const& data, Uuid const& uuid) { bool UniverseClient::reloadPlayer(Json const& data, Uuid const& uuid, bool resetInterfaces) {
auto player = mainPlayer(); auto player = mainPlayer();
bool playerInWorld = player->inWorld(); bool playerInWorld = player->inWorld();
auto world = as<WorldClient>(player->world()); auto world = as<WorldClient>(player->world());
@ -487,7 +487,7 @@ bool UniverseClient::reloadPlayer(Json const& data, Uuid const& uuid) {
: connectionEntitySpace(world->connection()).first; : connectionEntitySpace(world->connection()).first;
if (m_playerReloadPreCallback) if (m_playerReloadPreCallback)
m_playerReloadPreCallback(); m_playerReloadPreCallback(resetInterfaces);
if (playerInWorld) { if (playerInWorld) {
world->removeEntity(player->entityId(), false); world->removeEntity(player->entityId(), false);
@ -515,7 +515,7 @@ bool UniverseClient::reloadPlayer(Json const& data, Uuid const& uuid) {
player->universeMap()->filterMappedObjects(coordinate, m_systemWorldClient->objectKeys()); player->universeMap()->filterMappedObjects(coordinate, m_systemWorldClient->objectKeys());
if (m_playerReloadCallback) if (m_playerReloadCallback)
m_playerReloadCallback(); m_playerReloadCallback(resetInterfaces);
if (exception) if (exception)
std::rethrow_exception(exception); std::rethrow_exception(exception);
@ -527,7 +527,7 @@ bool UniverseClient::switchPlayer(Uuid const& uuid) {
if (uuid == mainPlayer()->uuid()) if (uuid == mainPlayer()->uuid())
return false; return false;
else if (auto data = m_playerStorage->maybeGetPlayerData(uuid)) else if (auto data = m_playerStorage->maybeGetPlayerData(uuid))
return reloadPlayer(*data, uuid); return reloadPlayer(*data, uuid, true);
else else
return false; return false;
} }
@ -546,11 +546,11 @@ bool UniverseClient::switchPlayer(String const& name) {
return false; return false;
} }
UniverseClient::Callback& UniverseClient::playerReloadPreCallback() { UniverseClient::ReloadPlayerCallback& UniverseClient::playerReloadPreCallback() {
return m_playerReloadPreCallback; return m_playerReloadPreCallback;
} }
UniverseClient::Callback& UniverseClient::playerReloadCallback() { UniverseClient::ReloadPlayerCallback& UniverseClient::playerReloadCallback() {
return m_playerReloadCallback; return m_playerReloadCallback;
} }

View File

@ -91,14 +91,15 @@ public:
void startLua(); void startLua();
void stopLua(); void stopLua();
bool reloadPlayer(Json const& data, Uuid const& uuid); bool reloadPlayer(Json const& data, Uuid const& uuid, bool resetInterfaces = false);
bool switchPlayer(Uuid const& uuid); bool switchPlayer(Uuid const& uuid);
bool switchPlayer(size_t index); bool switchPlayer(size_t index);
bool switchPlayer(String const& name); bool switchPlayer(String const& name);
typedef std::function<void()> Callback; typedef std::function<void()> Callback;
Callback& playerReloadPreCallback(); typedef std::function<void(bool)> ReloadPlayerCallback;
Callback& playerReloadCallback(); ReloadPlayerCallback& playerReloadPreCallback();
ReloadPlayerCallback& playerReloadCallback();
ClockConstPtr universeClock() const; ClockConstPtr universeClock() const;
CelestialLogConstPtr celestialLog() const; CelestialLogConstPtr celestialLog() const;
@ -161,8 +162,8 @@ private:
typedef shared_ptr<ScriptComponent> ScriptComponentPtr; typedef shared_ptr<ScriptComponent> ScriptComponentPtr;
StringMap<ScriptComponentPtr> m_scriptContexts; StringMap<ScriptComponentPtr> m_scriptContexts;
Callback m_playerReloadPreCallback; ReloadPlayerCallback m_playerReloadPreCallback;
Callback m_playerReloadCallback; ReloadPlayerCallback m_playerReloadCallback;
}; };
} }

View File

@ -128,6 +128,23 @@ void PaneManager::setBackgroundWidget(WidgetPtr bg) {
m_backgroundWidget = bg; m_backgroundWidget = bg;
} }
void PaneManager::dismissWhere(function<bool(PanePtr const&)> func) {
if (!func)
return;
for (auto& layerPair : m_displayedPanes) {
eraseWhere(layerPair.second, [&](auto& panePair) {
if (func(panePair.first)) {
panePair.first->dismissed();
if (panePair.second)
panePair.second(panePair.first);
return true;
}
return false;
});
}
}
PanePtr PaneManager::keyboardCapturedPane() const { PanePtr PaneManager::keyboardCapturedPane() const {
for (auto const& layerPair : m_displayedPanes) { for (auto const& layerPair : m_displayedPanes) {
for (auto const& panePair : layerPair.second) { for (auto const& panePair : layerPair.second) {

View File

@ -63,6 +63,8 @@ public:
void setBackgroundWidget(WidgetPtr bg); void setBackgroundWidget(WidgetPtr bg);
void dismissWhere(function<bool(PanePtr const&)> func);
// Returns the pane that has captured the keyboard, if any. // Returns the pane that has captured the keyboard, if any.
PanePtr keyboardCapturedPane() const; PanePtr keyboardCapturedPane() const;
// Returns true if the current pane that has captured the keyboard is // Returns true if the current pane that has captured the keyboard is

View File

@ -219,6 +219,10 @@ void Widget::hide() {
m_visible = false; m_visible = false;
} }
bool Widget::visibility() const {
return m_visible;
}
void Widget::toggleVisibility() { void Widget::toggleVisibility() {
m_visible = !m_visible; m_visible = !m_visible;
} }

View File

@ -58,6 +58,7 @@ public:
virtual void show(); virtual void show();
virtual void hide(); virtual void hide();
virtual bool visibility() const;
virtual void toggleVisibility(); virtual void toggleVisibility();
virtual void setVisibility(bool visibility); virtual void setVisibility(bool visibility);