Skip to content

Commit

Permalink
feat(UI): Allow shop items to be custom sorted within categories by c…
Browse files Browse the repository at this point in the history
…ontent creators, instead of always being alphabetical (#7054)


Co-authored-by: Amazinite <[email protected]>
  • Loading branch information
warp-core and Amazinite authored Apr 3, 2023
1 parent adf17d1 commit 7690c25
Show file tree
Hide file tree
Showing 29 changed files with 530 additions and 44 deletions.
5 changes: 5 additions & 0 deletions EndlessSkyLib.cbp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@
<Unit filename="source/CaptureOdds.h" />
<Unit filename="source/CargoHold.cpp" />
<Unit filename="source/CargoHold.h" />
<Unit filename="source/CategoryList.cpp" />
<Unit filename="source/CategoryList.h" />
<Unit filename="source/CategoryTypes.h" />
<Unit filename="source/ClickZone.h" />
<Unit filename="source/CollisionSet.cpp" />
Expand Down Expand Up @@ -380,6 +382,9 @@
<Unit filename="source/opengl.h" />
<Unit filename="source/pi.h" />
<Unit filename="source/shift.h" />
<Unit filename="source/comparators/ByGivenOrder.h" />
<Unit filename="source/comparators/ByName.h" />
<Unit filename="source/comparators/BySeriesAndIndex.h" />
<Unit filename="source/text/DisplayText.cpp" />
<Unit filename="source/text/DisplayText.h" />
<Unit filename="source/text/Font.cpp" />
Expand Down
1 change: 1 addition & 0 deletions EndlessSkyTests.cbp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
<Unit filename="tests/unit/src/test_account.cpp" />
<Unit filename="tests/unit/src/test_angle.cpp" />
<Unit filename="tests/unit/src/test_bitset.cpp" />
<Unit filename="tests/unit/src/test_categoryList.cpp" />
<Unit filename="tests/unit/src/test_conditionSet.cpp" />
<Unit filename="tests/unit/src/test_conditionsStore.cpp" />
<Unit filename="tests/unit/src/test_datafile.cpp" />
Expand Down
3 changes: 3 additions & 0 deletions source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ target_sources(EndlessSkyLib PRIVATE
CaptureOdds.h
CargoHold.cpp
CargoHold.h
CategoryList.cpp
CategoryList.h
CategoryTypes.h
ClickZone.h
CollisionSet.cpp
Expand Down Expand Up @@ -328,6 +330,7 @@ target_sources(EndlessSkyLib PRIVATE
Wormhole.h
comparators/ByGivenOrder.h
comparators/ByName.h
comparators/BySeriesAndIndex.h
opengl.cpp
opengl.h
pi.h
Expand Down
87 changes: 87 additions & 0 deletions source/CategoryList.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/* CategoryList.cpp
Copyright (c) 2022 by Amazinite
Endless Sky is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later version.
Endless Sky is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/

#include "CategoryList.h"

#include "DataNode.h"

#include <algorithm>
#include <limits>

using namespace std;



const bool CategoryList::Category::SortHelper(const CategoryList::Category &a, const CategoryList::Category &b)
{
if(a.precedence == b.precedence)
return a.name < b.name;
return a.precedence < b.precedence;
}



void CategoryList::Load(const DataNode &node)
{
for(const DataNode &child : node)
{
// Use the given precedence. If no precedence is given, use the previous
// precedence + 1.
if(child.Size() > 1)
currentPrecedence = child.Value(1);
Category cat(child.Token(0), currentPrecedence++);

// If a given category name already exists, its prescedence will be updated.
auto it = find_if(list.begin(), list.end(),
[&cat](const Category &c) noexcept -> bool { return cat.name == c.name; });
if(it != list.end())
it->precedence = cat.precedence;
else
{
list.push_back(cat);
byName.insert(pair<const string, Category>(cat.name, cat));
}

}
}



// Sort the CategoryList. Categories are sorted by precedence. If multiple categories
// share the same precedence then they are sorted alphabetically.
void CategoryList::Sort()
{
sort(list.begin(), list.end());
}



// Determine if the CategoryList contains a Category with the given name.
bool CategoryList::Contains(const string &name) const
{
const auto it = find_if(list.begin(), list.end(),
[&name](const Category &c) noexcept -> bool { return name == c.name; });
return it != list.end();
}



const CategoryList::Category CategoryList::GetCategory(const string &name) const
{
auto it = byName.find(name);
if(it != byName.end())
return it->second;
return Category("", numeric_limits<int>::max());
}
84 changes: 84 additions & 0 deletions source/CategoryList.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* CategoryList.h
Copyright (c) 2022 by Amazinite
Endless Sky is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later version.
Endless Sky is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
*/

#ifndef CATEGORY_LIST_H_
#define CATEGORY_LIST_H_

#include <iterator>
#include <map>
#include <string>
#include <utility>
#include <vector>

class DataNode;



// A CategoryList is a list of names that are associated to a Category of items (e.g. ships
// or outfits). Categories within the list are sorted by the precedence of each Category.
// Any conflicting precedencies are resolved by sorting the names of the Categories
// alphabetically.
class CategoryList {
public:
// A Category is a string with some precedence to it. The precedence is used to sort
// the Category within the CategoryList. Only the CategoryList has access to the
// precedence of each Category. All outside classes can only see the Category's
// name.
class Category {
public:
Category(std::string name, int precedence) : name(name), precedence(precedence) {}
const std::string &Name() const { return name; }
const bool operator<(Category other) const { return SortHelper(*this, other); }
const bool operator()(Category &a, Category &b) const { return SortHelper(a, b); }

private:
static const bool SortHelper(const Category &a, const Category &b);


private:
friend class CategoryList;
std::string name;
int precedence = 0;
};

public:
CategoryList() = default;

void Load(const DataNode &node);

// Sort the CategoryList. Categories are sorted by precedence. If multiple Categories
// share the same precedence then they are sorted alphabetically.
void Sort();

// Determine if the CategoryList contains a Category with the given name.
bool Contains(const std::string &name) const;

const Category GetCategory(const std::string &name) const;

typename std::vector<Category>::iterator begin() noexcept { return list.begin(); }
typename std::vector<Category>::const_iterator begin() const noexcept { return list.begin(); }
typename std::vector<Category>::iterator end() noexcept { return list.end(); }
typename std::vector<Category>::const_iterator end() const noexcept { return list.end(); }


private:
std::vector<Category> list;
std::map<const std::string, Category> byName;
int currentPrecedence = 0;
};



#endif
3 changes: 2 additions & 1 deletion source/CategoryTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ this program. If not, see <https://www.gnu.org/licenses/>.
enum class CategoryType : int {
SHIP,
BAY,
OUTFIT
OUTFIT,
SERIES
};


Expand Down
4 changes: 3 additions & 1 deletion source/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ this program. If not, see <https://www.gnu.org/licenses/>.

#include "AlertLabel.h"
#include "Audio.h"
#include "CategoryList.h"
#include "CategoryTypes.h"
#include "CoreStartData.h"
#include "DamageDealt.h"
Expand Down Expand Up @@ -415,8 +416,9 @@ void Engine::Place(const list<NPC> &npcs, shared_ptr<Ship> flagship)
if(ship->HasBays())
{
ship->UnloadBays();
for(const string &bayType : GameData::Category(CategoryType::BAY))
for(const auto &cat : GameData::GetCategory(CategoryType::BAY))
{
const string &bayType = cat.Name();
int baysTotal = ship->BaysTotal(bayType);
if(baysTotal)
carriers[bayType][&*ship] = baysTotal;
Expand Down
5 changes: 3 additions & 2 deletions source/GameData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ this program. If not, see <https://www.gnu.org/licenses/>.

#include "Audio.h"
#include "BatchShader.h"
#include "CategoryList.h"
#include "Color.h"
#include "Command.h"
#include "ConditionsStore.h"
Expand Down Expand Up @@ -777,8 +778,8 @@ const string &GameData::Rating(const string &type, int level)



// Strings for ship, bay type, and outfit categories.
const vector<string> &GameData::Category(const CategoryType type)
// Collections for ship, bay type, outfit, and other categories.
const CategoryList &GameData::GetCategory(const CategoryType type)
{
return objects.categories[type];
}
Expand Down
5 changes: 3 additions & 2 deletions source/GameData.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ this program. If not, see <https://www.gnu.org/licenses/>.
#include <string>
#include <vector>

class CategoryList;
class Color;
class ConditionsStore;
class Conversation;
Expand Down Expand Up @@ -159,8 +160,8 @@ class GameData {

// Strings for combat rating levels, etc.
static const std::string &Rating(const std::string &type, int level);
// Strings for ship, bay type, and outfit categories.
static const std::vector<std::string> &Category(const CategoryType type);
// Collections for ship, bay type, outfit, and other categories.
static const CategoryList &GetCategory(const CategoryType type);

static const StarField &Background();
static void SetHaze(const Sprite *sprite, bool allowAnimation);
Expand Down
6 changes: 4 additions & 2 deletions source/LocationFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ this program. If not, see <https://www.gnu.org/licenses/>.

#include "LocationFilter.h"

#include "CategoryList.h"
#include "CategoryTypes.h"
#include "DataNode.h"
#include "DataWriter.h"
Expand Down Expand Up @@ -299,8 +300,9 @@ bool LocationFilter::IsValid() const
if(!shipCategory.empty())
{
// At least one desired category must be valid.
const auto &shipCategories = GameData::Category(CategoryType::SHIP);
auto categoriesSet = set<string>(shipCategories.begin(), shipCategories.end());
set<string> categoriesSet;
for(const auto &category : GameData::GetCategory(CategoryType::SHIP))
categoriesSet.insert(category.Name());
if(!SetsIntersect(shipCategory, categoriesSet))
return false;
}
Expand Down
6 changes: 5 additions & 1 deletion source/MainPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ this program. If not, see <https://www.gnu.org/licenses/>.

#include "BoardingPanel.h"
#include "comparators/ByGivenOrder.h"
#include "CategoryList.h"
#include "CoreStartData.h"
#include "Dialog.h"
#include "text/Font.h"
Expand Down Expand Up @@ -368,7 +369,10 @@ void MainPanel::ShowScanDialog(const ShipEvent &event)
out << "This " + target->Noun() + " is not equipped with any outfits.\n";

// Split target->Outfits() into categories, then iterate over them in order.
auto comparator = ByGivenOrder<string>(GameData::Category(CategoryType::OUTFIT));
vector<string> categories;
for(const auto &category : GameData::GetCategory(CategoryType::OUTFIT))
categories.push_back(category.Name());
auto comparator = ByGivenOrder<string>(categories);
map<string, map<const string, int>, ByGivenOrder<string>> outfitsByCategory(comparator);
for(const auto &it : target->Outfits())
{
Expand Down
3 changes: 2 additions & 1 deletion source/MapOutfitterPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,9 @@ void MapOutfitterPanel::DrawItems()
DoHelp("map advanced shops");
list.clear();
Point corner = Screen::TopLeft() + Point(0, scroll);
for(const string &category : categories)
for(const auto &cat : categories)
{
const string &category = cat.Name();
auto it = catalog.find(category);
if(it == catalog.end())
continue;
Expand Down
8 changes: 4 additions & 4 deletions source/MapSalesPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const int MapSalesPanel::WIDTH = 270;

MapSalesPanel::MapSalesPanel(PlayerInfo &player, bool isOutfitters)
: MapPanel(player, SHOW_SPECIAL),
categories(GameData::Category(isOutfitters ? CategoryType::OUTFIT : CategoryType::SHIP)),
categories(GameData::GetCategory(isOutfitters ? CategoryType::OUTFIT : CategoryType::SHIP)),
isOutfitters(isOutfitters),
collapsed(player.Collapsed(isOutfitters ? "outfitter map" : "shipyard map"))
{
Expand All @@ -64,7 +64,7 @@ MapSalesPanel::MapSalesPanel(PlayerInfo &player, bool isOutfitters)

MapSalesPanel::MapSalesPanel(const MapPanel &panel, bool isOutfitters)
: MapPanel(panel),
categories(GameData::Category(isOutfitters ? CategoryType::OUTFIT : CategoryType::SHIP)),
categories(GameData::GetCategory(isOutfitters ? CategoryType::OUTFIT : CategoryType::SHIP)),
isOutfitters(isOutfitters),
collapsed(player.Collapsed(isOutfitters ? "outfitter map" : "shipyard map"))
{
Expand Down Expand Up @@ -439,8 +439,8 @@ void MapSalesPanel::ClickCategory(const string &name)
if(isHidden)
collapsed.clear();
else
for(const string &category : categories)
collapsed.insert(category);
for(const auto &category : categories)
collapsed.insert(category.Name());
}
else if(isHidden)
collapsed.erase(name);
Expand Down
3 changes: 2 additions & 1 deletion source/MapSalesPanel.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ this program. If not, see <https://www.gnu.org/licenses/>.

#include "MapPanel.h"

#include "CategoryList.h"
#include "ClickZone.h"

#include <set>
Expand Down Expand Up @@ -87,7 +88,7 @@ class MapSalesPanel : public MapPanel {
double scroll = 0.;
double maxScroll = 0.;

const std::vector<std::string> &categories;
const CategoryList &categories;
bool onlyShowSoldHere = false;


Expand Down
3 changes: 2 additions & 1 deletion source/MapShipyardPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,9 @@ void MapShipyardPanel::DrawItems()
DoHelp("map advanced shops");
list.clear();
Point corner = Screen::TopLeft() + Point(0, scroll);
for(const string &category : categories)
for(const auto &cat : categories)
{
const string &category = cat.Name();
auto it = catalog.find(category);
if(it == catalog.end())
continue;
Expand Down
Loading

0 comments on commit 7690c25

Please sign in to comment.