diff --git a/CMakeLists.txt b/CMakeLists.txt index d43f3a99..e0b267be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC "src/combat.h" "src/config.cc" "src/config.h" + "src/content_config.cc" + "src/content_config.h" "src/credits.cc" "src/credits.h" "src/critter.cc" diff --git a/SFALL_COMPATIBILITY.md b/SFALL_COMPATIBILITY.md index dde7f5ed..b53c411e 100644 --- a/SFALL_COMPATIBILITY.md +++ b/SFALL_COMPATIBILITY.md @@ -4,9 +4,13 @@ This document tracks Fallout 2 CE compatibility with sfall. This is for modders For now, this covers opcodes/metarules, and hooks. In the future, it will include other ways of modifying the engine (like ini files), and other Sfall-specific behaviour. -## Settings (ddraw.ini → fallout2.cfg) +## Settings (ddraw.ini → fallout2.cfg / game.cfg) -Some settings previously read from `ddraw.ini` have been moved into `fallout2.cfg`. The table below maps each setting to its new location. `HiResMode` had no effect in CE and has been removed. +Settings previously read from `ddraw.ini` have been moved into standard CE config files. + +Most settings that control game behavior (premade characters, extra message files, combat tweaks, worldmap, etc.) have been moved into [`/config/game.cfg`](files/ce.dat/config/game.cfg), which is a content-mod config file intended to be overridden by mods. See that file for the full list with descriptions. + +The following settings were moved into [`fallout2.cfg`](files/fallout2.cfg) instead: | ddraw.ini section | ddraw.ini key | fallout2.cfg section | fallout2.cfg key | | --- | --- | --- | --- | diff --git a/files/ce.dat/config/game.cfg b/files/ce.dat/config/game.cfg new file mode 100644 index 00000000..6b1e9b8c --- /dev/null +++ b/files/ce.dat/config/game.cfg @@ -0,0 +1,128 @@ +; +; Game general config +; +; Fallout 2 +; + +[start] +; Starting year, month, day. Month and day are 0-indexed (0 = January, 0 = first day of month). +year=2241 +month=6 +day=24 +; To start a new game somewhere other than artemple.map, set this to the map file name. +map= +; Starting (tribal) and default (jumpsuit) player models. Can also be changed in-game via script. +model_male=hmwarr +model_male_default=hmjmps +model_female=hfprim +model_female_default=hfjmps +; Set to 1 if you want the pipboy to be available at the start of the game. +; Set to 2 to make the pipboy available by only skipping the vault suit movie check. +pipboy=0 + +[karma] +; FRM numbers for karma icons on the character screen. Number of points entries should be 1 less than number of frms entries. +; Example: frms=47,48,49 points=-100,100 +frms= +points= + +[items] +; Proto IDs for healing items. +stimpak=40 +super_stimpak=144 +healing_powder=273 + +[dialog] +; Set to 1 to prevent using 0 to escape from dialogue at any time. +no_exit_hotkey=1 +; Set to 1 to allow using '^' in dialog msg files to specify gender-based alternative text. +; Text must be enclosed in angle brackets, e.g. . +gender_words=0 + +[main_menu] +; Custom version string shown on the main menu. Supports up to two %d placeholders for the game version numbers. +; Example: FALLOUT II v1.02d +version_string= +; Font color for the version string and copyright text. Last byte picks the color (default 0x3C). +; First byte flags: 1 - version string only, 2 - underline, 4 - monospace font. +; Example: 0x000055 +font_color=0 +; Font color for the main menu button text. +big_font_color=0 +; Pixel offset for main menu buttons. +offset_x=0 +offset_y=0 +; Pixel offset for the copyright text line. +credits_offset_x=0 +credits_offset_y=0 + +[movies] +; Number of days after game start when each Hakunin dream sequence becomes available. +artimer1=90 +artimer2=180 +artimer3=270 +artimer4=360 + +[combat] +; Damage formula used to calculate combat damage. +; 0 - Fallout default, 1 - Glovz's Damage Fix, 2 - Glovz's Damage Fix with Damage Multiplier tweak, 5 - Haenlomal's Yet Another Ammo Mod +; Don't change this unless another mod explicitly requires it. +damage_formula=0 +; Set to 1 to fix the bug that caused bonus HtH damage to not be applied correctly. +bonus_hth_damage_fix=1 +; Set to 1 to remove the time limits that prevent critical hits/misses in the first few days of the game. +remove_critical_time_limits=0 +; Set to 1 to allow science and repair to be used on the player, or 2 for all critters (rather than only brahmin/robots). +science_on_critters=0 +; Set to 1 to enable modified burst bullet distribution (spray mod). +burst_enabled=0 +; Ratio of bullets going to the center group vs. left/right in burst attacks. Multiplier is capped at divisor. +burst_center_mult=1 +burst_center_div=3 +; Ratio of center-group bullets that target the primary target directly. Multiplier is capped at divisor. +burst_target_mult=1 +burst_target_div=2 + +[explosions] +; Set to 1 to make explosions and projectiles emit light. +emit_light=0 +; Dynamite damage range. Max is capped at 9999; min is capped at max. +dynamite_max=50 +dynamite_min=30 +; Plastic explosive damage range. Max is capped at 9999; min is capped at max. +plastic_explosive_max=80 +plastic_explosive_min=40 + +[skilldex] +; FRM numbers for Skilldex skill cursors. Default is 293 for all skills. +lockpick=293 +steal=293 +traps=293 +first_aid=293 +doctor=293 +science=293 +repair=293 + +[worldmap] +; Set to 1 to prevent using number keys to enter unvisited areas on a town map. +town_map_hotkeys_fix=1 +; Set to 1 to show travel markers on the world map, or 2 to show only when sneaking. +trail_markers=0 +; Set to 1 to skip the encounter with Frank Horrigan. +disable_horrigan=0 +; City reputation overrides. Syntax: city_id:global_id pairs separated by commas. +; Example: 0:5001,2:5003,3:5004 +city_reputation_list= + +[characters] +; Custom premade characters for the character selection screen. +; premade_paths is a comma-separated list of file names (max 11 chars each) in the premade\ folder, without extension. +; premade_fids is a comma-separated list of FRM numbers for the character portrait, matching the order of premade_paths. +; Example: premade_paths=combat,diplomat,stealth premade_fids=201,203,202 +premade_paths= +premade_fids= + +[text] +; Comma-separated list of extra game message file names (without .msg extension) to load from the game\ folder. +; Use 'filename:number' syntax to assign explicit IDs. Access via message_str_game script function. +extra_msg_file_list= diff --git a/files/ddraw.ini b/files/ddraw.ini index 3f03f7bb..074d2dde 100644 --- a/files/ddraw.ini +++ b/files/ddraw.ini @@ -6,19 +6,6 @@ ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Misc] -;Uncomment these lines to change the timers of how many days after the game starts Hakunin dream sequences will occur -;MovieTimer_artimer1=5000 -;MovieTimer_artimer2=5000 -;MovieTimer_artimer3=5000 -;MovieTimer_artimer4=5000 - -;To start a new game somewhere other than artemple.map, uncomment the next line and set it to the map you want to load -;StartingMap=V13ent.map - -;To change the 'FALLOUT II v1.02d' version string on the main menu, uncomment the next line -;You can use up to 2 %d's in this if you want to include Fallout's version number somewhere -;VersionString=FALLOUT II v1.02d - ;To use a config file other than fallout2.cfg, uncomment the next line and add the name of your new file ;ConfigFile= @@ -26,40 +13,6 @@ ;If you want to load multiple patch files (up to 1000) at once, you can include a %d in the file name (sprintf syntax) ;PatchFile=patch%03d.dat -;Set to 1 to allow using the caret character '^' in dialog msg files to specify alternative text in dialogue based on the player's gender -;The text must be enclosed in angle brackets (example: ) -;DialogGenderWords=0 - -;To change the default and starting player models, uncomment the next four lines. -;The default models can also be changed in-game via script -;MaleStartModel=hmjmps -;MaleDefaultModel=hmjmps -;FemaleStartModel=hfjmps -;FemaleDefaultModel=hfjmps - -;To change the starting year, month or day, uncomment the next 3 lines -;Both StartMonth and StartDay are 0-indexed (i.e. 0 is January or the first day of a month) -;StartYear=2241 -;StartMonth=01 -;StartDay=01 - -;Set to 1 if you want the pipboy to be available at the start of the game -;Set to 2 to make the pipboy available by only skipping the vault suit movie check -;PipBoyAvailableAtGameStart=1 - -;Choose the damage formula used to calculate combat damage. -;Don't set this to anything other than 0 unless another mod you're using explicitly tells you to! -;0 - Fallout default -;1 - Glovz's Damage Fix -;2 - Glovz's Damage Fix with Damage Multiplier tweak -;5 - Haenlomal's Yet Another Ammo Mod -;DamageFormula=0 - -;Prevents you from using 0 to escape from dialogue at any time. -;DialogueFix=1 - -;Prevents you from using number keys to enter unvisited areas on a town map -TownMapHotkeysFix=1 ;Set to 1 to use a CriticalOverrides.ini file to override the default critical table ;Set to 2 to use the default critical with bug fixes (doesn't require an ini) @@ -67,86 +20,9 @@ TownMapHotkeysFix=1 ;If the ExtraKillTypes option is enabled, this should be set to 3, with containing entries for any new types ;Must be non-zero to use the edit/get/reset_critical script functions ;OverrideCriticalTable=2 - -;Change the Skilldex cursor FRM numbers -;Default is 293 for all skills -Lockpick=293 -Steal=293 -Traps=293 -FirstAid=293 -Doctor=293 -Science=293 -Repair=293 - -;Uncomment these lines to control the premade characters offered when starting a new game -;Multiple options should be separated by commas, and there must be the same number of entries in both lines -;Each name in PremadePaths is limited to 11 characters -;PremadePaths=combat,diplomat,stealth -;PremadeFIDs=201,203,202 - -;Use this line to modify the list of cities and their associated global variables used for city reputations -;Syntax is 'city id:global id', with each city/global pair separated by a comma. -;CityRepsList=0:5001,2:5003,3:5004,4:5005,5:5006,6:5007,7:5008,8:5009,10:5011,11:5012,60:1640 - ;Set to 1 to add additional pages of save slots !!! can be implemented !!! ;ExtraSaveSlots=1 -;These lines allow you to control the karma FRMs displayed on the character screen -;Number of KarmaPoints should be 1 less than number of KarmaFRMs -;KarmaFRMs=47,48,49 -;KarmaPoints=-100,100 - -;Set to 1 to allow science and repair to be used on the player, or 2 for all critters. (Rather than only brahmin/robots) -ScienceOnCritters=0 - -;Set to 1 to fix the bug that caused bonus HtH damage to not be applied correctly. -BonusHtHDamageFix=1 - -;Set to 1 to remove the limits that stop the game from rolling critical successes/failures in the first few days of game time -RemoveCriticalTimelimits=0 - -;Change the colour of the font used on the main menu for the Fallout/sfall version string and copyright text -;It's the last byte ('3C' by default) that picks the colour used. The first byte supplies additional flags for this option -;1 - change the colour for the version string only -;2 - underline text for the version string -;4 - use monospace font for the version string -;MainMenuFontColour=0x000055 -;Change the colour of the font used on the main menu for the button text -;MainMenuBigFontColour=0x3C - -;Uncomment the next four lines to move the main menu buttons and credit text (the 'Copyright(c)' line on the main menu) -;MainMenuOffsetX=392 -;MainMenuOffsetY=26 -;MainMenuCreditsOffsetX=0 -;MainMenuCreditsOffsetY=0 - -;These options modify the bullet distribution of burst attacks -;All the bullets are divided into three groups: center, left, and right -;These groups will then travel along three parallel tracks, trying to hit targets on the way -;CenterMult/Div set the ratio of how many bullets go to the center group, and the remaining are divided equally to the left and right sides -;TargetMult/Div set the ratio of how many bullets in the center group will attack the primary target directly -;Multiplier values are capped at divisor values -;ComputeSpray_CenterMult=1 -;ComputeSpray_CenterDiv=3 -;ComputeSpray_TargetMult=1 -;ComputeSpray_TargetDiv=2 - -;Set to 1 to make explosions and projectiles emit light -;ExplosionsEmitLight=1 - -;Uncomment these lines to change explosives damage. DmgMax can be set to 9999 at max, and DmgMin is capped at DmgMax -;Dynamite_DmgMax=50 -;Dynamite_DmgMin=30 -;PlasticExplosive_DmgMax=80 -;PlasticExplosive_DmgMin=40 - -;To add additional game msg files, uncomment the next line and set a comma delimited list of filenames without .msg extension -;By default, the files will have consecutive numbers assigned beginning with 0 -;You can use the syntax 'filename:number' to manually assign numbers to specific msg files, with each pair separated by a comma -;If a file after the specified pair does not have a number assigned, it will have the next consecutive number from the last pair -;You need to use the message_str_game script function to get messages from the files -;ExtraGameMsgFileList= - ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ; Configuration ini files ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX @@ -165,9 +41,6 @@ RemoveCriticalTimelimits=0 ;See the Unarmed.ini in the modders pack for an example file ;UnarmedFile=sfall\Unarmed.ini -;To change some engine parameters for the game mechanics, uncomment the next line -;TweaksFile=sfall\Tweaks.ini - ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Scripts] diff --git a/src/actions.cc b/src/actions.cc index 95a0acf7..1af4d34f 100644 --- a/src/actions.cc +++ b/src/actions.cc @@ -9,6 +9,7 @@ #include "combat.h" #include "combat_ai.h" #include "config.h" +#include "content_config.h" #include "critter.h" #include "debug.h" #include "display_monitor.h" @@ -28,7 +29,6 @@ #include "random.h" #include "scripts.h" #include "settings.h" -#include "sfall_config.h" #include "sfall_script_hooks.h" #include "skill.h" #include "stat.h" @@ -1410,7 +1410,7 @@ int actionUseSkill(Object* user, Object* target, int skill) // SFALL: Science on critters patch. if (1) { int targetType = SCIENCE_REPAIR_TARGET_TYPE_DEFAULT; - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_SCIENCE_REPAIR_TARGET_TYPE_KEY, &targetType); + configGetInt(&gContentConfig, CONTENT_CONFIG_COMBAT_SECTION, "science_on_critters", &targetType, SCIENCE_REPAIR_TARGET_TYPE_DEFAULT); if (targetType == SCIENCE_REPAIR_TARGET_TYPE_DUDE) { if (target == gDude) { break; diff --git a/src/art.cc b/src/art.cc index 8e99e0f3..8a2b2349 100644 --- a/src/art.cc +++ b/src/art.cc @@ -6,6 +6,7 @@ #include #include "animation.h" +#include "content_config.h" #include "debug.h" #include "draw.h" #include "game.h" @@ -13,7 +14,6 @@ #include "object.h" #include "proto.h" #include "settings.h" -#include "sfall_config.h" namespace fallout { @@ -190,28 +190,16 @@ int artInit() // SFALL: Modify player model settings. char* jumpsuitMaleFileName = nullptr; - configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_MALE_KEY, &jumpsuitMaleFileName); - if (jumpsuitMaleFileName == nullptr || jumpsuitMaleFileName[0] == '\0') { - jumpsuitMaleFileName = gDefaultJumpsuitMaleFileName; - } + configGetString(&gContentConfig, CONTENT_CONFIG_START_SECTION, "model_male_default", &jumpsuitMaleFileName, gDefaultJumpsuitMaleFileName); char* jumpsuitFemaleFileName = nullptr; - configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_FEMALE_KEY, &jumpsuitFemaleFileName); - if (jumpsuitFemaleFileName == nullptr || jumpsuitFemaleFileName[0] == '\0') { - jumpsuitFemaleFileName = gDefaultJumpsuitFemaleFileName; - } + configGetString(&gContentConfig, CONTENT_CONFIG_START_SECTION, "model_female_default", &jumpsuitFemaleFileName, gDefaultJumpsuitFemaleFileName); char* tribalMaleFileName = nullptr; - configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_MALE_KEY, &tribalMaleFileName); - if (tribalMaleFileName == nullptr || tribalMaleFileName[0] == '\0') { - tribalMaleFileName = gDefaultTribalMaleFileName; - } + configGetString(&gContentConfig, CONTENT_CONFIG_START_SECTION, "model_male", &tribalMaleFileName, gDefaultTribalMaleFileName); char* tribalFemaleFileName = nullptr; - configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_FEMALE_KEY, &tribalFemaleFileName); - if (tribalFemaleFileName == nullptr || tribalFemaleFileName[0] == '\0') { - tribalFemaleFileName = gDefaultTribalFemaleFileName; - } + configGetString(&gContentConfig, CONTENT_CONFIG_START_SECTION, "model_female", &tribalFemaleFileName, gDefaultTribalFemaleFileName); char* critterFileNames = gArtListDescriptions[OBJ_TYPE_CRITTER].fileNames; for (int critterIndex = 0; critterIndex < gArtListDescriptions[OBJ_TYPE_CRITTER].fileNamesLength; critterIndex++) { diff --git a/src/character_editor.cc b/src/character_editor.cc index d8af9753..eca3b5c5 100644 --- a/src/character_editor.cc +++ b/src/character_editor.cc @@ -11,6 +11,7 @@ #include "art.h" #include "color.h" #include "combat.h" +#include "content_config.h" #include "critter.h" #include "cycle.h" #include "db.h" @@ -37,7 +38,6 @@ #include "platform_compat.h" #include "proto.h" #include "scripts.h" -#include "sfall_config.h" #include "skill.h" #include "stat.h" #include "svga.h" @@ -7167,22 +7167,14 @@ static int genericReputationCompare(const void* a1, const void* a2) static void customKarmaFolderInit() { char* karmaFrms = nullptr; - configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_KARMA_FRMS_KEY, &karmaFrms); - if (karmaFrms != nullptr && karmaFrms[0] == '\0') { - karmaFrms = nullptr; - } - - if (karmaFrms == nullptr) { + configGetString(&gContentConfig, CONTENT_CONFIG_KARMA_SECTION, "frms", &karmaFrms, ""); + if (!*karmaFrms) { return; } char* karmaPoints = nullptr; - configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_KARMA_POINTS_KEY, &karmaPoints); - if (karmaPoints != nullptr && karmaPoints[0] == '\0') { - karmaPoints = nullptr; - } - - if (karmaPoints == nullptr) { + configGetString(&gContentConfig, CONTENT_CONFIG_KARMA_SECTION, "points", &karmaPoints, ""); + if (!*karmaPoints) { return; } @@ -7256,10 +7248,7 @@ static int customKarmaFolderGetFrmId() static void customTownReputationInit() { char* reputationList = nullptr; - configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_CITY_REPUTATION_LIST_KEY, &reputationList); - if (reputationList != nullptr && *reputationList == '\0') { - reputationList = nullptr; - } + configGetString(&gContentConfig, CONTENT_CONFIG_WORLDMAP_SECTION, "city_reputation_list", &reputationList, nullptr); char* curr = reputationList; while (curr != nullptr) { diff --git a/src/character_selector.cc b/src/character_selector.cc index d96d607d..5bdb5d6a 100644 --- a/src/character_selector.cc +++ b/src/character_selector.cc @@ -9,6 +9,7 @@ #include "art.h" #include "character_editor.h" #include "color.h" +#include "content_config.h" #include "critter.h" #include "db.h" #include "debug.h" @@ -26,7 +27,6 @@ #include "preferences.h" #include "proto.h" #include "settings.h" -#include "sfall_config.h" #include "skill.h" #include "stat.h" #include "svga.h" @@ -900,16 +900,10 @@ static bool characterSelectorWindowFatalError(bool result) void premadeCharactersInit() { char* fileNamesString; - configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_PREMADE_CHARACTERS_FILE_NAMES_KEY, &fileNamesString); - if (fileNamesString != nullptr && *fileNamesString == '\0') { - fileNamesString = nullptr; - } + configGetString(&gContentConfig, CONTENT_CONFIG_CHARACTERS_SECTION, "premade_paths", &fileNamesString, nullptr); char* faceFidsString; - configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_PREMADE_CHARACTERS_FACE_FIDS_KEY, &faceFidsString); - if (faceFidsString != nullptr && *faceFidsString == '\0') { - faceFidsString = nullptr; - } + configGetString(&gContentConfig, CONTENT_CONFIG_CHARACTERS_SECTION, "premade_fids", &faceFidsString, nullptr); if (fileNamesString != nullptr && faceFidsString != nullptr) { int fileNamesLength = 0; diff --git a/src/combat.cc b/src/combat.cc index 3f995e5f..6745f768 100644 --- a/src/combat.cc +++ b/src/combat.cc @@ -9,6 +9,7 @@ #include "art.h" #include "color.h" #include "combat_ai.h" +#include "content_config.h" #include "critter.h" #include "db.h" #include "debug.h" @@ -1983,13 +1984,14 @@ static const char* gCritDataMemberKeys[CRIT_DATA_MEMBER_COUNT] = { }; static bool gBurstModEnabled = false; -static int gBurstModCenterMultiplier = SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER; -static int gBurstModCenterDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR; -static int gBurstModTargetMultiplier = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_MULTIPLIER; -static int gBurstModTargetDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_DIVISOR; +static int gBurstModCenterMultiplier = 1; +static int gBurstModCenterDivisor = 3; +static int gBurstModTargetMultiplier = 1; +static int gBurstModTargetDivisor = 2; static UnarmedHitDescription gUnarmedHitDescriptions[HIT_MODE_COUNT]; static int gDamageCalculationType; static bool gBonusHthDamageFix; +static bool gRemoveCriticalTimeLimits; static bool gDisplayBonusDamage; // combat_init @@ -4237,12 +4239,8 @@ static int attackComputeCriticalFailure(Attack* attack) } if (attack->attacker == gDude) { - // SFALL: Remove criticals time limits. - bool criticalsTimeLimitsRemoved = false; - configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_REMOVE_CRITICALS_TIME_LIMITS_KEY, &criticalsTimeLimitsRemoved); - unsigned int gameTime = gameTimeGetTime(); - if (!criticalsTimeLimitsRemoved && gameTime / GAME_TIME_TICKS_PER_DAY < 6) { + if (!gRemoveCriticalTimeLimits && gameTime / GAME_TIME_TICKS_PER_DAY < 6) { return 0; } } @@ -6088,6 +6086,8 @@ int combatGetTargetHighlight() static void criticalsInit() { + configGetBool(&gContentConfig, CONTENT_CONFIG_COMBAT_SECTION, "remove_critical_time_limits", &gRemoveCriticalTimeLimits, false); + int mode = 2; configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_OVERRIDE_CRITICALS_MODE_KEY, &mode); if (mode < 0 || mode > 3) { @@ -6346,12 +6346,17 @@ void criticalsResetValue(int killType, int hitLocation, int effect, int dataMemb } } +bool criticalsNoTimeLimits() +{ + return gRemoveCriticalTimeLimits; +} + static void burstModInit() { - configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_ENABLED_KEY, &gBurstModEnabled); + configGetBool(&gContentConfig, CONTENT_CONFIG_COMBAT_SECTION, "burst_enabled", &gBurstModEnabled); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_CENTER_MULTIPLIER_KEY, &gBurstModCenterMultiplier); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_CENTER_DIVISOR_KEY, &gBurstModCenterDivisor); + configGetInt(&gContentConfig, CONTENT_CONFIG_COMBAT_SECTION, "burst_center_mult", &gBurstModCenterMultiplier, 1); + configGetInt(&gContentConfig, CONTENT_CONFIG_COMBAT_SECTION, "burst_center_div", &gBurstModCenterDivisor, 3); if (gBurstModCenterDivisor < 1) { gBurstModCenterDivisor = 1; } @@ -6359,8 +6364,8 @@ static void burstModInit() gBurstModCenterMultiplier = gBurstModCenterDivisor; } - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_TARGET_MULTIPLIER_KEY, &gBurstModTargetMultiplier); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_TARGET_DIVISOR_KEY, &gBurstModTargetDivisor); + configGetInt(&gContentConfig, CONTENT_CONFIG_COMBAT_SECTION, "burst_target_mult", &gBurstModTargetMultiplier, 1); + configGetInt(&gContentConfig, CONTENT_CONFIG_COMBAT_SECTION, "burst_target_div", &gBurstModTargetDivisor, 2); if (gBurstModTargetDivisor < 1) { gBurstModTargetDivisor = 1; } @@ -6701,10 +6706,10 @@ static int unarmedGetHitModeInRange(int firstHitMode, int lastHitMode, bool isSe static void damageModInit() { gDamageCalculationType = DAMAGE_CALCULATION_TYPE_VANILLA; - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DAMAGE_MOD_FORMULA_KEY, &gDamageCalculationType); + configGetInt(&gContentConfig, CONTENT_CONFIG_COMBAT_SECTION, "damage_formula", &gDamageCalculationType, DAMAGE_CALCULATION_TYPE_VANILLA); gBonusHthDamageFix = true; - configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BONUS_HTH_DAMAGE_FIX_KEY, &gBonusHthDamageFix); + configGetBool(&gContentConfig, CONTENT_CONFIG_COMBAT_SECTION, "bonus_hth_damage_fix", &gBonusHthDamageFix); gDisplayBonusDamage = settings.ui.display_bonus_damage; } diff --git a/src/combat.h b/src/combat.h index 7935844b..46e9b42f 100644 --- a/src/combat.h +++ b/src/combat.h @@ -62,6 +62,7 @@ int combatGetTargetHighlight(); int criticalsGetValue(int killType, int hitLocation, int effect, int dataMember); void criticalsSetValue(int killType, int hitLocation, int effect, int dataMember, int value); void criticalsResetValue(int killType, int hitLocation, int effect, int dataMember); +bool criticalsNoTimeLimits(); int unarmedGetDamage(int hitMode, int* minDamagePtr, int* maxDamagePtr); int unarmedGetBonusCriticalChance(int hitMode); int unarmedGetActionPointCost(int hitMode); diff --git a/src/config.cc b/src/config.cc index c6e0091a..a7cd520a 100644 --- a/src/config.cc +++ b/src/config.cc @@ -135,6 +135,7 @@ bool configParseCommandLineArguments(Config* config, int argc, char** argv) return true; } +// TODO: use const char** for valuePtr to enforce read-only API // 0x42BF48 bool configGetString(Config* config, const char* sectionKey, const char* key, char** valuePtr) { @@ -833,6 +834,17 @@ bool configGetBool(Config* config, const char* sectionKey, const char* key, bool return true; } +bool configGetBool(Config* config, const char* sectionKey, const char* key, bool* valuePtr, const bool defaultValue) +{ + if (config == nullptr || sectionKey == nullptr || key == nullptr || valuePtr == nullptr) { + return false; + } + if (!configGetBool(config, sectionKey, key, valuePtr)) { + *valuePtr = defaultValue; + } + return true; +} + // NOTE: Boolean-typed variant of [configGetInt]. bool configSetBool(Config* config, const char* sectionKey, const char* key, bool value) { diff --git a/src/config.h b/src/config.h index a9e10ff7..e74a16ec 100644 --- a/src/config.h +++ b/src/config.h @@ -36,6 +36,8 @@ void configFree(Config* config); bool configParseCommandLineArguments(Config* config, int argc, char** argv); // TODO: valuePtr must be const char** bool configGetString(Config* config, const char* sectionKey, const char* key, char** valuePtr); +// Tries to load a string value from Config into valuePtr without any conversion. If value doesn't exist, or it's an empty string, assigns defaultValue instead. +// No copy is performed. The returned pointer may refer either to an internal string or to defaultValue; it must be treated as read-only, and callers must ensure defaultValue remains valid for the duration of use. bool configGetString(Config* config, const char* sectionKey, const char* key, char** valuePtr, const char* defaultValue); bool configSetString(Config* config, const char* sectionKey, const char* key, const char* value); bool configGetInt(Config* config, const char* sectionKey, const char* key, int* valuePtr, unsigned char base = 0); @@ -49,6 +51,7 @@ bool configGetDouble(Config* config, const char* sectionKey, const char* key, do bool configSetDouble(Config* config, const char* sectionKey, const char* key, double value); bool configGetBool(Config* config, const char* sectionKey, const char* key, bool* valuePtr); +bool configGetBool(Config* config, const char* sectionKey, const char* key, bool* valuePtr, bool defaultValue); bool configSetBool(Config* config, const char* sectionKey, const char* key, bool value); } // namespace fallout diff --git a/src/content_config.cc b/src/content_config.cc new file mode 100644 index 00000000..9066edbb --- /dev/null +++ b/src/content_config.cc @@ -0,0 +1,38 @@ +#include "content_config.h" + +#include "game_config_migration.h" + +namespace fallout { + +Config gContentConfig; + +constexpr char kConfigPath[] = R"(config\game.cfg)"; +constexpr char kConfigPatchPath[] = R"(config\game#patch.cfg)"; + +void contentConfigInit() +{ + if (gContentConfig.isInitialized()) { + return; + } + + // Try to migrate some settings from sfall. + contentConfigTryMigrateFromSfall(kConfigPatchPath); + + if (!configInit(&gContentConfig)) { + return; + } + + configRead(&gContentConfig, kConfigPath, true); + // Patch config allows to override only certain fields, without replacing the whole file. + // TODO: remove this after config patching by mods is implemented inside configRead + configRead(&gContentConfig, kConfigPatchPath, true); +} + +void contentConfigExit() +{ + if (!gContentConfig.isInitialized()) return; + + configFree(&gContentConfig); +} + +} // namespace fallout \ No newline at end of file diff --git a/src/content_config.h b/src/content_config.h new file mode 100644 index 00000000..b9023534 --- /dev/null +++ b/src/content_config.h @@ -0,0 +1,29 @@ +#ifndef CONTENT_CONFIG_H +#define CONTENT_CONFIG_H + +#include "config.h" + +namespace fallout { + +#define CONTENT_CONFIG_START_SECTION "start" +#define CONTENT_CONFIG_KARMA_SECTION "karma" +#define CONTENT_CONFIG_ITEMS_SECTION "items" +#define CONTENT_CONFIG_DIALOG_SECTION "dialog" +#define CONTENT_CONFIG_MAIN_MENU_SECTION "main_menu" +#define CONTENT_CONFIG_MOVIES_SECTION "movies" +#define CONTENT_CONFIG_COMBAT_SECTION "combat" +#define CONTENT_CONFIG_EXPLOSIONS_SECTION "explosions" +#define CONTENT_CONFIG_SKILLDEX_SECTION "skilldex" +#define CONTENT_CONFIG_WORLDMAP_SECTION "worldmap" +#define CONTENT_CONFIG_CHARACTERS_SECTION "characters" +#define CONTENT_CONFIG_TEXT_SECTION "text" + +extern Config gContentConfig; + +// Initialize content config and try to load it from file. +void contentConfigInit(); +void contentConfigExit(); + +} // namespace fallout + +#endif // CONTENT_CONFIG_H diff --git a/src/db.h b/src/db.h index 30ecba60..ae5cd3ea 100644 --- a/src/db.h +++ b/src/db.h @@ -11,7 +11,7 @@ typedef XFile File; typedef void FileReadProgressHandler(); typedef char* StrdupProc(const char* string); -int dbOpen(const char* filePath1, const char* filePath2); +int dbOpen(const char* filePath1, const char* filePath2 = nullptr); int db_total(); void dbCloseAll(); int dbGetFileSize(const char* filePath, int* sizePtr); diff --git a/src/dictionary.cc b/src/dictionary.cc index 8aaa9e3c..b1a4480a 100644 --- a/src/dictionary.cc +++ b/src/dictionary.cc @@ -14,6 +14,11 @@ namespace fallout { // with a check for this value. #define DICTIONARY_MARKER 0xFEBAFEBA +bool Dictionary::isInitialized() const +{ + return marker == DICTIONARY_MARKER; +} + static int dictionaryFindIndexForKey(Dictionary* dictionary, const char* key, int* index); // 0x4D9BA8 @@ -28,8 +33,6 @@ int dictionaryInit(Dictionary* dictionary, int initialCapacity, size_t valueSize } else { dictionary->io.readProc = nullptr; dictionary->io.writeProc = nullptr; - dictionary->io.field_8 = 0; - dictionary->io.field_C = 0; } int rc = 0; diff --git a/src/dictionary.h b/src/dictionary.h index a9adf30a..c77bf7af 100644 --- a/src/dictionary.h +++ b/src/dictionary.h @@ -8,13 +8,9 @@ namespace fallout { typedef int(DictionaryReadProc)(FILE* stream, void* buffer, unsigned int size, int a3); typedef int(DictionaryWriteProc)(FILE* stream, void* buffer, unsigned int size, int a3); -// NOTE: Last unnamed fields are likely seek, tell, and filelength. typedef struct DictionaryIO { DictionaryReadProc* readProc; DictionaryWriteProc* writeProc; - int field_8; - int field_C; - int field_10; } DictionaryIO; // A tuple containing individual key-value pair of a dictionary. @@ -30,7 +26,7 @@ typedef struct DictionaryEntry { // is added to dictionary. For this reason the size of the value's type is // provided during dictionary initialization. typedef struct Dictionary { - int marker; + int marker = 0; // The number of key/value pairs in the dictionary. int entriesLength; @@ -46,6 +42,8 @@ typedef struct Dictionary { // The array of key-value pairs. DictionaryEntry* entries; + + bool isInitialized() const; } Dictionary; int dictionaryInit(Dictionary* dictionary, int initialCapacity, size_t valueSize, DictionaryIO* io); diff --git a/src/game.cc b/src/game.cc index a8d34ede..484d644f 100644 --- a/src/game.cc +++ b/src/game.cc @@ -13,6 +13,7 @@ #include "color.h" #include "combat.h" #include "combat_ai.h" +#include "content_config.h" #include "critter.h" #include "cycle.h" #include "db.h" @@ -69,7 +70,6 @@ #include "trait.h" #include "version.h" #include "window_manager.h" -#include "window_manager_private.h" #include "worldmap.h" namespace fallout { @@ -88,6 +88,8 @@ static void showHelp(); static int gameDbInit(); static void showSplash(); +inline constexpr char kBaseModPath[] = "ce.dat"; + // 0x501C9C static char _aGame_0[] = "game\\"; @@ -152,6 +154,9 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int fl return -1; } + // Content config reads from the VFS, so it must be initialized after gameDbInit. + contentConfigInit(); + // Message list repository is considered a specialized file manager, so // it should be initialized early in the process. messageListRepositoryInit(); @@ -495,6 +500,7 @@ void gameExit() messageListRepositoryExit(); dbCloseAll(); settingsExit(true); + contentConfigExit(); sfallConfigExit(); } @@ -1343,6 +1349,18 @@ int showQuitConfirmationDialog() return rc; } +static void TryLoadBaseCEMod() +{ + if (compat_access(kBaseModPath, 0) == 0) { + debugPrint("Loading base FO:CE mod: %s\n", kBaseModPath); + if (dbOpen(kBaseModPath) == -1) { + debugPrint("Error opening base mod file/folder!\n"); + } + } else { + debugPrint("Error opening base mod: no file or folder name %s found.\n", kBaseModPath); + } +} + // 0x44418C static int gameDbInit() { @@ -1363,6 +1381,10 @@ static int gameDbInit() if (*patch_file_name == '\0') { patch_file_name = nullptr; } + // Try to ensure that patches dir exists early. This is needed for auto-generated game.cfg later. + if (patch_file_name != nullptr) { + compat_mkdir_recursive(patch_file_name); + } int master_db_handle = dbOpen(main_file_name, patch_file_name); if (master_db_handle == -1) { @@ -1397,14 +1419,17 @@ static int gameDbInit() snprintf(filename, sizeof(filename), path_file_name_template, patch_index); if (compat_access(filename, 0) == 0) { - dbOpen(filename, nullptr); + dbOpen(filename); } } + // Load CE base mod after vanilla patches but before all regular mods. + TryLoadBaseCEMod(); + sfallLoadMods(); if (compat_access("f2_res.dat", 0) == 0) { - dbOpen("f2_res.dat", nullptr); + dbOpen("f2_res.dat"); } return 0; diff --git a/src/game_config_migration.cc b/src/game_config_migration.cc index c3f56447..8a86e8d7 100644 --- a/src/game_config_migration.cc +++ b/src/game_config_migration.cc @@ -4,8 +4,12 @@ #include #include +#include "content_config.h" +#include "debug.h" #include "game_config.h" #include "platform_compat.h" +#include "settings.h" +#include "sfall_config.h" namespace fallout { @@ -125,4 +129,168 @@ bool gameConfigMigrateFromF2Res(const char* gameConfigFilePath, Config* gameConf return migrated; } +namespace { + + constexpr char kSfallMisc[] = "Misc"; + constexpr char kSfallInterface[] = "Interface"; + + struct SfallMigrationEntry { + const char* sfallSection; + const char* sfallKey; + const char* targetSection; + const char* targetKey; + // If the sfall value matches this string, skip migration (value is already the default). + // nullptr means always migrate when the key is present. + const char* defaultValue; + }; + + constexpr SfallMigrationEntry kSfallMigrationEntries[] = { + // [start] + { kSfallMisc, "StartingMap", CONTENT_CONFIG_START_SECTION, "map", "" }, + { kSfallMisc, "MaleStartModel", CONTENT_CONFIG_START_SECTION, "model_male", "hmwarr" }, + { kSfallMisc, "MaleDefaultModel", CONTENT_CONFIG_START_SECTION, "model_male_default", "hmjmps" }, + { kSfallMisc, "FemaleStartModel", CONTENT_CONFIG_START_SECTION, "model_female", "hfprim" }, + { kSfallMisc, "FemaleDefaultModel", CONTENT_CONFIG_START_SECTION, "model_female_default", "hfjmps" }, + { kSfallMisc, "PipBoyAvailableAtGameStart", CONTENT_CONFIG_START_SECTION, "pipboy", "0" }, + // [karma] + { kSfallMisc, "KarmaFRMs", CONTENT_CONFIG_KARMA_SECTION, "frms" }, + { kSfallMisc, "KarmaPoints", CONTENT_CONFIG_KARMA_SECTION, "points" }, + // [dialog] + { kSfallMisc, "DialogueFix", CONTENT_CONFIG_DIALOG_SECTION, "no_exit_hotkey", "1" }, + { kSfallMisc, "DialogGenderWords", CONTENT_CONFIG_DIALOG_SECTION, "gender_words", "0" }, + // [main_menu] + { kSfallMisc, "VersionString", CONTENT_CONFIG_MAIN_MENU_SECTION, "version_string" }, + { kSfallMisc, "MainMenuFontColour", CONTENT_CONFIG_MAIN_MENU_SECTION, "font_color", "0" }, + { kSfallMisc, "MainMenuBigFontColour", CONTENT_CONFIG_MAIN_MENU_SECTION, "big_font_color", "0" }, + { kSfallMisc, "MainMenuOffsetX", CONTENT_CONFIG_MAIN_MENU_SECTION, "offset_x", "0" }, + { kSfallMisc, "MainMenuOffsetY", CONTENT_CONFIG_MAIN_MENU_SECTION, "offset_y", "0" }, + { kSfallMisc, "MainMenuCreditsOffsetX", CONTENT_CONFIG_MAIN_MENU_SECTION, "credits_offset_x", "0" }, + { kSfallMisc, "MainMenuCreditsOffsetY", CONTENT_CONFIG_MAIN_MENU_SECTION, "credits_offset_y", "0" }, + // [movies] + { kSfallMisc, "MovieTimer_artimer1", CONTENT_CONFIG_MOVIES_SECTION, "artimer1", "90" }, + { kSfallMisc, "MovieTimer_artimer2", CONTENT_CONFIG_MOVIES_SECTION, "artimer2", "180" }, + { kSfallMisc, "MovieTimer_artimer3", CONTENT_CONFIG_MOVIES_SECTION, "artimer3", "270" }, + { kSfallMisc, "MovieTimer_artimer4", CONTENT_CONFIG_MOVIES_SECTION, "artimer4", "360" }, + // [combat] + { kSfallMisc, "DamageFormula", CONTENT_CONFIG_COMBAT_SECTION, "damage_formula", "0" }, + { kSfallMisc, "BonusHtHDamageFix", CONTENT_CONFIG_COMBAT_SECTION, "bonus_hth_damage_fix", "1" }, + { kSfallMisc, "RemoveCriticalTimelimits", CONTENT_CONFIG_COMBAT_SECTION, "remove_critical_time_limits", "0" }, + { kSfallMisc, "ScienceOnCritters", CONTENT_CONFIG_COMBAT_SECTION, "science_on_critters", "0" }, + { kSfallMisc, "ComputeSprayMod", CONTENT_CONFIG_COMBAT_SECTION, "burst_enabled", "0" }, + { kSfallMisc, "ComputeSpray_CenterMult", CONTENT_CONFIG_COMBAT_SECTION, "burst_center_mult", "1" }, + { kSfallMisc, "ComputeSpray_CenterDiv", CONTENT_CONFIG_COMBAT_SECTION, "burst_center_div", "3" }, + { kSfallMisc, "ComputeSpray_TargetMult", CONTENT_CONFIG_COMBAT_SECTION, "burst_target_mult", "1" }, + { kSfallMisc, "ComputeSpray_TargetDiv", CONTENT_CONFIG_COMBAT_SECTION, "burst_target_div", "2" }, + // [explosions] + { kSfallMisc, "ExplosionsEmitLight", CONTENT_CONFIG_EXPLOSIONS_SECTION, "emit_light", "0" }, + { kSfallMisc, "Dynamite_DmgMax", CONTENT_CONFIG_EXPLOSIONS_SECTION, "dynamite_max", "50" }, + { kSfallMisc, "Dynamite_DmgMin", CONTENT_CONFIG_EXPLOSIONS_SECTION, "dynamite_min", "30" }, + { kSfallMisc, "PlasticExplosive_DmgMax", CONTENT_CONFIG_EXPLOSIONS_SECTION, "plastic_explosive_max", "80" }, + { kSfallMisc, "PlasticExplosive_DmgMin", CONTENT_CONFIG_EXPLOSIONS_SECTION, "plastic_explosive_min", "40" }, + // [skilldex] + { kSfallMisc, "Lockpick", CONTENT_CONFIG_SKILLDEX_SECTION, "lockpick", "293" }, + { kSfallMisc, "Steal", CONTENT_CONFIG_SKILLDEX_SECTION, "steal", "293" }, + { kSfallMisc, "Traps", CONTENT_CONFIG_SKILLDEX_SECTION, "traps", "293" }, + { kSfallMisc, "FirstAid", CONTENT_CONFIG_SKILLDEX_SECTION, "first_aid", "293" }, + { kSfallMisc, "Doctor", CONTENT_CONFIG_SKILLDEX_SECTION, "doctor", "293" }, + { kSfallMisc, "Science", CONTENT_CONFIG_SKILLDEX_SECTION, "science", "293" }, + { kSfallMisc, "Repair", CONTENT_CONFIG_SKILLDEX_SECTION, "repair", "293" }, + // [worldmap] + { kSfallMisc, "TownMapHotkeysFix", CONTENT_CONFIG_WORLDMAP_SECTION, "town_map_hotkeys_fix", "1" }, + { kSfallMisc, "DisableHorrigan", CONTENT_CONFIG_WORLDMAP_SECTION, "disable_horrigan", "0" }, + { kSfallMisc, "CityRepsList", CONTENT_CONFIG_WORLDMAP_SECTION, "city_reputation_list" }, + { kSfallInterface, "WorldMapTravelMarkers", CONTENT_CONFIG_WORLDMAP_SECTION, "trail_markers", "0" }, + // [characters] + { kSfallMisc, "PremadePaths", CONTENT_CONFIG_CHARACTERS_SECTION, "premade_paths" }, + { kSfallMisc, "PremadeFIDs", CONTENT_CONFIG_CHARACTERS_SECTION, "premade_fids" }, + // [text] + { kSfallMisc, "ExtraGameMsgFileList", CONTENT_CONFIG_TEXT_SECTION, "extra_msg_file_list" }, + }; + +} // anonymous namespace + +// Migrate sfall settings from ddraw.ini to game.cfg. +// +// Runs once when no local game.cfg exists at contentConfigFilePath. +// Writes only the settings found in sfallConfig to a new local file. +static bool contentConfigMigrateFromSfall(Config* sfallConfig, const char* contentConfigFilePath) +{ + assert(sfallConfig != nullptr && contentConfigFilePath != nullptr); + + // Skip if a local game.cfg already exists (already migrated or user-managed). + if (compat_file_exists(contentConfigFilePath)) { + return false; + } + + Config migratedConfig; + if (!configInit(&migratedConfig)) { + return false; + } + + bool migrated = false; + // Migrate start year/month/day only when explicitly set (not the sfall -1 sentinel). + auto migrateStartInt = [&](const char* sfallKey, const char* targetKey, int defaultValue) { + int value; + if (configGetInt(sfallConfig, "Misc", sfallKey, &value) && value >= 0 && value != defaultValue) { + char buf[32]; + snprintf(buf, sizeof(buf), "%d", value); + configSetString(&migratedConfig, CONTENT_CONFIG_START_SECTION, targetKey, buf); + migrated = true; + } + }; + migrateStartInt("StartYear", "year", 2241); + migrateStartInt("StartMonth", "month", 6); + migrateStartInt("StartDay", "day", 24); + + for (const auto& entry : kSfallMigrationEntries) { + char* value; + if (configGetString(sfallConfig, entry.sfallSection, entry.sfallKey, &value)) { + if (value[0] == '\0' || entry.defaultValue != nullptr && strcmp(value, entry.defaultValue) == 0) { + continue; + } + configSetString(&migratedConfig, entry.targetSection, entry.targetKey, value); + migrated = true; + } + } + + if (migrated) { + // Ensure all directory components exist before writing. + char drive[COMPAT_MAX_DRIVE]; + char dirPart[COMPAT_MAX_DIR]; + char pathWithoutFile[COMPAT_MAX_PATH]; + compat_splitpath(contentConfigFilePath, drive, dirPart, nullptr, nullptr); + compat_makepath(pathWithoutFile, drive, dirPart, nullptr, nullptr); + compat_mkdir_recursive(pathWithoutFile); + + if (!configWrite(&migratedConfig, contentConfigFilePath, false)) { + debugPrint("Failed to write migrated settings to %s!\n", contentConfigFilePath); + } + } + + configFree(&migratedConfig); + return migrated; +} + +void contentConfigTryMigrateFromSfall(const char* contentConfigPath) +{ + if (!gSfallConfig.isInitialized() || gSfallConfig.entriesLength == 0) { + // Nothing to migrate. + return; + } + const auto& masterPatches = settings.system.master_patches_path; + if (masterPatches.empty()) { + debugPrint("Failed to migrate from ddraw.ini: no master_patches is set.\n"); + return; + } + if (compat_file_exists(masterPatches.c_str())) { + // Master patches is pointing to a file instead of a folder. This shouldn't normally happen, so don't migrate in this case. + return; + } + char contentCfgPath[COMPAT_MAX_PATH]; + snprintf(contentCfgPath, sizeof(contentCfgPath), "%s\\%s", masterPatches.c_str(), contentConfigPath); + if (contentConfigMigrateFromSfall(&gSfallConfig, contentCfgPath)) { + debugPrint("Migrated settings from ddraw.ini to game.cfg.\n"); + } +} + } // namespace fallout diff --git a/src/game_config_migration.h b/src/game_config_migration.h index ee5f4e75..fdf3ed72 100644 --- a/src/game_config_migration.h +++ b/src/game_config_migration.h @@ -6,6 +6,7 @@ namespace fallout { bool gameConfigMigrateFromF2Res(const char* gameConfigFilePath, Config* gameConfig); +void contentConfigTryMigrateFromSfall(const char* contentConfigPath); } // namespace fallout diff --git a/src/game_dialog.cc b/src/game_dialog.cc index 35b05683..27a15823 100644 --- a/src/game_dialog.cc +++ b/src/game_dialog.cc @@ -12,6 +12,7 @@ #include "color.h" #include "combat.h" #include "combat_ai.h" +#include "content_config.h" #include "critter.h" #include "cycle.h" #include "debug.h" @@ -36,7 +37,6 @@ #include "random.h" #include "scripts.h" #include "settings.h" -#include "sfall_config.h" #include "skill.h" #include "stat.h" #include "svga.h" @@ -682,7 +682,7 @@ int gameDialogInit() { // SFALL: Prevents from using 0 to escape from dialogue at any time. gGameDialogFix = true; - configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_GAME_DIALOG_FIX_KEY, &gGameDialogFix); + configGetBool(&gContentConfig, CONTENT_CONFIG_DIALOG_SECTION, "no_exit_hotkey", &gGameDialogFix); // SFALL: Use numbers for replies (instead of default knobs). gNumberOptions = settings.ui.numbers_in_dialogue; diff --git a/src/game_mouse.cc b/src/game_mouse.cc index eda968cc..9f25b440 100644 --- a/src/game_mouse.cc +++ b/src/game_mouse.cc @@ -11,6 +11,7 @@ #include "art.h" #include "color.h" #include "combat.h" +#include "content_config.h" #include "critter.h" #include "draw.h" #include "game.h" @@ -24,7 +25,6 @@ #include "proto.h" #include "proto_instance.h" #include "settings.h" -#include "sfall_config.h" #include "skill.h" #include "skilldex.h" #include "svga.h" @@ -2470,13 +2470,13 @@ int objectIsDoor(Object* object) static void customMouseModeFrmsInit() { - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_FIRST_AID_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_FIRST_AID])); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_DOCTOR_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_DOCTOR])); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_LOCKPICK_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_LOCKPICK])); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_STEAL_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_STEAL])); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_TRAPS_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_TRAPS])); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_SCIENCE_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_SCIENCE])); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_REPAIR_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_REPAIR])); + configGetInt(&gContentConfig, CONTENT_CONFIG_SKILLDEX_SECTION, "first_aid", &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_FIRST_AID]), 293); + configGetInt(&gContentConfig, CONTENT_CONFIG_SKILLDEX_SECTION, "doctor", &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_DOCTOR]), 293); + configGetInt(&gContentConfig, CONTENT_CONFIG_SKILLDEX_SECTION, "lockpick", &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_LOCKPICK]), 293); + configGetInt(&gContentConfig, CONTENT_CONFIG_SKILLDEX_SECTION, "steal", &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_STEAL]), 293); + configGetInt(&gContentConfig, CONTENT_CONFIG_SKILLDEX_SECTION, "traps", &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_TRAPS]), 293); + configGetInt(&gContentConfig, CONTENT_CONFIG_SKILLDEX_SECTION, "science", &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_SCIENCE]), 293); + configGetInt(&gContentConfig, CONTENT_CONFIG_SKILLDEX_SECTION, "repair", &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_REPAIR]), 293); } void gameMouseRefreshImmediately() diff --git a/src/item.cc b/src/item.cc index ce191670..3ca2103f 100644 --- a/src/item.cc +++ b/src/item.cc @@ -9,6 +9,7 @@ #include "art.h" #include "automap.h" #include "combat.h" +#include "content_config.h" #include "critter.h" #include "debug.h" #include "display_monitor.h" @@ -71,8 +72,6 @@ static void explosionsReset(); static void explosionsExit(); static void healingItemsInit(); -static void healingItemsInitVanilla(); -static void healingItemsInitCustom(); typedef struct DrugDescription { int drugPid; @@ -3383,7 +3382,7 @@ bool booksGetInfo(int bookPid, int* messageIdPtr, int* skillPtr) static void explosionsInit() { gExplosionEmitsLight = false; - configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_EXPLOSION_EMITS_LIGHT_KEY, &gExplosionEmitsLight); + configGetBool(&gContentConfig, CONTENT_CONFIG_EXPLOSIONS_SECTION, "emit_light", &gExplosionEmitsLight); explosionsReset(); } @@ -3398,21 +3397,14 @@ static void explosionsReset() gPlasticExplosiveMinDamage = 40; gPlasticExplosiveMaxDamage = 80; - if (configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DYNAMITE_MAX_DAMAGE_KEY, &gDynamiteMaxDamage)) { - gDynamiteMaxDamage = std::clamp(gDynamiteMaxDamage, 0, 9999); - } - - if (configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DYNAMITE_MIN_DAMAGE_KEY, &gDynamiteMinDamage)) { - gDynamiteMinDamage = std::clamp(gDynamiteMinDamage, 0, gDynamiteMaxDamage); - } - - if (configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_PLASTIC_EXPLOSIVE_MAX_DAMAGE_KEY, &gPlasticExplosiveMaxDamage)) { - gPlasticExplosiveMaxDamage = std::clamp(gPlasticExplosiveMaxDamage, 0, 9999); - } - - if (configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_PLASTIC_EXPLOSIVE_MIN_DAMAGE_KEY, &gPlasticExplosiveMinDamage)) { - gPlasticExplosiveMinDamage = std::clamp(gPlasticExplosiveMinDamage, 0, gPlasticExplosiveMaxDamage); - } + configGetInt(&gContentConfig, CONTENT_CONFIG_EXPLOSIONS_SECTION, "dynamite_max", &gDynamiteMaxDamage, 50); + gDynamiteMaxDamage = std::clamp(gDynamiteMaxDamage, 0, 9999); + configGetInt(&gContentConfig, CONTENT_CONFIG_EXPLOSIONS_SECTION, "dynamite_min", &gDynamiteMinDamage, 30); + gDynamiteMinDamage = std::clamp(gDynamiteMinDamage, 0, gDynamiteMaxDamage); + configGetInt(&gContentConfig, CONTENT_CONFIG_EXPLOSIONS_SECTION, "plastic_explosive_max", &gPlasticExplosiveMaxDamage, 80); + gPlasticExplosiveMaxDamage = std::clamp(gPlasticExplosiveMaxDamage, 0, 9999); + configGetInt(&gContentConfig, CONTENT_CONFIG_EXPLOSIONS_SECTION, "plastic_explosive_min", &gPlasticExplosiveMinDamage, 40); + gPlasticExplosiveMinDamage = std::clamp(gPlasticExplosiveMinDamage, 0, gPlasticExplosiveMaxDamage); gExplosives.clear(); @@ -3600,39 +3592,9 @@ void explosionSetMaxTargets(int maxTargets) static void healingItemsInit() { - healingItemsInitVanilla(); - healingItemsInitCustom(); -} - -static void healingItemsInitVanilla() -{ - gHealingItemPids[HEALING_ITEM_STIMPACK] = PROTO_ID_STIMPACK; - gHealingItemPids[HEALING_ITEM_SUPER_STIMPACK] = PROTO_ID_SUPER_STIMPACK; - gHealingItemPids[HEALING_ITEM_HEALING_POWDER] = PROTO_ID_HEALING_POWDER; -} - -static void healingItemsInitCustom() -{ - char* tweaksFilePath = nullptr; - configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_TWEAKS_FILE_KEY, &tweaksFilePath); - if (tweaksFilePath != nullptr && *tweaksFilePath == '\0') { - tweaksFilePath = nullptr; - } - - if (tweaksFilePath == nullptr) { - return; - } - - Config tweaksConfig; - if (configInit(&tweaksConfig)) { - if (configRead(&tweaksConfig, tweaksFilePath, false)) { - configGetInt(&gSfallConfig, "Items", "STIMPAK", &(gHealingItemPids[HEALING_ITEM_STIMPACK])); - configGetInt(&gSfallConfig, "Items", "SUPER_STIMPAK", &(gHealingItemPids[HEALING_ITEM_SUPER_STIMPACK])); - configGetInt(&gSfallConfig, "Items", "HEALING_POWDER", &(gHealingItemPids[HEALING_ITEM_HEALING_POWDER])); - } - - configFree(&tweaksConfig); - } + configGetInt(&gContentConfig, CONTENT_CONFIG_ITEMS_SECTION, "stimpak", &gHealingItemPids[HEALING_ITEM_STIMPACK], PROTO_ID_STIMPACK); + configGetInt(&gContentConfig, CONTENT_CONFIG_ITEMS_SECTION, "super_stimpak", &gHealingItemPids[HEALING_ITEM_SUPER_STIMPACK], PROTO_ID_SUPER_STIMPACK); + configGetInt(&gContentConfig, CONTENT_CONFIG_ITEMS_SECTION, "healing_powder", &gHealingItemPids[HEALING_ITEM_HEALING_POWDER], PROTO_ID_HEALING_POWDER); } bool itemIsHealing(int pid) diff --git a/src/main.cc b/src/main.cc index 3c6b9c4f..04fc224a 100644 --- a/src/main.cc +++ b/src/main.cc @@ -7,6 +7,7 @@ #include "autorun.h" #include "character_selector.h" #include "color.h" +#include "content_config.h" #include "credits.h" #include "cycle.h" #include "db.h" @@ -32,7 +33,6 @@ #include "scripts.h" #include "settings.h" #include "sfall_callbacks.h" -#include "sfall_config.h" #include "sfall_global_scripts.h" #include "svga.h" #include "text_font.h" @@ -123,11 +123,7 @@ int falloutMain(int argc, char** argv) // SFALL: Override starting map. char* mapName = nullptr; - if (configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_STARTING_MAP_KEY, &mapName)) { - if (*mapName == '\0') { - mapName = nullptr; - } - } + configGetString(&gContentConfig, CONTENT_CONFIG_START_SECTION, "map", &mapName, nullptr); char* mapNameCopy = compat_strdup(mapName != nullptr ? mapName : _mainMap); _main_load_new(mapNameCopy); diff --git a/src/mainmenu.cc b/src/mainmenu.cc index 0bedd788..7a81335b 100644 --- a/src/mainmenu.cc +++ b/src/mainmenu.cc @@ -4,6 +4,7 @@ #include "art.h" #include "color.h" +#include "content_config.h" #include "draw.h" #include "game.h" #include "game_sound.h" @@ -12,7 +13,6 @@ #include "mouse.h" #include "palette.h" #include "preferences.h" -#include "sfall_config.h" #include "svga.h" #include "text_font.h" #include "version.h" @@ -132,14 +132,14 @@ int mainMenuWindowInit() // 0x020000 - underline text (only for the version string) // 0x040000 - monospace font (only for the version string) int fontSettings = _colorTable[21091], fontSettingsSFall = 0; - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_FONT_COLOR_KEY, &fontSettingsSFall); + configGetInt(&gContentConfig, CONTENT_CONFIG_MAIN_MENU_SECTION, "font_color", &fontSettingsSFall, 0); if (fontSettingsSFall && !(fontSettingsSFall & 0x010000)) fontSettings = fontSettingsSFall & 0xFF; // SFALL: Allow to move copyright text int offsetX = 0, offsetY = 0; - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_X_KEY, &offsetX); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_Y_KEY, &offsetY); + configGetInt(&gContentConfig, CONTENT_CONFIG_MAIN_MENU_SECTION, "credits_offset_x", &offsetX, 0); + configGetInt(&gContentConfig, CONTENT_CONFIG_MAIN_MENU_SECTION, "credits_offset_y", &offsetY, 0); // Copyright. msg.num = 20; @@ -187,9 +187,8 @@ int mainMenuWindowInit() } // SFALL: Allow to move menu buttons - offsetX = offsetY = 0; - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_OFFSET_X_KEY, &offsetX); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_OFFSET_Y_KEY, &offsetY); + configGetInt(&gContentConfig, CONTENT_CONFIG_MAIN_MENU_SECTION, "offset_x", &offsetX, 0); + configGetInt(&gContentConfig, CONTENT_CONFIG_MAIN_MENU_SECTION, "offset_y", &offsetY, 0); for (int index = 0; index < MAIN_MENU_BUTTON_COUNT; index++) { gMainMenuButtons[index] = buttonCreate(gMainMenuWindow, @@ -218,7 +217,7 @@ int mainMenuWindowInit() // SFALL: Allow to change font color of buttons fontSettings = _colorTable[21091]; fontSettingsSFall = 0; - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_BIG_FONT_COLOR_KEY, &fontSettingsSFall); + configGetInt(&gContentConfig, CONTENT_CONFIG_MAIN_MENU_SECTION, "big_font_color", &fontSettingsSFall, 0); if (fontSettingsSFall) fontSettings = fontSettingsSFall & 0xFF; diff --git a/src/message.cc b/src/message.cc index 9f8a1ce2..7fb1f160 100644 --- a/src/message.cc +++ b/src/message.cc @@ -10,13 +10,13 @@ #include #include +#include "content_config.h" #include "debug.h" #include "memory.h" #include "platform_compat.h" #include "proto_types.h" #include "random.h" #include "settings.h" -#include "sfall_config.h" namespace fallout { @@ -608,7 +608,7 @@ void messageListFilterGenderWords(MessageList* messageList, int gender) } bool enabled = false; - configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_GAME_DIALOG_GENDER_WORDS_KEY, &enabled); + configGetBool(&gContentConfig, CONTENT_CONFIG_DIALOG_SECTION, "gender_words", &enabled); if (!enabled) { return; } @@ -652,10 +652,7 @@ bool messageListRepositoryInit() } char* fileList; - configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY, &fileList); - if (fileList != nullptr && *fileList == '\0') { - fileList = nullptr; - } + configGetString(&gContentConfig, CONTENT_CONFIG_TEXT_SECTION, "extra_msg_file_list", &fileList, nullptr); char path[COMPAT_MAX_PATH]; int nextMessageListId = 0; diff --git a/src/pipboy.cc b/src/pipboy.cc index f161ca4f..c058d9f1 100644 --- a/src/pipboy.cc +++ b/src/pipboy.cc @@ -10,6 +10,7 @@ #include "color.h" #include "combat.h" #include "config.h" +#include "content_config.h" #include "critter.h" #include "cycle.h" #include "dbox.h" @@ -35,7 +36,6 @@ #include "random.h" #include "scripts.h" #include "settings.h" -#include "sfall_config.h" #include "stat.h" #include "svga.h" #include "text_font.h" @@ -863,9 +863,8 @@ static void _pip_init_() // bypassed. CE implements only the latter approach, as it does not have any // side effects. int value = 0; - if (configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_PIPBOY_AVAILABLE_AT_GAMESTART, &value)) { - pipboy_available_at_game_start = value == 1 || value == 2; - } + configGetInt(&gContentConfig, CONTENT_CONFIG_START_SECTION, "pipboy", &value, 0); + pipboy_available_at_game_start = value == 1 || value == 2; } // NOTE: Uncollapsed 0x497918. diff --git a/src/platform_compat.cc b/src/platform_compat.cc index 3f976cd8..5dc4ac39 100644 --- a/src/platform_compat.cc +++ b/src/platform_compat.cc @@ -215,6 +215,39 @@ int compat_mkdir(const char* path) #endif } +int compat_mkdir_recursive(const char* path) +{ + char drive[COMPAT_MAX_DRIVE]; + compat_splitpath(path, drive, nullptr, nullptr, nullptr); + + char pathCopy[COMPAT_MAX_PATH]; + strcpy(pathCopy, path); + + // Skip drive root (e.g. "C:\\" or leading "/") to avoid mkdir("") or mkdir("C:"). + char* sep = pathCopy + strlen(drive); + if (*sep == '\\' || *sep == '/') sep++; + for (; *sep != '\0'; sep++) { + if (*sep == '\\' || *sep == '/') { + char saved = *sep; + *sep = '\0'; + if (compat_mkdir(pathCopy) < 0) { + break; + } + *sep = saved; + } + } + return compat_mkdir(path); +} + +bool compat_file_exists(const char* filePath) +{ + FILE* file = compat_fopen(filePath, "rb"); + if (file == nullptr) return false; + + fclose(file); + return true; +} + unsigned int compat_timeGetTime() { #ifdef _WIN32 diff --git a/src/platform_compat.h b/src/platform_compat.h index 66db2bc4..c2645ca5 100644 --- a/src/platform_compat.h +++ b/src/platform_compat.h @@ -32,6 +32,8 @@ void compat_makepath(char* path, const char* drive, const char* dir, const char* long compat_tell(int fileHandle); long compat_filelength(int fd); int compat_mkdir(const char* path); +int compat_mkdir_recursive(const char* path); +bool compat_file_exists(const char* filePath); unsigned int compat_timeGetTime(); FILE* compat_fopen(const char* path, const char* mode); gzFile compat_gzopen(const char* path, const char* mode); diff --git a/src/random.cc b/src/random.cc index 7bfe7364..b1ef60f2 100644 --- a/src/random.cc +++ b/src/random.cc @@ -5,10 +5,10 @@ #include +#include "combat.h" #include "debug.h" #include "platform_compat.h" #include "scripts.h" -#include "sfall_config.h" namespace fallout { @@ -103,15 +103,11 @@ static int randomTranslateRoll(int delta, int criticalSuccessModifier) { unsigned int gameTime = gameTimeGetTime(); - // SFALL: Remove criticals time limits. - bool criticalsTimeLimitsRemoved = false; - configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_REMOVE_CRITICALS_TIME_LIMITS_KEY, &criticalsTimeLimitsRemoved); - int roll; if (delta < 0) { roll = ROLL_FAILURE; - if (criticalsTimeLimitsRemoved || (gameTime / GAME_TIME_TICKS_PER_DAY) >= 1) { + if (criticalsNoTimeLimits() || (gameTime / GAME_TIME_TICKS_PER_DAY) >= 1) { // 10% to become critical failure. if (randomBetween(1, 100) <= -delta / 10) { roll = ROLL_CRITICAL_FAILURE; @@ -120,7 +116,7 @@ static int randomTranslateRoll(int delta, int criticalSuccessModifier) } else { roll = ROLL_SUCCESS; - if (criticalsTimeLimitsRemoved || (gameTime / GAME_TIME_TICKS_PER_DAY) >= 1) { + if (criticalsNoTimeLimits() || (gameTime / GAME_TIME_TICKS_PER_DAY) >= 1) { // 10% + modifier to become critical success. if (randomBetween(1, 100) <= delta / 10 + criticalSuccessModifier) { roll = ROLL_CRITICAL_SUCCESS; diff --git a/src/scripts.cc b/src/scripts.cc index e62a7c42..923bfb1f 100644 --- a/src/scripts.cc +++ b/src/scripts.cc @@ -11,6 +11,7 @@ #include "art.h" #include "automap.h" #include "combat.h" +#include "content_config.h" #include "critter.h" #include "debug.h" #include "dialog.h" @@ -32,7 +33,6 @@ #include "queue.h" #include "scan_unimplemented.h" #include "sfall_arrays.h" -#include "sfall_config.h" #include "sfall_global_scripts.h" #include "sfall_script_hooks.h" #include "stat.h" @@ -1517,14 +1517,14 @@ int scriptsInit() messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SCRIPT, &gScrMessageList); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_YEAR, &gStartYear); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_MONTH, &gStartMonth); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_DAY, &gStartDay); + configGetInt(&gContentConfig, CONTENT_CONFIG_START_SECTION, "year", &gStartYear, 2241); + configGetInt(&gContentConfig, CONTENT_CONFIG_START_SECTION, "month", &gStartMonth, 6); + configGetInt(&gContentConfig, CONTENT_CONFIG_START_SECTION, "day", &gStartDay, 24); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER1, &gMovieTimerArtimer1); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER2, &gMovieTimerArtimer2); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER3, &gMovieTimerArtimer3); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER4, &gMovieTimerArtimer4); + configGetInt(&gContentConfig, CONTENT_CONFIG_MOVIES_SECTION, "artimer1", &gMovieTimerArtimer1, 90); + configGetInt(&gContentConfig, CONTENT_CONFIG_MOVIES_SECTION, "artimer2", &gMovieTimerArtimer2, 180); + configGetInt(&gContentConfig, CONTENT_CONFIG_MOVIES_SECTION, "artimer3", &gMovieTimerArtimer3, 270); + configGetInt(&gContentConfig, CONTENT_CONFIG_MOVIES_SECTION, "artimer4", &gMovieTimerArtimer4, 360); checkScriptsOpcodes(); diff --git a/src/sfall_callbacks.cc b/src/sfall_callbacks.cc index 0862e1e1..93941558 100644 --- a/src/sfall_callbacks.cc +++ b/src/sfall_callbacks.cc @@ -1,8 +1,8 @@ #include "sfall_callbacks.h" +#include "content_config.h" #include "display_monitor.h" #include "interface.h" -#include "sfall_config.h" #include "sfall_script_hooks.h" #include "worldmap.h" @@ -42,7 +42,7 @@ void sfallOnAfterGameStarted() { // Disable Horrigan Patch bool isDisableHorrigan = false; - configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DISABLE_HORRIGAN, &isDisableHorrigan); + configGetBool(&gContentConfig, CONTENT_CONFIG_WORLDMAP_SECTION, "disable_horrigan", &isDisableHorrigan); if (isDisableHorrigan) { gDidMeetFrankHorrigan = true; diff --git a/src/sfall_config.cc b/src/sfall_config.cc index 10e72eed..069e10e5 100644 --- a/src/sfall_config.cc +++ b/src/sfall_config.cc @@ -21,47 +21,13 @@ bool sfallConfigInit(int argc, char** argv) } // Initialize defaults. - configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_MALE_KEY, ""); - configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_FEMALE_KEY, ""); - configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_MALE_KEY, ""); - configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_FEMALE_KEY, ""); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_YEAR, 2241); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_MONTH, 6); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_START_DAY, 24); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_BIG_FONT_COLOR_KEY, 0); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_X_KEY, 0); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_Y_KEY, 0); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_FONT_COLOR_KEY, 0); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_OFFSET_X_KEY, 0); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MAIN_MENU_OFFSET_Y_KEY, 0); - configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_STARTING_MAP_KEY, ""); configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_OVERRIDE_CRITICALS_MODE_KEY, 2); configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_OVERRIDE_CRITICALS_FILE_KEY, ""); - configSetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_REMOVE_CRITICALS_TIME_LIMITS_KEY, false); configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BOOKS_FILE_KEY, ""); configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_ELEVATORS_FILE_KEY, ""); - configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_PREMADE_CHARACTERS_FILE_NAMES_KEY, ""); - configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_PREMADE_CHARACTERS_FACE_FIDS_KEY, ""); - configSetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_ENABLED_KEY, false); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_CENTER_MULTIPLIER_KEY, SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_CENTER_DIVISOR_KEY, SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_TARGET_MULTIPLIER_KEY, SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_MULTIPLIER); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BURST_MOD_TARGET_DIVISOR_KEY, SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_DIVISOR); - configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY, ""); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER1, 90); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER2, 180); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER3, 270); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_MOVIE_TIMER_ARTIMER4, 360); - configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_VERSION_STRING, ""); configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_CONFIG_FILE, ""); configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_PATCH_FILE, ""); - configSetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_CITIES_LIMIT_FIX, true); - configSetString(&gSfallConfig, SFALL_CONFIG_SCRIPTS_KEY, SFALL_CONFIG_INI_CONFIG_FOLDER, ""); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_PIPBOY_AVAILABLE_AT_GAMESTART, 0); - configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_WORLDMAP_TRAIL_MARKERS, 0); - - configSetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DISABLE_HORRIGAN, false); char path[COMPAT_MAX_PATH]; char* executable = argv[0]; diff --git a/src/sfall_config.h b/src/sfall_config.h index e3673aa0..303bcf2d 100644 --- a/src/sfall_config.h +++ b/src/sfall_config.h @@ -11,73 +11,14 @@ namespace fallout { #define SFALL_CONFIG_MISC_KEY "Misc" #define SFALL_CONFIG_SCRIPTS_KEY "Scripts" -#define SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_MALE_KEY "MaleDefaultModel" -#define SFALL_CONFIG_DUDE_NATIVE_LOOK_JUMPSUIT_FEMALE_KEY "FemaleDefaultModel" -#define SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_MALE_KEY "MaleStartModel" -#define SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_FEMALE_KEY "FemaleStartModel" -#define SFALL_CONFIG_START_YEAR "StartYear" -#define SFALL_CONFIG_START_MONTH "StartMonth" -#define SFALL_CONFIG_START_DAY "StartDay" -#define SFALL_CONFIG_MAIN_MENU_BIG_FONT_COLOR_KEY "MainMenuBigFontColour" -#define SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_X_KEY "MainMenuCreditsOffsetX" -#define SFALL_CONFIG_MAIN_MENU_CREDITS_OFFSET_Y_KEY "MainMenuCreditsOffsetY" -#define SFALL_CONFIG_MAIN_MENU_FONT_COLOR_KEY "MainMenuFontColour" -#define SFALL_CONFIG_MAIN_MENU_OFFSET_X_KEY "MainMenuOffsetX" -#define SFALL_CONFIG_MAIN_MENU_OFFSET_Y_KEY "MainMenuOffsetY" -#define SFALL_CONFIG_STARTING_MAP_KEY "StartingMap" -#define SFALL_CONFIG_KARMA_FRMS_KEY "KarmaFRMs" -#define SFALL_CONFIG_KARMA_POINTS_KEY "KarmaPoints" #define SFALL_CONFIG_OVERRIDE_CRITICALS_MODE_KEY "OverrideCriticalTable" #define SFALL_CONFIG_OVERRIDE_CRITICALS_FILE_KEY "OverrideCriticalFile" -#define SFALL_CONFIG_REMOVE_CRITICALS_TIME_LIMITS_KEY "RemoveCriticalTimelimits" #define SFALL_CONFIG_BOOKS_FILE_KEY "BooksFile" #define SFALL_CONFIG_ELEVATORS_FILE_KEY "ElevatorsFile" -#define SFALL_CONFIG_PREMADE_CHARACTERS_FILE_NAMES_KEY "PremadePaths" -#define SFALL_CONFIG_PREMADE_CHARACTERS_FACE_FIDS_KEY "PremadeFIDs" -#define SFALL_CONFIG_BURST_MOD_ENABLED_KEY "ComputeSprayMod" -#define SFALL_CONFIG_BURST_MOD_CENTER_MULTIPLIER_KEY "ComputeSpray_CenterMult" -#define SFALL_CONFIG_BURST_MOD_CENTER_DIVISOR_KEY "ComputeSpray_CenterDiv" -#define SFALL_CONFIG_BURST_MOD_TARGET_MULTIPLIER_KEY "ComputeSpray_TargetMult" -#define SFALL_CONFIG_BURST_MOD_TARGET_DIVISOR_KEY "ComputeSpray_TargetDiv" -#define SFALL_CONFIG_DYNAMITE_MIN_DAMAGE_KEY "Dynamite_DmgMin" -#define SFALL_CONFIG_DYNAMITE_MAX_DAMAGE_KEY "Dynamite_DmgMax" -#define SFALL_CONFIG_PLASTIC_EXPLOSIVE_MIN_DAMAGE_KEY "PlasticExplosive_DmgMin" -#define SFALL_CONFIG_PLASTIC_EXPLOSIVE_MAX_DAMAGE_KEY "PlasticExplosive_DmgMax" -#define SFALL_CONFIG_EXPLOSION_EMITS_LIGHT_KEY "ExplosionsEmitLight" -#define SFALL_CONFIG_MOVIE_TIMER_ARTIMER1 "MovieTimer_artimer1" -#define SFALL_CONFIG_MOVIE_TIMER_ARTIMER2 "MovieTimer_artimer2" -#define SFALL_CONFIG_MOVIE_TIMER_ARTIMER3 "MovieTimer_artimer3" -#define SFALL_CONFIG_MOVIE_TIMER_ARTIMER4 "MovieTimer_artimer4" -#define SFALL_CONFIG_CITY_REPUTATION_LIST_KEY "CityRepsList" #define SFALL_CONFIG_UNARMED_FILE_KEY "UnarmedFile" -#define SFALL_CONFIG_DAMAGE_MOD_FORMULA_KEY "DamageFormula" -#define SFALL_CONFIG_BONUS_HTH_DAMAGE_FIX_KEY "BonusHtHDamageFix" -#define SFALL_CONFIG_USE_LOCKPICK_FRM_KEY "Lockpick" -#define SFALL_CONFIG_USE_STEAL_FRM_KEY "Steal" -#define SFALL_CONFIG_USE_TRAPS_FRM_KEY "Traps" -#define SFALL_CONFIG_USE_FIRST_AID_FRM_KEY "FirstAid" -#define SFALL_CONFIG_USE_DOCTOR_FRM_KEY "Doctor" -#define SFALL_CONFIG_USE_SCIENCE_FRM_KEY "Science" -#define SFALL_CONFIG_USE_REPAIR_FRM_KEY "Repair" -#define SFALL_CONFIG_SCIENCE_REPAIR_TARGET_TYPE_KEY "ScienceOnCritters" -#define SFALL_CONFIG_GAME_DIALOG_FIX_KEY "DialogueFix" -#define SFALL_CONFIG_TWEAKS_FILE_KEY "TweaksFile" -#define SFALL_CONFIG_GAME_DIALOG_GENDER_WORDS_KEY "DialogGenderWords" -#define SFALL_CONFIG_TOWN_MAP_HOTKEYS_FIX_KEY "TownMapHotkeysFix" -#define SFALL_CONFIG_EXTRA_MESSAGE_LISTS_KEY "ExtraGameMsgFileList" #define SFALL_CONFIG_INI_CONFIG_FOLDER "IniConfigFolder" -#define SFALL_CONFIG_VERSION_STRING "VersionString" #define SFALL_CONFIG_CONFIG_FILE "ConfigFile" #define SFALL_CONFIG_PATCH_FILE "PatchFile" -#define SFALL_CONFIG_CITIES_LIMIT_FIX "CitiesLimitFix" -#define SFALL_CONFIG_PIPBOY_AVAILABLE_AT_GAMESTART "PipBoyAvailableAtGameStart" -#define SFALL_CONFIG_WORLDMAP_TRAIL_MARKERS "WorldMapTravelMarkers" -#define SFALL_CONFIG_DISABLE_HORRIGAN "DisableHorrigan" - -#define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1 -#define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3 -#define SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_MULTIPLIER 1 -#define SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_DIVISOR 2 extern bool gSfallConfigInitialized; extern Config gSfallConfig; diff --git a/src/version.cc b/src/version.cc index ca4f7ade..e252e072 100644 --- a/src/version.cc +++ b/src/version.cc @@ -1,5 +1,5 @@ #include "version.h" -#include "sfall_config.h" +#include "content_config.h" #include @@ -10,10 +10,9 @@ void versionGetVersion(char* dest, size_t size) { // SFALL: custom version string. char* versionString = nullptr; - if (configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_VERSION_STRING, &versionString)) { - if (*versionString == '\0') { - versionString = nullptr; - } + configGetString(&gContentConfig, CONTENT_CONFIG_MAIN_MENU_SECTION, "version_string", &versionString, ""); + if (!*versionString) { + versionString = nullptr; } snprintf(dest, size, (versionString != nullptr ? versionString : "FALLOUT II %d.%02d"), VERSION_MAJOR, VERSION_MINOR); } diff --git a/src/worldmap.cc b/src/worldmap.cc index 55830094..578ebff5 100644 --- a/src/worldmap.cc +++ b/src/worldmap.cc @@ -14,6 +14,7 @@ #include "color.h" #include "combat.h" #include "combat_ai.h" +#include "content_config.h" #include "critter.h" #include "cycle.h" #include "db.h" @@ -40,7 +41,6 @@ #include "random.h" #include "scripts.h" #include "settings.h" -#include "sfall_config.h" #include "sfall_global_scripts.h" #include "skill.h" #include "stat.h" @@ -846,7 +846,6 @@ static int wmMaxEncBaseTypes; static int wmMaxEncounterInfoTables; static bool gTownMapHotkeysFix; -static bool gCitiesLimitFix; static double gGameTimeIncRemainder = 0.0; static FrmImage _backgroundFrmImage; static FrmImage _townFrmImage; @@ -873,10 +872,6 @@ static void wmSetFlags(int* flagsPtr, int flag, int value) // 0x4BC89C int wmWorldMap_init() { - // SFALL - gCitiesLimitFix = true; - configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_CITIES_LIMIT_FIX, &gCitiesLimitFix); - char path[COMPAT_MAX_PATH]; if (wmGenDataInit() == -1) { @@ -905,10 +900,8 @@ int wmWorldMap_init() wmWorldMapSaveTempData(); // SFALL - gTownMapHotkeysFix = true; - worldmapTrailMarkers = 0; - configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_TOWN_MAP_HOTKEYS_FIX_KEY, &gTownMapHotkeysFix); - configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_WORLDMAP_TRAIL_MARKERS, &worldmapTrailMarkers); + configGetBool(&gContentConfig, CONTENT_CONFIG_WORLDMAP_SECTION, "town_map_hotkeys_fix", &gTownMapHotkeysFix, true); + configGetInt(&gContentConfig, CONTENT_CONFIG_WORLDMAP_SECTION, "trail_markers", &worldmapTrailMarkers, 0); // CE: City size fids should be initialized during startup. They are used // during |wmTeleportToArea| to calculate worldmap position when jumping @@ -1199,9 +1192,8 @@ int wmWorldMap_load(File* stream) int numCities; if (fileReadInt32(stream, &numCities) == -1) return -1; - if (gCitiesLimitFix && numCities != wmMaxAreaNum) { - debugPrint("WorldMap Error: Cities limit fix is enabled, " - "but the number of cities %d in the save file is different from " + if (numCities != wmMaxAreaNum) { + debugPrint("WorldMap Error: number of cities %d in the save file is different from " "the number of cities %d in the worldmap.txt file.", numCities, wmMaxAreaNum); } @@ -2578,10 +2570,11 @@ static int wmAreaInit() configFree(&cfg); - if (!gCitiesLimitFix && wmMaxAreaNum != CITY_COUNT) { + // SFALL: CitiesLimitFix (always on) + /*if (wmMaxAreaNum != CITY_COUNT) { showMesageBox("\nwmAreaInit::Error loading Cities!"); exit(1); - } + }*/ return 0; }