Files
voxelforge/core/src/Advancement.cpp
T

175 lines
6.1 KiB
C++

#include "vf/Advancement.h"
#include <algorithm>
#include "vf/Command.h"
#include "vf/PotionCatalog.h"
namespace vf {
namespace {
struct NamedDimension {
const char* name;
DimensionId id;
};
// Kept in step with DimensionConfig::nameOf, which Dimension.cpp cannot hand
// back a parser for without dragging every world generator into the link.
const NamedDimension dimensionNames[] = {
{"overworld", DimensionId::Overworld},
{"nether", DimensionId::Nether},
{"end", DimensionId::End},
};
bool parseDimensionName(const std::string& text, DimensionId& out) {
for (const NamedDimension& entry : dimensionNames) {
if (text == entry.name) {
out = entry.id;
return true;
}
}
return false;
}
bool parsePotionName(const std::string& text, PotionKind& out) {
const PotionCatalog& catalog = potionCatalog();
for (const PotionEntry& entry : catalog.entries()) {
if (text == entry.name) {
out = entry.kind;
return true;
}
}
return false;
}
bool parseProfessionName(const std::string& text, VillagerProfession& out) {
for (int i = 0; i < static_cast<int>(VillagerProfession::Count); ++i) {
VillagerProfession profession = static_cast<VillagerProfession>(i);
if (text == villagerProfessionName(profession)) {
out = profession;
return true;
}
}
return false;
}
bool readOptionalResource(const JsonValue& owner, const std::string& key,
const std::string& defaultSpace, ResourceId& out, bool& present,
std::string& problem) {
const JsonValue* field = owner.find(key);
present = false;
if (field == nullptr) return true;
if (!field->isString()) {
problem = "\"" + key + "\" must be a string";
return false;
}
if (!ResourceId::parse(field->stringValue(), defaultSpace, out)) {
problem = "\"" + key + "\" is not a valid resource id: " + field->stringValue();
return false;
}
present = true;
return true;
}
bool readResourceList(const JsonValue& owner, const std::string& key,
const std::string& defaultSpace, std::vector<ResourceId>& out,
std::string& problem) {
const JsonValue* list = owner.find(key);
if (list == nullptr) return true;
if (!list->isArray()) {
problem = "\"" + key + "\" must be an array of resource ids";
return false;
}
for (const JsonValue& element : list->elements()) {
ResourceId id;
if (!element.isString() || !ResourceId::parse(element.stringValue(), defaultSpace, id)) {
problem = "\"" + key + "\" holds something that is not a resource id";
return false;
}
out.push_back(id);
}
return true;
}
bool parseCriterionJson(const std::string& criterionName, const JsonValue& root,
const std::string& defaultSpace, AdvancementCriterion& out,
std::string& problem) {
if (!root.isObject()) {
problem = "criterion \"" + criterionName + "\" must be an object";
return false;
}
const JsonValue* trigger = root.find("trigger");
if (trigger == nullptr || !trigger->isString() ||
!parseAdvancementTriggerName(trigger->stringValue(), out.trigger)) {
problem = "criterion \"" + criterionName + "\" needs a known \"trigger\"";
return false;
}
out.name = criterionName;
out.requiredCount = static_cast<int>(root.intOr("count", 1));
if (out.requiredCount < 1) {
problem = "criterion \"" + criterionName + "\" needs a count of at least 1";
return false;
}
ResourceId id;
bool present = false;
if (!readOptionalResource(root, "item", defaultSpace, id, present, problem)) return false;
if (present) {
if (!resolveItemId(id, out.item)) {
problem = "criterion \"" + criterionName + "\": no such item " + id.text();
return false;
}
out.matchMask |= AdvancementCriterion::MatchItem;
}
if (!readOptionalResource(root, "block", defaultSpace, id, present, problem)) return false;
if (present) {
if (!resolveBlockId(id, out.block)) {
problem = "criterion \"" + criterionName + "\": no such block " + id.text();
return false;
}
out.matchMask |= AdvancementCriterion::MatchBlock;
}
if (!readOptionalResource(root, "tool", defaultSpace, id, present, problem)) return false;
if (present) {
if (!resolveItemId(id, out.tool)) {
problem = "criterion \"" + criterionName + "\": no such tool " + id.text();
return false;
}
out.matchMask |= AdvancementCriterion::MatchTool;
}
if (const JsonValue* species = root.find("species")) {
if (!species->isString() || !parseSpeciesName(species->stringValue(), out.species)) {
problem = "criterion \"" + criterionName + "\": unknown species";
return false;
}
out.matchMask |= AdvancementCriterion::MatchSpecies;
}
if (const JsonValue* dimension = root.find("dimension")) {
if (!dimension->isString() ||
!parseDimensionName(dimension->stringValue(), out.dimension)) {
problem = "criterion \"" + criterionName + "\": unknown dimension";
return false;
}
out.matchMask |= AdvancementCriterion::MatchDimension;
}
if (const JsonValue* potion = root.find("potion")) {
if (!potion->isString() || !parsePotionName(potion->stringValue(), out.potion)) {
problem = "criterion \"" + criterionName + "\": unknown potion";
return false;
}
out.matchMask |= AdvancementCriterion::MatchPotion;
}
if (const JsonValue* profession = root.find("profession")) {
if (!profession->isString() ||
!parseProfessionName(profession->stringValue(), out.profession)) {
problem = "criterion \"" + criterionName + "\": unknown villager profession";
return false;
}
out.matchMask |= AdvancementCriterion::MatchProfession;
}
return true;
}