Compare commits

...
Author SHA1 Message Date
phobos2077 1e3cd38a8f Add GEMINI.md and .gemini to gitignore
Verified to work well for simple tasks with Gemin CLI
2026-04-11 13:31:04 +02:00
phobos2077 50d6b14ff3 Vibe code test: move radiation levels and penalties to external config 2026-04-11 13:26:25 +02:00
phobos2077 4c53a5b85f Refactor settings save/load to use registry pattern
- Some AI work, needs review
2026-04-10 00:41:56 +02:00
8 changed files with 363 additions and 385 deletions
+4
View File
@@ -399,3 +399,7 @@ FodyWeavers.xsd
# versioning header
src/platform/git_version.h
CMakeUserPresets.json
# Local configs for AI agents
.aider*
.gemini
+44
View File
@@ -0,0 +1,44 @@
# Fallout 2 Community Edition - Gemini Instructions
## Project Overview
Fallout 2 Community Edition is a re-implementation of Fallout 2. It is written in C++ and uses CMake as its build system.
## Project Rules
- Only search for source code and symbols inside the `src/` directory.
## Coding Standards & Conventions
- **Language**: C++17.
- **Style**: Follow WebKit style as defined in `.clang-format`.
- Use `clang-format` before committing any changes.
- Short `if` statements without `else` can be on a single line.
- Fix namespace comments.
- **File Naming**:
- Source files: `.cc`
- Header files: `.h`
- **Architectural Constraint**:
- Don't use complicated and rarely used C++ features, unless they are well encapsulated within reusable code.
- Avoid large-scale refactorings. The project aims to reconcile changes from the Reference Edition, and major structural changes make this difficult.
- **Integration Priority**: Integrating features and fixes from Sfall is a top priority.
## Build System (CMake)
- **Version**: Requires CMake 3.18 or higher.
- **Dependencies**: Uses vendored third-party libraries by default (`FALLOUT_VENDORED=ON`).
- **Target**: The main executable is `fallout2-ce`.
- **Platforms**: Supports Windows, Linux, macOS, Android, iOS, and Emscripten (WebAssembly).
## Project Structure
- `src/`: Contains all core game logic and engine implementation.
- `third_party/`: Vendored dependencies (zlib, SDL2, etc.).
- `os/`: Platform-specific assets and configuration (icons, plists, etc.).
- `cmake/`: Custom CMake scripts.
- `files/`: Default configuration files and data assets.
- `sfall_testing/`: SSL scripts for testing Sfall-related functionality.
## Tool Usage
- **Prioritize CLion MCP**: Always use the CLion MCP tools (e.g., `mcp_clion_search_symbols`, `mcp_clion_get_symbol_info`) for finding code definitions and symbols instead of performing manual text-based searches with `grep_search` or `glob`. Use manual text searches only when symbol-based search is not applicable (e.g., searching for non-code text or specific patterns).
## Development Workflow
1. **Research**: Understand how a feature was implemented in the original engine or Sfall before attempting to port or fix it.
2. **Strategy**: Maintain compatibility with original game data and savegames.
3. **Execution**: Apply surgical changes to `src/`.
4. **Validation**: Ensure changes compile across platforms if possible, or at least don't break the build for the current platform.
+15
View File
@@ -0,0 +1,15 @@
[main]
RadiationEffectCount=8
RadiationLevelCount=6
RadiationEffectPrimaryStatCount=6
RadiationThresholds=99,199,399,599,999
RadiationEnduranceModifiers=2,0,-2,-4,-6,-8
RadiationEffectStats=0,1,2,3,4,5,6,7
[RadiationEffectPenalties]
Level0=0,0,0,0,0,0,0,0
Level1=-1,0,0,0,0,0,0,0
Level2=-1,0,0,0,0,-1,0,-3
Level3=-2,0,-1,0,0,-2,-5,-5
Level4=-4,-3,-3,-3,-1,-5,-15,-10
Level5=-6,-5,-5,-5,-3,-6,-20,-10
+67 -92
View File
@@ -1,3 +1,4 @@
#include "config.h"
#include "critter.h"
#include <stdio.h>
@@ -37,37 +38,25 @@ namespace fallout {
#define DUDE_NAME_MAX_LENGTH (32)
// The number of effects caused by radiation.
//
// A radiation effect is an identifier and does not have it's own name. It's
// stat is specified in [gRadiationEffectStats], and it's amount is specified
// in [gRadiationEffectPenalties] for every [RadiationLevel].
#define RADIATION_EFFECT_COUNT 8
static int gRadiationEffectCount = 8;
// Radiation levels.
//
// The names of levels are taken from Fallout 3, comments from Fallout 2.
typedef enum RadiationLevel {
// Very nauseous.
RADIATION_LEVEL_NONE,
// The number of radiation levels.
static int gRadiationLevelCount = 6;
// Slightly fatigued.
RADIATION_LEVEL_MINOR,
// Radiation thresholds.
static int* gRadiationThresholds = nullptr;
// Vomiting does not stop.
RADIATION_LEVEL_ADVANCED,
// Modifiers to endurance for performing radiation damage check.
static int* gRadiationEnduranceModifiers = nullptr;
// Hair is falling out.
RADIATION_LEVEL_CRITICAL,
// List of stats affected by radiation.
static int* gRadiationEffectStats = nullptr;
// Skin is falling off.
RADIATION_LEVEL_DEADLY,
// List of stat modifiers caused by radiation at different radiation levels.
static int** gRadiationEffectPenalties = nullptr;
// Intense agony.
RADIATION_LEVEL_FATAL,
// The number of radiation levels.
RADIATION_LEVEL_COUNT,
} RadiationLevel;
// Denotes how many primary stats at the top of [gRadiationEffectStats] array.
static int gRadiationEffectPrimaryStatCount = 6;
static int _get_rad_damage_level(Object* obj, void* data);
static int critter_kill_count_clear();
@@ -82,58 +71,6 @@ static char byte_501494[] = "";
// 0x51833C
static char* _name_critter = _aCorpse;
// Modifiers to endurance for performing radiation damage check.
//
// 0x518340
static const int gRadiationEnduranceModifiers[RADIATION_LEVEL_COUNT] = {
2,
0,
-2,
-4,
-6,
-8,
};
// List of stats affected by radiation.
//
// The values of this list specify stats that can be affected by radiation.
// The amount of penalty to every stat (identified by index) is stored
// separately in [gRadiationEffectPenalties] per radiation level.
//
// The order of stats is important - primary stats must be at the top. See
// [RADIATION_EFFECT_PRIMARY_STAT_COUNT] for more info.
//
// 0x518358
static const int gRadiationEffectStats[RADIATION_EFFECT_COUNT] = {
STAT_STRENGTH,
STAT_PERCEPTION,
STAT_ENDURANCE,
STAT_CHARISMA,
STAT_INTELLIGENCE,
STAT_AGILITY,
STAT_CURRENT_HIT_POINTS,
STAT_HEALING_RATE,
};
// Denotes how many primary stats at the top of [gRadiationEffectStats] array.
// These stats are used to determine if critter is alive after applying
// radiation effects.
#define RADIATION_EFFECT_PRIMARY_STAT_COUNT 6
// List of stat modifiers caused by radiation at different radiation levels.
//
// 0x518378
static const int gRadiationEffectPenalties[RADIATION_LEVEL_COUNT][RADIATION_EFFECT_COUNT] = {
// clang-format off
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ -1, 0, 0, 0, 0, 0, 0, 0 },
{ -1, 0, 0, 0, 0, -1, 0, -3 },
{ -2, 0, -1, 0, 0, -2, -5, -5 },
{ -4, -3, -3, -3, -1, -5, -15, -10 },
{ -6, -5, -5, -5, -3, -6, -20, -10 },
// clang-format on
};
// 0x518438
static Object* _critterClearObj = nullptr;
@@ -160,6 +97,10 @@ static int oldRadLevel;
// 0x42CF50
int critterInit()
{
if (critterInitRadiationEffectPenalties() == -1) {
return -1;
}
dudeResetName();
// NOTE: Uninline;
@@ -183,6 +124,46 @@ int critterInit()
return 0;
}
// 0x42CF50
int critterInitRadiationEffectPenalties()
{
Config config;
if (!configInit(&config)) {
return -1;
}
if (!configRead(&config, "data/radiation.txt", false)) {
debugPrint("\nError: Could not load radiation.txt");
return -1;
}
if (!configGetInt(&config, "main", "RadiationEffectCount", &gRadiationEffectCount)) gRadiationEffectCount = 8;
if (!configGetInt(&config, "main", "RadiationLevelCount", &gRadiationLevelCount)) gRadiationLevelCount = 6;
if (!configGetInt(&config, "main", "RadiationEffectPrimaryStatCount", &gRadiationEffectPrimaryStatCount)) gRadiationEffectPrimaryStatCount = 6;
gRadiationThresholds = (int*)malloc(gRadiationLevelCount * sizeof(int));
gRadiationEnduranceModifiers = (int*)malloc(gRadiationLevelCount * sizeof(int));
gRadiationEffectStats = (int*)malloc(gRadiationEffectCount * sizeof(int));
gRadiationEffectPenalties = (int**)malloc(gRadiationLevelCount * sizeof(int*));
for (int i = 0; i < gRadiationLevelCount; ++i) {
gRadiationEffectPenalties[i] = (int*)malloc(gRadiationEffectCount * sizeof(int));
}
// Load arrays...
configGetIntList(&config, "main", "RadiationThresholds", gRadiationThresholds, gRadiationLevelCount);
configGetIntList(&config, "main", "RadiationEnduranceModifiers", gRadiationEnduranceModifiers, gRadiationLevelCount);
configGetIntList(&config, "main", "RadiationEffectStats", gRadiationEffectStats, gRadiationEffectCount);
for (int i = 0; i < gRadiationLevelCount; ++i) {
char key[20];
snprintf(key, sizeof(key), "Level%d", i);
configGetIntList(&config, "RadiationEffectPenalties", key, gRadiationEffectPenalties[i], gRadiationEffectCount);
}
configFree(&config);
return 0;
}
// 0x42CFE4
void critterReset()
{
@@ -505,18 +486,11 @@ int critterCheckRadiationEvent(Object* obj)
int radiation = critterGetRadiation(obj);
int radiationLevel;
if (radiation > 999)
radiationLevel = RADIATION_LEVEL_FATAL;
else if (radiation > 599)
radiationLevel = RADIATION_LEVEL_DEADLY;
else if (radiation > 399)
radiationLevel = RADIATION_LEVEL_CRITICAL;
else if (radiation > 199)
radiationLevel = RADIATION_LEVEL_ADVANCED;
else if (radiation > 99)
radiationLevel = RADIATION_LEVEL_MINOR;
else
radiationLevel = RADIATION_LEVEL_NONE;
for (radiationLevel = gRadiationLevelCount - 1; radiationLevel >= 0; --radiationLevel) {
if (radiation > gRadiationThresholds[radiationLevel]) {
break;
}
}
if (statRoll(obj, STAT_ENDURANCE, gRadiationEnduranceModifiers[radiationLevel], nullptr) <= ROLL_FAILURE) {
radiationLevel++;
@@ -568,7 +542,7 @@ void radiationProcess(Object* obj, int radiationLevel, bool isHealing)
{
MessageListItem messageListItem;
if (radiationLevel == RADIATION_LEVEL_NONE) {
if (radiationLevel == 0) {
return;
}
@@ -577,6 +551,7 @@ void radiationProcess(Object* obj, int radiationLevel, bool isHealing)
if (obj == gDude) {
// Radiation level message, higher is worse.
// Assuming original behavior that message indices follow the level order.
messageListItem.num = 1000 + radiationLevelIndex;
// SFALL: Fix radiation message when removing radiation effects.
@@ -590,7 +565,7 @@ void radiationProcess(Object* obj, int radiationLevel, bool isHealing)
}
}
for (int effect = 0; effect < RADIATION_EFFECT_COUNT; effect++) {
for (int effect = 0; effect < gRadiationEffectCount; effect++) {
int value = critterGetBonusStat(obj, gRadiationEffectStats[effect]);
value += modifier * gRadiationEffectPenalties[radiationLevelIndex][effect];
critterSetBonusStat(obj, gRadiationEffectStats[effect], value);
@@ -601,7 +576,7 @@ void radiationProcess(Object* obj, int radiationLevel, bool isHealing)
if ((obj->data.critter.combat.results & DAM_DEAD) == 0) {
// Loop thru effects affecting primary stats. If any of the primary stat
// dropped below minimal value, kill it.
for (int effect = 0; effect < RADIATION_EFFECT_PRIMARY_STAT_COUNT; effect++) {
for (int effect = 0; effect < gRadiationEffectPrimaryStatCount; effect++) {
int base = critterGetBaseStatWithTraitModifier(obj, gRadiationEffectStats[effect]);
int bonus = critterGetBonusStat(obj, gRadiationEffectStats[effect]);
if (base + bonus < PRIMARY_STAT_MIN) {
+1
View File
@@ -14,6 +14,7 @@ typedef enum DudeState {
} DudeState;
int critterInit();
int critterInitRadiationEffectPenalties();
void critterReset();
void critterExit();
int critterLoad(File* stream);
+20 -75
View File
@@ -1,3 +1,4 @@
#include "settings.h"
#include "game_config.h"
#include "debug.h"
#include "game_config_migration.h"
@@ -63,74 +64,7 @@ bool gameConfigInit(bool isMapper, int argc, char** argv)
return false;
}
// TODO: consolidate settings setup in one place; not sure if this is needed when we have settings field initializers
// Initialize defaults.
configSetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_EXECUTABLE_KEY, "game");
configSetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_DAT_KEY, "master.dat");
configSetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_PATCHES_KEY, "data");
configSetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_DAT_KEY, "critter.dat");
configSetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_PATCHES_KEY, "data");
configSetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, ENGLISH);
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_SCROLL_LOCK_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_INTERRUPT_WALK_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_ART_CACHE_SIZE_KEY, 8);
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_COLOR_CYCLING_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_HASHING_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_SPLASH_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_FREE_SPACE_KEY, 20480);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_GAME_DIFFICULTY_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_DIFFICULTY_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_VIOLENCE_LEVEL_KEY, 3);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TARGET_HIGHLIGHT_KEY, 2);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_ITEM_HIGHLIGHT_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_LOOKS_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_MESSAGES_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_TAUNTS_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_LANGUAGE_FILTER_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_RUNNING_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_SUBTITLES_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_SPEED_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_PLAYER_SPEED_KEY, 0);
configSetDouble(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TEXT_BASE_DELAY_KEY, 3.5);
configSetDouble(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TEXT_LINE_DELAY_KEY, 1.399994);
configSetDouble(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_BRIGHTNESS_KEY, 1.0);
configSetDouble(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_MOUSE_SENSITIVITY_KEY, 1.0);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_INITIALIZE_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_DEVICE_KEY, -1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_PORT_KEY, -1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_IRQ_KEY, -1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_DMA_KEY, -1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SOUNDS_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SPEECH_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MASTER_VOLUME_KEY, 22281);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_VOLUME_KEY, 22281);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SNDFX_VOLUME_KEY, 22281);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SPEECH_VOLUME_KEY, 22281);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_CACHE_SIZE_KEY, 448);
configSetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_PATH1_KEY, "sound\\music\\");
configSetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_PATH2_KEY, "sound\\music\\");
configSetString(&gGameConfig, GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_MODE_KEY, "environment");
configSetInt(&gGameConfig, GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_TILE_NUM_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_SCRIPT_MESSAGES_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_LOAD_INFO_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_OUTPUT_MAP_DATA_INFO_KEY, 0);
if (isMapper) {
configSetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_EXECUTABLE_KEY, "mapper");
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_OVERRIDE_LIBRARIAN_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_LIBRARIAN_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_USE_ART_NOT_PROTOS_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_REBUILD_PROTOS_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_FIX_MAP_OBJECTS_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_FIX_MAP_INVENTORY_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_IGNORE_REBUILD_ERRORS_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_SHOW_PID_NUMBERS_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_SAVE_TEXT_MAPS_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_RUN_MAPPER_AS_GAME_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_DEFAULT_F8_AS_GAME_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_SORT_SCRIPT_LIST_KEY, 0);
}
initSettingsRegistry();
// CE: Detect alternative default music directory.
char alternativeMusicPath[COMPAT_MAX_PATH];
@@ -142,11 +76,14 @@ bool gameConfigInit(bool isMapper, int argc, char** argv)
int acmsLength = fileNameListInit(alternativeMusicPath, &acms);
if (acmsLength != -1) {
if (acmsLength > 0) {
// TODO: apply these to settings directly instead of config
configSetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_PATH1_KEY, "data\\sound\\music\\");
configSetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_PATH2_KEY, "data\\sound\\music\\");
}
fileNameListFree(&acms, 0);
}
settingsApplyDefaultsToConfig();
// SFALL: Custom config file name.
char* customConfigFileName = nullptr;
@@ -198,13 +135,21 @@ bool gameConfigInit(bool isMapper, int argc, char** argv)
configParseCommandLineArguments(&gGameConfig, argc, argv);
// CE: Normalize and resolve asset bundle paths.
gameConfigResolvePath(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_DAT_KEY);
gameConfigResolvePath(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_PATCHES_KEY);
gameConfigResolvePath(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_DAT_KEY);
gameConfigResolvePath(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_PATCHES_KEY);
gameConfigResolvePath(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_PATCHES_KEY);
gameConfigResolvePath(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_PATH1_KEY);
gameConfigResolvePath(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_PATH2_KEY);
static const struct {
const char* section;
const char* key;
} pathsToResolve[] = {
{GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_DAT_KEY},
{GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_PATCHES_KEY},
{GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_DAT_KEY},
{GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_PATCHES_KEY},
{GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_PATH1_KEY},
{GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_PATH2_KEY},
};
for (const auto& path : pathsToResolve) {
gameConfigResolvePath(path.section, path.key);
}
gGameConfigInitialized = true;
+210 -218
View File
@@ -1,228 +1,39 @@
#include "settings.h"
#include "game_config.h"
#include <algorithm>
#include <vector>
#include <functional>
#include <string>
namespace fallout {
static void settingsFromConfig();
static void settingsToConfig();
static void settingsRead(const char* section, const char* key, std::string& value);
static void settingsRead(const char* section, const char* key, int& value);
static void settingsRead(const char* section, const char* key, int& value, int minValue, int maxValue);
static void settingsRead(const char* section, const char* key, bool& value);
static void settingsRead(const char* section, const char* key, double& value);
static void settingsRead(const char* section, const char* key, double& value, double minValue, double maxValue);
static void settingsWrite(const char* section, const char* key, const std::string& value);
static void settingsWrite(const char* section, const char* key, int value);
static void settingsWrite(const char* section, const char* key, bool value);
static void settingsWrite(const char* section, const char* key, double value);
struct SettingDescriptor {
std::function<void()> read;
std::function<void()> write;
};
static std::vector<SettingDescriptor> settingsRegistry;
template<typename T>
void registerSetting(const char* section, const char* key, T& variable) {
settingsRegistry.push_back({
[&, section, key]() { settingsRead(section, key, variable); },
[&, section, key]() { settingsWrite(section, key, variable); }
});
}
template<typename T>
void registerSetting(const char* section, const char* key, T& variable, T min, T max) {
settingsRegistry.push_back({
[&, section, key, min, max]() { settingsRead(section, key, variable, min, max); },
[&, section, key]() { settingsWrite(section, key, variable); }
});
}
Settings settings;
bool settingsInit(bool isMapper, int argc, char** argv)
{
if (!gameConfigInit(isMapper, argc, argv)) {
return false;
}
settingsFromConfig();
return true;
}
bool settingsSave()
{
settingsToConfig();
return gameConfigSave();
}
bool settingsExit(bool shouldSave)
{
if (shouldSave) {
settingsToConfig();
}
return gameConfigExit(shouldSave);
}
static void settingsFromConfig()
{
settingsRead(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_EXECUTABLE_KEY, settings.system.executable);
settingsRead(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_DAT_KEY, settings.system.master_dat_path);
settingsRead(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_PATCHES_KEY, settings.system.master_patches_path);
settingsRead(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_DAT_KEY, settings.system.critter_dat_path);
settingsRead(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_PATCHES_KEY, settings.system.critter_patches_path);
settingsRead(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, settings.system.language);
settingsRead(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_SCROLL_LOCK_KEY, settings.system.scroll_lock);
settingsRead(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_INTERRUPT_WALK_KEY, settings.system.interrupt_walk);
settingsRead(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_ART_CACHE_SIZE_KEY, settings.system.art_cache_size);
settingsRead(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_COLOR_CYCLING_KEY, settings.system.color_cycling);
settingsRead(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CYCLE_SPEED_FACTOR_KEY, settings.system.cycle_speed_factor);
settingsRead(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_HASHING_KEY, settings.system.hashing);
settingsRead(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_SPLASH_KEY, settings.system.splash);
settingsRead(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_FREE_SPACE_KEY, settings.system.free_space);
settingsRead(GAME_CONFIG_SCREEN_KEY, GAME_CONFIG_RESOLUTION_X_KEY, settings.screen.resolution_x, 640, 7680);
settingsRead(GAME_CONFIG_SCREEN_KEY, GAME_CONFIG_RESOLUTION_Y_KEY, settings.screen.resolution_y, 480, 4320);
settingsRead(GAME_CONFIG_SCREEN_KEY, GAME_CONFIG_WINDOWED_KEY, settings.screen.windowed);
settingsRead(GAME_CONFIG_SCREEN_KEY, GAME_CONFIG_SCALE_KEY, settings.screen.scale, 1, 4);
settingsRead(GAME_CONFIG_UI_KEY, GAME_CONFIG_IFACE_BAR_MODE_KEY, settings.ui.iface_bar_mode);
settingsRead(GAME_CONFIG_UI_KEY, GAME_CONFIG_IFACE_BAR_WIDTH_KEY, settings.ui.iface_bar_width, 640, 4320);
settingsRead(GAME_CONFIG_UI_KEY, GAME_CONFIG_IFACE_BAR_SIDE_ART_KEY, settings.ui.iface_bar_side_art, 0, 999);
settingsRead(GAME_CONFIG_UI_KEY, GAME_CONFIG_IFACE_BAR_SIDES_ORI_KEY, settings.ui.iface_bar_sides_ori);
settingsRead(GAME_CONFIG_UI_KEY, GAME_CONFIG_SPLASH_SCREEN_SIZE_KEY, settings.ui.splash_screen_size, 0, 2);
settingsRead(GAME_CONFIG_UI_KEY, GAME_CONFIG_IGNORE_MAP_EDGES_KEY, settings.ui.ignore_map_edges);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_GAME_DIFFICULTY_KEY, settings.preferences.game_difficulty);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_DIFFICULTY_KEY, settings.preferences.combat_difficulty);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_VIOLENCE_LEVEL_KEY, settings.preferences.violence_level);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TARGET_HIGHLIGHT_KEY, settings.preferences.target_highlight);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_ITEM_HIGHLIGHT_KEY, settings.preferences.item_highlight);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_LOOKS_KEY, settings.preferences.combat_looks);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_MESSAGES_KEY, settings.preferences.combat_messages);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_TAUNTS_KEY, settings.preferences.combat_taunts);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_LANGUAGE_FILTER_KEY, settings.preferences.language_filter);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_RUNNING_KEY, settings.preferences.running);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_SUBTITLES_KEY, settings.preferences.subtitles);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_SPEED_KEY, settings.preferences.combat_speed);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_PLAYER_SPEED_KEY, settings.preferences.player_speedup);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TEXT_BASE_DELAY_KEY, settings.preferences.text_base_delay);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TEXT_LINE_DELAY_KEY, settings.preferences.text_line_delay);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_BRIGHTNESS_KEY, settings.preferences.brightness);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_MOUSE_SENSITIVITY_KEY, settings.preferences.mouse_sensitivity);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_RUNNING_BURNING_GUY_KEY, settings.preferences.running_burning_guy);
settingsRead(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_UI_ANIM_SPEED_KEY, settings.preferences.ui_anim_speed, 0.1, 100.0);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_INITIALIZE_KEY, settings.sound.initialize);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_DEBUG_KEY, settings.sound.debug);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_DEBUG_SFXC_KEY, settings.sound.debug_sfxc);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_DEVICE_KEY, settings.sound.device);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_PORT_KEY, settings.sound.port);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_IRQ_KEY, settings.sound.irq);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_DMA_KEY, settings.sound.dma);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SOUNDS_KEY, settings.sound.sounds);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_KEY, settings.sound.music);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SPEECH_KEY, settings.sound.speech);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MASTER_VOLUME_KEY, settings.sound.master_volume);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_VOLUME_KEY, settings.sound.music_volume);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SNDFX_VOLUME_KEY, settings.sound.sndfx_volume);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SPEECH_VOLUME_KEY, settings.sound.speech_volume);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_CACHE_SIZE_KEY, settings.sound.cache_size);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_PATH1_KEY, settings.sound.music_path1);
settingsRead(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_PATH2_KEY, settings.sound.music_path2);
settingsRead(GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_MODE_KEY, settings.debug.mode);
settingsRead(GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_TILE_NUM_KEY, settings.debug.show_tile_num);
settingsRead(GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_SCRIPT_MESSAGES_KEY, settings.debug.show_script_messages);
settingsRead(GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_LOAD_INFO_KEY, settings.debug.show_load_info);
settingsRead(GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_OUTPUT_MAP_DATA_INFO_KEY, settings.debug.output_map_data_info);
settingsRead(GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_DEBUG_WINDOW_WIDTH_KEY, settings.debug.debug_window_width, 200, 1920);
settingsRead(GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_DEBUG_WINDOW_HEIGHT_KEY, settings.debug.debug_window_height, 100, 1080);
settingsRead(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_OVERRIDE_LIBRARIAN_KEY, settings.mapper.override_librarian);
settingsRead(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_LIBRARIAN_KEY, settings.mapper.librarian);
settingsRead(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_USE_ART_NOT_PROTOS_KEY, settings.mapper.user_art_not_protos);
settingsRead(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_REBUILD_PROTOS_KEY, settings.mapper.rebuild_protos);
settingsRead(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_FIX_MAP_OBJECTS_KEY, settings.mapper.fix_map_objects);
settingsRead(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_FIX_MAP_INVENTORY_KEY, settings.mapper.fix_map_inventory);
settingsRead(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_IGNORE_REBUILD_ERRORS_KEY, settings.mapper.rebuild_protos);
settingsRead(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_SHOW_PID_NUMBERS_KEY, settings.mapper.show_pid_numbers);
settingsRead(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_SAVE_TEXT_MAPS_KEY, settings.mapper.save_text_maps);
settingsRead(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_RUN_MAPPER_AS_GAME_KEY, settings.mapper.run_mapper_as_game);
settingsRead(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_DEFAULT_F8_AS_GAME_KEY, settings.mapper.default_f8_as_game);
settingsRead(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_SORT_SCRIPT_LIST_KEY, settings.mapper.sort_script_list);
}
static void settingsToConfig()
{
settingsWrite(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_EXECUTABLE_KEY, settings.system.executable);
settingsWrite(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_DAT_KEY, settings.system.master_dat_path);
settingsWrite(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_PATCHES_KEY, settings.system.master_patches_path);
settingsWrite(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_DAT_KEY, settings.system.critter_dat_path);
settingsWrite(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_PATCHES_KEY, settings.system.critter_patches_path);
settingsWrite(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, settings.system.language);
settingsWrite(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_SCROLL_LOCK_KEY, settings.system.scroll_lock);
settingsWrite(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_INTERRUPT_WALK_KEY, settings.system.interrupt_walk);
settingsWrite(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_ART_CACHE_SIZE_KEY, settings.system.art_cache_size);
settingsWrite(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_COLOR_CYCLING_KEY, settings.system.color_cycling);
settingsWrite(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CYCLE_SPEED_FACTOR_KEY, settings.system.cycle_speed_factor);
settingsWrite(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_HASHING_KEY, settings.system.hashing);
settingsWrite(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_SPLASH_KEY, settings.system.splash);
settingsWrite(GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_FREE_SPACE_KEY, settings.system.free_space);
settingsWrite(GAME_CONFIG_SCREEN_KEY, GAME_CONFIG_RESOLUTION_X_KEY, settings.screen.resolution_x);
settingsWrite(GAME_CONFIG_SCREEN_KEY, GAME_CONFIG_RESOLUTION_Y_KEY, settings.screen.resolution_y);
settingsWrite(GAME_CONFIG_SCREEN_KEY, GAME_CONFIG_WINDOWED_KEY, settings.screen.windowed);
settingsWrite(GAME_CONFIG_SCREEN_KEY, GAME_CONFIG_SCALE_KEY, settings.screen.scale);
settingsWrite(GAME_CONFIG_UI_KEY, GAME_CONFIG_IFACE_BAR_MODE_KEY, settings.ui.iface_bar_mode);
settingsWrite(GAME_CONFIG_UI_KEY, GAME_CONFIG_IFACE_BAR_WIDTH_KEY, settings.ui.iface_bar_width);
settingsWrite(GAME_CONFIG_UI_KEY, GAME_CONFIG_IFACE_BAR_SIDE_ART_KEY, settings.ui.iface_bar_side_art);
settingsWrite(GAME_CONFIG_UI_KEY, GAME_CONFIG_IFACE_BAR_SIDES_ORI_KEY, settings.ui.iface_bar_sides_ori);
settingsWrite(GAME_CONFIG_UI_KEY, GAME_CONFIG_SPLASH_SCREEN_SIZE_KEY, settings.ui.splash_screen_size);
settingsWrite(GAME_CONFIG_UI_KEY, GAME_CONFIG_IGNORE_MAP_EDGES_KEY, settings.ui.ignore_map_edges);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_GAME_DIFFICULTY_KEY, settings.preferences.game_difficulty);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_DIFFICULTY_KEY, settings.preferences.combat_difficulty);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_VIOLENCE_LEVEL_KEY, settings.preferences.violence_level);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TARGET_HIGHLIGHT_KEY, settings.preferences.target_highlight);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_ITEM_HIGHLIGHT_KEY, settings.preferences.item_highlight);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_LOOKS_KEY, settings.preferences.combat_looks);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_MESSAGES_KEY, settings.preferences.combat_messages);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_TAUNTS_KEY, settings.preferences.combat_taunts);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_LANGUAGE_FILTER_KEY, settings.preferences.language_filter);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_RUNNING_KEY, settings.preferences.running);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_SUBTITLES_KEY, settings.preferences.subtitles);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_SPEED_KEY, settings.preferences.combat_speed);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_PLAYER_SPEED_KEY, settings.preferences.player_speedup);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TEXT_BASE_DELAY_KEY, settings.preferences.text_base_delay);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TEXT_LINE_DELAY_KEY, settings.preferences.text_line_delay);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_BRIGHTNESS_KEY, settings.preferences.brightness);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_MOUSE_SENSITIVITY_KEY, settings.preferences.mouse_sensitivity);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_RUNNING_BURNING_GUY_KEY, settings.preferences.running_burning_guy);
settingsWrite(GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_UI_ANIM_SPEED_KEY, settings.preferences.ui_anim_speed);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_INITIALIZE_KEY, settings.sound.initialize);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_DEBUG_KEY, settings.sound.debug);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_DEBUG_SFXC_KEY, settings.sound.debug_sfxc);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_DEVICE_KEY, settings.sound.device);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_PORT_KEY, settings.sound.port);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_IRQ_KEY, settings.sound.irq);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_DMA_KEY, settings.sound.dma);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SOUNDS_KEY, settings.sound.sounds);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_KEY, settings.sound.music);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SPEECH_KEY, settings.sound.speech);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MASTER_VOLUME_KEY, settings.sound.master_volume);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_VOLUME_KEY, settings.sound.music_volume);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SNDFX_VOLUME_KEY, settings.sound.sndfx_volume);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SPEECH_VOLUME_KEY, settings.sound.speech_volume);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_CACHE_SIZE_KEY, settings.sound.cache_size);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_PATH1_KEY, settings.sound.music_path1);
settingsWrite(GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_PATH2_KEY, settings.sound.music_path2);
settingsWrite(GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_MODE_KEY, settings.debug.mode);
settingsWrite(GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_TILE_NUM_KEY, settings.debug.show_tile_num);
settingsWrite(GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_SCRIPT_MESSAGES_KEY, settings.debug.show_script_messages);
settingsWrite(GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_LOAD_INFO_KEY, settings.debug.show_load_info);
settingsWrite(GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_OUTPUT_MAP_DATA_INFO_KEY, settings.debug.output_map_data_info);
settingsWrite(GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_DEBUG_WINDOW_WIDTH_KEY, settings.debug.debug_window_width);
settingsWrite(GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_DEBUG_WINDOW_HEIGHT_KEY, settings.debug.debug_window_height);
settingsWrite(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_OVERRIDE_LIBRARIAN_KEY, settings.mapper.override_librarian);
settingsWrite(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_LIBRARIAN_KEY, settings.mapper.librarian);
settingsWrite(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_USE_ART_NOT_PROTOS_KEY, settings.mapper.user_art_not_protos);
settingsWrite(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_REBUILD_PROTOS_KEY, settings.mapper.rebuild_protos);
settingsWrite(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_FIX_MAP_OBJECTS_KEY, settings.mapper.fix_map_objects);
settingsWrite(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_FIX_MAP_INVENTORY_KEY, settings.mapper.fix_map_inventory);
settingsWrite(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_IGNORE_REBUILD_ERRORS_KEY, settings.mapper.rebuild_protos);
settingsWrite(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_SHOW_PID_NUMBERS_KEY, settings.mapper.show_pid_numbers);
settingsWrite(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_SAVE_TEXT_MAPS_KEY, settings.mapper.save_text_maps);
settingsWrite(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_RUN_MAPPER_AS_GAME_KEY, settings.mapper.run_mapper_as_game);
settingsWrite(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_DEFAULT_F8_AS_GAME_KEY, settings.mapper.default_f8_as_game);
settingsWrite(GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_SORT_SCRIPT_LIST_KEY, settings.mapper.sort_script_list);
}
static void settingsRead(const char* section, const char* key, std::string& value)
{
char* v;
@@ -239,7 +50,7 @@ static void settingsRead(const char* section, const char* key, int& value)
}
}
void settingsRead(const char* section, const char* key, int& value, int minValue, int maxValue)
static void settingsRead(const char* section, const char* key, int& value, int minValue, int maxValue)
{
settingsRead(section, key, value);
value = std::clamp(value, minValue, maxValue);
@@ -261,7 +72,7 @@ static void settingsRead(const char* section, const char* key, double& value)
}
}
void settingsRead(const char* section, const char* key, double& value, double minValue, double maxValue)
static void settingsRead(const char* section, const char* key, double& value, double minValue, double maxValue)
{
settingsRead(section, key, value);
value = std::clamp(value, minValue, maxValue);
@@ -287,4 +98,185 @@ static void settingsWrite(const char* section, const char* key, double value)
configSetDouble(&gGameConfig, section, key, value);
}
struct SettingEntry {
std::function<void(const char*)> registerFunc;
SettingEntry(const char* key, std::string& variable)
: registerFunc([key, &variable](const char* section) { registerSetting(section, key, variable); })
{
}
SettingEntry(const char* key, int& variable)
: registerFunc([key, &variable](const char* section) { registerSetting(section, key, variable); })
{
}
SettingEntry(const char* key, int& variable, int min, int max)
: registerFunc([key, &variable, min, max](const char* section) { registerSetting(section, key, variable, min, max); })
{
}
SettingEntry(const char* key, bool& variable)
: registerFunc([key, &variable](const char* section) { registerSetting(section, key, variable); })
{
}
SettingEntry(const char* key, double& variable)
: registerFunc([key, &variable](const char* section) { registerSetting(section, key, variable); })
{
}
SettingEntry(const char* key, double& variable, double min, double max)
: registerFunc([key, &variable, min, max](const char* section) { registerSetting(section, key, variable, min, max); })
{
}
};
void initSettingsRegistry()
{
if (!settingsRegistry.empty()) return;
auto addSection = [](const char* section, std::initializer_list<SettingEntry> entries) {
for (const auto& entry : entries) {
entry.registerFunc(section);
}
};
addSection("system", {
{"executable", settings.system.executable},
{"master_dat", settings.system.master_dat_path},
{"master_patches", settings.system.master_patches_path},
{"critter_dat", settings.system.critter_dat_path},
{"critter_patches", settings.system.critter_patches_path},
{"language", settings.system.language},
{"scroll_lock", settings.system.scroll_lock},
{"interrupt_walk", settings.system.interrupt_walk},
{"art_cache_size", settings.system.art_cache_size},
{"color_cycling", settings.system.color_cycling},
{"cycle_speed_factor", settings.system.cycle_speed_factor},
{"hashing", settings.system.hashing},
{"splash", settings.system.splash},
{"free_space", settings.system.free_space},
});
addSection("screen", {
{"resolution_x", settings.screen.resolution_x, 640, 7680},
{"resolution_y", settings.screen.resolution_y, 480, 4320},
{"windowed", settings.screen.windowed},
{"scale", settings.screen.scale, 1, 4},
});
addSection("ui", {
{"iface_bar_mode", settings.ui.iface_bar_mode},
{"iface_bar_width", settings.ui.iface_bar_width, 640, 4320},
{"iface_bar_side_art", settings.ui.iface_bar_side_art, 0, 999},
{"iface_bar_sides_ori", settings.ui.iface_bar_sides_ori},
{"splash_screen_size", settings.ui.splash_screen_size, 0, 2},
{"ignore_map_edges", settings.ui.ignore_map_edges},
});
addSection("preferences", {
{"game_difficulty", settings.preferences.game_difficulty},
{"combat_difficulty", settings.preferences.combat_difficulty},
{"violence_level", settings.preferences.violence_level},
{"target_highlight", settings.preferences.target_highlight},
{"item_highlight", settings.preferences.item_highlight},
{"combat_looks", settings.preferences.combat_looks},
{"combat_messages", settings.preferences.combat_messages},
{"combat_taunts", settings.preferences.combat_taunts},
{"language_filter", settings.preferences.language_filter},
{"running", settings.preferences.running},
{"subtitles", settings.preferences.subtitles},
{"combat_speed", settings.preferences.combat_speed},
{"player_speed", settings.preferences.player_speedup},
{"text_base_delay", settings.preferences.text_base_delay},
{"text_line_delay", settings.preferences.text_line_delay},
{"brightness", settings.preferences.brightness},
{"mouse_sensitivity", settings.preferences.mouse_sensitivity},
{"running_burning_guy", settings.preferences.running_burning_guy},
{"ui_anim_speed", settings.preferences.ui_anim_speed, 0.1, 100.0},
});
addSection("sound", {
{"initialize", settings.sound.initialize},
{"debug", settings.sound.debug},
{"debug_sfxc", settings.sound.debug_sfxc},
{"device", settings.sound.device},
{"port", settings.sound.port},
{"irq", settings.sound.irq},
{"dma", settings.sound.dma},
{"sounds", settings.sound.sounds},
{"music", settings.sound.music},
{"speech", settings.sound.speech},
{"master_volume", settings.sound.master_volume},
{"music_volume", settings.sound.music_volume},
{"sndfx_volume", settings.sound.sndfx_volume},
{"speech_volume", settings.sound.speech_volume},
{"cache_size", settings.sound.cache_size},
{"music_path1", settings.sound.music_path1},
{"music_path2", settings.sound.music_path2},
});
addSection("debug", {
{"mode", settings.debug.mode},
{"show_tile_num", settings.debug.show_tile_num},
{"show_script_messages", settings.debug.show_script_messages},
{"show_load_info", settings.debug.show_load_info},
{"output_map_data_info", settings.debug.output_map_data_info},
{"window_width", settings.debug.debug_window_width, 200, 1920},
{"window_height", settings.debug.debug_window_height, 100, 1080},
});
addSection("mapper", {
{"override_librarian", settings.mapper.override_librarian},
{"librarian", settings.mapper.librarian},
{"use_art_not_protos", settings.mapper.user_art_not_protos},
{"rebuild_protos", settings.mapper.rebuild_protos},
{"fix_map_objects", settings.mapper.fix_map_objects},
{"fix_map_inventory", settings.mapper.fix_map_inventory},
{"ignore_rebuild_errors", settings.mapper.rebuild_protos},
{"show_pid_numbers", settings.mapper.show_pid_numbers},
{"save_text_maps", settings.mapper.save_text_maps},
{"run_mapper_as_game", settings.mapper.run_mapper_as_game},
{"default_f8_as_game", settings.mapper.default_f8_as_game},
{"sort_script_list", settings.mapper.sort_script_list},
});
}
bool settingsInit(bool isMapper, int argc, char** argv)
{
if (!gameConfigInit(isMapper, argc, argv)) {
return false;
}
initSettingsRegistry();
for (const auto& descriptor : settingsRegistry) {
descriptor.read();
}
return true;
}
void settingsApplyDefaultsToConfig()
{
for (const auto& descriptor : settingsRegistry) {
descriptor.write();
}
}
bool settingsSave()
{
settingsApplyDefaultsToConfig();
return gameConfigSave();
}
bool settingsExit(bool shouldSave)
{
if (shouldSave) {
settingsSave();
}
return gameConfigExit(shouldSave);
}
} // namespace fallout
+2
View File
@@ -136,6 +136,8 @@ extern Settings settings;
bool settingsInit(bool isMapper, int argc, char** argv);
bool settingsSave();
void settingsApplyDefaultsToConfig();
void initSettingsRegistry();
bool settingsExit(bool shouldSave);
} // namespace fallout