Skip to content

Commit

Permalink
Home|libdoomsday: Game profiles can specify start map and skill level
Browse files Browse the repository at this point in the history
IssueID #2229
  • Loading branch information
skyjake committed Apr 2, 2017
1 parent e1e2080 commit a94fb50
Show file tree
Hide file tree
Showing 8 changed files with 495 additions and 254 deletions.
15 changes: 15 additions & 0 deletions doomsday/apps/client/src/clientapp.cpp
Expand Up @@ -386,6 +386,21 @@ DENG2_PIMPL(ClientApp)
}

ClientWindow::main().console().zeroLogHeight();

if (!newGame.isNull())
{
// Auto-start the game?
auto const *prof = self().currentGameProfile();
if (prof && prof->autoStartMap())
{
LOG_NOTE("Starting in %s as configured in the game profile")
<< prof->autoStartMap();

Con_Executef(CMDS_DDAY, false, "setdefaultskill %i; setmap %s",
prof->autoStartSkill(),
prof->autoStartMap().toUtf8().constData());
}
}
}

void periodicAutosave()
Expand Down
113 changes: 110 additions & 3 deletions doomsday/apps/client/src/ui/dialogs/createprofiledialog.cpp
Expand Up @@ -22,17 +22,21 @@
#include <doomsday/DoomsdayApp>
#include <doomsday/Games>
#include <doomsday/GameProfiles>
#include <doomsday/DataBundle>

#include <de/ChoiceWidget>
#include <de/GridLayout>
#include <de/DialogContentStylist>
#include <de/PackageLoader>

using namespace de;

DENG_GUI_PIMPL(CreateProfileDialog)
{
ChoiceWidget *gameChoice;
PackagesButtonWidget *packages;
ChoiceWidget *autoStartMap;
ChoiceWidget *autoStartSkill;
DialogContentStylist stylist;
bool editing = false;
String oldName;
Expand Down Expand Up @@ -74,8 +78,69 @@ DENG_GUI_PIMPL(CreateProfileDialog)

void gameChanged()
{
// Used with the PackagesButtonWidget.
tempProfile->setGame(gameChoice->selectedItem().data().toString());
if (gameChoice->isValidSelection())
{
tempProfile->setGame(gameChoice->selectedItem().data().toString());
// Used with the PackagesButtonWidget.
updateMapList();
}
}

void updateMapList()
{
auto &mapItems = autoStartMap->items();

String oldChoice;
if (!mapItems.isEmpty())
{
oldChoice = autoStartMap->selectedItem().data().toString();
}

mapItems.clear();
mapItems << new ChoiceItem(tr("Title screen"), "");

// Find out all the required and selected packages.
StringList packageIds;
if (gameChoice->isValidSelection())
{
packageIds += tempProfile->game().requiredPackages();
}
packageIds += packages->packages();

// Create menu items for the Start Map choice.
for (int i = packageIds.size() - 1; i >= 0; --i)
{
String const pkgId = packageIds.at(i);
if (File const *pkgFile = PackageLoader::get().select(pkgId))
{
DataBundle const *bundle = pkgFile->target().maybeAs<DataBundle>();
if (!bundle || !bundle->lumpDirectory())
{
continue;
}

auto const maps = bundle->lumpDirectory()->findMapLumpNames();
if (!maps.isEmpty())
{
mapItems << new ui::Item(ui::Item::Separator);

String const wadName = Package::metadata(*pkgFile).gets(Package::VAR_TITLE);
foreach (String mapId, maps)
{
// Only show each map identifier once; only the last lump can
// be loaded.
if (mapItems.findData(mapId) == ui::Data::InvalidPos)
{
mapItems << new ChoiceItem(String("%1 " _E(s)_E(C) "%2").arg(mapId).arg(wadName),
mapId);
}
}
}
}
}

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

Expand Down Expand Up @@ -113,6 +178,22 @@ CreateProfileDialog::CreateProfileDialog(String const &gameFamily)
d->tempProfile.reset(new GameProfile);
d->packages->setGameProfile(*d->tempProfile);

// Auto start map.
form->add(d->autoStartMap = new ChoiceWidget);
d->autoStartMap->popup().menu().enableIndicatorDraw(true);
d->autoStartMap->items() << new ChoiceItem(tr("Title screen"), "");

// Auto start skill.
form->add(d->autoStartSkill = new ChoiceWidget);
d->autoStartSkill->items()
<< new ChoiceItem(tr("Novice"), 1)
<< new ChoiceItem(tr("Easy"), 2)
<< new ChoiceItem(tr("Normal"), 3)
<< new ChoiceItem(tr("Hard"), 4)
<< new ChoiceItem(tr("Nightmare"), 5);
d->autoStartSkill->disable();
d->autoStartSkill->setSelected(2);

GridLayout layout(form->rule().left(), form->rule().top() + rule("dialog.gap"));
layout.setGridSize(2, 0);
layout.setColumnAlignment(0, ui::AlignRight);
Expand All @@ -121,6 +202,17 @@ CreateProfileDialog::CreateProfileDialog(String const &gameFamily)
<< *LabelWidget::newWithText(tr("Packages:"), form)
<< *d->packages;

LabelWidget *optionsLabel = LabelWidget::newWithText(_E(D) + tr("Game Options"), form);
optionsLabel->setFont("separator.label");
optionsLabel->margins().setTop("gap");
layout.setCellAlignment(Vector2i(0, layout.gridSize().y), ui::AlignLeft);
layout.append(*optionsLabel, 2);

layout << *LabelWidget::newWithText(tr("Starts in:"), form)
<< *d->autoStartMap
<< *LabelWidget::newWithText(tr("Skill:"), form)
<< *d->autoStartSkill;

form->rule().setSize(layout.width(), layout.height());

buttons().clear()
Expand All @@ -134,6 +226,13 @@ CreateProfileDialog::CreateProfileDialog(String const &gameFamily)

d->gameChanged();
connect(d->gameChoice, &ChoiceWidget::selectionChanged, [this] () { d->gameChanged(); });
connect(d->packages, &PackagesButtonWidget::packageSelectionChanged,
[this] (QStringList) { d->updateMapList(); });
connect(d->autoStartMap, &ChoiceWidget::selectionChanged, [this] ()
{
d->autoStartSkill->disable(d->autoStartMap->items().isEmpty() ||
d->autoStartMap->selectedItem().data().toString().isEmpty());
});
connect(&editor(), &LineEditWidget::editorContentChanged,
[this] () { d->checkValidProfileName(); });
}
Expand All @@ -151,13 +250,21 @@ void CreateProfileDialog::fetchFrom(GameProfile const &profile)
editor().setText(profile.name());
d->gameChoice->setSelected(d->gameChoice->items().findData(profile.gameId()));
d->packages->setPackages(profile.packages());
d->updateMapList();
d->autoStartMap->setSelected(d->autoStartMap->items().findData(profile.autoStartMap()));
d->autoStartSkill->setSelected(d->autoStartSkill->items().findData(profile.autoStartSkill()));
}

void CreateProfileDialog::applyTo(GameProfile &profile) const
{
profile.setName(profileName());
profile.setGame(d->gameChoice->selectedItem().data().toString());
if (d->gameChoice->isValidSelection())
{
profile.setGame(d->gameChoice->selectedItem().data().toString());
}
profile.setPackages(d->packages->packages());
profile.setAutoStartMap(d->autoStartMap->selectedItem().data().toString());
profile.setAutoStartSkill(d->autoStartSkill->selectedItem().data().toInt());
}

String CreateProfileDialog::profileName() const
Expand Down
9 changes: 7 additions & 2 deletions doomsday/apps/libdoomsday/include/doomsday/defs/ded.h
Expand Up @@ -197,13 +197,18 @@ struct LIBDOOMSDAY_PUBLIC ded_s

ded_value_t *getValueById(char const *id) const;
ded_value_t *getValueById(de::String const &id) const;

ded_value_t *getValueByUri(de::Uri const &uri) const;

ded_compositefont_t *findCompositeFontDef(de::Uri const &uri) const;

ded_compositefont_t *getCompositeFont(char const *uriCString) const;

/**
* Finds the episode that has a specific map in it.
* @param mapId Map identifier.
* @return Episode ID.
*/
de::String findEpisode(de::String const &mapId) const;

protected:
void release();

Expand Down
4 changes: 4 additions & 0 deletions doomsday/apps/libdoomsday/include/doomsday/gameprofiles.h
Expand Up @@ -48,6 +48,8 @@ class LIBDOOMSDAY_PUBLIC GameProfiles : public de::Profiles
void setPackages(de::StringList packagesInOrder);
void setUserCreated(bool userCreated);
void setUseGameRequirements(bool useGameRequirements);
void setAutoStartMap(de::String const &map);
void setAutoStartSkill(int level);

bool appendPackage(de::String const &id);

Expand All @@ -56,6 +58,8 @@ class LIBDOOMSDAY_PUBLIC GameProfiles : public de::Profiles
de::StringList packages() const;
bool isUserCreated() const;
bool isUsingGameRequirements() const;
de::String autoStartMap() const;
int autoStartSkill() const;

/**
* Returns a list of the game's packages in addition to the profile's
Expand Down
16 changes: 16 additions & 0 deletions doomsday/apps/libdoomsday/src/defs/ded.cpp
Expand Up @@ -674,6 +674,22 @@ ded_compositefont_t *ded_s::getCompositeFont(char const *uriCString) const
return def;
}

String ded_s::findEpisode(String const &mapId) const
{
de::Uri mapUri(mapId, RC_NULL);
if (mapUri.scheme().isEmpty()) mapUri.setScheme("Maps");

for (int i = 0; i < episodes.size(); ++i)
{
defn::Episode episode(episodes[i]);
if (episode.tryFindMapGraphNode(mapUri.compose()))
{
return episode.gets("id");
}
}
return String();
}

int ded_s::getTextNum(char const *id) const
{
if (id && id[0])
Expand Down
65 changes: 59 additions & 6 deletions doomsday/apps/libdoomsday/src/gameprofiles.cpp
Expand Up @@ -29,10 +29,14 @@

using namespace de;

static String const VAR_GAME ("game");
static String const VAR_PACKAGES ("packages");
static String const VAR_USER_CREATED("userCreated");
static String const VAR_GAME ("game");
static String const VAR_PACKAGES ("packages");
static String const VAR_USER_CREATED ("userCreated");
static String const VAR_USE_GAME_REQUIREMENTS("useGameRequirements");
static String const VAR_AUTO_START_MAP ("autoStartMap");
static String const VAR_AUTO_START_SKILL("autoStartSkill");

static int const DEFAULT_SKILL = 3; // Normal skill level (1-5)

static GameProfile nullGameProfile;

Expand Down Expand Up @@ -169,6 +173,14 @@ Profiles::AbstractProfile *GameProfiles::profileFromInfoBlock(Info::BlockElement
prof->setUseGameRequirements(!block.keyValue(VAR_USE_GAME_REQUIREMENTS)
.text.compareWithoutCase("True"));
}
if (block.contains(VAR_AUTO_START_MAP))
{
prof->setAutoStartMap(block.keyValue(VAR_AUTO_START_MAP).text);
}
if (block.contains(VAR_AUTO_START_SKILL))
{
prof->setAutoStartSkill(block.keyValue(VAR_AUTO_START_SKILL).text.toInt());
}

return prof.release();
}
Expand All @@ -181,6 +193,8 @@ DENG2_PIMPL_NOREF(GameProfiles::Profile)
StringList packages;
bool userCreated = false;
bool useGameRequirements = true;
String autoStartMap;
int autoStartSkill = DEFAULT_SKILL;

Impl() {}

Expand All @@ -189,6 +203,8 @@ DENG2_PIMPL_NOREF(GameProfiles::Profile)
, packages (other.packages)
, userCreated (other.userCreated)
, useGameRequirements(other.useGameRequirements)
, autoStartMap (other.autoStartMap)
, autoStartSkill (other.autoStartSkill)
{}
};

Expand All @@ -210,6 +226,8 @@ GameProfiles::Profile &GameProfiles::Profile::operator = (Profile const &other)
d->packages = other.d->packages;
d->userCreated = other.d->userCreated;
d->useGameRequirements = other.d->useGameRequirements;
d->autoStartMap = other.d->autoStartMap;
d->autoStartSkill = other.d->autoStartSkill;
return *this;
}

Expand Down Expand Up @@ -249,6 +267,26 @@ void GameProfiles::Profile::setUseGameRequirements(bool useGameRequirements)
}
}

void GameProfiles::Profile::setAutoStartMap(String const &map)
{
if (d->autoStartMap != map)
{
d->autoStartMap = map;
notifyChange();
}
}

void GameProfiles::Profile::setAutoStartSkill(int level)
{
if (level < 1 || level > 5) level = DEFAULT_SKILL;

if (d->autoStartSkill != level)
{
d->autoStartSkill = level;
notifyChange();
}
}

bool GameProfiles::Profile::appendPackage(String const &id)
{
if (!d->packages.contains(id))
Expand Down Expand Up @@ -285,6 +323,16 @@ bool GameProfiles::Profile::isUsingGameRequirements() const
return d->useGameRequirements;
}

String GameProfiles::Profile::autoStartMap() const
{
return d->autoStartMap;
}

int GameProfiles::Profile::autoStartSkill() const
{
return d->autoStartSkill;
}

StringList GameProfiles::Profile::allRequiredPackages() const
{
StringList list;
Expand Down Expand Up @@ -362,10 +410,15 @@ String GameProfiles::Profile::toInfoSource() const
QTextStream os(&info);
os.setCodec("UTF-8");

os << VAR_GAME << ": " << d->gameId << "\n"
<< VAR_PACKAGES << " <" << String::join(de::map(d->packages, Info::quoteString), ", ") << ">\n"
<< VAR_USER_CREATED << ": " << (d->userCreated? "True" : "False") << "\n"
os << VAR_GAME << ": " << d->gameId << "\n"
<< VAR_PACKAGES << " <" << String::join(de::map(d->packages, Info::quoteString), ", ") << ">\n"
<< VAR_USER_CREATED << ": " << (d->userCreated? "True" : "False") << "\n"
<< VAR_USE_GAME_REQUIREMENTS << ": " << (d->useGameRequirements? "True" : "False");
if (d->autoStartMap)
{
os << "\n" << VAR_AUTO_START_MAP << ": " << d->autoStartMap;
}
os << "\n" << VAR_AUTO_START_SKILL << ": " << d->autoStartSkill;

return info;
}
Expand Down
1 change: 1 addition & 0 deletions doomsday/apps/plugins/common/src/game/g_defs.cpp
Expand Up @@ -74,3 +74,4 @@ de::Uri TranslateMapWarpNumber(String const &episodeId, dint warpNumber)
}
return de::Uri("Maps:", RC_NULL); // Not found.
}

0 comments on commit a94fb50

Please sign in to comment.