a (not so) mini update

This commit is contained in:
2026-04-01 02:08:50 -04:00
parent fb6f902f28
commit af04a9b313
64 changed files with 1158 additions and 583 deletions

View File

@@ -43,28 +43,31 @@ namespace game::resource::xml
query_vec3(root, "ColorR", "ColorG", "ColorB", color);
root->QueryFloatAttribute("Weight", &weight);
root->QueryDoubleAttribute("Weight", &weight);
root->QueryDoubleAttribute("WeightMax", &weightMax);
root->QueryFloatAttribute("Capacity", &capacity);
root->QueryFloatAttribute("CapacityMin", &capacityMin);
root->QueryFloatAttribute("CapacityMax", &capacityMax);
root->QueryFloatAttribute("CapacityMaxMultiplier", &capacityMaxMultiplier);
root->QueryFloatAttribute("CapacityIfOverStuffedOnDigestBonus", &capacityIfOverStuffedOnDigestBonus);
root->QueryDoubleAttribute("Capacity", &capacity);
root->QueryDoubleAttribute("CapacityMin", &capacityMin);
root->QueryDoubleAttribute("CapacityMax", &capacityMax);
root->QueryDoubleAttribute("CapacityMaxMultiplier", &capacityMaxMultiplier);
root->QueryDoubleAttribute("CapacityIfOverStuffedOnDigestBonus", &capacityIfOverStuffedOnDigestBonus);
root->QueryFloatAttribute("CaloriesToKilogram", &caloriesToKilogram);
root->QueryDoubleAttribute("CaloriesToKilogram", &caloriesToKilogram);
root->QueryFloatAttribute("DigestionRate", &digestionRate);
root->QueryFloatAttribute("DigestionRateMin", &digestionRateMin);
root->QueryFloatAttribute("DigestionRateMax", &digestionRateMax);
root->QueryDoubleAttribute("DigestionRate", &digestionRate);
root->QueryDoubleAttribute("DigestionRateMin", &digestionRateMin);
root->QueryDoubleAttribute("DigestionRateMax", &digestionRateMax);
root->QueryIntAttribute("DigestionTimerMax", &digestionTimerMax);
root->QueryFloatAttribute("EatSpeed", &eatSpeed);
root->QueryFloatAttribute("EatSpeedMin", &eatSpeedMin);
root->QueryFloatAttribute("EatSpeedMax", &eatSpeedMax);
root->QueryDoubleAttribute("EatSpeed", &eatSpeed);
root->QueryDoubleAttribute("EatSpeedMin", &eatSpeedMin);
root->QueryDoubleAttribute("EatSpeedMax", &eatSpeedMax);
root->QueryFloatAttribute("BlinkChance", &blinkChance);
root->QueryFloatAttribute("GurgleChance", &gurgleChance);
root->QueryFloatAttribute("GurgleCapacityMultiplier", &gurgleCapacityMultiplier);
root->QueryDoubleAttribute("BlinkChance", &blinkChance);
root->QueryDoubleAttribute("GurgleChance", &gurgleChance);
root->QueryDoubleAttribute("GurgleCapacityMultiplier", &gurgleCapacityMultiplier);
root->QueryIntAttribute("TextBlipPeriodBase", &textBlipPeriodBase);
auto dialoguePath = physfs::Path(archive + "/" + "dialogue.xml");
@@ -100,6 +103,7 @@ namespace game::resource::xml
if (auto element = root->FirstChildElement("Sounds"))
{
query_sound_entry_collection(element, "Blip", archive, soundRootPath, sounds.blip);
query_sound_entry_collection(element, "Digest", archive, soundRootPath, sounds.digest);
query_sound_entry_collection(element, "Gurgle", archive, soundRootPath, sounds.gurgle);
}
@@ -127,6 +131,7 @@ namespace game::resource::xml
child->QueryFloatAttribute("Threshold", &stage.threshold);
child->QueryIntAttribute("AreaID", &stage.areaID);
dialogue.query_pool_id(child, "DialoguePoolID", stage.pool.id);
query_string_attribute(child, "AnimationAppendID", &stage.animationAppendID);
stages.emplace_back(std::move(stage));
}
}
@@ -178,12 +183,11 @@ namespace game::resource::xml
if (child->FindAttribute("Layer")) query_layer_id(child, "Layer", anm2, interactArea.layerID);
query_null_id(child, "Null", anm2, interactArea.nullID);
query_string_attribute(child, "Animation", &interactArea.animation);
query_string_attribute(child, "AnimationFull", &interactArea.animationFull);
query_string_attribute(child, "AnimationCursorHover", &interactArea.animationCursorHover);
query_string_attribute(child, "AnimationCursorActive", &interactArea.animationCursorActive);
query_sound_entry_collection(child, "Sound", archive, soundRootPath, interactArea.sound, "Path");
dialogue.query_pool_id(child, "DialoguePoolID", interactArea.pool.id);
dialogue.query_pool_id(child, "DialoguePoolIDFull", interactArea.poolFull.id);
query_bool_attribute(child, "IsHold", &interactArea.isHold);
child->QueryFloatAttribute("DigestionBonusRub", &interactArea.digestionBonusRub);
child->QueryFloatAttribute("DigestionBonusClick", &interactArea.digestionBonusClick);
@@ -225,6 +229,9 @@ namespace game::resource::xml
else
logger.warning(std::format("No character skill_check.xml file found: {}", path.string()));
if (auto stringsPath = physfs::Path(archive + "/" + "strings.xml"); stringsPath.is_valid())
strings = Strings(stringsPath);
logger.info(std::format("Initialized character: {}", name));
this->path = path;

View File

@@ -11,8 +11,9 @@
#include "dialogue.hpp"
#include "item.hpp"
#include "menu.hpp"
#include "skill_check.hpp"
#include "save.hpp"
#include "skill_check.hpp"
#include "strings.hpp"
namespace game::resource::xml
{
@@ -24,6 +25,7 @@ namespace game::resource::xml
float threshold{};
int areaID{};
Dialogue::PoolReference pool{-1};
std::string animationAppendID{};
};
struct EatArea
@@ -42,8 +44,6 @@ namespace game::resource::xml
struct InteractArea
{
std::string animation{};
std::string animationFull{};
std::string animationCursorActive{};
std::string animationCursorHover{};
SoundEntryCollection sound{};
@@ -53,6 +53,7 @@ namespace game::resource::xml
int typeID{-1};
bool isHold{};
Dialogue::PoolReference pool{-1};
Dialogue::PoolReference poolFull{-1};
float digestionBonusRub{};
float digestionBonusClick{};
@@ -73,6 +74,7 @@ namespace game::resource::xml
struct Sounds
{
SoundEntryCollection blip{};
SoundEntryCollection gurgle{};
SoundEntryCollection digest{};
};
@@ -98,6 +100,7 @@ namespace game::resource::xml
Menu menuSchema{};
Cursor cursorSchema{};
SkillCheck skillCheckSchema{};
Strings strings{};
Save save{};
@@ -119,23 +122,25 @@ namespace game::resource::xml
std::string name{};
std::filesystem::path path{};
float weight{50};
float capacity{2000.0f};
float capacityMin{2000.0f};
float capacityMax{99999.0f};
float capacityMaxMultiplier{1.5f};
float capacityIfOverStuffedOnDigestBonus{0.25f};
float caloriesToKilogram{1000.0f};
float digestionRate{0.05f};
float digestionRateMin{0.0f};
float digestionRateMax{0.25f};
double weight{50};
double weightMax{1000};
double capacity{2000.0};
double capacityMin{2000.0};
double capacityMax{99999.0};
double capacityMaxMultiplier{1.5};
double capacityIfOverStuffedOnDigestBonus{0.25};
double caloriesToKilogram{1000.0};
double digestionRate{0.05};
double digestionRateMin{0.0};
double digestionRateMax{0.25};
int digestionTimerMax{60};
float eatSpeed{1.0f};
float eatSpeedMin{1.0f};
float eatSpeedMax{3.0f};
float blinkChance{1.0f};
float gurgleChance{1.0f};
float gurgleCapacityMultiplier{1.0f};
int textBlipPeriodBase{3};
double eatSpeed{1.0};
double eatSpeedMin{1.0};
double eatSpeedMax{3.0};
double blinkChance{1.0};
double gurgleChance{1.0};
double gurgleCapacityMultiplier{1.0};
Dialogue::PoolReference pool{-1};
Character() = default;

View File

@@ -41,7 +41,7 @@ namespace game::resource::xml
query_string_attribute(root, "Name", &name);
query_string_attribute(root, "Description", &description);
query_string_attribute(root, "Author", &author);
query_string_attribute(root, "Credits", &credits);
query_vec3(root, "ColorR", "ColorG", "ColorB", color);
root->QueryFloatAttribute("Weight", &weight);

View File

@@ -28,7 +28,7 @@ namespace game::resource::xml
int stages{1};
std::string name{};
std::string author{};
std::string credits{};
std::string description{};
std::filesystem::path path{};
float weight{50};

View File

@@ -4,6 +4,7 @@
#include "../../log.hpp"
#include "../../util/math.hpp"
#include <algorithm>
#include <format>
using namespace tinyxml2;
@@ -98,6 +99,8 @@ namespace game::resource::xml
id++;
}
entrySelectionOrder.assign(entries.size(), -1);
}
if (auto element = root->FirstChildElement("Pools"))
@@ -158,15 +161,32 @@ namespace game::resource::xml
}
}
int Dialogue::Pool::get() const
int Dialogue::entry_pick(Pool& pool)
{
if (this->empty()) return -1;
auto index = rand() % this->size();
return this->at(index);
if (pool.empty()) return -1;
std::vector<int> unselected{};
for (auto id : pool)
if (id >= 0 && id < (int)entrySelectionOrder.size() && entrySelectionOrder[id] < 0) unselected.emplace_back(id);
std::vector<int> candidates = unselected.empty() ? std::vector<int>(pool.begin(), pool.end()) : unselected;
if (candidates.empty()) return -1;
auto oldestOrder = entrySelectionOrder[candidates.front()];
for (auto id : candidates)
oldestOrder = std::min(oldestOrder, entrySelectionOrder[id]);
std::vector<int> oldestCandidates{};
for (auto id : candidates)
if (entrySelectionOrder[id] == oldestOrder) oldestCandidates.emplace_back(id);
auto pickedID = oldestCandidates.at(rand() % oldestCandidates.size());
entrySelectionOrder[pickedID] = selectionCounter++;
return pickedID;
}
Dialogue::Entry* Dialogue::get(int id) { return &entries.at(id); }
Dialogue::Entry* Dialogue::get(Dialogue::EntryReference& entry) { return &entries.at(entry.id); }
Dialogue::Entry* Dialogue::get(const std::string& string) { return &entries.at(entryIDMap.at(string)); }
Dialogue::Entry* Dialogue::get(Dialogue::PoolReference& pool) { return &entries.at(pools.at(pool.id).get()); }
Dialogue::Entry* Dialogue::get(Dialogue::Pool& pool) { return &entries.at(pool.get()); }
Dialogue::Entry* Dialogue::get(Dialogue::PoolReference& pool) { return &entries.at(entry_pick(pools.at(pool.id))); }
Dialogue::Entry* Dialogue::get(Dialogue::Pool& pool) { return &entries.at(entry_pick(pool)); }
}

View File

@@ -5,6 +5,7 @@
#include <string>
#include <map>
#include <vector>
#include "../../util/physfs.hpp"
@@ -45,11 +46,7 @@ namespace game::resource::xml
inline bool is_valid() const { return id != -1; };
};
class Pool : public std::vector<int>
{
public:
int get() const;
};
class Pool : public std::vector<int> {};
std::map<std::string, int> entryIDMap;
std::map<int, std::string> entryIDMapReverse;
@@ -57,6 +54,8 @@ namespace game::resource::xml
std::vector<Pool> pools{};
std::map<std::string, int> poolMap{};
std::vector<long long> entrySelectionOrder{};
long long selectionCounter{};
EntryReference start{-1};
EntryReference end{-1};
@@ -83,8 +82,9 @@ namespace game::resource::xml
Entry* get(Dialogue::EntryReference&);
Entry* get(Dialogue::Pool&);
Entry* get(Dialogue::PoolReference&);
int entry_pick(Pool&);
void query_entry_id(tinyxml2::XMLElement* element, const char* name, int& id);
void query_pool_id(tinyxml2::XMLElement* element, const char* name, int& id);
inline bool is_valid() const { return isValid; };
};
}
}

View File

@@ -110,7 +110,8 @@ namespace game::resource::xml
std::string itemTextureRootPath{};
query_string_attribute(element, "TextureRootPath", &itemTextureRootPath);
element->QueryIntAttribute("ChewCount", &chewCount);
element->QueryIntAttribute("Durability", &durability);
if (element->FindAttribute("ChewCount")) element->QueryIntAttribute("ChewCount", &durability);
element->QueryIntAttribute("QuantityMax", &quantityMax);
for (auto child = element->FirstChildElement("Item"); child; child = child->NextSiblingElement("Item"))
@@ -121,11 +122,14 @@ namespace game::resource::xml
query_string_attribute(child, "Description", &item.description);
query_float_optional_attribute(child, "Calories", item.calories);
query_float_optional_attribute(child, "CapacityBonus", item.capacityBonus);
query_optional_vec3(child, "ColorR", "ColorG", "ColorB", item.color);
query_float_optional_attribute(child, "DigestionBonus", item.digestionBonus);
query_float_optional_attribute(child, "EatSpeedBonus", item.eatSpeedBonus);
query_float_optional_attribute(child, "Gravity", item.gravity);
query_int_optional_attribute(child, "ChewCount", item.chewCount);
query_int_optional_attribute(child, "Durability", item.durability);
if (!item.durability.has_value()) query_int_optional_attribute(child, "ChewCount", item.durability);
if (child->FindAttribute("UpgradeID"))
{

View File

@@ -6,6 +6,8 @@
#include <unordered_set>
#include <vector>
#include <glm/glm.hpp>
#include "../audio.hpp"
#include "anm2.hpp"
#include "sound_entry.hpp"
@@ -46,10 +48,12 @@ namespace game::resource::xml
std::optional<int> upgradeID{};
std::optional<int> flavorID;
std::optional<float> calories{};
std::optional<float> capacityBonus{};
std::optional<glm::vec3> color{};
std::optional<float> eatSpeedBonus{};
std::optional<float> digestionBonus{};
std::optional<float> gravity{};
std::optional<int> chewCount{};
std::optional<int> durability{};
bool isSkillCheckReward{};
bool isToggleSpritesheet{};
};
@@ -90,7 +94,7 @@ namespace game::resource::xml
Animations animations{};
Sounds sounds{};
Anm2 baseAnm2{};
int chewCount{2};
int durability{2};
int quantityMax{99};
bool isValid{};

View File

@@ -91,7 +91,8 @@ namespace game::resource::xml
{
Item item{};
child->QueryIntAttribute("ID", &item.id);
child->QueryIntAttribute("ChewCount", &item.chewCount);
child->QueryIntAttribute("Durability", &item.durability);
if (child->FindAttribute("ChewCount")) child->QueryIntAttribute("ChewCount", &item.durability);
child->QueryFloatAttribute("PositionX", &item.position.x);
child->QueryFloatAttribute("PositionY", &item.position.y);
child->QueryFloatAttribute("VelocityX", &item.velocity.x);
@@ -162,7 +163,7 @@ namespace game::resource::xml
auto itemElement = itemsElement->InsertNewChildElement("Item");
itemElement->SetAttribute("ID", item.id);
itemElement->SetAttribute("ChewCount", item.chewCount);
itemElement->SetAttribute("Durability", item.durability);
itemElement->SetAttribute("PositionX", item.position.x);
itemElement->SetAttribute("PositionY", item.position.y);
itemElement->SetAttribute("VelocityX", item.velocity.x);

View File

@@ -15,7 +15,7 @@ namespace game::resource::xml
struct Item
{
int id{};
int chewCount{};
int durability{};
glm::vec2 position{};
glm::vec2 velocity{};
float rotation{};

View File

@@ -23,7 +23,7 @@ namespace game::resource::xml
int volume{50};
bool isUseCharacterColor{true};
glm::vec3 color{0.120f, 0.515f, 0.115f};
glm::vec3 color{0.120f, 0.120f, 0.120f};
glm::ivec2 windowSize{1600, 900};
glm::vec2 windowPosition{};

View File

@@ -0,0 +1,48 @@
#include "strings.hpp"
#include "../../log.hpp"
#include "util.hpp"
#include <format>
using namespace tinyxml2;
namespace game::resource::xml
{
namespace
{
std::string definition_element_name_get(const Strings::Definition& definition)
{
std::string name = definition.attribute;
if (name.rfind("Text", 0) == 0) name.replace(0, 4, "String");
return name;
}
}
Strings::Strings()
{
for (int i = 0; i < Count; i++)
values[i] = definitions[i].fallback;
}
Strings::Strings(const util::physfs::Path& path)
{
for (int i = 0; i < Count; i++)
values[i] = definitions[i].fallback;
XMLDocument document;
if (document_load(path, document) != XML_SUCCESS) return;
auto root = document.RootElement();
if (!root) return;
for (int i = 0; i < Count; i++)
if (auto element = root->FirstChildElement(definition_element_name_get(definitions[i]).c_str()))
query_string_attribute(element, "Text", &values[i]);
isValid = true;
logger.info(std::format("Initialized strings: {}", path.c_str()));
}
const std::string& Strings::get(Type type) const { return values[type]; }
}

View File

@@ -0,0 +1,159 @@
#pragma once
#include "../../util/physfs.hpp"
#include <array>
#include <string>
namespace game::resource::xml
{
#define GAME_XML_STRING_LIST(X) \
X(MenuTabInteract, "TextMenuTabInteract", "Interact") \
X(MenuTabArcade, "TextMenuTabArcade", "Arcade") \
X(MenuTabInventory, "TextMenuTabInventory", "Inventory") \
X(MenuTabSettings, "TextMenuTabSettings", "Settings") \
X(MenuTabCheats, "TextMenuTabCheats", "Cheats") \
X(MenuTabDebug, "TextMenuTabDebug", "Debug") \
X(MenuOpenTooltip, "TextMenuOpenTooltip", "Open Main Menu") \
X(MenuCloseTooltip, "TextMenuCloseTooltip", "Close Main Menu") \
X(InteractChatButton, "TextInteractChatButton", "Let's chat!") \
X(InteractHelpButton, "TextInteractHelpButton", "Help") \
X(InteractFeelingButton, "TextInteractFeelingButton", "How are you feeling?") \
X(InteractWeightFormat, "TextInteractWeightFormat", "Weight: %0.2f %s (Stage: %i)") \
X(InteractCapacityFormat, "TextInteractCapacityFormat", "Capacity: %0.0f kcal (Max: %0.0f kcal)") \
X(InteractDigestionRateFormat, "TextInteractDigestionRateFormat", "Digestion Rate: %0.2f%%/sec") \
X(InteractEatingSpeedFormat, "TextInteractEatingSpeedFormat", "Eating Speed: %0.2fx") \
X(InteractTotalCaloriesFormat, "TextInteractTotalCaloriesFormat", "Total Calories Consumed: %0.0f kcal") \
X(InteractTotalFoodItemsFormat, "TextInteractTotalFoodItemsFormat", "Total Food Items Eaten: %i") \
X(SettingsMeasurementSystem, "TextSettingsMeasurementSystem", "Measurement System") \
X(SettingsMetric, "TextSettingsMetric", "Metric") \
X(SettingsMetricTooltip, "TextSettingsMetricTooltip", "Use kilograms (kg).") \
X(SettingsImperial, "TextSettingsImperial", "Imperial") \
X(SettingsImperialTooltip, "TextSettingsImperialTooltip", "Use pounds (lbs).") \
X(SettingsSound, "TextSettingsSound", "Sound") \
X(SettingsVolume, "TextSettingsVolume", "Volume") \
X(SettingsVolumeTooltip, "TextSettingsVolumeTooltip", "Adjust master volume.") \
X(SettingsAppearance, "TextSettingsAppearance", "Appearance") \
X(SettingsUseCharacterColor, "TextSettingsUseCharacterColor", "Use Character Color") \
X(SettingsUseCharacterColorTooltip, "TextSettingsUseCharacterColorTooltip", \
"When playing, the UI will use the character's preset UI color.") \
X(SettingsColor, "TextSettingsColor", "Color") \
X(SettingsColorTooltip, "TextSettingsColorTooltip", "Change the UI color.") \
X(SettingsResetButton, "TextSettingsResetButton", "Reset to Default") \
X(SettingsSaveButton, "TextSettingsSaveButton", "Save") \
X(SettingsSaveTooltip, "TextSettingsSaveTooltip", "Save the game.\n(Note: the game autosaves frequently.)") \
X(SettingsReturnToCharactersButton, "TextSettingsReturnToCharactersButton", "Return to Characters") \
X(SettingsReturnToCharactersTooltip, "TextSettingsReturnToCharactersTooltip", \
"Go back to the character selection screen.\nProgress will be saved.") \
X(ToastCheatsUnlocked, "TextToastCheatsUnlocked", "Cheats unlocked!") \
X(ToastSaving, "TextToastSaving", "Saving...") \
X(ToolsHomeButton, "TextToolsHomeButton", "Home") \
X(ToolsHomeTooltip, "TextToolsHomeTooltip", "Reset camera view.\n(Shortcut: Home)") \
X(ToolsOpenTooltip, "TextToolsOpenTooltip", "Open Tools") \
X(ToolsCloseTooltip, "TextToolsCloseTooltip", "Close Tools") \
X(DebugCursorScreenFormat, "TextDebugCursorScreenFormat", "Cursor Pos (Screen): %0.0f, %0.0f") \
X(DebugCursorWorldFormat, "TextDebugCursorWorldFormat", "Cursor Pos (World): %0.0f, %0.0f") \
X(DebugAnimations, "TextDebugAnimations", "Animations") \
X(DebugNowPlayingFormat, "TextDebugNowPlayingFormat", "Now Playing: %s") \
X(DebugDialogue, "TextDebugDialogue", "Dialogue") \
X(DebugShowNulls, "TextDebugShowNulls", "Show Nulls (Hitboxes)") \
X(DebugShowWorldBounds, "TextDebugShowWorldBounds", "Show World Bounds") \
X(DebugItem, "TextDebugItem", "Item") \
X(DebugHeld, "TextDebugHeld", "Held") \
X(DebugItemTypeFormat, "TextDebugItemTypeFormat", "Type: %i") \
X(DebugItemPositionFormat, "TextDebugItemPositionFormat", "Position: %0.0f, %0.0f") \
X(DebugItemVelocityFormat, "TextDebugItemVelocityFormat", "Velocity: %0.0f, %0.0f") \
X(DebugItemDurabilityFormat, "TextDebugItemDurabilityFormat", "Durability: %i") \
X(InventoryEmptyHint, "TextInventoryEmptyHint", "Check the \"Arcade\" tab to earn rewards!") \
X(InventoryFlavorFormat, "TextInventoryFlavorFormat", "Flavor: %s") \
X(InventoryCaloriesFormat, "TextInventoryCaloriesFormat", "%0.0f kcal") \
X(InventoryDurabilityFormat, "TextInventoryDurabilityFormat", "Durability: %i") \
X(InventoryCapacityBonusFormat, "TextInventoryCapacityBonusFormat", "Capacity Bonus: +%0.0f kcal") \
X(InventoryDigestionRateBonusFormat, "TextInventoryDigestionRateBonusFormat", "Digestion Rate Bonus: +%0.2f%% / sec") \
X(InventoryDigestionRatePenaltyFormat, "TextInventoryDigestionRatePenaltyFormat", "Digestion Rate Penalty: %0.2f%% / sec") \
X(InventoryEatSpeedBonusFormat, "TextInventoryEatSpeedBonusFormat", "Eat Speed Bonus: +%0.2f%% / sec") \
X(InventoryEatSpeedPenaltyFormat, "TextInventoryEatSpeedPenaltyFormat", "Eat Speed Penalty: %0.2f%% / sec") \
X(InventoryUpgradePreviewFormat, "TextInventoryUpgradePreviewFormat", "Upgrade: %ix -> %s") \
X(InventoryUnknown, "TextInventoryUnknown", "???") \
X(InventorySpawnButton, "TextInventorySpawnButton", "Spawn") \
X(InventoryUpgradeButton, "TextInventoryUpgradeButton", "Upgrade") \
X(InventoryUpgradeAllButton, "TextInventoryUpgradeAllButton", "Upgrade All") \
X(InventoryUpgradeNoPath, "TextInventoryUpgradeNoPath", "This item cannot be upgraded.") \
X(InventoryUpgradeNeedsTemplate, "TextInventoryUpgradeNeedsTemplate", "Needs {}x to upgrade into {}!") \
X(InventoryUpgradeOneTemplate, "TextInventoryUpgradeOneTemplate", "Use {}x to upgrade into 1x {}.") \
X(InventoryUpgradeAllTemplate, "TextInventoryUpgradeAllTemplate", "Use {}x to upgrade into {}x {}.") \
X(ArcadeSkillCheckName, "TextArcadeSkillCheckName", "Skill Check") \
X(ArcadeSkillCheckDescription, "TextArcadeSkillCheckDescription", \
"Test your timing to build score, chain combos, and earn rewards based on your performance.") \
X(ArcadePlayButton, "TextArcadePlayButton", "Play") \
X(ArcadeStatsButton, "TextArcadeStatsButton", "Stats") \
X(ArcadeBackButton, "TextArcadeBackButton", "Back") \
X(ArcadeBestFormat, "TextArcadeBestFormat", "Best: %i pts (%ix)") \
X(ArcadeTotalSkillChecksFormat, "TextArcadeTotalSkillChecksFormat", "Total Skill Checks: %i") \
X(ArcadeAccuracyFormat, "TextArcadeAccuracyFormat", "Accuracy: %0.2f%%") \
X(InfoProgressMax, "TextInfoProgressMax", "MAX") \
X(InfoProgressToNextStage, "TextInfoProgressToNextStage", "To Next Stage") \
X(InfoStageProgressFormat, "TextInfoStageProgressFormat", "Stage: %i/%i (%0.1f%%)") \
X(InfoMaxedOut, "TextInfoMaxedOut", "Maxed out!") \
X(InfoStageStartFormat, "TextInfoStageStartFormat", "Start: %0.2f %s") \
X(InfoStageCurrentFormat, "TextInfoStageCurrentFormat", "Current: %0.2f %s") \
X(InfoStageNextFormat, "TextInfoStageNextFormat", "Next: %0.2f %s") \
X(InfoDigestion, "TextInfoDigestion", "Digestion") \
X(InfoDigesting, "TextInfoDigesting", "Digesting...") \
X(InfoDigestionInProgress, "TextInfoDigestionInProgress", "Digestion in progress...") \
X(InfoGiveFoodToStartDigesting, "TextInfoGiveFoodToStartDigesting", "Give food to start digesting!") \
X(InfoDigestionRateFormat, "TextInfoDigestionRateFormat", "Rate: %0.2f%% / sec") \
X(InfoEatingSpeedFormat, "TextInfoEatingSpeedFormat", "Eating Speed: %0.2fx") \
X(SkillCheckScoreFormat, "TextSkillCheckScoreFormat", "Score: %i pts (%ix)") \
X(SkillCheckBestFormat, "TextSkillCheckBestFormat", "Best: %i pts (%ix)") \
X(SkillCheckInstructions, "TextSkillCheckInstructions", "Match the line to the colored areas with Space/click! Better performance, better rewards!") \
X(SkillCheckScoreLoss, "TextSkillCheckScoreLoss", "-1") \
X(SkillCheckRewardToast, "TextSkillCheckRewardToast", "Fantastic score! Congratulations!") \
X(SkillCheckHighScoreToast, "TextSkillCheckHighScoreToast", "High Score!") \
X(SkillCheckGradeSuccessTemplate, "TextSkillCheckGradeSuccessTemplate", "{} (+{})") \
X(SkillCheckMenuButton, "TextSkillCheckMenuButton", "Menu") \
X(CheatsCalories, "TextCheatsCalories", "Calories") \
X(CheatsCapacity, "TextCheatsCapacity", "Capacity") \
X(CheatsWeight, "TextCheatsWeight", "Weight") \
X(CheatsWeightFormat, "TextCheatsWeightFormat", "%0.2f kg") \
X(CheatsStage, "TextCheatsStage", "Stage") \
X(CheatsDigestionRate, "TextCheatsDigestionRate", "Digestion Rate") \
X(CheatsDigestionRateFormat, "TextCheatsDigestionRateFormat", "%0.2f% / tick") \
X(CheatsEatSpeed, "TextCheatsEatSpeed", "Eat Speed") \
X(CheatsEatSpeedFormat, "TextCheatsEatSpeedFormat", "%0.2fx") \
X(CheatsDigestButton, "TextCheatsDigestButton", "Digest") \
X(CheatsInventory, "TextCheatsInventory", "Inventory")
class Strings
{
public:
enum Type
{
#define X(type, attr, fallback) type,
GAME_XML_STRING_LIST(X)
#undef X
Count
};
struct Definition
{
const char* attribute;
const char* fallback;
};
inline static constexpr std::array<Definition, Count> definitions{{
#define X(type, attr, fallback) {attr, fallback},
GAME_XML_STRING_LIST(X)
#undef X
}};
std::array<std::string, Count> values{};
bool isValid{};
Strings();
Strings(const util::physfs::Path&);
const std::string& get(Type) const;
};
}

View File

@@ -117,6 +117,29 @@ namespace game::resource::xml
return result;
}
XMLError query_optional_vec3(XMLElement* element, const char* attributeX, const char* attributeY,
const char* attributeZ, std::optional<glm::vec3>& value)
{
auto hasX = element->FindAttribute(attributeX);
auto hasY = element->FindAttribute(attributeY);
auto hasZ = element->FindAttribute(attributeZ);
if (!hasX && !hasY && !hasZ)
{
value.reset();
return XML_NO_ATTRIBUTE;
}
value = glm::vec3();
auto result = XML_SUCCESS;
if (hasX) result = query_result_merge(result, element->QueryFloatAttribute(attributeX, &value->x));
if (hasY) result = query_result_merge(result, element->QueryFloatAttribute(attributeY, &value->y));
if (hasZ) result = query_result_merge(result, element->QueryFloatAttribute(attributeZ, &value->z));
return result;
}
XMLError document_load(const physfs::Path& path, XMLDocument& document)
{
if (!path.is_valid())

View File

@@ -32,6 +32,8 @@ namespace game::resource::xml
std::optional<float>& value);
tinyxml2::XMLError query_int_optional_attribute(tinyxml2::XMLElement* element, const char* attribute,
std::optional<int>& value);
tinyxml2::XMLError query_optional_vec3(tinyxml2::XMLElement* element, const char* attributeX, const char* attributeY,
const char* attributeZ, std::optional<glm::vec3>& value);
tinyxml2::XMLError query_event_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& eventID);
tinyxml2::XMLError query_layer_id(tinyxml2::XMLElement* element, const char* name, const Anm2& anm2, int& layerID);