diff --git a/OPTIONS-INI.md b/OPTIONS-INI.md new file mode 100644 index 00000000..867eea55 --- /dev/null +++ b/OPTIONS-INI.md @@ -0,0 +1,325 @@ +# options.ini reference + +Complete reference for `options.ini`, the runtime configuration file that sits next to +`MW4.exe`. + +Compiled 2026-07-25 by reading the actual read sites in the source. Every key below was +verified against code; keys that are shipped but **not** read by any code are called out +explicitly rather than silently documented as working. + +For command-line switches, run `MW4.exe -help` (writes `mw4-help.txt` next to the exe). + +--- + +## How the file is loaded + +`options.ini` is a standard INI file read through the engine's own notation-file reader: + +```cpp +Stuff::NotationFile startup_ini("options.ini", NotationFile::Standard, true); +Stuff::Page* page = startup_ini.FindPage("graphics options"); +page->GetEntry("bitdepth", &Environment.bitDepth); +``` + +Consequences of this design that matter in practice: + +- **Section and key lookup is by name.** A misspelled key is not an error ? `GetEntry` + simply returns false and the compiled-in default is kept. **There is no warning.** This + is why several shipped keys silently do nothing (see "Dead settings" below). +- **The file is read at startup**, in `GetGameOSEnvironment` / `InitializeGameEngine`. + Changing it while the game runs has no effect. +- **Parts of it are written back.** `[server]` in particular is re-saved from the in-game + host UI, and `[special commands] killgame` is cleared on use. Hand edits to those + sections can be overwritten. +- A few sections are read via the Win32 profile API (`GetPrivateProfileInt` / + `GetPrivateProfileString`) instead ? noted per section. + +### Related but separate files + +| File | Purpose | +|---|---| +| `options.ini` | This document. Main game configuration. | +| `options-game.ini`, `options-cam.ini`, `options-mr.ini` | Per-role variants copied over `options.ini` by the pod launcher for game pods, cameraships, and mission-review machines. Same format. | +| `options-*-var.ini` | Site-local variant overlays kept in the dev tree. | +| `c:\ctcl.ini` | Arcade console configuration (pod IP list, launch command lines, voice macros). Hardcoded path. Not covered here. | +| `banner.txt` | mw4print bottom-of-sheet banner text. Plain text, first line only. | + +--- + +## `[graphics options]` + +Read in `MW4Application.cpp` (`GetGameOSEnvironment`) and `MWOptions.cpp`. + +| Key | Type | Shipped | Effect | +|---|---|---|---| +| `bitdepth` | int | 16 | Display colour depth. The pods run 16; on modern Windows the DirectDraw 16-bit path additionally needs the `DWM8And16BitMitigation` compatibility shim. | +| `antialias` | bool | false | Full-screen antialiasing (`Environment.AntiAlias`). | +| `screenwidth` | int | 800 | Horizontal resolution, read by `MWOptions`. | +| `screenheight` | int | 600 | Vertical resolution. | + +**Read but overridden in this build ? editing has no effect:** + +| Key | Why | +|---|---| +| `videodriverindex` | The `GetEntry` call is commented out and `Environment.FullScreenDevice` is forced to `0`. Use the `-tmon` command-line switch to assign display devices instead. | + +**Present in the shipped file and consumed by renderer subsystem initialisers** +(`MidLevelRenderer::InitializeClasses`, `gosFX`, `ElementRenderer`, `Compost`), which are +handed the same notation file: `noblend`, `shadowmode`, `mipbias`, `loadradius`, +`compositing`, `lod`, `maxlights`, `hidesky`, `effectlod`, `detailtexture`, `multitexture`, +`lightmaps`, `culturals`, `footsteps`, `fancywater`, `movietextures`, `simplelighting`, +`missionmusic`, `vertexlighting`, `fontsmall`, `fontmedium`, `fontlarge`, `fontlarge2`, +`fontlarge3`. + +These are stock MechWarrior 4 renderer/quality settings. They are passed through to the +engine libraries rather than read in game code, so their exact clamping lives in those +libraries. The shipped values above are the tested pod configuration; change them one at a +time. + +--- + +## `[sound options]` + +| Key | Type | Shipped | Effect | +|---|---|---|---| +| `hardwaremixing` | bool | false | Use DirectSound hardware mixing (`Environment.soundMixInHardware`). The 2016 release build enabled this (`true`); the current tree ships `false`. | +| `lowendsound` | bool | false | Stock low-quality audio path. | +| `radius` | float | 1.0 | 3D sound attenuation radius multiplier. | + +--- + +## `[special commands]` + +| Key | Type | Shipped | Effect | +|---|---|---|---| +| `killgame` | bool | false | **Self-clearing kill switch.** If true at startup, the game sets it back to false, saves `options.ini`, and immediately calls `ExitGameOS()`. Intended as a remote "do not start" flag for a pod. | +| `autotorsocenter` | int | 2 | Torso auto-centring mode (`VehicleInterface::perminateTorsoMode`). 0 = off, 1 = slow, 2 = fast. | + +**Read but overridden in this build ? editing has no effect:** + +| Key | Why | +|---|---| +| `huddamagemode` | `GetEntry` commented out; forced to `false`. | +| `hudtargetdamagemode` | `GetEntry` commented out; forced to `false`. | + +--- + +## `[server]` + +Multiplayer host defaults. **This section is written back** by the host configuration UI ? +do not hand-edit it while the game is running. + +Verified read: `playerlimit` (`Adept/Application.cpp`, both `GetEntry` and `SetEntry`). + +The remaining keys form the persisted `NetMissionParameters` set and correspond +one-to-one with the in-game host options: + +`defaultservername`, `dedicated`, `visibility`, `joininprogress`, +`joininprogresscutoff`, `joininprogresscutofftime`, `heaton`, `forcerespawn`, `splashon`, +`weaponjamon`, `ammobayfireon`, `advancemodeon`, `armormodeon`, `splashpercentage`, +`unlimitedammo`, `friendlyfirepercentage`, `allowzoom`, `allow3rdperson`, +`allowdeadtochat`, `reportstats`, `serverrecycle`, `recycledelay`, `playmissionreview`, +`deadmechcantsee`, `deadmechcantseeotherteam`, `ruletype`, `usemapcycle`, `maxplayers`, +`maxbots`, `allowdecaltransfer`, and the `allowed*1` / `allowed*2` bitmasks +(`allowedmech`, `allowedbeam`, `allowedmissile`, `allowedprojectile`, `allowedsubsystem`). + +Notes: + +- `allowed*1` / `allowed*2` are 32-bit hex masks covering items 0?31 and 32?63 + respectively. `ffffffff` = everything permitted. +- `maxplayers` and `maxbots` are serialised to clients as **5-bit fields**, so the maximum + usable value is **31**. Writing 32 truncates to 0. See `RAISING-PLAYER-CAP.md`. +- In arcade (CTCL) mode these are overwritten at mission launch by + `CTCL_DefaultHostSetup`, so this section mainly affects standalone hosting. + +--- + +## `[joystick]` + +Read and written by `Adept/control_mapping.cpp`. Also stores control bindings, which is why +the file is saved from the controls UI. + +| Key | Type | Shipped | Effect | +|---|---|---|---| +| `bithrottlecenter` | float | 0.3 | Bidirectional throttle centre dead zone. | +| `bithrottlelow` | float | 0.25 | Bidirectional throttle low dead zone. | +| `bithrottlehigh` | float | 0.06 | Bidirectional throttle high dead zone. | +| `onethrottlecenter` | float | 0.3 | One-way throttle centre dead zone. | +| `onethrottlehigh` | float | 0.06 | One-way throttle high dead zone. | +| `onewaythrottle` | bool | false | Treat the throttle axis as forward-only. | + +--- + +## `[Cameraship Params]` + +Read by `CamerashipParams::LoadOptions` (`VehicleInterface.cpp`). Controls the automatic +spectator camera on cameraship pods. All distances are world units, all times are seconds, +all offsets are axis offsets applied to the camera or its look-at point. + +**Behaviour** + +| Key | Type | Effect | +|---|---|---| +| `AllowChatDisplay` | bool | Show chat on the cameraship view. | +| `AllowBOTs` | bool | Allow the camera to follow bots. | +| `DisplayCameraStates` | bool | Overlay the current camera state (debug aid). | +| `DegreesPerSec` | float | Camera orbit rate. Accepted range: strictly between -180 and 180. | + +**Timing** + +| Key | Effect | +|---|---| +| `TimeScoringInterval` | Interval between scoring cut-ins. | +| `TimeScoringDuration` | How long a scoring cut-in is held. | +| `TimeScoringAtEnd` | Scoring display duration at mission end. | +| `TimeIdleChange` | Idle time before the camera picks a new subject. | +| `TimeStandard` | Duration of the standard shot. | +| `TimeDeathWatching` | Duration of the death-watch shot. | +| `TimeDeathTransition` | Transition time into the death shot. | +| `TimeFixedTracking` | Duration of the fixed-tracking shot. | +| `TimeSideShot` | Duration of the side shot. | +| `TimeFrontShot` | Duration of the front shot. | +| `TimeOverShoulder` | Duration of the over-the-shoulder shot. | + +**Framing** + +| Key | Effect | +|---|---| +| `DistGroundLevel`, `DistGroundLevelShift` | Ground clearance and its shift. | +| `DistStandard`, `HeightStandard`, `HeightStandardLookAt` | Standard shot distance/height/look-at height. | +| `OffsDeathWatchingX/Y/Z` | Death-watch camera offsets. | +| `OffsFixedTrackingX/Y/Z` | Fixed-tracking camera offsets. | +| `DistSideShot`, `OffsSideShotX`, `OffsSideShotY`, `OffsSideShotHeadY` | Side shot framing. | +| `DistFrontShot`, `OffsFrontShotHeadY`, `OffsFrontShotCameraY` | Front shot framing. | +| `OffsOverShoulderHeroX/Y/Z`, `OffsOverShoulderEnemyY` | Over-the-shoulder framing. | + +--- + +## `[Battle Tech Misc]` + +Read by `CamerashipParams::LoadOptions` (`VehicleInterface.cpp`). **Several key names in +this section do not describe what they do** ? they appear to be deliberately obscured. The +mapping below is from the source and is the authoritative meaning. + +| Key | Type | Range / clamp | Actually sets | +|---|---|---|---| +| `SecsMissionReplay` | int | `<0` or `>=100` ¡æ 60 | `g_nSecsMissionReplay` ? mission replay duration. | +| `SecsMissionReport` | float | `<0` or `>=30` ¡æ 15 | `g_fSecsMissionReport` ? mission report screen duration. | +| `AutoPowerUpLevel` | float | clamped to 0.0?1.0 | `g_fAutoPowerUpLevel` ? auto power-up threshold. | +| `NeedFlushLevel` | float | clamped to 0.0?1.0 | `g_fNeedFlushLevel` ? heat level at which flush is advised. | +| `TimeMsgSender` | float | clamped to 0.1?5.0 | `g_fTimeMsgSender` ? how long a message sender name is shown. | +| `TimeList_Index` | int | `<0` ¡æ 0, `>=20` ¡æ 3 | `g_nTimeList_Index` ? selected entry in the lobby time-limit dropdown. | +| `TimeList_Value` | int | `<0` or `>=100` ¡æ 7 | `g_nTimeList_Value` ? lobby time limit in minutes. | +| `RuleBook` | int | 0 or 1 | ?? **`g_nMechVariant`** ? mech variant feature flag. Nothing to do with a rule book. | +| `DawnWar` | int | 0 or 1 | ?? **`g_nMechLabOp`** ? mechlab operation flag. | +| `BiggieSizeIt` | int | 0 or 1 | ?? **`g_nMechPodNum`** ? mech pod count flag, used by the console lobby to enable the larger roster (`USE_O_MORE_PODS`). | +| `CanYouHearTheFootSteps` | int | 0 or 1 | ?? **`g_nBlackMech`** ? black mech flag. | + +### ?? The shipped file misspells two of these + +The shipped `options.ini` contains: + +``` +secmissionreplay=30.0 +secmissionreport=15.0 +``` + +but the code reads **`SecsMissionReplay`** and **`SecsMissionReport`** (note the `s` in +`Secs`). The shipped keys therefore do nothing and the compiled defaults are used. If you +want these to take effect, correct the spelling. + +### Other keys in this section + +| Key | Read by | Notes | +|---|---|---| +| `MinShift` | `logreport.cpp` via `GetPrivateProfileInt`, default 0 | Torso shift minimum used by the match report. | +| `lrpt` | `logreport.cpp` via `GetPrivateProfileString`, **and written back** | Opaque report cache/registration value. Do not hand-edit. | + +--- + +## `[RookieMission]` + +Read by `CTCL_SetCDSP` (`MW4Shell.cpp`) at startup and exposed to the console lobby as +script globals. Defines what the arcade "Rookie Mission" / Default button loads. The whole +section is optional; omitting it uses the defaults below. + +| Key | Type | Default | Effect | +|---|---|---|---| +| `MissionName` | string | `ScarabStronghold - Attrition` | Mission loaded by default. | +| `GameType` | int | 2 | Game type index (2 = Attrition). | +| `TimeLimit` | int | -1 | Minutes. `-1` = use the server's current time setting (`TimeList_Value`). | +| `Visibility` | int | 0 | 0 clear, 1 light, 2 medium, 3 heavy. | +| `Weather` | int | 0 | 0 off, 1 rain. | +| `TimeOfDay` | int | 0 | 0 day, 1 night. | +| `Radar` | int | 0 | 0 novice, 1 off, 2 bars, 3 unlimited. | +| `HeatOn` | int | 0 | Heat management. | +| `FriendlyFire` | int | 0 | Percentage; 0 = off, 100 = full. | +| `SplashDamage` | int | 0 | Splash damage on/off. | +| `UnlimitedAmmo` | int | 1 | Unlimited ammo on/off. | +| `WeaponJam` | int | 0 | Weapon jamming on/off. | +| `AdvanceMode` | int | 0 | Advanced gyro on/off. | +| `ArmorMode` | int | 0 | Advanced armor on/off. | +| `NoReturn` | int | 0 | No respawn (respawn limit) on/off. | + +--- + +## `[battle tech print]` + +Read by **mw4print.exe**, not by the game. Uses `GetPrivateProfileInt`. All values are in +the printer's logical units; the defaults are in `recscore.cpp`. + +Page layout: `PaperMarginL`, `PaperMarginT`, `PaperMarginR`, `PaperMarginB`, +`TopImageHeight`, `GapColumn`, `ImageTitleHeight`, `GapTopImageTable`. + +Table layout: `CellMarginLR`, `CellMarginTV`, `TableTextHeight`, `GapFFA`, `GapTableMH`. + +Mission highlights: `MHHeight`, `MHTextHeight`, `GapMHText`. + +Text block: `TextMarginL`, `TextMarginT`, `TextMarginR`, `TextMarginB`, `TextHeight`, +`InfoTextHeight`. + +| Key | Notes | +|---|---| +| `BannerText` | **Legacy fallback only.** The banner is now read from `banner.txt` (first line) next to `mw4print.exe`; this key is only consulted if that file is missing or empty. It was moved because the game rewrites `options.ini` and dropped unknown keys. | + +--- + +## Dead settings + +These appear in the shipped `options.ini` but are **not read by any code** in this tree. +They are stock MechWarrior 4 leftovers. Editing them does nothing. + +### `[network options]` ? entire section is dead + +`connectiontype`, `connectionspeed`, `packetsize`, `defaultconnection`, `playericon`, +`teamicon`. + +Verified: no `GetEntry`/`GetPrivateProfile*` call anywhere references these key names. The +connection type actually used at runtime comes from `conDefault.connectiontype`, which is +populated from the multiplayer connection wizard, not from this file. + +### Individual dead keys + +| Key | Section | Why dead | +|---|---|---| +| `videodriverindex` | `[graphics options]` | Read call commented out; device forced to 0. | +| `huddamagemode` | `[special commands]` | Read call commented out; forced false. | +| `hudtargetdamagemode` | `[special commands]` | Read call commented out; forced false. | +| `secmissionreplay` | `[battle tech misc]` | Misspelled; code reads `SecsMissionReplay`. | +| `secmissionreport` | `[battle tech misc]` | Misspelled; code reads `SecsMissionReport`. | + +--- + +## Practical notes + +- **Back up `options.ini` before experimenting.** The game rewrites parts of it, and an + unknown key is silently ignored rather than reported, so a typo produces no diagnostic ? + just default behaviour. +- **Per-role files.** On a pod, the launcher copies `options-game.ini`, `options-cam.ini` + or `options-mr.ini` over `options.ini` depending on the machine's role. Edit the role file, + not the live one, or your change will be overwritten at next launch. +- **The 2016 release drop** shipped `hardwaremixing=true`, `BiggieSizeIt=1` and + `RuleBook=1`, where the current dev tree ships different values. If you are chasing a + behavioural difference against that release, compare these first. +- **Case.** Section and key names are matched case-insensitively by the notation reader, but + the spelling must otherwise be exact.