Introduction

Some mistakes in a level don't show up until you play it: the game crashes the moment a nineteenth character spawns, a sound bank silently never loads, a reflection draws black shapes, textures go blocky. Each of the checks in the Level Check is one of those, found the hard way while building big custom levels, so that you find out in the editor instead of after an hour in game.

It runs by itself at the end of every Save and Build, and you can run it at any time from File → Level Check... (just under Save and Build Level). It only reads the level: nothing changes until you press a Fix button.

If a build finds anything you haven't dismissed, the Level Check window opens on what it found. If any of it will crash the game, a message box says so as well, so you see it before the game is launched.

The Level Check Window

The window lists what the check found in the open level, most serious first:

  • CRASH (in red): the game will crash, or can.
  • Won't work: something in the level silently doesn't work in game.
  • Check: likely to go wrong, worth a look.

Select a finding to read what happens in game, why, and what to do about it, with the list of entities (or textures, or sound banks) it comes from. Go To (or double-clicking the finding) opens the composite of the first one and selects the entity. Check Now runs the check again on the level as it is now, saved or not.

The line under the buttons sums up the level: how many characters it can have alive at once out of the game's 18, and roughly how much texture memory it needs at once out of the game's texture heap.

The window stays open while you work, so you can fix things in the editor and press Check Now to see them go.

Fixing a Finding

Where the fix is mechanical, the finding has a Fix button saying what it does - "Link a VariableString to it", "Tick unique_material on 4 emitters", "Remove the dead tracks". A fix is an ordinary edit to the level's script: it appears in the undo history as Level Check: ..., Undo puts it back, and a Save and Build takes it into the game.

One fix is different: Raise the texture heap to 512 MB changes the game's own DATA\ENGINE_SETTINGS.XML, not the level. That is global - every level uses it, and a mod exported with its config changes carries it to everyone who plays it - so OpenCAGE asks first. Reset Configs (Graphics) puts the shipped 300 MB back.

Dismissing a Finding

Every check is a rule of thumb, and some can't tell what your script does at runtime. If a finding doesn't apply to your level - your script never has all of those characters alive at once, say - press Dismiss. A Save and Build won't mention it again for that level. Tick Show dismissed to see dismissed findings, and Bring Back to undo a dismissal.

Dismissals are kept on your PC (in OpenCAGE Level Check.json beside OpenCAGE's settings), per game install and level. They are never written into the level, so they don't travel with a mod.

What It Checks

CheckWhat goes wrong in gameFix
Character capThe level places more than 17 Characters: the game holds 18 (the player and corpses included) and crashes on the 19th.-
Alien without ventsAn alien that can spawn with no PathfindingAlienBackstageNode crashes the game the first time it retreats. File → Alien Vents adds them (see Alien Vents).Add vents
Alien vents it can't useVents you placed that the alien can't travel between: a network with fewer than 3 (no backstage is baked over them), or all in a line (a narrow strip, not a sheet); all within 10 m of each other (a flamed alien has nowhere to break out to); not in the navmesh yet (Save and Build with the navmesh baker); or off the navmesh, where the alien can't walk to them (see Alien Vents).Add vents
Animation memoryThe animation sets the level's characters use don't fit the game's resident animation memory: those characters never spawn.Allow larger animation memory (global)
NPC with no display modelAn NPC's display_model names a DisplayModel composite the level doesn't have, and the level has no character accessory set for it (what custom characters such as retail's 'NPC' are built from): the game can't make the character, and it never appears.-
Texture memoryThe textures that stream at once don't fit the texture heap: some draw blocky.Raise the heap to 512 MB (global)
Sound bank never loadsA SoundLoadBank with the bank only typed on its sound_bank pin loads nothing.Link a VariableString
Sound bank memoryThe banks the level loads at the start pass the ~20.5 MB pool: the next bank silently doesn't load.-
Black planar reflectionsWater or a mirror reflects geometry, but no light is flagged include_in_planar_reflections: the reflection draws black.Flag the lights
SimpleWater: no model, or culled far awayA SimpleWater with no model (one made with Add Entity) crashes the game as the level loads. One 60 m or more across without disable_size_culling (Don't cull when far away) vanishes in the distance.Copy another water's model; tick disable_size_culling
Emitters sharing an idA duplicated composite placed beside its original: emitters with one id corrupt each other (hard white quads).-
Emitters sharing a materialEmitters copied from one emitter into your own composites, coloured differently, share one material: their colours bleed.Tick unique_material
Animation converges to no markerA CMD_PlayAnimation with convergence on and no Marker: an error-log line, and the clip plays where the character stands.Turn convergence off
Fake sense with no positionAn NPC_FakeSense with FakePosition not linked: the NPC never reacts.-
Random delay that never variesRandomFloat.Result linked to LogicDelay.delay doesn't vary the delay: use retail's RandomDelay.-
Dead animation tracksCAGEAnimation tracks bound to deleted entities: they key nothing, and the animation grows with every delete and re-create.Remove the dead tracks
Animation inside a keyed instanceA CAGEAnimation (or RotateInTime / RotateAtSpeed) inside a composite instance another CAGEAnimation keys stays frozen at time 0.-
Instances of missing compositesAn entity instances a composite the level doesn't have (one an import didn't bring, or one that was deleted): the game skips it, so its links and what it stood for never happen. Import the composite again under the same ID to bring it back (see Missing Composites).-
Outside the physics worldTriggers, physics bodies or characters more than 512 m from the origin are outside the game's Havok world: no collision, no trigger.-
Physics props that won't moveA moving physics prop placed straight into the level hangs asleep where it is put. One an AssetSpawner spawns needs its ModelReferences keyed by its bodies' names, each with collision of its own, or the game logs "Unable to create physics system" and it never appears (see Physics Props That Move).Spawn it, as retail's are; key the bodies by name
NPC voices through wallsNPCs but no SoundEnvironmentMarker: the sound bake builds no sound network, so voices carry through walls.-
Off the navmeshThe player's start is off the level's navmesh (as last built): the game moves the player onto the nearest navmesh, a ceiling or roof say. Without a NavMeshReachabilitySeedPoint the navmesh bake keeps only the largest walkable area, so add one on each floor the player and NPCs use.-
Pins the game cannot findComposite pins saved in an order the game can't search (by an older OpenCAGE): links through them don't work. Saving the level fixes it.Save the level

The Character Cap

Alien: Isolation keeps every character - the player, each NPC and alien, and each corpse until it is despawned - in one pool of 18. A spawn only checks that a slot is free without reserving it, so a nineteenth character, or two spawns racing for the last slot, crashes the game. Keep the total at 17 or under at every moment.

The check counts the Characters your level places: each one is a character the script can have alive at once (respawning the same Character reuses its slot once its corpse is despawned). It can't see what your script does at runtime - an intro actor despawned before the player spawns, one of two aliens picked at random - so it says the level can reach that many, lists where each comes from, and leaves it to you to dismiss it if your script keeps them apart. Characters spawning together as the level starts (spawn_on_reset) are counted on their own: more than 18 of those always crashes.

The game's own levels place more than 18 Characters in places and keep them apart by mission flow, so on a retail level the check only speaks up when you've added Characters on top of what it shipped with.

In game, the Debug Menu's character list shows the characters alive (and dead) at that moment.

Texture Memory

The game streams each drawn texture's full-size version into a fixed amount of memory, the Streaming Texture Heap Size in DATA\ENGINE_SETTINGS.XML (300 MB as shipped). Whatever doesn't fit is drawn from its small 128 px copy, which looks blurry and blocky. A zone's textures are released when the zone unloads; a level with one zone never releases any, so all of its textures have to fit at once.

The check adds up the textures the level's models draw in each zone, plus those drawn everywhere and those no placed model draws (characters, weapons and effects, counted everywhere to be safe), as the game counts them, and compares the biggest zone with the heap. It lists the biggest textures, which are usually where to start: import big texture sets with Compact textures (below), use smaller images, or split the level into zones. Raising the heap to 512 MB is the last resort, and the most it can go: the heap is part of the 32-bit game's own memory, and 768 MB took it past 2 GB.

Compact textures is a tick box in the model import window. It encodes the colour, specular, dirt and AO maps as DXT1 (DXT5 where the base colour's alpha cuts the surface out) instead of BC7: half the memory, as most of the game's own world textures are. Normal maps are unchanged. Under it, the window says how much texture memory the import adds and how full the level's heap would be.

Texture streaming slots (texture_slots) checks the number of streamed textures, not their bytes. The game holds about 1,020 streamed textures at full size at once, however big the heap is, because the heap has only 1,024 block handles and the gaps between textures use them too. The check counts the streamed textures the level can ask for at once: the busiest zone plus the zones linked to it and what is drawn everywhere, or every drawable streamed texture in a level with one zone. Past 1,000 it is reported as broken, and past 850 as a warning, since the player, weapons and effects take slots from the global textures too. The fix keeps every streamed texture of 256 px or less loaded, which frees their slots at the memory cost it shows. See Keep Loaded & Texture Streaming.

Script Checks

The checks that look for scripting mistakes (convergence with no marker, a fake sense with no position, random delays, dead animation tracks, animations inside keyed instances, instances of missing composites, emitters sharing a material) look only at the composites you made, brought in or changed with OpenCAGE, which the level remembers. The game's own composites play as they shipped, even where they do some of these things themselves.

The level-wide checks (the character cap, texture and sound memory, reflections, the physics world, the sound network, the navmesh, pin order, sound banks that never load, emitter ids) look at everything the level places.

Retail Levels

Run on the game's own levels as they ship, the Level Check reports nothing, except where the game itself does what a check looks for: on ENG_TOWPLATFORM, a SoundLoadBank in the M33 mission has its EVA_SUIT bank typed on it and nothing else loads that bank, so the game never loads it there either. Dismiss it if you are working on that level. The game's own levels place far more Characters than 18 in places, and keep them apart by mission flow, so the character cap compares a retail level with what it shipped with (see The Character Cap).

For AI Assistants

An AI assistant connected to OpenCAGE runs the same check with check_level (all checks, or those named), which also returns the figures behind them: characters, texture memory per zone with the biggest textures, sound banks, reflections. fix_level_check applies a finding's fix (a global one only with global: true), or dismisses or brings back a finding. set_texture_heap reads or sets the heap. save_level with a build lists what the check found as level_warnings.

Related Docs

  • Save and Build - the build the Level Check runs after
  • Model Editor - importing models, and Compact textures
  • Debug Menu - characters, texture streaming and sound in game
  • Sound Events - sound banks and the level's sound package
  • Configs - the graphics settings, and Reset Configs