431a9c00a5
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.
448 lines
16 KiB
C++
448 lines
16 KiB
C++
#include "StarQuestInterface.hpp"
|
|
#include "StarQuestManager.hpp"
|
|
#include "StarCinematic.hpp"
|
|
#include "StarGuiReader.hpp"
|
|
#include "StarRoot.hpp"
|
|
#include "StarUniverseClient.hpp"
|
|
#include "StarPaneManager.hpp"
|
|
#include "StarListWidget.hpp"
|
|
#include "StarItemGridWidget.hpp"
|
|
#include "StarButtonWidget.hpp"
|
|
#include "StarLabelWidget.hpp"
|
|
#include "StarLabelWidget.hpp"
|
|
#include "StarImageWidget.hpp"
|
|
#include "StarAssets.hpp"
|
|
#include "StarItemDatabase.hpp"
|
|
#include "StarRandom.hpp"
|
|
#include "StarJsonExtra.hpp"
|
|
#include "StarPlayer.hpp"
|
|
#include "StarVerticalLayout.hpp"
|
|
#include "StarItemTooltip.hpp"
|
|
|
|
namespace Star {
|
|
|
|
QuestLogInterface::QuestLogInterface(QuestManagerPtr manager, PlayerPtr player, CinematicPtr cinematic, UniverseClientPtr client) {
|
|
m_manager = manager;
|
|
m_player = player;
|
|
m_cinematic = cinematic;
|
|
m_client = client;
|
|
|
|
auto assets = Root::singleton().assets();
|
|
auto config = assets->json("/interface/windowconfig/questlog.config");
|
|
|
|
m_trackLabel = config.getString("trackLabel");
|
|
m_untrackLabel = config.getString("untrackLabel");
|
|
|
|
GuiReader reader;
|
|
|
|
reader.registerCallback("close", [=](Widget*) { dismiss(); });
|
|
reader.registerCallback("btnToggleTracking",
|
|
[=](Widget*) {
|
|
toggleTracking();
|
|
});
|
|
reader.registerCallback("btnAbandon",
|
|
[=](Widget*) {
|
|
abandon();
|
|
});
|
|
reader.registerCallback("filter",
|
|
[=](Widget*) {
|
|
fetchData();
|
|
});
|
|
|
|
reader.construct(config.get("paneLayout"), this);
|
|
|
|
auto mainQuestList = fetchChild<ListWidget>("scrollArea.verticalLayout.mainQuestList");
|
|
auto sideQuestList = fetchChild<ListWidget>("scrollArea.verticalLayout.sideQuestList");
|
|
mainQuestList->setCallback([sideQuestList](Widget* widget) {
|
|
auto listWidget = as<ListWidget>(widget);
|
|
if (listWidget->selectedItem() != NPos)
|
|
sideQuestList->clearSelected();
|
|
});
|
|
sideQuestList->setCallback([mainQuestList](Widget* widget) {
|
|
auto listWidget = as<ListWidget>(widget);
|
|
if (listWidget->selectedItem() != NPos)
|
|
mainQuestList->clearSelected();
|
|
});
|
|
mainQuestList->disableScissoring();
|
|
sideQuestList->disableScissoring();
|
|
|
|
m_rewardItems = make_shared<ItemBag>(5);
|
|
fetchChild<ItemGridWidget>("rewardItems")->setItemBag(m_rewardItems);
|
|
|
|
m_refreshRate = 30;
|
|
m_refreshTimer = 0;
|
|
}
|
|
|
|
void QuestLogInterface::pollDialog(PaneManager* paneManager) {
|
|
if (paneManager->topPane({PaneLayer::ModalWindow}))
|
|
return;
|
|
|
|
if (auto failableQuest = m_manager->getFirstFailableQuest()) {
|
|
auto qfi = make_shared<QuestFailedInterface>(failableQuest.value(), m_player);
|
|
(*failableQuest)->setDialogShown();
|
|
paneManager->displayPane(PaneLayer::ModalWindow, qfi);
|
|
} else if (auto completableQuest = m_manager->getFirstCompletableQuest()) {
|
|
auto qci = make_shared<QuestCompleteInterface>(completableQuest.value(), m_player, m_cinematic);
|
|
(*completableQuest)->setDialogShown();
|
|
paneManager->displayPane(PaneLayer::ModalWindow, qci);
|
|
} else if (auto newQuest = m_manager->getFirstNewQuest()) {
|
|
auto nqd = make_shared<NewQuestInterface>(m_manager, newQuest.value(), m_player);
|
|
paneManager->displayPane(PaneLayer::ModalWindow, nqd);
|
|
}
|
|
}
|
|
|
|
void QuestLogInterface::displayed() {
|
|
Pane::displayed();
|
|
tick(0);
|
|
fetchData();
|
|
}
|
|
|
|
void QuestLogInterface::tick(float dt) {
|
|
Pane::tick(dt);
|
|
auto selected = getSelected();
|
|
if (selected && m_manager->hasQuest(selected->data().toString())) {
|
|
auto quest = m_manager->getQuest(selected->data().toString());
|
|
|
|
m_manager->markAsRead(quest->questId());
|
|
|
|
if (m_manager->isActive(quest->questId())) {
|
|
fetchChild<ButtonWidget>("btnToggleTracking")->enable();
|
|
fetchChild<ButtonWidget>("btnToggleTracking")->setText(m_manager->isCurrent(quest->questId()) ? m_untrackLabel : m_trackLabel);
|
|
if (quest->canBeAbandoned())
|
|
fetchChild<ButtonWidget>("btnAbandon")->enable();
|
|
else
|
|
fetchChild<ButtonWidget>("btnAbandon")->disable();
|
|
} else {
|
|
fetchChild<ButtonWidget>("btnToggleTracking")->disable();
|
|
fetchChild<ButtonWidget>("btnToggleTracking")->setText(m_trackLabel);
|
|
fetchChild<ButtonWidget>("btnAbandon")->disable();
|
|
}
|
|
fetchChild<LabelWidget>("lblQuestTitle")->setText(quest->title());
|
|
fetchChild<LabelWidget>("lblQuestBody")->setText(quest->text());
|
|
|
|
auto portraitName = "Objective";
|
|
auto imagePortrait = quest->portrait(portraitName);
|
|
if (imagePortrait) {
|
|
auto portraitTitleLabel = fetchChild<LabelWidget>("lblPortraitTitle");
|
|
String portraitTitle = quest->portraitTitle(portraitName).value("");
|
|
Maybe<unsigned> charLimit = portraitTitleLabel->getTextCharLimit();
|
|
portraitTitleLabel->setText(portraitTitle);
|
|
|
|
Drawable::scaleAll(*imagePortrait, Vec2F(-1, 1));
|
|
fetchChild<ImageWidget>("imgPortrait")->setDrawables(*imagePortrait);
|
|
fetchChild<ImageWidget>("imgPolaroid")->setVisibility(true);
|
|
fetchChild<ImageWidget>("imgPolaroidBack")->setVisibility(true);
|
|
|
|
} else {
|
|
fetchChild<LabelWidget>("lblPortraitTitle")->setText("");
|
|
fetchChild<ImageWidget>("imgPortrait")->setDrawables({});
|
|
fetchChild<ImageWidget>("imgPolaroid")->setVisibility(false);
|
|
fetchChild<ImageWidget>("imgPolaroidBack")->setVisibility(false);
|
|
}
|
|
|
|
m_rewardItems->clearItems();
|
|
if (quest->rewards().size() > 0) {
|
|
fetchChild<LabelWidget>("lblRewards")->setVisibility(true);
|
|
fetchChild<ItemGridWidget>("rewardItems")->setVisibility(true);
|
|
for (auto const& reward : quest->rewards())
|
|
m_rewardItems->addItems(reward->clone());
|
|
} else {
|
|
fetchChild<LabelWidget>("lblRewards")->setVisibility(false);
|
|
fetchChild<ItemGridWidget>("rewardItems")->setVisibility(false);
|
|
}
|
|
} else {
|
|
fetchChild<ButtonWidget>("btnToggleTracking")->disable();
|
|
fetchChild<ButtonWidget>("btnToggleTracking")->setText(m_trackLabel);
|
|
fetchChild<ButtonWidget>("btnAbandon")->disable();
|
|
fetchChild<LabelWidget>("lblQuestTitle")->setText("");
|
|
fetchChild<LabelWidget>("lblQuestBody")->setText("");
|
|
fetchChild<LabelWidget>("lblPortraitTitle")->setText("");
|
|
fetchChild<ImageWidget>("imgPortrait")->setDrawables({});
|
|
fetchChild<LabelWidget>("lblRewards")->setVisibility(false);
|
|
fetchChild<ItemGridWidget>("rewardItems")->setVisibility(false);
|
|
fetchChild<ImageWidget>("imgPolaroid")->setVisibility(false);
|
|
fetchChild<ImageWidget>("imgPolaroidBack")->setVisibility(false);
|
|
m_rewardItems->clearItems();
|
|
}
|
|
|
|
m_refreshTimer--;
|
|
if (m_refreshTimer < 0) {
|
|
fetchData();
|
|
m_refreshTimer = m_refreshRate;
|
|
}
|
|
}
|
|
|
|
PanePtr QuestLogInterface::createTooltip(Vec2I const& screenPosition) {
|
|
ItemPtr item;
|
|
if (auto child = getChildAt(screenPosition)) {
|
|
if (auto itemSlot = as<ItemSlotWidget>(child))
|
|
item = itemSlot->item();
|
|
if (auto itemGrid = as<ItemGridWidget>(child))
|
|
item = itemGrid->itemAt(screenPosition);
|
|
}
|
|
if (item)
|
|
return ItemTooltipBuilder::buildItemTooltip(item, m_player);
|
|
return {};
|
|
}
|
|
|
|
WidgetPtr QuestLogInterface::getSelected() {
|
|
auto mainQuestList = fetchChild<ListWidget>("scrollArea.verticalLayout.mainQuestList");
|
|
if (auto selected = mainQuestList->selectedWidget())
|
|
return selected;
|
|
|
|
auto sideQuestList = fetchChild<ListWidget>("scrollArea.verticalLayout.sideQuestList");
|
|
if (auto selected = sideQuestList->selectedWidget())
|
|
return selected;
|
|
|
|
return {};
|
|
}
|
|
|
|
void QuestLogInterface::setSelected(WidgetPtr selected) {
|
|
auto mainQuestList = fetchChild<ListWidget>("scrollArea.verticalLayout.mainQuestList");
|
|
auto mainQuestListPos = mainQuestList->itemPosition(selected);
|
|
if (mainQuestListPos != NPos) {
|
|
mainQuestList->setSelected(mainQuestListPos);
|
|
return;
|
|
}
|
|
|
|
auto sideQuestList = fetchChild<ListWidget>("scrollArea.verticalLayout.sideQuestList");
|
|
auto sideQuestListPos = sideQuestList->itemPosition(selected);
|
|
if (sideQuestListPos != NPos) {
|
|
sideQuestList->setSelected(sideQuestListPos);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void QuestLogInterface::toggleTracking() {
|
|
if (auto selected = getSelected()) {
|
|
String questId = selected->data().toString();
|
|
if (!m_manager->isCurrent(questId))
|
|
m_manager->setAsTracked(questId);
|
|
else
|
|
m_manager->setAsTracked({});
|
|
}
|
|
}
|
|
|
|
void QuestLogInterface::abandon() {
|
|
if (auto selected = getSelected()) {
|
|
m_manager->getQuest(selected->data().toString())->abandon();
|
|
}
|
|
}
|
|
|
|
void QuestLogInterface::fetchData() {
|
|
auto filter = fetchChild<ButtonGroupWidget>("filter")->checkedButton()->data().toString();
|
|
if (filter.equalsIgnoreCase("inProgress"))
|
|
showQuests(m_manager->listActiveQuests());
|
|
else if (filter.equalsIgnoreCase("completed"))
|
|
showQuests(m_manager->listCompletedQuests());
|
|
else
|
|
throw StarException(strf("Unknown quest filter '{}'", filter));
|
|
}
|
|
|
|
void QuestLogInterface::showQuests(List<QuestPtr> quests) {
|
|
auto mainQuestList = fetchChild<ListWidget>("scrollArea.verticalLayout.mainQuestList");
|
|
auto sideQuestList = fetchChild<ListWidget>("scrollArea.verticalLayout.sideQuestList");
|
|
|
|
auto mainQuestHeader = fetchChild<Widget>("scrollArea.verticalLayout.mainQuestHeader");
|
|
auto sideQuestHeader = fetchChild<Widget>("scrollArea.verticalLayout.sideQuestHeader");
|
|
|
|
String selectedQuest;
|
|
if (auto selected = getSelected())
|
|
selectedQuest = selected->data().toString();
|
|
else if (m_manager->currentQuest())
|
|
selectedQuest = (*m_manager->currentQuest())->questId();
|
|
|
|
mainQuestList->clear();
|
|
mainQuestHeader->hide();
|
|
sideQuestList->clear();
|
|
sideQuestHeader->hide();
|
|
for (auto const& quest : quests) {
|
|
WidgetPtr entry;
|
|
if (quest->mainQuest()) {
|
|
entry = mainQuestList->addItem();
|
|
mainQuestHeader->show();
|
|
} else {
|
|
entry = sideQuestList->addItem();
|
|
sideQuestHeader->show();
|
|
}
|
|
|
|
entry->setData(quest->questId());
|
|
entry->fetchChild<LabelWidget>("lblQuestEntry")->setText(quest->title());
|
|
entry->fetchChild<ImageWidget>("imgNew")->setVisibility(quest->unread());
|
|
entry->fetchChild<ImageWidget>("imgTracked")->setVisibility(m_manager->isCurrent(quest->questId()));
|
|
|
|
bool currentWorld = false;
|
|
if (auto questWorld = quest->worldId()) {
|
|
currentWorld = m_client->playerWorld() == *questWorld;
|
|
}
|
|
entry->fetchChild<ImageWidget>("imgCurrent")->setVisibility(currentWorld);
|
|
|
|
entry->fetchChild<ImageWidget>("imgPortrait")->setDrawables(quest->portrait("QuestStarted").value({}));
|
|
entry->show();
|
|
if (quest->questId() == selectedQuest)
|
|
setSelected(entry);
|
|
}
|
|
|
|
auto verticalLayout = fetchChild<VerticalLayout>("scrollArea.verticalLayout");
|
|
verticalLayout->update(0);
|
|
}
|
|
|
|
QuestPane::QuestPane(QuestPtr const& quest, PlayerPtr player) : Pane(), m_quest(quest), m_player(std::move(player)) {}
|
|
|
|
void QuestPane::commonSetup(Json config, String bodyText, String const& portraitName) {
|
|
GuiReader reader;
|
|
|
|
reader.registerCallback("close", [=](Widget*) { close(); });
|
|
reader.registerCallback("btnDecline", [=](Widget*) { close(); });
|
|
reader.registerCallback("btnAccept", [=](Widget*) { accept(); });
|
|
reader.construct(config.get("paneLayout"), this);
|
|
|
|
if (auto titleLabel = fetchChild<LabelWidget>("lblQuestTitle"))
|
|
titleLabel->setText(m_quest->title());
|
|
if (auto bodyLabel = fetchChild<LabelWidget>("lblQuestBody"))
|
|
bodyLabel->setText(bodyText);
|
|
|
|
if (auto portraitImage = fetchChild<ImageWidget>("portraitImage")) {
|
|
Maybe<List<Drawable>> portrait = m_quest->portrait(portraitName);
|
|
portraitImage->setDrawables(portrait.value({}));
|
|
}
|
|
|
|
if (auto portraitTitleLabel = fetchChild<LabelWidget>("portraitTitle")) {
|
|
Maybe<String> portraitTitle = m_quest->portraitTitle(portraitName);
|
|
String text = m_quest->portraitTitle(portraitName).value("");
|
|
Maybe<unsigned> charLimit = portraitTitleLabel->getTextCharLimit();
|
|
portraitTitleLabel->setText(text);
|
|
}
|
|
|
|
if (auto rewardItemsWidget = fetchChild<ItemGridWidget>("rewardItems")) {
|
|
auto rewardItems = make_shared<ItemBag>(5);
|
|
for (auto const& reward : m_quest->rewards())
|
|
rewardItems->addItems(reward->clone());
|
|
rewardItemsWidget->setItemBag(rewardItems);
|
|
}
|
|
|
|
auto sound = Random::randValueFrom(config.get("onShowSound").toArray(), "").toString();
|
|
if (!sound.empty())
|
|
context()->playAudio(sound);
|
|
}
|
|
|
|
void QuestPane::close() {
|
|
dismiss();
|
|
}
|
|
|
|
void QuestPane::accept() {
|
|
close();
|
|
}
|
|
|
|
PanePtr QuestPane::createTooltip(Vec2I const& screenPosition) {
|
|
ItemPtr item;
|
|
if (auto child = getChildAt(screenPosition)) {
|
|
if (auto itemSlot = as<ItemSlotWidget>(child))
|
|
item = itemSlot->item();
|
|
if (auto itemGrid = as<ItemGridWidget>(child))
|
|
item = itemGrid->itemAt(screenPosition);
|
|
}
|
|
if (item)
|
|
return ItemTooltipBuilder::buildItemTooltip(item, m_player);
|
|
return {};
|
|
}
|
|
|
|
NewQuestInterface::NewQuestInterface(QuestManagerPtr const& manager, QuestPtr const& quest, PlayerPtr player)
|
|
: QuestPane(quest, std::move(player)), m_manager(manager), m_declined(false) {
|
|
auto assets = Root::singleton().assets();
|
|
|
|
List<Drawable> objectivePortrait = m_quest->portrait("Objective").value({});
|
|
bool shortDialog = objectivePortrait.size() == 0;
|
|
|
|
String configFile;
|
|
if (shortDialog)
|
|
configFile = m_quest->getTemplate()->newQuestGuiConfig.value(assets->json("/quests/quests.config:defaultGuiConfigs.newQuest").toString());
|
|
else
|
|
configFile = m_quest->getTemplate()->newQuestGuiConfig.value(assets->json("/quests/quests.config:defaultGuiConfigs.newQuestPortrait").toString());
|
|
|
|
Json config = assets->json(configFile);
|
|
|
|
commonSetup(config, m_quest->text(), "QuestStarted");
|
|
|
|
m_declined = m_quest->canBeAbandoned();
|
|
if (!m_declined) {
|
|
if (auto declineButton = fetchChild<ButtonWidget>("btnDecline"))
|
|
declineButton->disable();
|
|
}
|
|
|
|
if (!shortDialog) {
|
|
if (auto objectivePortraitImage = fetchChild<ImageWidget>("objectivePortraitImage")) {
|
|
Drawable::scaleAll(objectivePortrait, Vec2F(-1, 1));
|
|
objectivePortraitImage->setDrawables(objectivePortrait);
|
|
|
|
String objectivePortraitTitle = m_quest->portraitTitle("Objective").value("");
|
|
auto portraitLabel = fetchChild<LabelWidget>("objectivePortraitTitle");
|
|
portraitLabel->setText(objectivePortraitTitle);
|
|
portraitLabel->setVisibility(objectivePortrait.size() > 0);
|
|
|
|
fetchChild<ImageWidget>("imgPolaroid")->setVisibility(objectivePortrait.size() > 0);
|
|
fetchChild<ImageWidget>("imgPolaroidBack")->setVisibility(objectivePortrait.size() > 0);
|
|
}
|
|
}
|
|
|
|
if (auto rewardItemsWidget = fetchChild<ItemGridWidget>("rewardItems"))
|
|
rewardItemsWidget->setVisibility(m_quest->rewards().size() > 0);
|
|
if (auto rewardsLabel = fetchChild<LabelWidget>("lblRewards"))
|
|
rewardsLabel->setVisibility(m_quest->rewards().size() > 0);
|
|
}
|
|
|
|
void NewQuestInterface::close() {
|
|
m_declined = true;
|
|
dismiss();
|
|
}
|
|
|
|
void NewQuestInterface::accept() {
|
|
m_declined = false;
|
|
dismiss();
|
|
}
|
|
|
|
void NewQuestInterface::dismissed() {
|
|
QuestPane::dismissed();
|
|
if (m_declined && m_quest->canBeAbandoned()) {
|
|
m_manager->getQuest(m_quest->questId())->declineOffer();
|
|
} else {
|
|
m_manager->getQuest(m_quest->questId())->start();
|
|
}
|
|
}
|
|
|
|
QuestCompleteInterface::QuestCompleteInterface(QuestPtr const& quest, PlayerPtr player, CinematicPtr cinematic)
|
|
: QuestPane(quest, player) {
|
|
auto assets = Root::singleton().assets();
|
|
String configFile = m_quest->getTemplate()->questCompleteGuiConfig.value(assets->json("/quests/quests.config:defaultGuiConfigs.questComplete").toString());
|
|
Json config = assets->json(configFile);
|
|
|
|
m_player = player;
|
|
m_cinematic = cinematic;
|
|
|
|
commonSetup(config, m_quest->completionText(), "QuestComplete");
|
|
|
|
if (auto moneyLabel = fetchChild<LabelWidget>("lblMoneyAmount"))
|
|
moneyLabel->setText(toString(m_quest->money()));
|
|
disableScissoring();
|
|
}
|
|
|
|
void QuestCompleteInterface::close() {
|
|
auto assets = Root::singleton().assets();
|
|
if (m_quest->completionCinema() && m_cinematic) {
|
|
String cinema = m_quest->completionCinema()->replaceTags(
|
|
StringMap<String>{{"species", m_player->species()}, {"gender", GenderNames.getRight(m_player->gender())}});
|
|
m_cinematic->load(assets->fetchJson(cinema));
|
|
}
|
|
dismiss();
|
|
}
|
|
|
|
QuestFailedInterface::QuestFailedInterface(QuestPtr const& quest, PlayerPtr player) : QuestPane(quest, std::move(player)) {
|
|
auto assets = Root::singleton().assets();
|
|
String configFile = m_quest->getTemplate()->questFailedGuiConfig.value(assets->json("/quests/quests.config:defaultGuiConfigs.questFailed").toString());
|
|
Json config = assets->json(configFile);
|
|
commonSetup(config, m_quest->failureText(), "QuestFailed");
|
|
disableScissoring();
|
|
}
|
|
|
|
}
|