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

summaryrefslogtreecommitdiff
path: root/source/frontend/StarAiInterface.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/StarAiInterface.cpp
parent6741a057e5639280d85d0f88ba26f000baa58f61 (diff)
everything everywhere
all at once
Diffstat (limited to 'source/frontend/StarAiInterface.cpp')
-rw-r--r--source/frontend/StarAiInterface.cpp402
1 files changed, 402 insertions, 0 deletions
diff --git a/source/frontend/StarAiInterface.cpp b/source/frontend/StarAiInterface.cpp
new file mode 100644
index 0000000..d9a543a
--- /dev/null
+++ b/source/frontend/StarAiInterface.cpp
@@ -0,0 +1,402 @@
+#include "StarAiInterface.hpp"
+#include "StarLexicalCast.hpp"
+#include "StarJsonExtra.hpp"
+#include "StarJsonRpc.hpp"
+#include "StarAssets.hpp"
+#include "StarContainerEntity.hpp"
+#include "StarItemBag.hpp"
+#include "StarItemDatabase.hpp"
+#include "StarPlayer.hpp"
+#include "StarPlayerCompanions.hpp"
+#include "StarPlayerInventory.hpp"
+#include "StarQuests.hpp"
+#include "StarQuestManager.hpp"
+#include "StarRoot.hpp"
+#include "StarUniverseClient.hpp"
+#include "StarPlayerStorage.hpp"
+#include "StarClientContext.hpp"
+#include "StarCanvasWidget.hpp"
+#include "StarLabelWidget.hpp"
+#include "StarImageWidget.hpp"
+#include "StarImageStretchWidget.hpp"
+#include "StarGuiReader.hpp"
+#include "StarListWidget.hpp"
+#include "StarButtonWidget.hpp"
+#include "StarOrderedSet.hpp"
+#include "StarAiDatabase.hpp"
+#include "StarTabSet.hpp"
+#include "StarPlayerTech.hpp"
+#include "StarPlayerBlueprints.hpp"
+#include "StarItemSlotWidget.hpp"
+#include "StarStackWidget.hpp"
+#include "StarCinematic.hpp"
+#include "StarWorldClient.hpp"
+
+namespace Star {
+
+AiInterface::AiInterface(UniverseClientPtr client, CinematicPtr cinematic, MainInterfacePaneManager* paneManager) {
+ m_client = client;
+ m_cinematic = cinematic;
+ m_paneManager = paneManager;
+
+ m_textLength = 0.0;
+ m_textMaxLength = 0.0;
+
+ m_aiDatabase = Root::singleton().aiDatabase();
+ auto assets = Root::singleton().assets();
+
+ GuiReader reader;
+ reader.registerCallback("close", bind(mem_fn(&AiInterface::dismiss), this));
+ reader.registerCallback("missionItemList", bind(mem_fn(&AiInterface::selectMission), this));
+ reader.registerCallback("startMission", bind(mem_fn(&AiInterface::startMission), this));
+ reader.registerCallback("crewItemList", bind(mem_fn(&AiInterface::selectRecruit), this));
+ reader.registerCallback("dismissRecruit", bind(mem_fn(&AiInterface::dismissRecruit), this));
+ reader.registerCallback("showMissions", bind(mem_fn(&AiInterface::showMissions), this));
+ reader.registerCallback("showCrew", bind(mem_fn(&AiInterface::showCrew), this));
+ reader.registerCallback("goBack", bind(mem_fn(&AiInterface::goBack), this));
+
+ reader.construct(assets->json("/interface/ai/ai.config:guiConfig"), this);
+
+ m_mainStack = fetchChild<StackWidget>("mainStack");
+ m_missionStack = findChild<StackWidget>("missionStack");
+ m_crewStack = findChild<StackWidget>("crewStack");
+
+ m_breadcrumbLeftPadding = assets->json("/interface/ai/ai.config:breadcrumbLeftPadding").toInt();
+ m_breadcrumbRightPadding = assets->json("/interface/ai/ai.config:breadcrumbRightPadding").toInt();
+ m_homeBreadcrumbBackground = fetchChild<ImageStretchWidget>("homeBreadcrumbBg");
+ m_pageBreadcrumbBackground = fetchChild<ImageStretchWidget>("pageBreadcrumbBg");
+ m_itemBreadcrumbBackground = fetchChild<ImageStretchWidget>("itemBreadcrumbBg");
+
+ m_homeBreadcrumbWidget = fetchChild<LabelWidget>("homeBreadcrumb");
+ m_pageBreadcrumbWidget = fetchChild<LabelWidget>("pageBreadcrumb");
+ m_itemBreadcrumbWidget = fetchChild<LabelWidget>("itemBreadcrumb");
+
+ m_aiFaceCanvasWidget = findChild<CanvasWidget>("aiFaceCanvas");
+ m_missionListWidget = findChild<ListWidget>("missionItemList");
+ m_crewListWidget = findChild<ListWidget>("crewItemList");
+
+ m_showMissionsButton = findChild<ButtonWidget>("showMissions");
+ m_showCrewButton = findChild<ButtonWidget>("showCrew");
+ m_backButton = findChild<ButtonWidget>("backButton");
+
+ m_missionNameLabel = findChild<LabelWidget>("missionName");
+ m_missionIcon = findChild<ImageWidget>("missionIcon");
+ m_startMissionButton = findChild<ButtonWidget>("startMission");
+
+ m_recruitNameLabel = findChild<LabelWidget>("recruitName");
+ m_recruitIcon = findChild<ImageWidget>("recruitIcon");
+ m_dismissRecruitButton = findChild<ButtonWidget>("dismissRecruit");
+
+ m_species = m_client->mainPlayer()->species();
+ m_staticAnimation = m_aiDatabase->staticAnimation(m_species);
+ m_scanlineAnimation = m_aiDatabase->scanlineAnimation();
+
+ m_missionBreadcrumbText = assets->json("/interface/ai/ai.config:missionBreadcrumbText").toString();
+ m_missionDeployText = assets->json("/interface/ai/ai.config:missionDeployText").toString();
+ m_crewBreadcrumbText = assets->json("/interface/ai/ai.config:crewBreadcrumbText").toString();
+ m_defaultRecruitName = assets->json("/interface/ai/ai.config:defaultRecruitName").toString();
+ m_defaultRecruitDescription = assets->json("/interface/ai/ai.config:defaultRecruitDescription").toString();
+}
+
+void AiInterface::update() {
+ if (!m_client->playerOnOwnShip())
+ dismiss();
+
+ Pane::update();
+
+ m_showCrewButton->setVisibility(m_currentPage == AiPages::StatusPage);
+ m_showMissionsButton->setVisibility(m_currentPage == AiPages::StatusPage);
+ m_backButton->setVisibility(m_currentPage != AiPages::StatusPage);
+
+ m_staticAnimation.update(WorldTimestep);
+ m_scanlineAnimation.update(WorldTimestep);
+
+ if (m_currentSpeech) {
+ m_textLength += m_currentSpeech->speedModifier * m_aiDatabase->charactersPerSecond() * WorldTimestep;
+ m_currentTextWidget->setText(m_currentSpeech->text);
+ m_currentTextWidget->setTextCharLimit(min(m_textMaxLength, floor(m_textLength)));
+
+ if (m_textLength < m_textMaxLength) {
+ setFaceAnimation(m_currentSpeech->animation);
+ if (!m_chatterSound || m_chatterSound->finished()) {
+ auto assets = Root::singleton().assets();
+ m_chatterSound = make_shared<AudioInstance>(*assets->audio(assets->json("/interface/ai/ai.config:chatterSound").toString()));
+ m_chatterSound->setLoops(-1);
+ GuiContext::singleton().playAudio(m_chatterSound);
+ }
+ } else {
+ setFaceAnimation("idle");
+ if (m_chatterSound)
+ m_chatterSound->stop();
+ }
+ m_faceAnimation.second.update(WorldTimestep * m_currentSpeech->speedModifier);
+ } else {
+ setFaceAnimation("idle");
+ m_faceAnimation.second.update(WorldTimestep);
+ if (m_chatterSound)
+ m_chatterSound->stop();
+ }
+
+ // If the enabled missions list changes, update the mission list
+ auto aiState = m_client->mainPlayer()->aiState();
+ if (aiState.availableMissions.values() != m_availableMissions
+ || aiState.completedMissions.values() != m_completedMissions) {
+ m_availableMissions = aiState.availableMissions.values();
+ m_completedMissions = aiState.completedMissions.values();
+ populateMissions();
+ }
+
+ auto crew = m_client->mainPlayer()->companions()->getCompanions("crew");
+ if (crew != m_crew) {
+ m_crew = crew;
+ populateCrew();
+ }
+
+ m_aiFaceCanvasWidget->clear();
+ m_aiFaceCanvasWidget->drawDrawable(m_faceAnimation.second.drawable(1.0f), Vec2F(0, 0));
+ m_aiFaceCanvasWidget->drawDrawable(m_staticAnimation.drawable(1.0f), Vec2F(0, 0));
+ m_aiFaceCanvasWidget->drawDrawable(m_scanlineAnimation.drawable(1.0f), Vec2F(0, 0));
+}
+
+void AiInterface::updateBreadcrumbs() {
+ // Home breadcrumb
+ auto width = m_homeBreadcrumbWidget->size()[0];
+ width += m_breadcrumbLeftPadding + m_breadcrumbRightPadding;
+ m_homeBreadcrumbBackground->setSize(Vec2I(width, m_homeBreadcrumbBackground->size()[1]));
+
+ // Middle breadcrumb
+ if (m_currentPage != AiPages::StatusPage) {
+ Vec2I pagePosition = m_homeBreadcrumbBackground->position() + Vec2I(width, 0);
+ m_pageBreadcrumbWidget->setPosition(Vec2I(pagePosition[0] + m_breadcrumbLeftPadding, m_homeBreadcrumbWidget->position()[1]));
+ m_pageBreadcrumbBackground->setPosition(pagePosition - Vec2I(m_breadcrumbRightPadding, 0));
+ width = m_pageBreadcrumbWidget->size()[0] + (2 * m_breadcrumbRightPadding) + m_breadcrumbLeftPadding; // Add right padding twice because of the overlap to the left
+ m_pageBreadcrumbBackground->setSize(Vec2I(width, m_pageBreadcrumbBackground->size()[1]));
+
+ // End breadcrumb
+ if (m_currentPage == AiPages::MissionPage || m_currentPage == AiPages::CrewPage) {
+ Vec2I itemPosition = m_pageBreadcrumbBackground->position() + Vec2I(width, 0);
+ m_itemBreadcrumbWidget->setPosition(Vec2I(itemPosition[0] + m_breadcrumbLeftPadding, m_homeBreadcrumbWidget->position()[1]));
+ m_itemBreadcrumbBackground->setPosition(itemPosition - Vec2I(m_breadcrumbRightPadding, 0));
+ width = m_itemBreadcrumbWidget->size()[0] + (2 * m_breadcrumbRightPadding) + m_breadcrumbLeftPadding;
+ m_itemBreadcrumbBackground->setSize(Vec2I(width, m_itemBreadcrumbBackground->size()[1]));
+ }
+ }
+
+ // Visibility
+ m_pageBreadcrumbBackground->setVisibility(m_currentPage != AiPages::StatusPage);
+ m_pageBreadcrumbWidget->setVisibility(m_currentPage != AiPages::StatusPage);
+ m_itemBreadcrumbBackground->setVisibility(m_currentPage == AiPages::MissionPage || m_currentPage == AiPages::CrewPage);
+ m_itemBreadcrumbWidget->setVisibility(m_currentPage == AiPages::MissionPage || m_currentPage == AiPages::CrewPage);
+}
+
+void AiInterface::displayed() {
+ if (!m_client->playerOnOwnShip())
+ return;
+
+ Pane::displayed();
+
+ showStatus();
+}
+
+void AiInterface::dismissed() {
+ if (m_chatterSound)
+ m_chatterSound->stop();
+
+ m_selectedMission = {};
+ m_selectedRecruit = {};
+
+ Pane::dismissed();
+}
+
+void AiInterface::setSourceEntityId(EntityId sourceEntityId) {
+ m_sourceEntityId = sourceEntityId;
+}
+
+void AiInterface::showStatus() {
+ m_currentPage = AiPages::StatusPage;
+ setCurrentSpeech("shipStatusText", m_aiDatabase->shipStatus(m_client->clientContext()->shipUpgrades().shipLevel));
+ m_mainStack->showPage(0);
+
+ updateBreadcrumbs();
+}
+
+void AiInterface::showMissions() {
+ m_currentPage = AiPages::MissionList;
+ m_currentSpeech = {};
+ m_pageBreadcrumbWidget->setText(m_missionBreadcrumbText);
+
+ populateMissions();
+
+ m_missionListWidget->clearSelected();
+ m_mainStack->showPage(1);
+ m_missionStack->showPage(0);
+ if (m_availableMissions.empty() && m_completedMissions.empty()) {
+ m_missionStack->showPage(0);
+ setCurrentSpeech("noMissionsText", m_aiDatabase->noMissionsSpeech());
+ } else {
+ m_missionStack->showPage(1);
+ }
+
+ updateBreadcrumbs();
+}
+
+void AiInterface::selectMission() {
+ size_t selectedItem = m_missionListWidget->selectedItem();
+
+ Maybe<String> mission = {};
+ if (selectedItem < m_availableMissions.size())
+ mission = m_availableMissions.at(selectedItem);
+ else if (selectedItem < m_availableMissions.size() + m_completedMissions.size())
+ mission = m_completedMissions.at(selectedItem - m_availableMissions.size());
+
+ m_selectedMission = mission;
+
+ if (m_selectedMission) {
+ m_currentPage = AiPages::MissionPage;
+
+ auto mission = m_aiDatabase->mission(*m_selectedMission);
+ auto missionText = mission.speciesText.value(m_species, mission.speciesText.value("default"));
+ setCurrentSpeech("missionText", missionText.selectSpeech);
+ m_missionNameLabel->setText(missionText.buttonText);
+ m_missionIcon->setImage(mission.icon);
+
+ m_itemBreadcrumbWidget->setText(m_missionDeployText);
+
+ m_missionStack->showPage(2);
+
+ updateBreadcrumbs();
+ }
+}
+
+void AiInterface::showCrew() {
+ m_currentPage = AiPages::CrewList;
+ m_currentSpeech = {};
+ m_pageBreadcrumbWidget->setText(m_crewBreadcrumbText);
+
+ populateMissions();
+
+ m_crewListWidget->clearSelected();
+ m_mainStack->showPage(2);
+ if (m_crew.empty()) {
+ m_crewStack->showPage(0);
+ setCurrentSpeech("noCrewText", m_aiDatabase->noCrewSpeech());
+ } else {
+ m_crewStack->showPage(1);
+ }
+
+ updateBreadcrumbs();
+}
+
+void AiInterface::selectRecruit() {
+ CompanionPtr recruit = {};
+ size_t selectedItem = m_crewListWidget->selectedItem();
+ if (selectedItem < m_crew.size())
+ recruit = m_crew.at(selectedItem);
+
+ m_selectedRecruit = recruit;
+
+ if (m_selectedRecruit) {
+ m_currentPage = AiPages::CrewPage;
+
+ auto speech = m_selectedRecruit->description().value(m_defaultRecruitDescription);
+ setCurrentSpeech("recruitText", {m_aiDatabase->defaultAnimation(), speech, 1.0f});
+ m_recruitNameLabel->setText(m_selectedRecruit->name().value(m_defaultRecruitName));
+ m_recruitIcon->setDrawables(m_selectedRecruit->portrait());
+
+ m_itemBreadcrumbWidget->setText(recruit->name().value(m_defaultRecruitName));
+
+ m_crewStack->showPage(2);
+
+ updateBreadcrumbs();
+ }
+}
+
+void AiInterface::populateMissions() {
+ m_missionListWidget->clear();
+ for (auto const& missionName : m_availableMissions) {
+ auto widget = m_missionListWidget->addItem();
+ auto label = widget->fetchChild<LabelWidget>("itemName");
+ auto icon = widget->fetchChild<ImageWidget>("itemIcon");
+
+ auto const& mission = m_aiDatabase->mission(missionName);
+ icon->setImage(mission.icon);
+ auto const& missionText = mission.speciesText.value(m_species, mission.speciesText.value("default"));
+ label->setText(missionText.buttonText);
+ }
+ for (auto const& missionName : m_completedMissions) {
+ auto widget = m_missionListWidget->addItem();
+ auto label = widget->fetchChild<LabelWidget>("itemName");
+ auto icon = widget->fetchChild<ImageWidget>("itemIcon");
+
+ auto const& mission = m_aiDatabase->mission(missionName);
+ icon->setImage(mission.icon);
+ auto const& missionText = mission.speciesText.value(m_species, mission.speciesText.value("default"));
+ label->setText(missionText.repeatButtonText);
+ }
+ m_missionListWidget->setSelected(NPos);
+}
+
+void AiInterface::startMission() {
+ if (m_selectedMission) {
+ auto const& mission = m_aiDatabase->mission(*m_selectedMission);
+ m_client->warpPlayer(
+ WarpToWorld{InstanceWorldId(mission.missionUniqueWorld, m_client->teamUuid()), {}},
+ true,
+ mission.warpAnimation.value("default"),
+ mission.warpDeploy.value(false));
+ dismiss();
+ }
+}
+
+void AiInterface::populateCrew() {
+ m_crewListWidget->clear();
+ for (auto const& recruit : m_crew) {
+ auto widget = m_crewListWidget->addItem();
+ auto label = widget->fetchChild<LabelWidget>("itemName");
+ auto icon = widget->fetchChild<ImageWidget>("itemIcon");
+
+ icon->setDrawables(recruit->portrait());
+ label->setText(recruit->name().value(m_defaultRecruitName));
+ }
+ m_crewListWidget->setSelected(NPos);
+}
+
+void AiInterface::dismissRecruit() {
+ if (!m_selectedRecruit)
+ return;
+
+ Uuid podUuid = m_selectedRecruit->podUuid();
+ m_client->mainPlayer()->companions()->dismissCompanion("crew", podUuid);
+
+ m_crewStack->showPage(1);
+}
+
+void AiInterface::goBack() {
+ if (m_currentPage == AiPages::MissionPage)
+ showMissions();
+ else if (m_currentPage == AiPages::CrewPage)
+ showCrew();
+ else if (m_currentPage == AiPages::MissionList || m_currentPage == AiPages::CrewList)
+ showStatus();
+
+ updateBreadcrumbs();
+}
+
+void AiInterface::setFaceAnimation(String const& name) {
+ if (m_faceAnimation.first != name)
+ m_faceAnimation = {name, m_aiDatabase->animation(m_species, name)};
+}
+
+void AiInterface::setCurrentSpeech(String const& textWidget, AiSpeech speech) {;
+ m_currentSpeech = move(speech);
+ m_textLength = 0.0;
+ m_textMaxLength = Text::stripEscapeCodes(m_currentSpeech->text).size();
+ m_currentTextWidget = findChild<LabelWidget>(textWidget);
+ m_currentTextWidget->setText("");
+}
+
+void AiInterface::giveBlueprint(String const& blueprintName) {
+ m_client->mainPlayer()->addBlueprint(ItemDescriptor(blueprintName));
+}
+
+}