Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ARE Format (Area Static Blueprint)

The Area (.are) blueprint format operates as the static environmental foundation of any game module. It establishes the rigid, overarching properties of a level, orchestrating the terrain’s grass rendering definitions, dynamic sunlight and fog constraints, ambient audio scale, and the primary interior/exterior state configurations. It effectively constructs the structural ‘stage’ that dynamic entities (like creatures and doors) populate later on.

At a Glance

PropertyValue
Extension(s).are
Magic SignatureARE / V3.2
TypeArea Static Blueprint
Rust ReferenceView rakata_generics::Are in Rustdocs

Data Model Structure

Rakata maps an Area into the rakata_generics::Are struct. The struct’s Rustdocs document every field’s binary schema and GFF mapping; the table below is the high-level anatomy.

CategoryCoversRepresentative fields
Core Identity & StateThe area’s tag, localized name, and interior/exterior state flagsTag, Name, Flags, RestrictMode
Weather & TerrainRain, snow, and lightning chances, wind strength, and grass renderingChanceRain, WindPower, Grass_TexName
Lighting & FogSeparate sun and moon ambient/diffuse tints, fog ranges, and shadow limitsSunAmbientColor, MoonFogNear, ShadowOpacity
Stealth XPThe stealth-run XP pool an area can awardStealthXPMax, StealthXPCurrent, StealthXPLoss
Event HooksThe area-level event scriptsOnEnter, OnExit, OnHeartbeat, OnUserDefined
Map & RoomsMinimap projection data and the per-room sound listMap, Rooms
MinigameThe optional nested swoop or turret minigame configurationMiniGame

rakata-lint validates these fields against the engine constraints documented below.

Note

The ARE never gets re-saved – it gets re-copied. When a module is saved, the engine doesn’t parse or rewrite a single ARE field. CSWSModule::SaveModuleFinish hands the area’s static ARE resource to a generic “copy this resource type verbatim” helper, which looks up whatever ARE the resource manager currently has bound for that area and copies it byte-for-byte into the save archive. Every engine rule and clamping behaviour documented on this page (weather truncation, fog clamping, tag lowercasing, and so on) applies only to the initial parse when a module is freshly loaded, never to the save/resume cycle.

The area’s actual dynamic, session-changeable state – weather, stealth XP, map exploration, cameras, transition flags – lives entirely in the module’s GIT resource instead, not in a re-saved ARE. If you’re building a save-editing tool, patch the GIT; don’t expect to patch an ARE inside a save archive.

Engine Audits & Decompilation

The following documents the engine’s exact load sequence and field requirements for .are files mapped from swkotor.exe.

(Decompilation logic for this section was entirely audited and verified via native Ghidra pipeline against swkotor.exe, explicitly pulling from CSWSArea::LoadArea at 0x0050e190.)

The initial LoadArea dispatch branches out to parse the .are GFF, .lyt layout, .git instance tracking, and .pth bounds. The engine processes roughly 61 scalar fields, 4 scripts, 3 lists, and a nested minigame struct natively within the LoadAreaHeader subroutine.

Core Environmental Identity

Field CategoryEngine Property & Behavioral Quirk
IdentityName (LocString), Comments (String), ID (Int) -> Standard definition strings. All three default unconditionally when absent: Name to an empty localized string, Comments to an empty string, ID to 0. Creator_ID (Int) and Version (DWord) share the same unconditional-0 default.
IdentityTag (String) -> Lowercased on load (via CExoString::LowerCase). The only tag to behave this way!
ScriptsOnHeartbeat, OnUserDefined, ... -> CResRef script payloads.
State FlagsFlags (DWord) -> Bit 0 explicitly marks an Interior environment.
State FlagsRestrictMode (Byte) -> Hardcoded Event: Changing this to a non-zero value during gameplay forces CSWPartyTable::UnstealthParty.
IdentityCameraStyle (Int), DefaultEnvMap (ResRef), LoadScreenID (WORD) -> All three default unconditionally when absent: CameraStyle to 0, DefaultEnvMap to an empty resref, LoadScreenID to 0.

Note

Internal Weather Truncation If Flags (Bit 0) marks the area as an interior space, the engine zeros out all weather properties upon load, actively discarding any prior weather assignments.

Weather & Terrain Generation

FieldTypeEngine Evaluation
ChanceFogINTStored persistently as an integer. Defaults to 0 if missing, unconditional – the same read shape as ChanceRain/ChanceSnow/ChanceLightning/WindPower (all default 0 too), just not narrowed to a byte. When the area’s interior flag is set, ChanceFog gets zeroed a second time in the post-process pass alongside the other four, so an interior area’s fog chance is forced to 0 twice over rather than once.
ChanceRain, ChanceSnow, ChanceLightning, WindPowerINTWarning: The engine explicitly truncates these INT properties to 8-bit bytes at runtime. Values over 255 silently wrap around. All four default to 0 if missing, unconditional.
Grass_TexNameResRefIf empty or invalid, the engine forces a hard fallback to "grass".
AlphaTestFLOATDefaults to 0.2 (older tools commonly assume 0.0).
ModSpotCheck, ModListenCheckINTPerception-check modifiers. Both default to 0 if missing, unconditional.
Grass_Density, Grass_QuadSize, Grass_Prob_LL/LR/UL/URFLOATGrass rendering density and per-corner spawn probabilities. All six default to 0.0 if missing, unconditional, identical read shape across the set.

Area Lighting & Sun/Moon Tracking

KOTOR handles dynamic sunlight constraints separately between Sun and Moon.

Property GroupsTypeEngine Evaluation
Fog Ranges (MoonFogNear/Far, SunFogNear/Far)FLOATDefaults to an immense distance of 10000.0. The engine aggressively clamps values to be ≥0.0.
Tints (*AmbientColor, *DiffuseColor, *FogColor)DWORDProcessed seamlessly as standard DWORD color masks. All nine (MoonAmbientColor, MoonDiffuseColor, MoonFogColor, SunAmbientColor, SunDiffuseColor, SunFogColor, DynAmbientColor, Grass_Ambient, Grass_Diffuse) default to 0 if missing, unconditional, identical read shape across the set.
Environment Shadows (ShadowOpacity, *Shadows)BYTEBasic toggles and opacities orchestrating render limits. SunFogOn, SunShadows, MoonFogOn, MoonShadows default to 0/false if missing, unconditional. Unescapable, StealthXPEnabled, and StealthXPLoss are the one group in this table that genuinely carries over a constructed value rather than stamping a fresh literal – functionally identical to 0/false in practice, since LoadAreaHeader only ever runs once, immediately after construction, on a single call path.

Note

DayNightCycle doesn’t inherit its own constructed default. The area constructor sets day_night_cycle = 1 (cycle on) before any GFF read happens, but the read itself uses a hardcoded literal 0 as its fallback, not the constructed value – so an area missing DayNightCycle loads with the cycle forced off, silently overriding what the object was built with. This looks like it could be an engine oversight (the constructor’s own default is never actually reachable through this load path), but intent aside, the mechanism is unambiguous: absent means 0, not 1. IsNight and LightingScheme share the ordinary unconditional-0 pattern with no such mismatch.

NoRest, TransPending, TransPendNextID, and TransPendCurrID also carry over a constructed value (all 0/false), same practical outcome as an unconditional default given the single-call-path caveat above.

Note

Grass_Emissive and the entire Dirty* overlay set (12 fields) are confirmed dead in this K1 build, not merely unread. None of their field-name strings – Grass_Emissive, DirtyARGBOne/Two/Three, DirtySizeOne/Two/Three, DirtyFormulaOne/Two/DirtyFormulaThre, DirtyFuncOne/Two/Three – exist anywhere in swkotor.exe’s string table, verified against a binary-wide search that does find every neighboring Grass_* field. This is stronger than “the loader doesn’t consume it”: no code path in this build can even look these fields up. Treat them the same as DisableTransit/NoHangBack/PlayerOnly/PlayerVsPlayer below – toolset-only, invisible to K1’s engine.

Map Transitions & Saving states

Feature CategoryEngine Evaluation & Triggers
Minimap LogicGeographic vectors (MapResX, spatial coordinate structs like WorldPt1X) are only loaded if an actual Minimap TGA/TPC asset matching the level name exists on disk! Two further gates sit behind that one: the Map sub-struct must itself be present in the GFF, and MapResX’s own resolved value must be nonzero. MapResX reads with an unconditional literal default of 0 – so an absent MapResX isn’t just “zero,” it’s a genuine gating sentinel, since that same 0 is then tested directly and, if it holds, skips reading NorthAxis/MapPt*/WorldPt* entirely and falls through to a fully disabled map initialization with MapZoom fixed at 1.
Parsing TypeIf read, the engine parses MapPt along a dual-path logic checking if it is formally a FLOAT or INT type. An absent MapPt1X/MapPt1Y/MapPt2X/MapPt2Y resolves to 0 either way the type check goes – the INT branch reads a literal 0 directly, and the FLOAT branch’s own 0.0 default survives its floor-conversion step landing on 0 too. NorthAxis defaults to 0 if missing, unconditional. WorldPt1X/WorldPt1Y/WorldPt2X/WorldPt2Y are independent FLOAT reads, each defaulting to 0.0, unconditional, no gating between them.
Zoom BiasArea maps evaluate MapZoom to a default scaling scalar of 1, not 0!
Stealth Save-StatesThe stealth framework leverages the .are struct to snapshot .StealthXPMax and .StealthXPCurrent directly as DWORDs when parsing the layout.

Per-Room EnvAudio

Each entry in the Rooms list carries its own EnvAudio INT, read by LoadAreaHeader alongside that room’s AmbientScale, defaulting to 0 when absent. It’s a genuine per-room reverb/environment-audio zone selector, not inert data: CSWRoom::SetRoomEnvAudioProps matches each room by name and copies its EnvAudio/AmbientScale pair into the client-side audio setup that runs when an area loads. (The exact downstream reverb table or effect it selects wasn’t traced past that per-room handoff.)

Don’t confuse this with a same-named field on the GIT’s own area-level AreaProperties struct (see GIT’s AreaProperties.EnvAudio) – that one is a different struct entirely, and unlike this per-room field, it’s never read by anything.

Rooms and PartSounds: Remaining Field Defaults, and Two Confirmed-Dead Fields

No entry-level presence-chain abort exists in either list: the room loop and the nested PartSounds loop both process every index regardless of whether an individual GetListElement call succeeds, so a partially-specified entry is always kept, with each field defaulting independently.

RoomName defaults to an empty string if missing, unconditional – the same name SetRoomEnvAudioProps matches against a live room, so an empty RoomName simply never matches anything. AmbientScale defaults to 0.0, unconditional and independently gated from EnvAudio’s own default (the two are read back-to-back but neither gates the other). ForceRating and DisableWeather are confirmed dead the same decisive way as Grass_Emissive/Dirty* above: neither field-name string exists anywhere in swkotor.exe, confirmed by a binary-wide search. Toolset-only in this build.

PartSounds entries: Looping defaults to 0/false, unconditional. ModelPart defaults to an empty string, unconditional. OmenEvent is read as a CExoString, not the INT its name might suggest – confirmed by its sole cross-reference in the binary, landing on a ReadFieldCExoString call inside this exact loop – and defaults to an empty string, unconditional. Sound defaults to an empty resref, unconditional; the (possibly-default) result is appended to the room’s sound list regardless of whether the file actually supplied one.

Expansion_List Defaults

Expansion_Name defaults to an empty localized string if missing, unconditional. Expansion_ID defaults to 0, unconditional. The list itself is only processed at all if the Expansion_List field is present in the GFF – that’s a list-level gate, not a per-entry one – but once inside, no entry is ever dropped for a missing field, same as Rooms/PartSounds above.

The Minigame Struct

Read via CSWMiniGame::Load (0x006723d0). If a minigame context triggers, the .are reads the nested Type (DWORD mapping 1=Swoop, 2=Turret). It injects highly specialized float properties modifying basic terrain speeds:

FieldInjection Default / Constraint
LateralAccelDefaults safely to 60.0.
MovementPerSecScales to 6.0 (Swoops), 90.0 (Turrets), or 0.0 otherwise!
Bump_PlaneBounds are heavily clamped to 0..3.
Nested ArraysThe struct natively requires sub-struct Player arrays (Models, Camera, Axes) and Enemy/Obstacles lists to operate properly.

The Player struct, each Enemies list entry, and each Obstacles list entry are three genuinely different shapes, not siblings sharing one flat layout – Player and Enemies share a common vehicle base (hitpoints, a nested weapon Gun_Banks list, Scripts, Sounds) with Player adding its own movement/track-boundary fields on top, while Obstacles are a much lighter leaf with only a Scripts struct. See the Swoop & Turret Minigame Deep Dive for the full field-by-field breakdown and absent-field defaults.


Implemented Linter Rules (Rakata-Lint)

Phase 1 (intra-resource, no context)

Implemented under rakata_lint::rules::are.

  1. ARE-001 (Context Discards): Warns when interior areas (Flags & 1) carry non-zero ChanceRain, ChanceSnow, ChanceLightning, or WindPower; the engine discards weather for interiors.
  2. ARE-002 (Weather Truncation): Warns when ChanceRain, ChanceSnow, ChanceLightning, or WindPower exceed 255; the engine truncates these to bytes at runtime.
  3. ARE-003 (Fog Clamping): Warns when MoonFogNear/Far or SunFogNear/Far are negative; the engine clamps fog distances to >= 0.0.
  4. ARE-004 (Tag Lowercasing): Warns when Tag contains uppercase characters; the engine lowercases area tags on load.
  5. ARE-005 (Toolset Fields): Informs when DisableTransit, NoHangBack, PlayerOnly, or PlayerVsPlayer are set; never read by the K1 engine.

Phase 2 (resource existence, requires LintContext)

Implemented under rakata_lint::rules::are_range.

  1. ARE-006 (Resref Existence): Warns when any of OnEnter, OnExit, OnHeartbeat, or OnUserDefined (.ncs) does not resolve, or when any Rooms[i].PartSounds[j].Sound (.wav) does not resolve in the configured resource sources.

Pending

  • Grass Texture Fallback: Informs when Grass_TexName is empty; the engine treats this as the literal string "grass".
  • Texture / MiniGame Resref Existence: DefaultEnvMap, Grass_TexName, and the nested MiniGame model / track / music graph – ResourceTypeCode mapping for engine-specific texture and model packs is still being audited.