make quest episode filter configurable

This commit is contained in:
Martin Michelsen
2024-03-01 21:22:14 -08:00
parent ef101894d1
commit a2e3f4882d
5 changed files with 26 additions and 49 deletions
+4 -11
View File
@@ -777,14 +777,9 @@ vector<shared_ptr<const QuestCategoryIndex::Category>> QuestIndex::categories(
Episode episode, Episode episode,
Version version, Version version,
IncludeCondition include_condition) const { IncludeCondition include_condition) const {
// The episode filter should apply in normal or solo mode
if ((menu_type != QuestMenuType::NORMAL) && (menu_type != QuestMenuType::SOLO)) {
episode = Episode::NONE;
}
vector<shared_ptr<const QuestCategoryIndex::Category>> ret; vector<shared_ptr<const QuestCategoryIndex::Category>> ret;
for (const auto& cat : this->category_index->categories) { for (const auto& cat : this->category_index->categories) {
if (cat->check_flag(menu_type) && !this->filter(menu_type, episode, version, cat->category_id, include_condition, 1).empty()) { if (cat->check_flag(menu_type) && !this->filter(episode, version, cat->category_id, include_condition, 1).empty()) {
ret.emplace_back(cat); ret.emplace_back(cat);
} }
} }
@@ -792,15 +787,13 @@ vector<shared_ptr<const QuestCategoryIndex::Category>> QuestIndex::categories(
} }
vector<pair<QuestIndex::IncludeState, shared_ptr<const Quest>>> QuestIndex::filter( vector<pair<QuestIndex::IncludeState, shared_ptr<const Quest>>> QuestIndex::filter(
QuestMenuType menu_type,
Episode episode, Episode episode,
Version version, Version version,
uint32_t category_id, uint32_t category_id,
IncludeCondition include_condition, IncludeCondition include_condition,
size_t limit) const { size_t limit) const {
if ((menu_type != QuestMenuType::NORMAL) && (menu_type != QuestMenuType::SOLO)) { auto cat = this->category_index->at(category_id);
episode = Episode::NONE; Episode effective_episode = cat->enable_episode_filter() ? episode : Episode::NONE;
}
vector<pair<IncludeState, shared_ptr<const Quest>>> ret; vector<pair<IncludeState, shared_ptr<const Quest>>> ret;
auto category_it = this->quests_by_category_id_and_number.find(category_id); auto category_it = this->quests_by_category_id_and_number.find(category_id);
@@ -808,7 +801,7 @@ vector<pair<QuestIndex::IncludeState, shared_ptr<const Quest>>> QuestIndex::filt
return ret; return ret;
} }
for (auto it : category_it->second) { for (auto it : category_it->second) {
if (((episode == Episode::NONE) || (it.second->episode == episode)) && if (((effective_episode == Episode::NONE) || (it.second->episode == effective_episode)) &&
it.second->has_version_any_language(version)) { it.second->has_version_any_language(version)) {
IncludeState state = include_condition ? include_condition(it.second) : IncludeState::AVAILABLE; IncludeState state = include_condition ? include_condition(it.second) : IncludeState::AVAILABLE;
if (state == IncludeState::HIDDEN) { if (state == IncludeState::HIDDEN) {
+4 -1
View File
@@ -31,6 +31,7 @@ enum class QuestMenuType {
GOVERNMENT = 4, GOVERNMENT = 4,
DOWNLOAD = 5, DOWNLOAD = 5,
EP3_DOWNLOAD = 6, EP3_DOWNLOAD = 6,
// 7 can't be used as a menu type (it enables the per-episode filter)
}; };
struct QuestCategoryIndex { struct QuestCategoryIndex {
@@ -46,6 +47,9 @@ struct QuestCategoryIndex {
[[nodiscard]] inline bool check_flag(QuestMenuType menu_type) const { [[nodiscard]] inline bool check_flag(QuestMenuType menu_type) const {
return this->enabled_flags & (1 << static_cast<uint8_t>(menu_type)); return this->enabled_flags & (1 << static_cast<uint8_t>(menu_type));
} }
[[nodiscard]] inline bool enable_episode_filter() const {
return this->enabled_flags & 0x80;
}
}; };
std::vector<std::shared_ptr<Category>> categories; std::vector<std::shared_ptr<Category>> categories;
@@ -151,7 +155,6 @@ struct QuestIndex {
Version version, Version version,
IncludeCondition include_condition = nullptr) const; IncludeCondition include_condition = nullptr) const;
std::vector<std::pair<QuestIndex::IncludeState, std::shared_ptr<const Quest>>> filter( std::vector<std::pair<QuestIndex::IncludeState, std::shared_ptr<const Quest>>> filter(
QuestMenuType menu_type,
Episode episode, Episode episode,
Version version, Version version,
uint32_t category_id, uint32_t category_id,
+4 -24
View File
@@ -2228,7 +2228,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
auto quest_index = s->quest_index(c->version()); auto quest_index = s->quest_index(c->version());
const auto& categories = quest_index->categories(menu_type, Episode::EP3, c->version()); const auto& categories = quest_index->categories(menu_type, Episode::EP3, c->version());
if (categories.size() == 1) { if (categories.size() == 1) {
auto quests = quest_index->filter(menu_type, Episode::EP3, c->version(), categories[0]->category_id); auto quests = quest_index->filter(Episode::EP3, c->version(), categories[0]->category_id);
send_quest_menu(c, quests, true); send_quest_menu(c, quests, true);
break; break;
} }
@@ -2491,32 +2491,12 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
shared_ptr<Lobby> l = c->lobby.lock(); shared_ptr<Lobby> l = c->lobby.lock();
Episode episode = l ? l->episode : Episode::NONE; Episode episode = l ? l->episode : Episode::NONE;
QuestMenuType menu_type = QuestMenuType::NORMAL;
QuestIndex::IncludeCondition include_condition = nullptr; QuestIndex::IncludeCondition include_condition = nullptr;
if (!l) { if (l && !c->license->check_flag(License::Flag::DISABLE_QUEST_REQUIREMENTS)) {
// Assume the menu to be sent is the download quest menu if the client include_condition = l->quest_include_condition();
// is not in any lobby
menu_type = is_ep3(c->version()) ? QuestMenuType::EP3_DOWNLOAD : QuestMenuType::DOWNLOAD;
} else {
auto cat = quest_index->category_index->at(item_id);
static const std::array<QuestMenuType, 4> menu_types({
QuestMenuType::GOVERNMENT,
QuestMenuType::CHALLENGE,
QuestMenuType::BATTLE,
QuestMenuType::SOLO,
});
for (QuestMenuType check_menu_type : menu_types) {
if (cat->check_flag(check_menu_type)) {
menu_type = check_menu_type;
break;
}
}
if (!c->license->check_flag(License::Flag::DISABLE_QUEST_REQUIREMENTS)) {
include_condition = l->quest_include_condition();
}
} }
const auto& quests = quest_index->filter(menu_type, episode, c->version(), item_id, include_condition); const auto& quests = quest_index->filter(episode, c->version(), item_id, include_condition);
send_quest_menu(c, quests, !l); send_quest_menu(c, quests, !l);
break; break;
} }
+8 -7
View File
@@ -648,18 +648,19 @@
// 0x10 - appears at government counter (BB) // 0x10 - appears at government counter (BB)
// 0x20 - appears in download quest menu // 0x20 - appears in download quest menu
// 0x40 - appears in Episode 3 download quest menu // 0x40 - appears in Episode 3 download quest menu
// 0x80 - hide quests that don't match the game's episode
// directory_name: the directory inside system/quests that contains quests // directory_name: the directory inside system/quests that contains quests
// for this category. // for this category.
// category_name: what appears in the quest menu on the client. // category_name: what appears in the quest menu on the client.
// description: what appears in the category description window (may // description: what appears in the category description window (may
// contain color escape codes like $C6). // contain color escape codes like $C6).
[0x01, "retrieval", "Retrieval", "$E$C6Quests that involve\nretrieving an object"], [0x81, "retrieval", "Retrieval", "$E$C6Quests that involve\nretrieving an object"],
[0x01, "extermination", "Extermination", "$E$C6Quests that involve\ndestroying all\nmonsters"], [0x81, "extermination", "Extermination", "$E$C6Quests that involve\ndestroying all\nmonsters"],
[0x01, "events", "Events", "$E$C6Quests that are part\nof an event"], [0x81, "events", "Events", "$E$C6Quests that are part\nof an event"],
[0x01, "shops", "Shops", "$E$C6Quests that contain\nshops"], [0x81, "shops", "Shops", "$E$C6Quests that contain\nshops"],
[0x01, "vr", "Virtual Reality", "$E$C6Quests that are\ndone in a simulator"], [0x81, "vr", "Virtual Reality", "$E$C6Quests that are\ndone in a simulator"],
[0x01, "tower", "Control Tower", "$E$C6Quests that take\nplace at the Control\nTower"], [0x81, "tower", "Control Tower", "$E$C6Quests that take\nplace at the Control\nTower"],
[0x01, "team", "Team", "$E$C6Quests for you\nand your team\nmembers."], [0x81, "team", "Team", "$E$C6Quests for you\nand your team\nmembers."],
[0x02, "battle", "Battle", "$E$C6Battle mode rule\nsets"], [0x02, "battle", "Battle", "$E$C6Battle mode rule\nsets"],
[0x04, "challenge-ep1", "Challenge (Episode 1)", "$E$C6Challenge mode\nquests in Episode 1"], [0x04, "challenge-ep1", "Challenge (Episode 1)", "$E$C6Challenge mode\nquests in Episode 1"],
[0x04, "challenge-ep2", "Challenge (Episode 2)", "$E$C6Challenge mode\nquests in Episode 2"], [0x04, "challenge-ep2", "Challenge (Episode 2)", "$E$C6Challenge mode\nquests in Episode 2"],
+6 -6
View File
@@ -178,16 +178,16 @@
}, },
"QuestCategories": [ "QuestCategories": [
[0x01, "retrieval", "Retrieval", "$E$C6Quests that involve\nretrieving an object"], [0x81, "retrieval", "Retrieval", "$E$C6Quests that involve\nretrieving an object"],
[0x01, "extermination", "Extermination", "$E$C6Quests that involve\ndestroying all\nmonsters"], [0x81, "extermination", "Extermination", "$E$C6Quests that involve\ndestroying all\nmonsters"],
[0x01, "events", "Events", "$E$C6Quests that are part\nof an event"], [0x81, "events", "Events", "$E$C6Quests that are part\nof an event"],
[0x01, "shops", "Shops", "$E$C6Quests that contain\nshops"], [0x81, "shops", "Shops", "$E$C6Quests that contain\nshops"],
[0x01, "vr", "Virtual Reality", "$E$C6Quests that are\ndone in a simulator"], [0x81, "vr", "Virtual Reality", "$E$C6Quests that are\ndone in a simulator"],
[0x81, "tower", "Control Tower", "$E$C6Quests that take\nplace at the Control\nTower"], [0x81, "tower", "Control Tower", "$E$C6Quests that take\nplace at the Control\nTower"],
[0x81, "team", "Team", "$E$C6Quests for you\nand your team\nmembers."], [0x81, "team", "Team", "$E$C6Quests for you\nand your team\nmembers."],
[0x02, "battle", "Battle", "$E$C6Battle mode rule\nsets"], [0x02, "battle", "Battle", "$E$C6Battle mode rule\nsets"],
[0x04, "challenge-ep1", "Challenge (Episode 1)", "$E$C6Challenge mode\nquests in Episode 1"], [0x04, "challenge-ep1", "Challenge (Episode 1)", "$E$C6Challenge mode\nquests in Episode 1"],
[0x84, "challenge-ep2", "Challenge (Episode 2)", "$E$C6Challenge mode\nquests in Episode 2"], [0x04, "challenge-ep2", "Challenge (Episode 2)", "$E$C6Challenge mode\nquests in Episode 2"],
[0x08, "solo-story", "Story", "$E$C6Quests that follow\nthe Episode 1 story"], [0x08, "solo-story", "Story", "$E$C6Quests that follow\nthe Episode 1 story"],
[0x08, "solo-extra", "Solo", "$E$C6Quests that require\na single player"], [0x08, "solo-extra", "Solo", "$E$C6Quests that require\na single player"],
[0x10, "government-ep1", "Hero in Red", "$E$CG-Red Ring Rico-\n$C6Quests that follow\nthe Episode 1\nstoryline"], [0x10, "government-ep1", "Hero in Red", "$E$CG-Red Ring Rico-\n$C6Quests that follow\nthe Episode 1\nstoryline"],