最终整理版
This commit is contained in:
@@ -0,0 +1,394 @@
|
||||
#include "QuestSystem.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* kUnknownQuestState = "ProgressCommons.UnknownProgress";
|
||||
constexpr const char* kCompletedToken = ".REWARDS_WITHDREW";
|
||||
constexpr const char* kTutorialCompleted = "ProgressCommons.TUTORIAL.EKINU_DONE";
|
||||
constexpr const char* kCompletedProgress = "ProgressCommons.CompletedProgress";
|
||||
|
||||
bool HasSuffix(const std::string& value, const std::string& suffix)
|
||||
{
|
||||
return value.size() >= suffix.size()
|
||||
&& value.compare(value.size() - suffix.size(), suffix.size(), suffix) == 0;
|
||||
}
|
||||
|
||||
std::string TrimQuestValue(std::string value)
|
||||
{
|
||||
while (!value.empty() && (value.front() == ' ' || value.front() == '\t')) {
|
||||
value.erase(value.begin());
|
||||
}
|
||||
while (!value.empty() && (value.back() == ' ' || value.back() == '\t' || value.back() == '\r')) {
|
||||
value.pop_back();
|
||||
}
|
||||
if (value.size() >= 2 && value.front() == '"' && value.back() == '"') {
|
||||
value = value.substr(1, value.size() - 2);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string QuestSymbolForId(int id)
|
||||
{
|
||||
static const std::map<int, std::string> symbols = {
|
||||
{0, "ProgressCommons.Quest.SPLATYNA_OFFERING"},
|
||||
{1, "ProgressCommons.Quest.GRAIN_IN_THE_SAND"},
|
||||
{2, "ProgressCommons.Quest.SNAKE_PIT_THIEF"},
|
||||
{3, "ProgressCommons.Quest.SNAKE_PIT_BITING_THIRST"},
|
||||
{4, "ProgressCommons.Quest.SANDSTORM_MINE_ABANDONED_TREASURE"},
|
||||
{5, "ProgressCommons.Quest.DESERT_DEEP_XAKELBAEL"},
|
||||
{6, "ProgressCommons.Quest.TULIMSHAR_OLD_FRIENDSHIP"},
|
||||
{7, "ProgressCommons.Quest.TUTORIAL"},
|
||||
{8, "ProgressCommons.Quest.ELANORE_POTION"},
|
||||
{9, "ProgressCommons.Quest.NINA_HUNGRY"},
|
||||
{10, "ProgressCommons.Quest.MINE_EXPLORATION"},
|
||||
{11, "ProgressCommons.Quest.SANDSTORM_NATHAN_WATER"},
|
||||
};
|
||||
const auto it = symbols.find(id);
|
||||
return it == symbols.end() ? std::string{} : it->second;
|
||||
}
|
||||
|
||||
void ApplyQuestField(QuestDefinition& definition, const std::string& key, const std::string& value)
|
||||
{
|
||||
if (key == "id") {
|
||||
definition.id = std::stoi(value);
|
||||
definition.symbol = QuestSymbolForId(definition.id);
|
||||
} else if (key == "name") {
|
||||
definition.name = value;
|
||||
} else if (key == "description") {
|
||||
definition.description = value;
|
||||
} else if (key == "giver") {
|
||||
definition.giver = value;
|
||||
} else if (key == "giverLocation") {
|
||||
definition.giverLocation = value;
|
||||
} else if (key == "target") {
|
||||
definition.target = value;
|
||||
} else if (key == "targetLocation") {
|
||||
definition.targetLocation = value;
|
||||
} else if (key == "reward") {
|
||||
definition.reward = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool ObjectiveMatchesEvent(const QuestObjectiveProgress& objective, const QuestEvent& event)
|
||||
{
|
||||
if (objective.completed) {
|
||||
return false;
|
||||
}
|
||||
if (objective.kind == QuestObjectiveKind::CatchAnyPet && event.kind == QuestObjectiveKind::CatchAnyPet) {
|
||||
return true;
|
||||
}
|
||||
if (objective.kind != event.kind) {
|
||||
return false;
|
||||
}
|
||||
return objective.target.empty() || objective.target == event.target;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string UnknownQuestState()
|
||||
{
|
||||
return kUnknownQuestState;
|
||||
}
|
||||
|
||||
bool IsUnknownQuestState(const std::string& questState)
|
||||
{
|
||||
return questState.empty()
|
||||
|| questState == kUnknownQuestState
|
||||
|| HasSuffix(questState, ".INACTIVE");
|
||||
}
|
||||
|
||||
bool IsCompletedQuestState(const std::string& questState)
|
||||
{
|
||||
return questState == kCompletedProgress
|
||||
|| questState == kTutorialCompleted
|
||||
|| HasSuffix(questState, kCompletedToken);
|
||||
}
|
||||
|
||||
std::string QuestStateStore::Get(const std::string& questName) const
|
||||
{
|
||||
const auto it = states_.find(questName);
|
||||
return it == states_.end() ? UnknownQuestState() : it->second;
|
||||
}
|
||||
|
||||
QuestTransition QuestStateStore::Set(const std::string& questName, const std::string& questState)
|
||||
{
|
||||
QuestTransition transition;
|
||||
transition.questName = questName;
|
||||
transition.previousState = Get(questName);
|
||||
transition.newState = questState.empty() ? UnknownQuestState() : questState;
|
||||
transition.changed = transition.previousState != transition.newState;
|
||||
transition.started = transition.changed
|
||||
&& IsUnknownQuestState(transition.previousState)
|
||||
&& !IsUnknownQuestState(transition.newState);
|
||||
transition.completed = transition.changed && IsCompletedQuestState(transition.newState);
|
||||
states_[questName] = transition.newState;
|
||||
return transition;
|
||||
}
|
||||
|
||||
bool QuestStateStore::IsStarted(const std::string& questName) const
|
||||
{
|
||||
const std::string state = Get(questName);
|
||||
return !IsUnknownQuestState(state) && !IsCompletedQuestState(state);
|
||||
}
|
||||
|
||||
bool QuestStateStore::IsCompleted(const std::string& questName) const
|
||||
{
|
||||
return IsCompletedQuestState(Get(questName));
|
||||
}
|
||||
|
||||
const std::map<std::string, std::string>& QuestStateStore::RawStates() const
|
||||
{
|
||||
return states_;
|
||||
}
|
||||
|
||||
void QuestStateStore::ImportRawStates(const std::map<std::string, std::string>& rawStates)
|
||||
{
|
||||
states_ = rawStates;
|
||||
}
|
||||
|
||||
std::vector<QuestDefinition> LoadQuestDefinitionsFromDirectory(const std::filesystem::path& questDirectory)
|
||||
{
|
||||
std::vector<QuestDefinition> definitions;
|
||||
if (!std::filesystem::exists(questDirectory)) {
|
||||
return definitions;
|
||||
}
|
||||
|
||||
for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(questDirectory)) {
|
||||
if (!entry.is_regular_file() || entry.path().extension() != ".tres") {
|
||||
continue;
|
||||
}
|
||||
|
||||
QuestDefinition definition;
|
||||
std::ifstream in(entry.path());
|
||||
std::string line;
|
||||
while (std::getline(in, line)) {
|
||||
const std::size_t equals = line.find('=');
|
||||
if (equals == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
const std::string key = TrimQuestValue(line.substr(0, equals));
|
||||
const std::string value = TrimQuestValue(line.substr(equals + 1));
|
||||
ApplyQuestField(definition, key, value);
|
||||
}
|
||||
|
||||
if (!definition.symbol.empty() && !definition.name.empty()) {
|
||||
definitions.push_back(definition);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(definitions.begin(), definitions.end(), [](const QuestDefinition& a, const QuestDefinition& b) {
|
||||
return a.id < b.id;
|
||||
});
|
||||
return definitions;
|
||||
}
|
||||
|
||||
const QuestDefinition* FindQuestDefinition(
|
||||
const std::vector<QuestDefinition>& definitions,
|
||||
const std::string& questSymbol)
|
||||
{
|
||||
const auto it = std::find_if(definitions.begin(), definitions.end(), [&](const QuestDefinition& definition) {
|
||||
return definition.symbol == questSymbol;
|
||||
});
|
||||
return it == definitions.end() ? nullptr : &*it;
|
||||
}
|
||||
|
||||
std::string QuestTitleForSymbol(
|
||||
const std::vector<QuestDefinition>& definitions,
|
||||
const std::string& questSymbol)
|
||||
{
|
||||
const QuestDefinition* definition = FindQuestDefinition(definitions, questSymbol);
|
||||
return definition ? definition->name : questSymbol;
|
||||
}
|
||||
|
||||
QuestEvent QuestEvent::VisitedMap(std::string mapName)
|
||||
{
|
||||
return {QuestObjectiveKind::VisitMap, std::move(mapName), 1};
|
||||
}
|
||||
|
||||
QuestEvent QuestEvent::CapturedPet(std::string speciesName)
|
||||
{
|
||||
return {QuestObjectiveKind::CatchAnyPet, std::move(speciesName), 1};
|
||||
}
|
||||
|
||||
QuestEvent QuestEvent::SawPet(std::string speciesName)
|
||||
{
|
||||
return {QuestObjectiveKind::SeeSpecies, std::move(speciesName), 1};
|
||||
}
|
||||
|
||||
QuestEvent QuestEvent::DefeatedPet(std::string speciesName)
|
||||
{
|
||||
return {QuestObjectiveKind::DefeatSpecies, std::move(speciesName), 1};
|
||||
}
|
||||
|
||||
QuestEvent QuestEvent::OwnedItem(std::string itemName, int count)
|
||||
{
|
||||
return {QuestObjectiveKind::OwnItem, std::move(itemName), count};
|
||||
}
|
||||
|
||||
QuestEvent QuestEvent::TalkedToNpc(std::string npcName)
|
||||
{
|
||||
return {QuestObjectiveKind::TalkToNpc, std::move(npcName), 1};
|
||||
}
|
||||
|
||||
QuestEvent QuestEvent::HatchedEgg(std::string eggName)
|
||||
{
|
||||
return {QuestObjectiveKind::HatchEgg, std::move(eggName), 1};
|
||||
}
|
||||
|
||||
QuestEvent QuestEvent::ChoseStarterPet(std::string speciesName)
|
||||
{
|
||||
return {QuestObjectiveKind::ChooseStarterPet, std::move(speciesName), 1};
|
||||
}
|
||||
|
||||
void ApplyQuestEvent(QuestRuntime& runtime, const QuestEvent& event)
|
||||
{
|
||||
for (QuestObjectiveProgress& objective : runtime.objectives) {
|
||||
if (!ObjectiveMatchesEvent(objective, event)) {
|
||||
continue;
|
||||
}
|
||||
if (objective.kind == QuestObjectiveKind::OwnItem) {
|
||||
objective.current = std::max(objective.current, event.amount);
|
||||
} else {
|
||||
objective.current += event.amount;
|
||||
}
|
||||
objective.completed = objective.current >= objective.required;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<QuestJournalEntry> BuildQuestJournal(
|
||||
const std::vector<QuestDefinition>& definitions,
|
||||
const QuestRuntime& runtime)
|
||||
{
|
||||
std::vector<QuestJournalEntry> journal;
|
||||
for (const QuestDefinition& definition : definitions) {
|
||||
const bool started = runtime.states.IsStarted(definition.symbol);
|
||||
const bool completed = runtime.states.IsCompleted(definition.symbol);
|
||||
if (!started && !completed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QuestJournalEntry entry;
|
||||
entry.questSymbol = definition.symbol;
|
||||
entry.title = definition.name;
|
||||
entry.description = definition.description;
|
||||
entry.giver = definition.giver;
|
||||
entry.target = definition.target;
|
||||
entry.reward = definition.reward;
|
||||
entry.completed = completed;
|
||||
|
||||
for (const QuestObjectiveProgress& objective : runtime.objectives) {
|
||||
if (!objective.questSymbol.empty() && objective.questSymbol != definition.symbol) {
|
||||
continue;
|
||||
}
|
||||
QuestJournalObjective journalObjective;
|
||||
journalObjective.title = objective.title;
|
||||
journalObjective.current = objective.current;
|
||||
journalObjective.required = objective.required;
|
||||
journalObjective.completed = objective.completed;
|
||||
entry.objectives.push_back(journalObjective);
|
||||
}
|
||||
|
||||
journal.push_back(entry);
|
||||
}
|
||||
return journal;
|
||||
}
|
||||
|
||||
std::size_t CompletedObjectiveCount(const QuestRuntime& runtime)
|
||||
{
|
||||
return static_cast<std::size_t>(std::count_if(
|
||||
runtime.objectives.begin(),
|
||||
runtime.objectives.end(),
|
||||
[](const QuestObjectiveProgress& objective) {
|
||||
return objective.completed;
|
||||
}));
|
||||
}
|
||||
|
||||
std::size_t QuestObjectiveWindowStart(const QuestRuntime& runtime, std::size_t maxVisible)
|
||||
{
|
||||
if (maxVisible == 0 || runtime.objectives.size() <= maxVisible) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto firstIncomplete = std::find_if(
|
||||
runtime.objectives.begin(),
|
||||
runtime.objectives.end(),
|
||||
[](const QuestObjectiveProgress& objective) {
|
||||
return !objective.completed;
|
||||
});
|
||||
if (firstIncomplete == runtime.objectives.end()) {
|
||||
return runtime.objectives.size() - maxVisible;
|
||||
}
|
||||
|
||||
const std::size_t index = static_cast<std::size_t>(std::distance(runtime.objectives.begin(), firstIncomplete));
|
||||
const std::size_t lead = std::min<std::size_t>(2, maxVisible / 2);
|
||||
return std::min(index > lead ? index - lead : 0, runtime.objectives.size() - maxVisible);
|
||||
}
|
||||
|
||||
std::optional<std::string> NextQuestTargetMap(const QuestRuntime& runtime)
|
||||
{
|
||||
for (const QuestObjectiveProgress& objective : runtime.objectives) {
|
||||
if (!objective.completed && objective.kind == QuestObjectiveKind::VisitMap && !objective.target.empty()) {
|
||||
return objective.target;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<std::string> QuestTargetMaps(const QuestRuntime& runtime)
|
||||
{
|
||||
std::vector<std::string> maps;
|
||||
for (const QuestObjectiveProgress& objective : runtime.objectives) {
|
||||
if (objective.kind == QuestObjectiveKind::VisitMap && !objective.target.empty()) {
|
||||
maps.push_back(objective.target);
|
||||
}
|
||||
}
|
||||
return maps;
|
||||
}
|
||||
|
||||
QuestRuntime MakeInitialTonoriQuestRuntime()
|
||||
{
|
||||
QuestRuntime runtime;
|
||||
runtime.states.Set(
|
||||
"ProgressCommons.Quest.TUTORIAL",
|
||||
"ProgressCommons.TUTORIAL.INTRO_ITEMS_GIVEN");
|
||||
|
||||
runtime.objectives = {
|
||||
{"visit_tulimshar", "抵达图利姆沙", QuestObjectiveKind::VisitMap, "Tulimshar", 1, 0, false, "ProgressCommons.Quest.TUTORIAL"},
|
||||
{"talk_kael", "找凯尔确认身体状况", QuestObjectiveKind::TalkToNpc, "Kael", 1, 0, false, "ProgressCommons.Quest.TUTORIAL"},
|
||||
{"choose_starter_pet", "选择初始宠物", QuestObjectiveKind::ChooseStarterPet, {}, 1, 0, false, "ProgressCommons.Quest.TUTORIAL"},
|
||||
{"clear_peyotes", "清理 5 只仙人掌怪", QuestObjectiveKind::DefeatSpecies, "Peyote", 5, 0, false, "ProgressCommons.Quest.TUTORIAL"},
|
||||
{"clear_maggots", "清理 5 只沙虫", QuestObjectiveKind::DefeatSpecies, "Maggot", 5, 0, false, "ProgressCommons.Quest.TUTORIAL"},
|
||||
{"report_ekinu", "向艾基努汇报农田情况", QuestObjectiveKind::TalkToNpc, "Ekinu", 1, 0, false, "ProgressCommons.Quest.TUTORIAL"},
|
||||
{"catch_first_pet", "捕捉第一只野外宠物", QuestObjectiveKind::CatchAnyPet, {}, 1, 0, false, "ProgressCommons.Quest.TUTORIAL"},
|
||||
{"elanore_maggot_slime", "收集 6 份沙虫黏液", QuestObjectiveKind::OwnItem, "Maggot Slime", 6, 0, false, "ProgressCommons.Quest.ELANORE_POTION"},
|
||||
{"elanore_water", "准备 1 瓶水", QuestObjectiveKind::OwnItem, "Water Bottle", 1, 0, false, "ProgressCommons.Quest.ELANORE_POTION"},
|
||||
{"elanore_cactus_drink", "准备 1 杯仙人掌饮料", QuestObjectiveKind::OwnItem, "Cactus Drink", 1, 0, false, "ProgressCommons.Quest.ELANORE_POTION"},
|
||||
{"nina_croissant", "给妮娜带一份可颂", QuestObjectiveKind::OwnItem, "Croissant", 1, 0, false, "ProgressCommons.Quest.NINA_HUNGRY"},
|
||||
{"riskim_flour", "在港口找到蓝封蜡面粉桶", QuestObjectiveKind::OwnItem, "Flour Barrel", 1, 0, false, "ProgressCommons.Quest.GRAIN_IN_THE_SAND"},
|
||||
{"talk_dausen", "向城外的道森询问矿洞路线", QuestObjectiveKind::TalkToNpc, "Dausen", 1, 0, false, "ProgressCommons.Quest.MINE_EXPLORATION"},
|
||||
{"visit_sandstorm", "前往沙漠风暴谷地", QuestObjectiveKind::VisitMap, "Sandstorm", 1, 0, false, "ProgressCommons.Quest.MINE_EXPLORATION"},
|
||||
{"talk_nathan", "与矿洞入口的内森会合", QuestObjectiveKind::TalkToNpc, "Nathan", 1, 0, false, "ProgressCommons.Quest.MINE_EXPLORATION"},
|
||||
{"visit_desert_mines", "进入沙漠矿洞", QuestObjectiveKind::VisitMap, "Desert Mines", 1, 0, false, "ProgressCommons.Quest.MINE_EXPLORATION"},
|
||||
{"defeat_scorpion", "击败矿洞里的沙蝎", QuestObjectiveKind::DefeatSpecies, "Scorpion", 1, 0, false, "ProgressCommons.Quest.MINE_EXPLORATION"},
|
||||
{"visit_deep_level", "抵达沙漠深层矿道", QuestObjectiveKind::VisitMap, "Desert Deep Level", 1, 0, false, "ProgressCommons.Quest.DESERT_DEEP_XAKELBAEL"},
|
||||
{"face_xakelbael", "直面夏凯尔贝尔", QuestObjectiveKind::TalkToNpc, "Xakelbael", 1, 0, false, "ProgressCommons.Quest.DESERT_DEEP_XAKELBAEL"},
|
||||
{"defeat_xakelbael", "击败夏凯尔贝尔", QuestObjectiveKind::DefeatSpecies, "Xakelbael", 1, 0, false, "ProgressCommons.Quest.DESERT_DEEP_XAKELBAEL"},
|
||||
{"find_mine_key", "找到废弃矿层钥匙", QuestObjectiveKind::OwnItem, "Chest Mine Key", 1, 0, false, "ProgressCommons.Quest.SANDSTORM_MINE_ABANDONED_TREASURE"},
|
||||
{"open_mine_chest", "打开矿洞遗落宝箱", QuestObjectiveKind::OwnItem, "Short Sword", 1, 0, false, "ProgressCommons.Quest.SANDSTORM_MINE_ABANDONED_TREASURE"},
|
||||
{"nathan_water", "带给内森一瓶水", QuestObjectiveKind::OwnItem, "Water Bottle", 1, 0, false, "ProgressCommons.Quest.SANDSTORM_NATHAN_WATER"},
|
||||
{"visit_snake_pit", "调查蛇坑", QuestObjectiveKind::VisitMap, "Snake Pit", 1, 0, false, "ProgressCommons.Quest.SNAKE_PIT_THIEF"},
|
||||
{"find_snake_clues", "找到 5 条蛇坑线索", QuestObjectiveKind::OwnItem, "Thief's Key", 1, 0, false, "ProgressCommons.Quest.SNAKE_PIT_THIEF"},
|
||||
{"open_thief_chest", "打开盗贼宝箱", QuestObjectiveKind::OwnItem, "Scimitar", 1, 0, false, "ProgressCommons.Quest.SNAKE_PIT_THIEF"},
|
||||
{"mauro_accept", "答应帮助毛罗取水", QuestObjectiveKind::TalkToNpc, "Mauro", 1, 0, false, "ProgressCommons.Quest.SNAKE_PIT_BITING_THIRST"},
|
||||
{"mauro_water", "从蛇坑清水池装满水", QuestObjectiveKind::OwnItem, "Water Bottle", 1, 0, false, "ProgressCommons.Quest.SNAKE_PIT_BITING_THIRST"},
|
||||
{"old_friend_letters", "从城堡暗处取回旧信件", QuestObjectiveKind::OwnItem, "Sealed Letters", 1, 0, false, "ProgressCommons.Quest.TULIMSHAR_OLD_FRIENDSHIP"},
|
||||
{"collect_first_pet_partner", "收服 1 只宠物伙伴", QuestObjectiveKind::CatchAnyPet, {}, 1, 0, false, "ProgressCommons.Quest.TUTORIAL"},
|
||||
};
|
||||
|
||||
return runtime;
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct QuestTransition {
|
||||
bool changed = false;
|
||||
bool started = false;
|
||||
bool completed = false;
|
||||
std::string questName;
|
||||
std::string previousState;
|
||||
std::string newState;
|
||||
};
|
||||
|
||||
class QuestStateStore {
|
||||
public:
|
||||
std::string Get(const std::string& questName) const;
|
||||
QuestTransition Set(const std::string& questName, const std::string& questState);
|
||||
bool IsStarted(const std::string& questName) const;
|
||||
bool IsCompleted(const std::string& questName) const;
|
||||
const std::map<std::string, std::string>& RawStates() const;
|
||||
void ImportRawStates(const std::map<std::string, std::string>& rawStates);
|
||||
|
||||
private:
|
||||
std::map<std::string, std::string> states_;
|
||||
};
|
||||
|
||||
struct QuestDefinition {
|
||||
int id = -1;
|
||||
std::string symbol;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string giver;
|
||||
std::string giverLocation;
|
||||
std::string target;
|
||||
std::string targetLocation;
|
||||
std::string reward;
|
||||
};
|
||||
|
||||
enum class QuestObjectiveKind {
|
||||
VisitMap,
|
||||
CatchAnyPet,
|
||||
CatchSpecies,
|
||||
SeeSpecies,
|
||||
DefeatSpecies,
|
||||
OwnItem,
|
||||
TalkToNpc,
|
||||
HatchEgg,
|
||||
ChooseStarterPet,
|
||||
};
|
||||
|
||||
struct QuestObjectiveProgress {
|
||||
std::string id;
|
||||
std::string title;
|
||||
QuestObjectiveKind kind = QuestObjectiveKind::VisitMap;
|
||||
std::string target;
|
||||
int required = 1;
|
||||
int current = 0;
|
||||
bool completed = false;
|
||||
std::string questSymbol;
|
||||
};
|
||||
|
||||
struct QuestRuntime {
|
||||
QuestStateStore states;
|
||||
std::vector<QuestObjectiveProgress> objectives;
|
||||
};
|
||||
|
||||
struct QuestEvent {
|
||||
QuestObjectiveKind kind = QuestObjectiveKind::VisitMap;
|
||||
std::string target;
|
||||
int amount = 1;
|
||||
|
||||
static QuestEvent VisitedMap(std::string mapName);
|
||||
static QuestEvent CapturedPet(std::string speciesName);
|
||||
static QuestEvent SawPet(std::string speciesName);
|
||||
static QuestEvent DefeatedPet(std::string speciesName);
|
||||
static QuestEvent OwnedItem(std::string itemName, int count);
|
||||
static QuestEvent TalkedToNpc(std::string npcName);
|
||||
static QuestEvent HatchedEgg(std::string eggName);
|
||||
static QuestEvent ChoseStarterPet(std::string speciesName);
|
||||
};
|
||||
|
||||
struct QuestJournalObjective {
|
||||
std::string title;
|
||||
int current = 0;
|
||||
int required = 1;
|
||||
bool completed = false;
|
||||
};
|
||||
|
||||
struct QuestJournalEntry {
|
||||
std::string questSymbol;
|
||||
std::string title;
|
||||
std::string description;
|
||||
std::string giver;
|
||||
std::string target;
|
||||
std::string reward;
|
||||
bool completed = false;
|
||||
std::vector<QuestJournalObjective> objectives;
|
||||
};
|
||||
|
||||
bool IsUnknownQuestState(const std::string& questState);
|
||||
bool IsCompletedQuestState(const std::string& questState);
|
||||
std::string UnknownQuestState();
|
||||
|
||||
std::vector<QuestDefinition> LoadQuestDefinitionsFromDirectory(const std::filesystem::path& questDirectory);
|
||||
const QuestDefinition* FindQuestDefinition(
|
||||
const std::vector<QuestDefinition>& definitions,
|
||||
const std::string& questSymbol);
|
||||
std::string QuestTitleForSymbol(
|
||||
const std::vector<QuestDefinition>& definitions,
|
||||
const std::string& questSymbol);
|
||||
|
||||
void ApplyQuestEvent(QuestRuntime& runtime, const QuestEvent& event);
|
||||
std::vector<QuestJournalEntry> BuildQuestJournal(
|
||||
const std::vector<QuestDefinition>& definitions,
|
||||
const QuestRuntime& runtime);
|
||||
std::size_t CompletedObjectiveCount(const QuestRuntime& runtime);
|
||||
std::size_t QuestObjectiveWindowStart(const QuestRuntime& runtime, std::size_t maxVisible);
|
||||
std::optional<std::string> NextQuestTargetMap(const QuestRuntime& runtime);
|
||||
std::vector<std::string> QuestTargetMaps(const QuestRuntime& runtime);
|
||||
QuestRuntime MakeInitialTonoriQuestRuntime();
|
||||
Reference in New Issue
Block a user