Skip to content

Commit

Permalink
UI|Home|Resources: Custom data file in game profiles
Browse files Browse the repository at this point in the history
Starting a profile with custom data file doesn’t work yet because the game plugin’s required PK3 isn’t being automatically loaded either.

IssueID #2271
  • Loading branch information
skyjake committed Nov 4, 2018
1 parent f42f390 commit 6092019
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 49 deletions.
1 change: 1 addition & 0 deletions doomsday/apps/client/include/resource/idtech1image.h
Expand Up @@ -64,6 +64,7 @@ class IdTech1Image : public de::Image
ColorizedByFamily = 0x1,
Downscale50Percent = 0x2,
NullImageIfFails = 0x4, // by default returns a small fallback image
AlwaysTryLoad = 0x8,

DefaultLogoFlags = ColorizedByFamily | Downscale50Percent,
};
Expand Down
7 changes: 3 additions & 4 deletions doomsday/apps/client/include/ui/dialogs/packagesdialog.h
Expand Up @@ -20,6 +20,7 @@
#define DENG_CLIENT_PACKAGESDIALOG_H

#include <de/DialogWidget>
#include <doomsday/GameProfiles>

/**
* Package selection UI.
Expand All @@ -31,13 +32,11 @@ class PackagesDialog : public de::DialogWidget
public:
PackagesDialog(de::String const &titleText = "");

void setGame(de::String const &gameId);
// void setGame(de::String const &gameId);
void setProfile(const GameProfile &profile);
void setSelectedPackages(de::StringList packages);
de::StringList selectedPackages() const;

//public slots:
// void refreshPackages();

protected:
void preparePanelForOpening() override;

Expand Down
2 changes: 1 addition & 1 deletion doomsday/apps/client/src/resource/idtech1image.cpp
Expand Up @@ -82,7 +82,7 @@ Image IdTech1Image::makeGameLogo(Game const &game,
{
try
{
if (game.isPlayableWithDefaultPackages())
if (flags.testFlag(AlwaysTryLoad) || game.isPlayableWithDefaultPackages())
{
Block const playPal = catalog.read("PLAYPAL");
Block const title = catalog.read("TITLE");
Expand Down
115 changes: 99 additions & 16 deletions doomsday/apps/client/src/ui/dialogs/createprofiledialog.cpp
Expand Up @@ -18,13 +18,17 @@

#include "ui/dialogs/createprofiledialog.h"
#include "ui/widgets/packagesbuttonwidget.h"
#include "ui/widgets/nativepathwidget.h"
#include "ui/widgets/packageswidget.h"
#include "ui/dialogs/datafilesettingsdialog.h"
//#include "ui/widgets/nativepathwidget.h"

#include <doomsday/DoomsdayApp>
#include <doomsday/Games>
#include <doomsday/GameProfiles>
#include <doomsday/DataBundle>

#include <de/AuxButtonWidget>
#include <de/CallbackAction>
#include <de/ChoiceWidget>
#include <de/GridLayout>
#include <de/DialogContentStylist>
Expand All @@ -33,17 +37,21 @@
using namespace de;

DENG_GUI_PIMPL(CreateProfileDialog)
, DENG2_OBSERVES(NativePathWidget, UserChange)
//, DENG2_OBSERVES(NativePathWidget, UserChange)
{
ChoiceWidget *gameChoice;
PackagesButtonWidget *packages;
ChoiceWidget *autoStartMap;
ChoiceWidget *autoStartSkill;
NativePathWidget *customDataFile;
//NativePathWidget *customDataFile;
//String customDataFile;
AuxButtonWidget *customDataFileName;
ui::ListData customDataFileActions;
SafeWidgetPtr<PackagesWidget> customPicker;
DialogContentStylist stylist;
bool editing = false;
String oldName;
std::unique_ptr<GameProfile> tempProfile;
std::unique_ptr<GameProfile> tempProfile; // Used only internally.

Impl(Public *i) : Base(i) {}

Expand Down Expand Up @@ -110,9 +118,8 @@ DENG_GUI_PIMPL(CreateProfileDialog)
StringList packageIds;
if (gameChoice->isValidSelection())
{
packageIds += tempProfile->game().requiredPackages();
packageIds += tempProfile->allRequiredPackages();
}
packageIds += packages->packages();

// Create menu items for the Start Map choice.
for (int i = packageIds.size() - 1; i >= 0; --i)
Expand Down Expand Up @@ -147,12 +154,39 @@ DENG_GUI_PIMPL(CreateProfileDialog)
}

auto const pos = mapItems.findData(oldChoice);
autoStartMap->setSelected(pos != ui::Data::InvalidPos? pos : 0);
autoStartMap->setSelected(pos != ui::Data::InvalidPos ? pos : 0);
}

void pathChangedByUser(NativePathWidget &) override
{
// void pathChangedByUser(NativePathWidget &) override
// {
// setCustomDataFile(customDataFile->path());
// }

// void setCustomDataFile(const NativePath &path)
// {
// tempProfile->setUseGameRequirements(path.isEmpty());
// tempProfile->setCustomDataFile(path.toString());
// }

void updateDataFile()
{
if (tempProfile->customDataFile().isEmpty())
{
customDataFileName->setText(_E(l) "Default game data");
}
else
{
if (const auto *pkgFile = PackageLoader::get().select(tempProfile->customDataFile()))
{
const auto path = pkgFile->correspondingNativePath();
customDataFileName->setText(path.pretty());
}
else
{
customDataFileName->setText(_E(b) "Not found");
}
}
updateMapList();
}
};

Expand Down Expand Up @@ -184,10 +218,54 @@ CreateProfileDialog::CreateProfileDialog(String const &gameFamily)
d->gameChoice->items() << new ChoiceItem(tr("No playable games"), "");
}

// Custom data file.
form->add(d->customDataFile = new NativePathWidget);
d->customDataFile->setBlankText("Default game data");
d->customDataFile->audienceForUserChange() += d;
// Custom data file selection.
{
form->add(d->customDataFileName = new AuxButtonWidget);
d->customDataFileName->setMaximumTextWidth(
rule().right() - d->customDataFileName->rule().left() -
d->customDataFileName->margins().width());
d->customDataFileName->setTextLineAlignment(ui::AlignLeft);
d->customDataFileName->auxiliary().setText("Reset");
d->customDataFileActions
<< new ui::ActionItem("Select", new CallbackAction([this]() {
if (d->customPicker)
{
d->tempProfile->setCustomDataFile(d->customPicker->actionPackage());
d->customPicker->ancestorOfType<MessageDialog>()->accept();
d->updateDataFile();
}
}));
d->customDataFileName->setActionFn([this]() {
auto *dlg = new MessageDialog;
dlg->buttons() << new DialogButtonItem(Reject, "Cancel")
<< new DialogButtonItem(Action | Id1, style().images().image("gear"),
"Data Files", new CallbackAction([dlg]() {
auto *dfsDlg = new DataFileSettingsDialog;
dfsDlg->setAnchorAndOpeningDirection(dlg->buttonWidget(Id1)->rule(), ui::Up);
dfsDlg->setDeleteAfterDismissed(true);
dlg->add(dfsDlg);
dfsDlg->open();
}));
dlg->setDeleteAfterDismissed(true);
dlg->title().setText("Game Data File");
dlg->message().setText("Select the main data file. The default data files "
"of the game will not be automatically loaded.");
dlg->area().enableIndicatorDraw(true);
d->customPicker.reset(new PackagesWidget);
dlg->area().add(d->customPicker);
d->customPicker->setHiddenTags({"core"});
d->customPicker->setAllowPackageInfoActions(false);
d->customPicker->setActionItems(d->customDataFileActions);
dlg->setAnchorAndOpeningDirection(d->customDataFileName->rule(), ui::Right);
dlg->updateLayout();
root().addOnTop(dlg);
dlg->open(Modal);
});
d->customDataFileName->auxiliary().setActionFn([this]() {
d->tempProfile->setCustomDataFile("");
d->updateDataFile();
});
}

// Packages selection.
form->add(d->packages = new PackagesButtonWidget);
Expand Down Expand Up @@ -217,7 +295,7 @@ CreateProfileDialog::CreateProfileDialog(String const &gameFamily)
layout << *LabelWidget::newWithText(tr("Game:"), form)
<< *d->gameChoice
<< *LabelWidget::newWithText("Data File:", form)
<< *d->customDataFile
<< *d->customDataFileName
<< *LabelWidget::newWithText(tr("Mods:"), form)
<< *d->packages;

Expand All @@ -242,8 +320,9 @@ CreateProfileDialog::CreateProfileDialog(String const &gameFamily)
buttonWidget(Id1)->disable();

updateLayout();

d->updateDataFile();
d->gameChanged();

connect(d->gameChoice, &ChoiceWidget::selectionChanged, [this] () { d->gameChanged(); });
connect(d->packages, &PackagesButtonWidget::packageSelectionChanged,
[this] (QStringList) { d->updateMapList(); });
Expand All @@ -268,8 +347,10 @@ void CreateProfileDialog::fetchFrom(GameProfile const &profile)
{
editor().setText(profile.name());
d->gameChoice->setSelected(d->gameChoice->items().findData(profile.gameId()));
d->tempProfile->setCustomDataFile(profile.customDataFile());
d->tempProfile->setUseGameRequirements(profile.customDataFile().isEmpty());
d->packages->setPackages(profile.packages());
d->updateMapList();
d->updateDataFile();
d->autoStartMap->setSelected(d->autoStartMap->items().findData(profile.autoStartMap()));
d->autoStartSkill->setSelected(d->autoStartSkill->items().findData(profile.autoStartSkill()));
}
Expand All @@ -281,6 +362,8 @@ void CreateProfileDialog::applyTo(GameProfile &profile) const
{
profile.setGame(d->gameChoice->selectedItem().data().toString());
}
profile.setCustomDataFile(d->tempProfile->customDataFile());
profile.setUseGameRequirements(d->tempProfile->customDataFile().isEmpty());
profile.setPackages(d->packages->packages());
profile.setAutoStartMap(d->autoStartMap->selectedItem().data().toString());
profile.setAutoStartSkill(d->autoStartSkill->selectedItem().data().toInt());
Expand Down
48 changes: 29 additions & 19 deletions doomsday/apps/client/src/ui/dialogs/packagesdialog.cpp
Expand Up @@ -53,15 +53,14 @@ DENG_GUI_PIMPL(PackagesDialog)
, public PackagesWidget::IPackageStatus
, DENG2_OBSERVES(Widget, ChildAddition)
{
StringList requiredPackages; // loaded first, cannot be changed
StringList selectedPackages;
LabelWidget *nothingSelected;
ui::ListData actions;
HomeMenuWidget *menu;
PackagesWidget *browser;
LabelWidget *gameTitle;
LabelWidget *gameDataFiles;
Game const *game = nullptr;
const GameProfile *gameProfile = nullptr;
res::LumpCatalog catalog;

/**
Expand Down Expand Up @@ -301,23 +300,34 @@ DENG_GUI_PIMPL(PackagesDialog)

void updateGameTitle()
{
if (game && catalog.setPackages(requiredPackages + selectedPackages))
if (gameProfile && catalog.setPackages(gameProfile->allRequiredPackages() + selectedPackages))
{
gameTitle->setImage(IdTech1Image::makeGameLogo(*game, catalog,
IdTech1Image::UnmodifiedAppearance));
gameTitle->setImage(IdTech1Image::makeGameLogo(gameProfile->game(), catalog,
IdTech1Image::UnmodifiedAppearance |
IdTech1Image::AlwaysTryLoad));
// List of the native required files.
StringList dataFiles;
for (String packageId : requiredPackages)
if (gameProfile->customDataFile())
{
if (File const *file = App::packageLoader().select(packageId))
if (const auto *file = PackageLoader::get().select(gameProfile->customDataFile()))
{
// Only list here the game data files; Doomsday's PK3s are always
// there so listing them is not very helpful.
if (Package::matchTags(*file, QStringLiteral("\\bgamedata\\b")))
dataFiles << file->source()->description(0);
}
}
else
{
for (String packageId : gameProfile->allRequiredPackages())
{
if (const File *file = PackageLoader::get().select(packageId))
{
// Resolve indirection (symbolic links and interpretations) to
// describe the actual source file of the package.
dataFiles << file->source()->description(0);
// Only list here the game data files; Doomsday's PK3s are always
// there so listing them is not very helpful.
if (Package::matchTags(*file, QStringLiteral("\\bgamedata\\b")))
{
// Resolve indirection (symbolic links and interpretations) to
// describe the actual source file of the package.
dataFiles << file->source()->description(0);
}
}
}
}
Expand All @@ -333,12 +343,12 @@ DENG_GUI_PIMPL(PackagesDialog)
}
}

GuiWidget *makeItemWidget(ui::Item const &item, GuiWidget const *)
GuiWidget *makeItemWidget(ui::Item const &item, GuiWidget const *) override
{
return new SelectedPackageWidget(item.as<SelectedPackageItem>(), self());
}

void updateItemWidget(GuiWidget &widget, ui::Item const &)
void updateItemWidget(GuiWidget &widget, ui::Item const &) override
{
widget.as<SelectedPackageWidget>().updateContents();
}
Expand All @@ -358,7 +368,7 @@ DENG_GUI_PIMPL(PackagesDialog)
updateGameTitle();
}

void widgetChildAdded(Widget &child)
void widgetChildAdded(Widget &child) override
{
ui::DataPos pos = menu->findItem(child.as<GuiWidget>());
// We use a delay here because ScrollAreaWidget does scrolling based on
Expand Down Expand Up @@ -414,10 +424,10 @@ PackagesDialog::PackagesDialog(String const &titleText)
d->browser->setPopulationEnabled(true);
}

void PackagesDialog::setGame(String const &gameId)
void PackagesDialog::setProfile(const GameProfile &profile)
{
d->game = &DoomsdayApp::games()[gameId];
d->requiredPackages = d->game->requiredPackages();
d->gameProfile = &profile;
//d->requiredPackages = d->game->requiredPackages();
d->updateGameTitle();
}

Expand Down
Expand Up @@ -72,7 +72,7 @@ DENG_GUI_PIMPL(PackagesButtonWidget)
dlg->heading().setStyleImage(dialogIcon);
if (profile)
{
dlg->setGame(profile->gameId());
dlg->setProfile(*profile);
}
dlg->setDeleteAfterDismissed(true);
dlg->setSelectedPackages(packages);
Expand Down
1 change: 0 additions & 1 deletion doomsday/apps/client/src/ui/widgets/taskbarwidget.cpp
Expand Up @@ -30,7 +30,6 @@
#include "ui/dialogs/inputsettingsdialog.h"
#include "ui/dialogs/manualconnectiondialog.h"
#include "ui/dialogs/networksettingsdialog.h"
#include "ui/dialogs/packagesdialog.h"
#include "ui/dialogs/renderersettingsdialog.h"
#include "ui/dialogs/uisettingsdialog.h"
#include "ui/dialogs/videosettingsdialog.h"
Expand Down
2 changes: 2 additions & 0 deletions doomsday/apps/libdoomsday/include/doomsday/gameprofiles.h
Expand Up @@ -45,6 +45,7 @@ class LIBDOOMSDAY_PUBLIC GameProfiles : public de::Profiles
Profile &operator = (Profile const &other);

void setGame(de::String const &id);
void setCustomDataFile(const de::String &id);
void setPackages(de::StringList packagesInOrder);
void setUserCreated(bool userCreated);
void setUseGameRequirements(bool useGameRequirements);
Expand All @@ -56,6 +57,7 @@ class LIBDOOMSDAY_PUBLIC GameProfiles : public de::Profiles

de::String gameId() const;
Game &game() const;
de::String customDataFile() const;
de::StringList packages() const;
bool isUserCreated() const;
bool isUsingGameRequirements() const;
Expand Down

0 comments on commit 6092019

Please sign in to comment.