#pragma once #include "common.h" #include #include #include #include #include // 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 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 void field_read(const char* text, Field& field) { if constexpr (std::is_same_v) field = text; else if constexpr (std::is_same_v) std::sscanf(text, "%f %f", &field.x, &field.y); else if constexpr (std::is_same_v) std::sscanf(text, "%f %f %f", &field.r, &field.g, &field.b); else if constexpr (std::is_floating_point_v) field = (Field)SDL_atof(text); else field = (Field)SDL_atoi(text); } template std::string field_write(const Field& field) { if constexpr (std::is_same_v) return field; else if constexpr (std::is_same_v) return field_write(field.x) + " " + field_write(field.y); else if constexpr (std::is_same_v) return field_write(field.r) + " " + field_write(field.g) + " " + field_write(field.b); else if constexpr (std::is_floating_point_v) return std::to_string(field); else return std::to_string((int)field); } // Sets whichever of a struct's fields a line names. template 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 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 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 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 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); } };