Split play into body, items, interactions, autofeed and camera, with events, commands and literal strings

This commit is contained in:
shweet committed 2026-11-12 22:14:04 -05:00
1 parent 167f1a5ddd
commit 3e9a0cbd34
21 files changed
+2504 -2431

No files matched your search

+108 -22
View File
@@ -1,9 +1,14 @@
#pragma once
#include "common.h"
#include <SDL3/SDL_iostream.h>
#include <SDL3/SDL_video.h>
#include <cstdio>
#include <string>
#include <vector>
#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
{
@@ -11,6 +16,76 @@ enum Measurement
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
{
@@ -29,23 +104,26 @@ struct Settings
void load();
void save();
void reset();
template <class Visitor> void visit(Visitor&& visitor);
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);
}
};
struct SavedItem
// 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
{
std::string name;
Vec2 position;
Vec2 velocity;
float rotation = 0;
};
// A character's progress, kept in saves/<character>.txt, and the live state of one being played.
struct Progress
{
std::vector<std::string> discovered;
std::vector<SavedItem> items;
Vec2 pan;
float calories = 0;
float capacity = 0;
float digestionProgress = 0;
@@ -53,17 +131,25 @@ struct Progress
float playtime = 0;
float totalCalories = 0;
float weight = 0;
float zoom = 0;
int digestionTimer = 0;
int spawnDelay = 0;
int totalConsumed = 0;
int totalSpawned = 0;
bool isAlternate = false;
bool isDigesting = false;
bool isPostgame = false;
bool isValid = false;
void load(const std::string& path);
void save(const std::string& path);
template <class Visitor> void visit(Visitor&& visitor);
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);
}
};