Files
shweets-sim/src/save.h
T

156 lines
4.8 KiB
C++

#pragma once
#include "common.h"
#include <SDL3/SDL_iostream.h>
#include <SDL3/SDL_video.h>
#include <cstdio>
#include <string>
#include <type_traits>
// Text files of key=value lines. A struct names the fields it keeps with visit(visitor), calling visitor(key, field)
// for each, and reads and writes them through fields_read and fields_write.
enum Measurement
{
MEASUREMENT_METRIC,
MEASUREMENT_IMPERIAL
};
void file_write(const std::string& path, const std::string& text);
// Calls line with each key and value of a file. Returns whether it was there.
template <class Line> bool is_file_read(const std::string& path, Line&& line)
{
char* text = (char*)SDL_LoadFile(path.c_str(), nullptr);
if (!text)
return false;
char* rest = nullptr;
for (char* entry = SDL_strtok_r(text, "\r\n", &rest); entry; entry = SDL_strtok_r(nullptr, "\r\n", &rest))
{
char* value = SDL_strchr(entry, '=');
if (!value)
continue;
*value++ = '\0';
line(std::string(entry), (const char*)value);
}
SDL_free(text);
return true;
}
template <class Field> void field_read(const char* text, Field& field)
{
if constexpr (std::is_same_v<Field, std::string>)
field = text;
else if constexpr (std::is_same_v<Field, Vec2>)
std::sscanf(text, "%f %f", &field.x, &field.y);
else if constexpr (std::is_same_v<Field, Color>)
std::sscanf(text, "%f %f %f", &field.r, &field.g, &field.b);
else if constexpr (std::is_floating_point_v<Field>)
field = (Field)SDL_atof(text);
else
field = (Field)SDL_atoi(text);
}
template <class Field> std::string field_write(const Field& field)
{
if constexpr (std::is_same_v<Field, std::string>)
return field;
else if constexpr (std::is_same_v<Field, Vec2>)
return field_write(field.x) + " " + field_write(field.y);
else if constexpr (std::is_same_v<Field, Color>)
return field_write(field.r) + " " + field_write(field.g) + " " + field_write(field.b);
else if constexpr (std::is_floating_point_v<Field>)
return std::to_string(field);
else
return std::to_string((int)field);
}
// Sets whichever of a struct's fields a line names.
template <class Fields> void field_line_read(Fields& fields, const std::string& key, const char* value)
{
fields.visit([&](const char* name, auto& field) {
if (key == name)
field_read(value, field);
});
}
template <class Fields> bool is_fields_read(const std::string& path, Fields& fields)
{
return is_file_read(path, [&](const std::string& key, const char* value) { field_line_read(fields, key, value); });
}
template <class Fields> std::string fields_write(Fields& fields)
{
std::string text;
fields.visit([&](const char* name, auto& field) { text += std::string(name) + "=" + field_write(field) + "\n"; });
return text;
}
// The game's preferences, kept in settings.txt.
struct Settings
{
std::string lastCharacter;
Color color = {0.12f, 0.515f, 0.115f, 1};
int height = 900;
int volume = 50;
int width = 1600;
int x = SDL_WINDOWPOS_CENTERED;
int y = SDL_WINDOWPOS_CENTERED;
Measurement measurement = MEASUREMENT_METRIC;
bool isCharacterColor = true;
bool isConsole = false;
bool isMuted = false;
void load();
void save();
void reset();
template <class Visitor> void visit(Visitor&& visitor)
{
visitor("character_color", isCharacterColor);
visitor("color", color);
visitor("console", isConsole);
visitor("height", height);
visitor("last_character", lastCharacter);
visitor("measurement", measurement);
visitor("mute", isMuted);
visitor("volume", volume);
visitor("width", width);
visitor("x", x);
visitor("y", y);
}
};
// A character's body as it is now: what's in it, how it digests, what it's eaten. Rates are percent per tick.
struct Stats
{
float calories = 0;
float capacity = 0;
float digestionProgress = 0;
float digestionRate = 0;
float playtime = 0;
float totalCalories = 0;
float weight = 0;
int digestionTimer = 0;
int totalConsumed = 0;
int totalSpawned = 0;
bool isAlternate = false;
bool isDigesting = false;
template <class Visitor> void visit(Visitor&& visitor)
{
visitor("alternate", isAlternate);
visitor("calories", calories);
visitor("capacity", capacity);
visitor("digesting", isDigesting);
visitor("digestion_progress", digestionProgress);
visitor("digestion_rate", digestionRate);
visitor("digestion_timer", digestionTimer);
visitor("playtime", playtime);
visitor("total_calories", totalCalories);
visitor("total_consumed", totalConsumed);
visitor("total_spawned", totalSpawned);
visitor("weight", weight);
}
};