156 lines
4.8 KiB
C++
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);
|
|
}
|
|
};
|