osb/source/game/StarQuestDescriptor.cpp
Kae 0aee45a117 Cache certain item generation calls from interfaces
Helps a little with the lag from recipes when having crafting interfaces open, but it's still noticeable.
Also micro-optimized Root maintenance by unlocking the Root mutexes for their respective shared_ptrs earlier once we have our own shared_ptr.
2023-07-23 22:44:02 +10:00

445 lines
14 KiB
C++

#include "StarQuestDescriptor.hpp"
#include "StarDataStreamExtra.hpp"
#include "StarItemDatabase.hpp"
#include "StarJsonExtra.hpp"
#include "StarMonster.hpp"
#include "StarMonsterDatabase.hpp"
#include "StarObject.hpp"
#include "StarObjectDatabase.hpp"
#include "StarVersioningDatabase.hpp"
namespace Star {
bool QuestItem::operator==(QuestItem const& rhs) const {
return itemName == rhs.itemName && parameters == rhs.parameters;
}
ItemDescriptor QuestItem::descriptor() const {
return ItemDescriptor(itemName, 1, parameters);
}
bool QuestEntity::operator==(QuestEntity const& rhs) const {
return uniqueId == rhs.uniqueId && species == rhs.species && gender == rhs.gender;
}
bool QuestLocation::operator==(QuestLocation const& rhs) const {
return uniqueId == rhs.uniqueId && region == rhs.region;
}
bool QuestMonsterType::operator==(QuestMonsterType const& rhs) const {
return typeName == rhs.typeName && parameters == rhs.parameters;
}
bool QuestNpcType::operator==(QuestNpcType const& rhs) const {
return species == rhs.species && typeName == rhs.typeName && parameters == rhs.parameters && seed == rhs.seed;
}
bool QuestCoordinate::operator==(QuestCoordinate const& rhs) const {
return coordinate == coordinate;
}
QuestParamDetail questParamDetailFromJson(Json const& json) {
String type = json.getString("type");
if (type == "item") {
ItemDescriptor itemDescriptor = ItemDescriptor(json.get("item"));
return QuestItem{itemDescriptor.name(), itemDescriptor.parameters()};
} else if (type == "itemTag") {
return QuestItemTag(json.getString("tag"));
} else if (type == "itemList") {
return QuestItemList(json.getArray("items").transformed(construct<ItemDescriptor>()));
} else if (type == "entity") {
return QuestEntity{
json.optString("uniqueId"),
json.optString("species"),
json.optString("gender").apply([](String const& gender) { return GenderNames.getLeft(gender); })
};
} else if (type == "location") {
return QuestLocation{json.optString("uniqueId"), jsonToRectF(json.get("region"))};
} else if (type == "monsterType") {
return QuestMonsterType{json.getString("typeName"), json.getObject("parameters", JsonObject{})};
} else if (type == "npcType") {
return QuestNpcType{
json.getString("species"),
json.getString("typeName"),
json.getObject("parameters", JsonObject{}),
json.optUInt("seed")
};
} else if (type == "coordinate") {
return QuestCoordinate{ CelestialCoordinate(json.get("coordinate")) };
} else if (type == "json") {
return json;
} else if (type == "noDetail") {
return {};
} else {
throw StarException::format("Invalid QuestParam type {}", type);
}
}
QuestParam QuestParam::fromJson(Json const& json) {
return QuestParam{
questParamDetailFromJson(json), json.optString("name"), json.opt("portrait"), json.optString("indicator")};
}
QuestParamDetail questParamDetailDiskLoad(Json const& json) {
String type = json.getString("type");
if (type == "item") {
ItemDescriptor itemDescriptor = ItemDescriptor::loadStore(json.get("item"));
return QuestItem{itemDescriptor.name(), itemDescriptor.parameters()};
} else if (type == "itemList") {
return QuestItemList(json.getArray("items").transformed(&ItemDescriptor::loadStore));
} else {
return questParamDetailFromJson(json);
}
}
QuestParam QuestParam::diskLoad(Json const& json) {
return QuestParam{
questParamDetailDiskLoad(json), json.optString("name"), json.opt("portrait"), json.optString("indicator")};
}
Json questParamDetailToJson(QuestParamDetail const& detail) {
if (detail.is<QuestItem>()) {
QuestItem item = detail.get<QuestItem>();
return JsonObject{{"type", "item"}, {"item", item.descriptor().toJson()}};
} else if (detail.is<QuestItemTag>()) {
String tag = detail.get<QuestItemTag>();
return JsonObject{{"type", "itemTag"}, {"tag", tag}};
} else if (detail.is<QuestItemList>()) {
List<ItemDescriptor> itemList = detail.get<QuestItemList>();
return JsonObject{{"type", "itemList"}, {"items", itemList.transformed(mem_fn(&ItemDescriptor::toJson))}};
} else if (detail.is<QuestEntity>()) {
QuestEntity entity = detail.get<QuestEntity>();
return JsonObject{
{"type", "entity"},
{"uniqueId", jsonFromMaybe(entity.uniqueId)},
{"species", jsonFromMaybe(entity.species)},
{"gender", jsonFromMaybe(entity.gender.apply([](Gender gender) { return GenderNames.getRight(gender); }))}
};
} else if (detail.is<QuestLocation>()) {
QuestLocation location = detail.get<QuestLocation>();
return JsonObject{
{"type", "location"},
{"uniqueId", jsonFromMaybe<String>(location.uniqueId)},
{"region", jsonFromRectF(location.region)}
};
} else if (detail.is<QuestMonsterType>()) {
QuestMonsterType monsterType = detail.get<QuestMonsterType>();
return JsonObject{
{"type", "monsterType"}, {"typeName", monsterType.typeName}, {"parameters", monsterType.parameters}};
}
else if (detail.is<QuestNpcType>()) {
QuestNpcType npcType = detail.get<QuestNpcType>();
return JsonObject{ {"type", "npcType"},
{"species", npcType.species},
{"typeName", npcType.typeName},
{"parameters", npcType.parameters},
{"seed", jsonFromMaybe(npcType.seed)}
};
} else if (detail.is<QuestCoordinate>()) {
return JsonObject{
{"type", "coordinate"},
{"coordinate", detail.get<QuestCoordinate>().coordinate.toJson()}
};
} else if (detail.is<QuestJson>()) {
return detail.get<QuestJson>().set("type", "json");
} else {
starAssert(detail.empty());
return JsonObject{{"type", "noDetail"}};
}
}
Json QuestParam::toJson() const {
Json detailJson = questParamDetailToJson(detail);
return detailJson.set("name", jsonFromMaybe(name))
.set("portrait", jsonFromMaybe(portrait))
.set("indicator", jsonFromMaybe(indicator));
}
Json questParamDetailDiskStore(QuestParamDetail const& detail) {
if (detail.is<QuestItem>()) {
QuestItem item = detail.get<QuestItem>();
return JsonObject{{"type", "item"}, {"item", item.descriptor().diskStore()}};
} else if (detail.is<QuestItemList>()) {
List<ItemDescriptor> itemList = detail.get<QuestItemList>();
return JsonObject{{"type", "itemList"}, {"items", itemList.transformed(mem_fn(&ItemDescriptor::diskStore))}};
} else {
return questParamDetailToJson(detail);
}
}
Json QuestParam::diskStore() const {
Json detailJson = questParamDetailDiskStore(detail);
return detailJson.set("name", jsonFromMaybe(name))
.set("portrait", jsonFromMaybe(portrait))
.set("indicator", jsonFromMaybe(indicator));
}
bool QuestParam::operator==(QuestParam const& rhs) const {
return detail == rhs.detail && name == rhs.name && portrait == rhs.portrait && indicator == rhs.indicator;
}
QuestDescriptor QuestDescriptor::fromJson(Json const& json) {
if (json.isType(Json::Type::String)) {
return {json.toString(), json.toString(), {}, Random::randu64()};
} else {
return {json.getString("questId"),
json.getString("templateId"),
questParamsFromJson(json.get("parameters")),
json.getUInt("seed", Random::randu64())};
}
}
QuestDescriptor QuestDescriptor::diskLoad(Json const& spec) {
auto versioningDatabase = Root::singleton().versioningDatabase();
Json json = versioningDatabase->loadVersionedJson(VersionedJson::fromJson(spec), "QuestDescriptor");
return {json.getString("questId"),
json.getString("templateId"),
questParamsDiskLoad(json.get("parameters")),
json.getUInt("seed", Random::randu64())};
}
Json QuestDescriptor::toJson() const {
return JsonObject{
{"questId", questId}, {"templateId", templateId}, {"parameters", questParamsToJson(parameters)}, {"seed", seed}};
}
Json QuestDescriptor::diskStore() const {
auto versioningDatabase = Root::singleton().versioningDatabase();
auto res = JsonObject{{"questId", questId},
{"templateId", templateId},
{"parameters", questParamsDiskStore(parameters)},
{"seed", seed}};
return versioningDatabase->makeCurrentVersionedJson("QuestDescriptor", res).toJson();
}
bool QuestDescriptor::operator==(QuestDescriptor const& rhs) const {
return questId == rhs.questId && templateId == rhs.templateId && parameters == rhs.parameters && seed == rhs.seed;
}
QuestArcDescriptor QuestArcDescriptor::fromJson(Json const& json) {
if (json.isType(Json::Type::Object) && json.contains("quests")) {
return QuestArcDescriptor{
json.getArray("quests").transformed(&QuestDescriptor::fromJson), json.optString("stagehandUniqueId")};
} else {
return QuestArcDescriptor{{QuestDescriptor::fromJson(json)}, {}};
}
}
QuestArcDescriptor QuestArcDescriptor::diskLoad(Json const& spec) {
auto versioningDatabase = Root::singleton().versioningDatabase();
Json json = versioningDatabase->loadVersionedJson(VersionedJson::fromJson(spec), "QuestArcDescriptor");
return QuestArcDescriptor{
json.getArray("quests").transformed(&QuestDescriptor::diskLoad), json.optString("stagehandUniqueId")};
}
Json QuestArcDescriptor::toJson() const {
return JsonObject{{"quests", quests.transformed(mem_fn(&QuestDescriptor::toJson))},
{"stagehandUniqueId", jsonFromMaybe(stagehandUniqueId)}};
}
Json QuestArcDescriptor::diskStore() const {
auto versioningDatabase = Root::singleton().versioningDatabase();
auto res = JsonObject{{"quests", quests.transformed(mem_fn(&QuestDescriptor::diskStore))},
{"stagehandUniqueId", jsonFromMaybe(stagehandUniqueId)}};
return versioningDatabase->makeCurrentVersionedJson("QuestArcDescriptor", res).toJson();
}
bool QuestArcDescriptor::operator==(QuestArcDescriptor const& rhs) const {
return quests == rhs.quests && stagehandUniqueId == rhs.stagehandUniqueId;
}
String questParamText(QuestParam const& parameter) {
if (parameter.name)
return *parameter.name;
auto itemDatabase = Root::singleton().itemDatabase();
if (parameter.detail.is<QuestItem>()) {
QuestItem item = parameter.detail.get<QuestItem>();
return itemDatabase->itemShared(item.descriptor())->friendlyName();
} else if (parameter.detail.is<QuestItemList>()) {
QuestItemList itemList = parameter.detail.get<QuestItemList>();
StringList itemStrings = itemList.transformed([&itemDatabase](ItemDescriptor const& itemDesc) -> String {
return strf("{} {}", itemDesc.count(), itemDatabase->itemShared(itemDesc)->friendlyName());
});
return itemStrings.join(", ");
} else {
return "";
}
}
template <typename Fun,
typename ArgType = typename FunctionTraits<Fun>::template Arg<0>,
typename RetType = typename FunctionTraits<Fun>::Return>
StringMap<RetType> transformedMapValues(StringMap<ArgType> const& map, Fun fun) {
return StringMap<RetType>::from(map.pairs().transformed(
[fun](pair<String, ArgType> entry) { return make_pair(entry.first, fun(entry.second)); }));
}
StringMap<String> questParamTags(StringMap<QuestParam> const& parameters) {
return transformedMapValues(parameters, questParamText);
}
StringMap<QuestParam> questParamsFromJson(Json const& json) {
return transformedMapValues(json.toObject(), &QuestParam::fromJson);
}
StringMap<QuestParam> questParamsDiskLoad(Json const& json) {
return transformedMapValues(json.toObject(), &QuestParam::diskLoad);
}
Json questParamsToJson(StringMap<QuestParam> const& parameters) {
return transformedMapValues(parameters, [](QuestParam const& quest) { return quest.toJson(); });
}
Json questParamsDiskStore(StringMap<QuestParam> const& parameters) {
return transformedMapValues(parameters, [](QuestParam const& quest) { return quest.diskStore(); });
}
DataStream& operator>>(DataStream& ds, QuestItem& item) {
ds.read(item.itemName);
ds.read(item.parameters);
return ds;
}
DataStream& operator<<(DataStream& ds, QuestItem const& item) {
ds.write(item.itemName);
ds.write(item.parameters);
return ds;
}
DataStream& operator>>(DataStream& ds, QuestEntity& entity) {
ds.read(entity.uniqueId);
ds.read(entity.species);
ds.read(entity.gender);
return ds;
}
DataStream& operator<<(DataStream& ds, QuestEntity const& entity) {
ds.write(entity.uniqueId);
ds.write(entity.species);
ds.write(entity.gender);
return ds;
}
DataStream& operator>>(DataStream& ds, QuestLocation& location) {
ds.read(location.uniqueId);
ds.read(location.region);
return ds;
}
DataStream& operator<<(DataStream& ds, QuestLocation const& location) {
ds.write(location.uniqueId);
ds.write(location.region);
return ds;
}
DataStream& operator>>(DataStream& ds, QuestMonsterType& monsterType) {
ds.read(monsterType.typeName);
ds.read(monsterType.parameters);
return ds;
}
DataStream& operator<<(DataStream& ds, QuestMonsterType const& monsterType) {
ds.write(monsterType.typeName);
ds.write(monsterType.parameters);
return ds;
}
DataStream& operator>>(DataStream& ds, QuestNpcType& npcType) {
ds.read(npcType.species);
ds.read(npcType.typeName);
ds.read(npcType.parameters);
ds.read(npcType.seed);
return ds;
}
DataStream& operator<<(DataStream& ds, QuestNpcType const& npcType) {
ds.write(npcType.species);
ds.write(npcType.typeName);
ds.write(npcType.parameters);
ds.write(npcType.seed);
return ds;
}
DataStream& operator>>(DataStream& ds, QuestCoordinate& coordinate) {
ds.read(coordinate.coordinate);
return ds;
}
DataStream& operator<<(DataStream& ds, QuestCoordinate const& coordinate) {
ds.write(coordinate.coordinate);
return ds;
}
DataStream& operator>>(DataStream& ds, QuestParam& param) {
ds.read(param.detail);
ds.read(param.name);
ds.read(param.portrait);
ds.read(param.indicator);
return ds;
}
DataStream& operator<<(DataStream& ds, QuestParam const& param) {
ds.write(param.detail);
ds.write(param.name);
ds.write(param.portrait);
ds.write(param.indicator);
return ds;
}
DataStream& operator>>(DataStream& ds, QuestDescriptor& quest) {
ds.read(quest.questId);
ds.read(quest.templateId);
ds.read(quest.parameters);
ds.read(quest.seed);
return ds;
}
DataStream& operator<<(DataStream& ds, QuestDescriptor const& quest) {
ds.write(quest.questId);
ds.write(quest.templateId);
ds.write(quest.parameters);
ds.write(quest.seed);
return ds;
}
DataStream& operator>>(DataStream& ds, QuestArcDescriptor& questArc) {
ds.read(questArc.quests);
ds.read(questArc.stagehandUniqueId);
return ds;
}
DataStream& operator<<(DataStream& ds, QuestArcDescriptor const& questArc) {
ds.write(questArc.quests);
ds.write(questArc.stagehandUniqueId);
return ds;
}
}