Compare commits

...
36 changed files with 546 additions and 408 deletions
+5
View File
@@ -399,3 +399,8 @@ FodyWeavers.xsd
# versioning header
src/platform/git_version.h
CMakeUserPresets.json
# Local configs for AI agents
.aider*
.gemini
.claude
+25
View File
@@ -0,0 +1,25 @@
## Further Reading
- Refer to the project README for overall architecture.
## Working With Code
- Only search for code and symbols inside the `src/` directory.
- NEVER search entire code base for certain file or symbol when it's location isn't known, ask first.
- NEVER read entire files unless absolutely necessary for task. Use MCP search instead.
- Prefer CLion MCP for searching code and basic refactoring over raw text tools such as grep.
- Test changes by compiling (separate cc file for small local changes, or via CLion build_project MCP for big changes).
- If game run test is request by prompt, start the game from C:\Games\Fallout2\@CE folder. It always contains last built executable, config file and game assets for full testing.
## Language Standard
- The project targets C++17 (`CMAKE_CXX_STANDARD 17` in `CMakeLists.txt`).
- Prefer C++17 library APIs and semantics when they simplify code. For example, `std::string::data()` is mutable in C++17.
- static global variable need no prefix. exported globals get g*.
- generally prefer camelCase
## Project Structure
- `src/`: 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.
+2
View File
@@ -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"
+6 -2
View File
@@ -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 [`<DAT>/config/game.cfg`](files/foce_base.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 |
| --- | --- | --- | --- |
-127
View File
@@ -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: <MaleText^FemaleText>)
;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]
+130
View File
@@ -0,0 +1,130 @@
;
; 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. <MaleText^FemaleText>.
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 fix cities limit bug (allows more than 128 cities).
cities_limit_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=
+2 -2
View File
@@ -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;
+5 -17
View File
@@ -6,6 +6,7 @@
#include <string.h>
#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++) {
+6 -17
View File
@@ -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) {
+3 -9
View File
@@ -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;
+21 -16
View File
@@ -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;
}
}
@@ -6087,6 +6085,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) {
@@ -6345,12 +6345,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;
}
@@ -6358,8 +6363,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;
}
@@ -6700,10 +6705,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;
}
+1
View File
@@ -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);
+12
View File
@@ -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)
{
+3
View File
@@ -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.
// Pointer to an internal string is returned, no copy is performed.
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
+35
View File
@@ -0,0 +1,35 @@
#include "content_config.h"
#include "game_config_migration.h"
namespace fallout {
Config gContentConfig;
constexpr char kConfigPath[] = R"(config\game.cfg)";
bool contentConfigInit(const char* baseModPath)
{
if (gContentConfig.isInitialized()) {
return false;
}
if (!configInit(&gContentConfig)) {
return false;
}
if (!configRead(&gContentConfig, kConfigPath, true)) {
// Failed to load config, try to migrate some settings from sfall.
contentConfigTryMigrateFromSfall(baseModPath, kConfigPath);
}
return true;
}
void contentConfigExit()
{
if (!gContentConfig.isInitialized()) return;
configFree(&gContentConfig);
}
} // namespace fallout
+29
View File
@@ -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 and content config and load it from file. Returns false if either failed.
bool contentConfigInit(const char* baseModPath);
void contentConfigExit();
} // namespace fallout
#endif // CONTENT_CONFIG_H
+1 -1
View File
@@ -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);
+5 -2
View File
@@ -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;
+3 -5
View File
@@ -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);
+24 -3
View File
@@ -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[] = "foce_base.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(kBaseModPath);
// 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()
{
@@ -1397,14 +1415,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;

Some files were not shown because too many files have changed in this diff Show More