diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
commit | 6352e8e3196f78388b6c771073f9e03eaa612673 (patch) | |
tree | e23772f79a7fbc41bc9108951e9e136857484bf4 /source/frontend/StarAiInterface.cpp | |
parent | 6741a057e5639280d85d0f88ba26f000baa58f61 (diff) |
everything everywhere
all at once
Diffstat (limited to 'source/frontend/StarAiInterface.cpp')
-rw-r--r-- | source/frontend/StarAiInterface.cpp | 402 |
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)); +} + +} |