Alone in the Dark (1-3)/Game Mechanics and Glitches

From SDA Knowledge Base

Jump to: navigation, search

Contents

ABOUT THE GUIDE

  • This guide is mainly about AitD 1 but it acts as the main source of information on all the games that came out on this engine.
  • In case it's not clear what the basic structure of these notes is, here is how it works. Wherever the indentation increases, this means whatever is written there is subordinate to (relates to) the last less indented paragraph. This is exactly the same as how higher and lower -level headings work.
    • I.e. this text is subordinate to what is written above.
    • This text is also subordinate to the first line but doesn't necessarily have a direct connection with other lines of the same level of indentation, such as the line directly above.
      • This one is subordinate to the line above.
    • This is subordinate to the first line. Etc.
  • Most of the information here is derived from testing the CD-ROM version (GOG). If you want to study AitD 1 more, you should consider using the floppy version, which has not been tested nearly as thoroughly, though the differences between versions have been looked into to some extent.
  • Mostly Emily was used in testing, there being no known reasons to go with Edward in a speedrun. Any unlabeled data is therefore about Emily, with Edward having different movement offsets and keyframe lengths, which might affect time lost or saved with a trick as well.
  • The division into topics is sometimes quite arbitrary. Feel free to discuss the format on the discussion page (top left) so future guides may be better-organized. That having been said, to get well-enough versed in the game's/games' workings to be able to build on the theory, you will have to cut through a very decent portion of the guide regardless of its exact structure.
  • Inevitably some points are made more than once so edits should ideally happen in both, and even more vague and more detailed bullets may be given on the same topics. The notes were generally made from a segmented run's point of view, being able to abuse saving and loading at will with the small natural penalty segmented runs incur for this. Some save/load tricks may sound too optimistic though, and this is because I (LotBlind) made the false assumption the time in the menu before reaching the save menu would not be counted.
  • Because of some issues with file conversions, I may have been working with files that were at 25 FPS instead of 60 FPS (what you should use if you want to count frames) sometimes when counting frames between events happening. Thus there may be a small amount of incorrect frame counts given in the guide. Please report this and any other mistakes.
  • Feel free to contact the primary author of the guide (LotBlind) or tigrou (code-related and more technical questions) through the SDA forums for consultancy during any serious [speedrunning] endeavours. Also the guide is in a wiki format for a reason: it may be edited by anyone. Use the discussion page for relevant questions.


GLOSSARY

Game-internal terms

Game-internal terms Meaning
object the entities in the OBJETS.ITD file that have properties like BODY, LIFE, FOUNDLIFE etc., but in the guide this may have been used in the everyday sense as well
actor refers to any object that has been moved into the active actors array in the RAM, but may have been confused with objects in some places
FRAME the game uses this to refer to keyframes, confusingly; there is no game-internal term for frames; the terms "keyframe" and "frame" are used in the guide
TRACK pre-scripted movement, defined in LISTTRAK
BODY 3D models defined in the LISTBODY and LISTBOD2 files
ANIM animations defined in the LISTANIM and LISTANI2 files
LIFE the game's scripts are called LIFEs. The term "script" has been used sometimes. Stored in a file called LISTLIFE.
LIFE_MODE called LIFEMODE in the guide, determines if an actor is active at a given time based on focused actor location
TRACK_MODE describes the way in which an actor can move around, TRACKMODE in the guide; the possible values are: 0 can't move; 1 manual;

2 follow actor X; 3 follow track X; this guide uses verbal names for them: NONE, MANUAL, FOLLOW and TRACK.

animActionType an actor field, ACTIONTYPE in the guide
HIT This is all of: a possible actor ACTIONTYPE field value; the name of the command that causes an actor to enter the PRE_HIT and HIT ACTIONTYPEs (in the LIFEs); and an actor field that stores the ID of the actor that has just been HIT by it.
IN_HAND called INHAND in the guide, the item currently equipped, e.g. a weapon
VAR/CVAR the variables and constant variables the game writes into during gameplay
ExRy floor and room, as given in the game files and shown in the Room Viewer
TYPE Sets the flags for the actor in question. Replaced with the word "FLAGS" by LIFEDISA.
Additional terms Meaning
focused actor actor that was set as the CAMERA_TARGET, usually the PC
PC player character
EM/ED Emily/Edward
"having control" VAR 0 being 1
INV/ALLOW_INVENTORY FlagInventory = inventory being accessible
OOB [to go] out-of-bounds (verb, noun, adjective...)
IB in-bounds
desync by default means desync of the ZV and ROOM Y-coordinates from their normal difference = vertical desync
kf keyframe inside an ANIM
hitbox the box that is used to check whether a melee attack is connecting with another actor, displayed in red in the RV
bbox bounding box (also known as "zv"): the box used for collision checks with other actors and colliders, displayed in green (or white) in the RV
transition the PC hitting a floor/transition trigger to move onto another floor, accompanied by moving into LIFE 550
s/l save and reload the same file
slt save/load turn
ftt deprecated term "first-time turn" referring to an incomplete slt
Cx camera with ID x
ExCy camera on floor x with ID y
RV/Viewer Room Viewer
MV Memory Viewer
CV Cache Viewer
FitD Free in the Dark; a very accurate source port of Alone in the Dark.
HARD_COL abbreviation for HARD_COLLIDER
vertices etc. If it's not clear, "vertices" and "vortices" are the plural forms of "vertex" (a point) and "vortex" (what the dancers turn into). Also "indices" is the plural of "index" and "axes" the plural of "axis".
virtual position This refers to an actor's ROOM coordinates plus their MODX/MODY/MODZ, which is a position that the game considers the actor to be at for some but not all calculations. It's possible it's only used for collider collisions and if so that term could be used instead.
anchor point This term has been used for the ROOM position without MODs added in.
collisions "Having collisions on" means an actor has their COL flag 0001 set to 1. This just means that the actor will be ejected from colliders and other actors. Even when the flag is 0 (the actor is "noclipping"), the COL array and COL_BY are set normally.
T1 / T2 Timer 1 / Timer 2.
  • LIFE, ANIM etc. numbers are given in the guide with a space in-between for clarity (ANIM 55, LIFE 112) even though in the scripts they're always spelled together. Also in the scripts CVAR is spelled C_VAR.
  • All of the VARs, CVARs, ANIMs, BODYs etc. were named by LotBlind and those names are, as such, unofficial. All the names were written into a file called vars.txt. The game doesn't come with human-readable names for most things, except for items that you pick up which have the names shown in-game. Even those are only referred to by their IDs in the code if vars.txt was not present during decompiling LISTLIFE.ITD.
  • Some variables and such may be referred to with different names/typography since during the project, tigrou may have renamed game-internal fields to be easier to understand, and in some cases, the original terminology was returned to afterwards. An attempt was made to conformize everything. Then again, the information is often too technical to get a perfect grasp of through this guide alone anyway.
  • Keyboard inputs should be in all-caps. In most cases it doesn't matter which key of the many mapped to a given command you press.

META

USEFUL FILES

  • If you aren't sure where to find a particular file or program, look here.
  • The LIFEDISA, TRACKDISA, Memory Viewer, Cache Viewer and VARS Viewer are all packaged into AITD Tools. The page linked has some instructions.


DOSBOX / EMULATORS

  • While runs can only officially be recorded on whatever GOG or Steam copies are packaged with (unless the SDA rules have changed since writing this), other emulators may offer features beneficial to testing.
  • Allowed settings: You're not allowed to change the default GOG CPU cycles setting of 11.000 during runs. During testing it can be useful to reduce the cycles since it makes the timers advance in greater steps and makes the RAM as shown by the RV display some data for longer. Cheat Engine can be used to find, read, change and freeze memory values, which is useful for a variety of testing. Cheat Engine also can be used to study fleeting events in more details by using the "speedhack" option and adjusting the game speed to e.g. 0.1x. This is better than slowing the game down in DOSBox since doing that also alters its behavior.
    • The GOG default cputype=auto setting should be used for speedruns.
    • The output method (called "output" in the DOSBox config) should be set to whatever is fastest. For LotBlind, the fastest was Surface/OpenGL[Lnb]. Other methods caused slow-down during screen shaking. Ddraw was perhaps slightly faster when there was no shaking. Please report if this is different for you.
    • If you experience slow-down during shaking, this should be understood as cheating since it technically allows you to be more precise with your inputs. Doubly so if you were using DOSBox native recording since the slow-down doesn't show on such footage and so what you're seeing and what the viewer is seeing are different. The game is clearly not supposed to slow down when it's happening, it's an emulation issue. On real hardware, the shaking was implemented by changing the start address of the VGA buffer, which only requires writing a few bytes.
  • If DOSBox has been left in the background while you're doing something else, it sometimes fails to refresh the window when you refocus it. Just minimize and un-minimize it until it shows right.
  • ALT-F12 in DOSBox fast-forwards the game. Holding ALT but pressing F12 more times causes the game to speed up even more. This is not supposed to affect its functioning.
  • DOSBox SVN-Daum has save states and other features that make it superior to DOSBox 1.4. It was not utilized at all.
  • The state of PCem as a TAS-ready emulator should be kept an eye on. It seems the most likely candidate that could actually allow to TAS AitD for TASVideos.org.

DOSBOX DEBUGGER

  • There is a debugger built into DOSBox that allows you to monitor file-reading, DMA activity, dumping the RAM etc. A custom separate build of it is available here. It might work better than the official build which can sometimes miss breakpoints or go two frames forward instead of one. It can be set up with breakpoints in the following manner:
    • Press alt + pause key. It should stop game execution.
    • Enter e.g. one of the following commands in debugger console, then press enter:
bp 04CF:0080      //set breakpoint at start of game loop
bp 0664:082c      //breakpoint after an actor has been initialized and is visible in RV
bp 105A:0033 //breakpoint right before cache entry is deleted
    • Press F5 to resume execution.
    • The game will now stop automatically when a breakpoint is hit. Press F5 to resume execution again.
    • Other useful commands :
bplist            //list all breakpoints
bpdel [nr] / *    //remove a breakpoint or all breakpoints (eg: BPDEL 0 or BPDEL *)

ROOM VIEWER

  • The Room Viewer (Viewer, RV) is a custom-made RAM watch utility coded by tigrou for Alone in the Dark series games that was gradually expanded on over the course of the segmented run project. Its functionalities are the widest in scope when used in conjunction with AitD 1 but it supports AitD 2/3/Jack and Time Gate as well. It comes with an attached readme for instructions but here are some additional notes as well.
  • The RV has a built-in function (hold SHIFT and hit the "1" and "2" keys) to quickly test the workings of the two timers by winding them back 5 seconds at a time. The game state that results, however, is not the same one it would have been had the timer been reset by rebooting the game, and so this is definitely not allowed between segments during actual speedrunning.
  • When recording runs and other material for AITD, recording your key presses will help analyze what you did. This can be done using NohBoard with THIS PRESET.

LINK: EDIT IN NOHBOARD AND THE PRESET FILE

    • You might also find this Windows .bat file handy:
      • LINK LATEST VERSION OF THE .BAT
    • Just create a .bat file in the same folder as the .exes and that should work. Change the width and height to match your screen. The NUMLOCK reminder is there because NohBoard needs it to be on to see the keypad, but feel free to delete it. Also to make NohBoard see the inputs in the first place, you have to run it as administrator, which is a setting you can change through right-clicking and going to the compatibility tab.
    • "-speedrun" makes the Room Viewer use default settings that make recording speedruns etc. more convenient and consistent: for example always making sure the extra info is shown on the bottom right. It may also cause some data to be displayed that wouldn't be otherwise.
    • In order to avoid having to give every program permission to run separately, you can also just make a shortcut to the .bat file, and choose the "run as administrator" option for it.
  • The Viewer's second mode is the Model Viewer. It shows not only the BODYs but also can play ANIMs on top of them (indiscriminately). It also gives you the keyframe lengths and offsets, and default bbox sizes. Pressing B cycles between the three bbox modes based on the default of the BODY in question.
    • The Model Viewer shows BODYs as they're given in the LISTBODY/LISTBOD2 files. The way they're presented in the game could be different based on angles set in OBJETS.ITD. Similarly the default bboxes aren't always used in-game.
  • The warp command, even when just changing the actor's angle, can desync its coordinates horizontally. If it's happened to the PC, it should be reflected on by a new red bbox being drawn where the ROOM coordinates are (always drawn with any kind of PC ROOM/ZV desync). To avoid this, you should pause the game or make sure the actor isn't moving when you warp it.
    • You can only get some actors (most enemies and all pushables) to move between rooms, not e.g. any room script actors.
    • Warping uses the actor's slot, not its ID, so if the actors present in the scene change, you will end up warping whatever has taken the old actor's slot.
    • Warping thrown objects around often causes a softlock that may be resolved by warping them to specific locations. (See under "THROWING".)
  • The Room Viewer refreshes its memory (reads from the game process) at the same speed as your display refresh. The RV memory read operation is not atomic (it's not guaranteed to be done in a single go). Additionally, it is not synchronized with the game loop. It might read data while the game has already entered game loop and is updating entities. The consequence is that you might see some state in the RV that never existed in the game, though this effect is negligible. During a frame, most of the time is spent in renderer and most entities/variables are not updated at that time.
  • There may be very small (e.g. one-frame) delays before a changed memory value shows in the Viewer. This may explain why it sometimes seems things that are supposed to happen on the same frame do not. You shouldn't always read too much into discrepancies of this size. (Note that ANIMs actually are set one frame later than the instruction has been given).
    • The same goes for any keyboard visualizers you're using. I've seen the Room Viewer reacting to a key press before NohBoard did (which means the game reacted even quicker).
  • There's information that can only be displayed in the RV for the CD-ROM release of AitD 1. This includes everything to do with timers.
  • The delay/lag meter is set to only time instances of lag of over 100ms long (i.e. major loading lag). The total delay counter, on the other hand, adds up everything, though it doesn't seem entirely consistent. Both work on the sole basis of when the game's graphics have and have not been updated. Thus neither can detect what happens before and after the FRONTBUFFER has been updated during a room etc. transition as part of the same loading process.
  • The Room Viewer gives you back the player character's information automatically if the currently selected thing is clicked on again to deselect it. This is so speedruns etc. recordings are less likely to drop that information. However, if you never deselect that other entity before it's disappeared from memory (as when moving between floors), it will not revert to the player. This is because of another feature that's designed to make it easier to monitor some actor from the moment it's been loaded into memory.
  • The frames counter as shown in the RV is not reset when the game is reloaded. The field only exists in the RV, not in RAM.
    • Just in general, the RV does not directly know if the game has been reloaded.
  • The actor DRAW flag is not shown on purpose as it's sometimes turning on and off very fast making things hard to read.
  • SPEED is shown for all actors that have a TRACKMODE other than NONE.
  • Timers are shown frozen when their "frozen/saved" flag is set even though in the game's memory, they keep ticking. A second timer is shown for each showing how long it's been frozen for.
  • There is another branch of the RV called "highlighting" that highlights changes in all or most of the actor fields etc. shown in its window like they are in the VARs Viewer. This was deemed to make the screen too busy, and thus removed from the master.
  • There is also an "experimental" build with in-progress or not well-functioning functionalities.
  • There are a few kinds of artifacts shown in the RV that look like the game glitches for a frame in a way that you can't see in the game's own window. Most of these are explained by the RV reading memory more frequently than the game updates it, thus showing mid-frame states as well as completed frame ones. The common trait is that you can't ever pause the game in the glitched state, which is how you can tell as well.
  • Remember that you can always use multiple instances of the RV to monitor multiple actors at the same time, which could save effort replicating a test multiple times.
  • The RV shows actors relative to the rooms they're in, and actors that wrap around the edge of the world are shown as occupying space on the side that their center is located at. Such actors still largely behave as if they existed on both sides. This can be seen the clearest around E5R10 and adjacent rooms where some bridge segments can wrap around the world but you can still run from one end to the other normally.
  • Whenever an actor's bbox has dimensions less than 100 units, it gets displayed as 100 units to ensure that very small actors are visible. This affects at least the hand mirrors.

VARS VIEWER

  • This program was separated from the Room Viewer late into its development to save CPU usage, and it incorporates the Cache Viewer and Actors Viewer / Objects Viewer.
  • It's divided into multiple views. Pressing the F1-F4 keys will activate views.
  • The refresh speed is 60 times per second.
  • It's recommended to use Windows Terminal for improved experience. It's installed by default with Windows 11.
  • If you get garbled graphics, click on top left console window icon > properties > "layout" tab > un-check "Wrap text output on resize" option. If enabled, any resize of the window will produce visual garbage.
    • You can change the font size in the "font" tab.

VARS

It displays the VARs and CVARs which can also be overwritten using this tool.

  • The [F]reeze, [S]ave state and [C]ompare can be used to change the display. Compare highlights values that have changed compared to the save state (the save state values are shown).

CACHE

  • Displays the contents of the cache in detail. The data shown is as follows:
    • Bytes Used – Bytes Maximum ... Number of Entries – Maximum Number of Entries
    • Then for each entry: ID – Size – Timestamp
  • You can forcibly empty the cache by hitting F5. Pause the game first to avoid a possible crash.
  • Hit SPACE to switch between displaying item timestamps and names (taken from vars.txt).
  • Hit 'S' to cycle between displaying static, unsorted lists where new entries are added to the bottom, lists that get sorted every frame according to the age of the items, and lists that show the order in which items will get removed (accounting for the LRU bug).

ACTORS/OBJECTS

Display all fields of all actors and objects (which are actors not currently instantiated).

  • Hit SPACE to switch between active only/all actors.
  • Hit TAB to expand/shrink columns.
  • Hit 'F' to freeze display.

LIFEDISA/TRACKDISA

  • The "DISAs" are the tools used to unpack the game's LIFE and TRACK scripts. Usage instructions are given here.
  • The files ENGLISH.TXT and OBJETS.ITD allow the tool to convert objects' hard-coded IDs inside scripts to human-readable names. In addition, the file called vars.txt can optionally be placed in the same folder before running LifeDISA to replace almost all IDs with names as well, including all the ANIMs, LIFEs and VARs. This will make the output file more cluttered but a lot easier to understand.
  • After you have obtained output.txt, opening it with something like NotePad++ choosing "Assembly" as the language is recommended to make it even easier to read.
    • LifeDISA prints the byte data of the scripts together with the humanly readable version. This byte data can be searched for in a RAM editor to edit in real time for testing.
    • Using this style in NotePad++ with a dark theme like Bespin causes the byte data to be in gray.
  • The output file is by default a cleaned-up version of what's in the PAKs. Use the -raw -parameter to output scripts without any optimization.
  • TrackDISA works the same, except it extracts the file called LISTTRAK.PAK that contains the game's TRACK scripts. vars.txt also helps make TrackDISA's output file easier to read.

MEMORY VIEWER

  • The Memory Viewer reads the first 640K of DOSBox emulated machine RAM (and a 64K buffer allocated in extended memory using EMS) and displays it using the in-game palette. Launch the program before or after AitD and it should recognize when it's running. Give it admin privileges if DOSBox also has those.
    • The exact positions of items in the memory are dynamically allocated and so might not be exactly the same from time to time (or after starting a new game) but they're always stored in the same order. In practice, if you launch the game the same way, the DOSBox RAM should also be identical.
  • The MV also refreshes itself at the same speed as your monitor.
  • Hitting SPACE shows the memory blocks color-coded like this:
red: game executable
blue: block dynamically allocated by AITD
yellow: block dynamically allocated by another program
green: free block

PAK EXTRACT

Allow to extract files from .PAK archives. There is two ways of using it :

  • put one or more PAK files into a folder named GAMEDATA. Run PAKExtract. Each archive will be extracted to a separate folder.
  • drag and drop a PAK file into PAKExtract executable.

FREE IN THE DARK / FITD HACK

  • Free in the Dark is a very accurate source port. It has been made using reverse engineering and the help of a disassembler like IDA. It is part of the reason so much is known about various aspects of how AitD works.
  • There is a hacked .exe for the "Free in the Dark" port of Alone in the Dark that makes it easier to study the 2D wraparound specifically (see "2D WRAPAROUND"). It is capable of rendering 32000 units of the game world in a given resolution (e.g. 1080p) while the camera still uses the same 320x200 projection settings.
    • Just copy the files in your INDARK folder into the port's folder and you are good to go. If asked to overwrite SDL.dll or SDL_mixer.dll files, say no.
    • The .exe, like FitD in general, is not compatible with the Room Viewer.
  • FitD keys: T - top camera, C - no hard clipping, B - 3D mode, N - 2D mode.
  • Hack keys: +, -, * and /.
    • You can zoom with O and P, at least after you've hit B/N or T to give the top-down view.
    • The red lines are the -32768/32767 range (so 65536 values) to give an idea of when the 2D array should be about to overflow.
    • You can import a save file from AitD (FitD scans for SAVE0.ITD in its folder). This means you can first set up whatever situation you like in AitD with the help of the Viewer, then quickly copy-paste the resulting save file and resume inside the FitD hack to see what the full projection looks like.
  • FitD is very buggy...
    • In case the character suddenly starts to spin uncontrollably, you have to restart the program.
  • If you get "The procedure entry point SDL_getenv could not be located in the dynamic link library E:\GOG Games\Alone in the Dark\INDARK\FITD.exe", you have to make a backup of the DLLs in the INDARK folder and replace them with the ones in the zip.

OTHER TOOLS

  • Cheat Engine has a feature called "speed hack" that you can use to make games execute slower. This may enable the Room Viewer to catch things happening that take less than a full frame without affecting the game's functioning like adjusting the DOSBox cycles or other settings would.

GAME

GAME FILES

File Contents
CAMERAXX.PAK All background camera images (in 320x200 8-bit 256 color). Can be opened with PAKExtract or in Photoshop etc. as RAW images.
ENDSEQ.PAK End sequence after Pregzt is defeated. The first entry is an image of Pregzt with a black background (shown after palette has turned into yellow-red tones). The other entries are flames animations, rendered on the top of the first image. Each animation is rendered two times, then palette turn into yellow, then red.
0 1-11 12-22 23-31
AITD ending.png AITD flames 1.gif AITD flames 2.gif AITD flames 3.gif
ETAGEXX.PAK Room and camera information, used by the Room Viewer.
ENGLISH.PAK 00000000.dat is the English translation for the object names and also user interface/messages, the other files are the readables. There are also other language files with obvious names. The one called USA.PAK doesn't seem to be used. It has the same data as ENGLISH.PAK but in the wrong order.
LISTANIM/LISTANI2.PAK 3D models' skeletal animation data. Can be viewed in the Room Viewer.
LISTBODY/LISTBOD2.PAK 3D models. Can be viewed in the Room Viewer.
LISTLIFE.PAK Scripts. Can be decompiled in LIFEDisa.
LISTMUS.PAK Music files in the ADL format.
LISTSAMP.PAK Sound samples (8000 Hz 8-bit mono VOC files). Can be opened in many audio programs like Audacity.
LISTTRAK.PAK Pre-recorded 2D paths for actors (e.g. when climbing stairs). Can be decompiled in LIFEDisa.
PRESENT.PAK The screenshots shown in attract mode (let it idle in the main menu).
ITD_RESS.PAK Various GUI/gfx data (e.g.: menus, fonts...), main 256 colors palette.
# Description Image
0 tatou 3D model
1 tatou palette (3D model)
2 tatou logo
3 main palette AITD palette.png
4 menu gfx AITD menu.png
5 font gfx AITD fonts.png
6 paper
7 book
8 notepad
9 book artwork AITD book artwork.png
10 character selection (emily / edward)
11 intro (frog)
12 game over (house)
13 title screen
14 character selection (purple / green)
15 intro (road)
16 intro (house)
17 game over (Pregzt room)
18 game over (Pregzt room)
19 game over (altar)
VARS.ITD The initial values of VARs.
DEFINES.ITD The initial values of CVARs.
PRIORITY.ITD Sound samples priority list.
OBJECTS.ITD A table with all objects used in the game and their properties (e.g.: name, room, 3D position, ...). Unpacked into the AitD Objects worksheet.
  • Save files are called SAVE0.ITD – SAVE5.ITD. The one shown up top in the save menu is SAVE0.

KNOWN VERSION DIFFERENCES

  • The game was released for the PC as two demo versions, a DOS floppy full release and a DOS CD-ROM full release, and also some kind of Mac release. At least all the full releases contain all the four language files: French, English, Spanish and Italian. Those language differences are confined into the respective language PAK files. As such, there are no "different language versions" per se, and the only differences in behaviour that depend on choice of language (stored in the file called INDARK2.CFG), aside from the obvious, are assumed to be limited to cases where the game is gotten to read the wrong part of memory that has some text or voice-over. These are the downloads that were used in studying and testing the game:
    • The GOG release, which is probably just the old official CD-ROM version. (Used by LotBlind for almost all the testing she did)
    • Floppy version.
    • Debug CD-ROM release (same download page, labelled as French).
CDROM GOG   155088      Borland C++ - Copyright 1991 Borland Intl
CDROM DEBUG 242370      Borland C++ - Copyright 1991 Borland Intl

FLOPPY      137870 (*)  Turbo-C - Copyright (c) 1988 Borland Intl 
DEMO        137814 (*)  Turbo-C - Copyright (c) 1988 Borland Intl

(*) size of unpacked executable
  • The CD-ROM version labelled French on the abandonware-france page has debug symbols included. Turbo-C 2.0 (1988) and Turbo C++ 3.0 (1990) didn't have a special debug/release mode, but instead had options for optimizing for either size or speed (probably speed was chosen for AitD) and for including or leaving out debug symbols. If a Borland utility called TDSTRIP is used on a compiled .exe, it removes the debug info from it. Running it on the debug version of AitD results in the exact same non-debug executable. Thus the symbols themselves seem to be the only difference between debug/non-debug versions.
  • Demo vs. full releases:
    • Putting the demo executable in the floppy version folder causes the game to behave normally, except when switching floors, floor 0 is forced. This is probably a measure against piracy to stop being able to combine the unprotected demo executable with floppy version data to get a full working game.
      • You can't use the demo data to play the floppy version since it tries to start the game in ETAGE 7, which doesn't exist in the demo.
    • The demo and floppy releases, which are similar, have different instructions compared to the CD version. They do the same thing but are contained in fewer bytes. This might mean the floppy versions were optimized for size (understandable), and could further imply that they could be a little bit slower. However, this was tested and no difference was noted. This could be because most of the CPU time is spent in the renderer, many routines in which were written by hand using assembly. The Turbo-C compiler options wouldn't have affected those parts. Another explanation could be that DOSBox emulates instructions in a crude way: with cycles count X, it runs X instructions regardless of what they are. On actual hardware, the time taken to execute instructions varies.
    • After compiling, the floppy/demo executable was compressed using PKLITE (which was a popular .exe compressor back in the days), resulting in a 68K file. Further the compressed .exe was encrypted using a custom loader called TATOU.COM. This was to make it more difficult to crack the game.
  • The Japanese release (3DO/FM Towns) has the known difference of taking more damage from some sources such as the smoke in the smoke room. It is unknown which other ports were the same. Generally no testing was done on non-PC versions.
  • In the earlier of the two demo versions, spending time in the menu still causes the CHRONO to advance.


KNOWN DIFFERENCES BETWEEN THE FLOPPY AND CD VERSIONS

  • Interfacing with the AitD Tools: Some Room Viewer data is only displayed for the CD-ROM releases of the game. This includes anything to do with the game's timers (FPS, lagometer, CHRONO...).
    • Also ALLOW_INVENTORY is only shown for the GOG release.
  • A few LIFEs have different numbers due to a new LIFE 559 being added in the CD-ROM version, pushing everything past that one number higher than in the floppy version. The CD version LIFE numbering is used in this guide. Also one has to keep in mind the names of the last LIFEs are wrong (one off) when using LIFEDISA with the floppy version.
  • Game texts: Some typos and other English mistakes were corrected for the CD-ROM release. E.g. locked doors that used to give the message "It's closed!" when you weren't using the right key on them were changed to "It's locked!". "Reflexions" was changed to "Reflections". "Sabre" was respelled "saber" etc. Some corrections were made to the credits text as well.
    • The other languages weren't checked for differences.
  • Copy protection: The CD-ROM release does not have a copy protection screen. That screen has not been studied at all, but can, apparently, be skipped by typing in "benjaminyaelfred".
  • Tendency to crash: The floppy version (tested on the English one) seems to crash a lot more seldom due to the 2D wraparound though generally similar outcomes are seen. It's not known why. The CPU values were matched with the GOG version to make sure that's not likely to be the cause.
  • Falling/climbing: There's a new LIFE 559 in the CD-ROM version that relates to climbing. Specifically, some code that used to be in LIFE 552 was moved to LIFE 559. This was to eliminate certain bugs that are unlikely to be relevant for speedrunning.
    • For more details on the floppy version bugs, look for "Anomalous behaviors when falling while climbing in the floppy version".
  • Comparison of LIFE 552 (floppy) vs. LIFE 552 (CD-ROM): this ignores all the lines that are the same and in the same place in both versions.
    • Floppy version has these that the CD-ROM doesn't:
      • LIFE 549, INV, control and TRACKMODE are returned directly in the first IF clause instead of first going to LIFE 559 a frame later.
    • CD-ROM has these that floppy doesn't:
      • PC ANIM_ALL_ONCE player_climbing_end (268) -> idling in the first IF clause.
        • The player_climbing_end ANIM is normally already given in the ANIM_ALL_ONCE command in LIFE 549 when starting to climb, but it's the second parameter. The second ANIM given in an ANIM_ALL_ONCE is interruptable. Thus by changing the code this way, this second climbing-related ANIM is also made uninterruptable, and most critically, LIFE 559 requires that it's finished before sending the player back to LIFE 549 and giving back INV. The net effect is eliminating the buggy behavior.
  • Because of the extra LIFE 559, when moving save files from CD version to floppy version, if the PC was in LIFE 559 or 560, this will result in anomalous behavior as those LIFEs are different. Also the "actions" item will bug out and cause a softlock. Similar will happen going the other way (mixing save files like that isn't allowed anyway).
  • The way the pausing works in the floppy version is very different compared to the CD-ROM. In the former it seems pressing any key will unpause it (checking for all of "key", "joy" and "click" variables), in the latter, you have to press SPACE specifically ("click" variable), which means you will also perform the SPACE action at the same time. If you press and hold 'p' to unpause in the floppy version, the effect is a frame forward feature within the game itself that at least works most of the time. There can still be lag frames like normal.


MUSIC/SOUND FILES

  • The CD version has a slightly different tracklist with more tracks and with changes in the associated logic. Also the READ commands in the LIFEs have a third parameter: the voice file to play back.
    • These differences are in at least all of these LIFEs: 15, 118, 140, 175, 185, 191, 192, 193, 194, 265, 380, 430, 438.
    • The orchestral hit sound sample played when entering dangerous areas etc. has been replaced with a short piece of music (string cacophony).
    • vars.txt was written with the CD-ROM release in mind, some track and sample names will be wrong when decompiling the floppy version scripts.
  • Because the CD-ROM version has more tracks than the floppy version, CD-ROM save files may have values for the currentMusic variable that are invalid for the floppy version and fail to load in it. Moving save files from one version to the other has also caused anomalous samples to be played, sometimes on an endless loop.
  • Only the floppy version has the command FADE_MUSIC used to fade out the current track and start playing another.
    • Interestingly, it's used in the script for Jeremy's suicide note in E0 after reading the note but not with any of the other readables.
    • FADE_MUSIC is used 10 times in total. This list shows when each time happens with the two numbers giving the following info: (goes to music track #)(happens in LIFE #)
      • you've read Jeremy's suicide note (0)(13)
        • Interestingly, only used with this one readable.
      • you've died to dropping to 0 health (5)(LIFE 39, twice on the same frame)
      • you play the Blue Danube (3)(283)
      • you play the Dance of Death (4)(379)
      • you play Chopin's Posthumous Opus 69 Nr 1 (5)(431)
      • you beat the game and are walking towards the road (6)(544)
      • at the start of the intro (7)(546)
      • you're stepping outside after beating Pregzt (-1)(550)
      • at the start of the gameplay (0)(561)
  • sample_then is used more in the floppy version:
    • The CD-ROM VERSION HAS IT IN LIFES: 20, 100, 326, 538, 545, 547
    • The floppy version has it in lifes: 20, 52, 83 (twice), 100, 326, 441 (twice), 444 (twice), 447 (twice), 522, 538, 545, 547
    • These are only in the floppy version: 52, 83 (twice), 441 (twice), 444 (twice), 447 (twice), 522
    • A few of these differences are due to making the "danger" SAMPLE into part of the music track instead.
    • In the floppy version, rats aggroing causes the danger SAMPLE to play before the rat squeal SAMPLE.
    • In the floppy version, Pregzt getting hit by the lit lamp causes two samples to be played in LIFE 522 but none are played in the CD-ROM version.</pre>
  • Other sound/music-related differences:
    • #518 has a SAMPLE played when placing the talisman ("sound 103") that sounds like Pregzt snarling in pain. No SAMPLE is played in the CD-ROM release.
    • #522 has several extra SAMPLES after Pregzt has been hit by the lit lamp. One that repeats (burning) and another sample_then that plays the same Pregzt pain sound followed by a thunderclap. The CD-ROM version just plays a music track.
    • #539 (intro) plays the intro music in two separate parts in the CD-ROM version. This could be to help with syncing the track to the gameplay, but it still doesn't sync very well in the CD-ROM version (the first track doesn't dovetail into the second one well, and the second track cuts short when the intro ends).
    • #544 (end cutscene) a repeating SAMPLE and a different music track are played here in the floppy version but missing in CD-ROM
    • #546 the Infogrames copyright message has an added mention of I-Motion.
    • #550 (transitions) changes the music when entering the cellar from E3 in the CD release but not floppy. The same change was made for the transition from E4 to E5 and vice versa and for going from E3 to E5.

TRIGGER AND COLLIDER ETC. DATA

    • There are no differences in the trigger or camera trigger data.
    • There is a small difference in the CAMERA.PAK file. Only the CD-ROM version has filenames in the header of each entry within it, with the format CAM00000.MCG. The first two digits represent the etage and the last three the camera ID. "MCG" is probably short for "MCGA" (which had a 320 × 200 with 256 colors mode as used by AitD).
    • The CD-ROM version attract mode has a different slideshow. The artwork that had a temporary hand-drawn Edward head has been replaced with a screenshot showing the finished 3D model (not in the same part of the game) in most cases. Also the pictures used and their order was changed around.
    • There are small differences in the room/collider data, from the floppy to the CD-ROM version:
      • E3R2:
        • 3 colliders have their width increased by 200 units, including two that overlap (on the W side, they actually reach into R3 in a way that might make some theoretical difference in getting OOB from one of the two rooms if you had to get OOB from that area)
        • 2 colliders have flag set to 0 instead of 1 (not known why)
      • E5R0:
        • 6 colliders have been added. They are all the walls around the bridge. For some reason, the long E wall is 10 units lower than the rest.
      • E5R7:
        • 4 colliders have been changed (size+position):
          • 6950, -3000, -100 (made thicker along Z-axis, presumably to prevent the player from getting into a blind corner)
          • 5949, -1000, 3000 (made thicker along X-axis so it overlaps with E wall, preventing clipping into corner)
          • 6800, -1000, -2000 (made thicker along Z-axis so it overlaps with the collider to the N of it, preventing clipping into it)
          • -5699, -1000, -5650 (made thicker along Z-axis so it overlaps with S wall, preventing clipping into corner)
        • 2 colliders were moved inside the list for some reason, but otherwise left unchanged.
    • Because the lagometer only works for the CD-ROM version, it's difficult to tell exactly which version has the most lag, or if there even is a difference. The CD-ROM music is played off the CD image directly, so it's not cached and thus doesn't cause lag. The midi files loaded in by the floppy version might cause a very small amount of lag.
    • The headers of room data files: In the floppy version some of them contained garbage (they pointed to areas outside the file). In the GOG/CD-ROM version those have been set to zero (cleaned by the developers perhaps). No known relevance.
    • The floppy version seems to temporarily store some other data right after starting a new game in the part of RAM where the VARs are kept during gameplay.

GENERAL

  • There's various situations where there are one-frame windows to e.g. still be able to access your inventory. These should be checked for by buffering the inputs.
  • When, e.g. loading the same save file again and again, some of the reasons the game's behavior is not completely deterministic:
    • The state of the RAM is not exactly the same. The game keeps whatever sound samples were cached in the cache. The Timer 2 value is not stored in save files. Especially if the game is reading memory from the wrong locations due to a bug, this kind of stuff could alter its behavior.
    • Non-fixed time steps / different lag: this can cause some events to happen on different frames than before.
    • For any process that uses timer values for anything, at least if the timers are supposed to reset afterwards, there may exist the possibility of rare occurrences where the timers are updated in the middle of instructions that they usually are not updated in the middle of (i.e. very fast instructions). This can create edge cases of rare and difficult-to-reproduce behavior that could explain some anomalies.
      • Aside from the Actor CHRONO Underflow and Inventory Timer Glitch, no processes have been explicitly identified that can be affected by this.
  • If it benefits testing in any way, you can change VAR 9 to a different value in the middle of dropping something to cause the object with that ID to be spawned in front of you. This just teleports it to that location, it doesn't create a copy. There needs to be room for it to be dropped. The only known way to do it without cheating is to corrupt memory, and as such, is of theoretical/testing interest.
    • Aside from inventory items, other actors dropped this way are dangerous to touch and usually cause instant crashing with few exceptions (reason might be they have the collectible flag set but don't have FOUNDLIFEs).
  • A small difference between holding the main ENTER key and keypad ENTER (see "LAG MANIPULATION") was only spotted late into the project. In the future the two keys should probably be tested separately but it seems very unlikely there's other differences. Similarly hitting 'I' to get to the inventory was initially overlooked, but is does the exact same thing as either ENTER key outside the inventory itself.

CONTROLS

  • The game checks the state of the keyboard at the beginning of the frame and stores it. This state will be used for all logic for the entire frame, and thus pressing or releasing buttons during the frame won't alter the state until the start of the next frame (there's no sub-frame inputs).
  • AitD uses three variables to manage player inputs:
    • "key" ("input3" in FitD): Holds the last key pressed, whichever one it is.
    • "joy" ("input4" in FitD): Holds a combination of directional inputs using flags to enable simultaneous running/walking/rotating/actions. Only the following keys affect this – the arrow keys, the keypad num keys except for 5, SPACE and some other keys mapped to the directional inputs for an unknown reason. The flags used are:
      • UP 0x01
      • DOWN 0x02
      • LEFT 0x04
      • RIGHT 0x08
      • ACTION 0x80
    • "click" ("button" in FitD): Set to 1 if you hold SPACE or numpad 0.
  • 'P' to pause, "click" to unpause. (In the floppy version, all of "key", "joy" and "click" are checked, i.e. any key will unpause.)
  • alternative keys:
    • ENTER/KP_ENTER for "menu" and "select"
      • there seems to be a small difference between the functionalities of these two keys (see "LAG MANIPULATION")
    • SPACE/KP_INS/KP_DEL for "action" and "select"
    • ESC to "cancel", also to select not picking up an object quicker
    • other keypad keys for movement, also with KP_7 and KP_9 you can combine running with turning and KP_1 and KP_3 are the same backwards
    • some other random keys are mapped to movement as well
    • these can and may be adjusted in the DOSBox keymapper as well and may be different with different keyboards
  • 'I' takes you to the inventory but doesn't otherwise act as "select"
  • The 'Ö' key in a Finnish keyboard switches music on and off in the CD version. In most keyboards this is probably 'M'.
    • In the floppy version, only the 'M' key works for this.
  • 'S' toggles sound effects, interrupting the current sample.
  • If you just hold down KP_ENTER, after entering the inventory, the game seems to eventually count it as another key press, and eventually selects fighting all on its own. The same behaviour is observed in the menu. No other keys seem to work this way. It's not clear what's causing this or whether it works the same on every keyboard.
  • You can skip the game intro cutscene before it's even started by hitting ENTER to select the PC, then hold SPACE as it starts to fade out.

MAIN LOOP

  • Like any game, AitD first calculates all movement and such for every actor, then renders everything in one go separately after all logic has been processed. These are called the update loop and the render loop.
  • The whole loop is designed to run 70 times per second if the computer is fast enough, and so everything listed in it will also be done that frequently, including redrawing the graphics. The 70 frames limit is based on the maximum refresh rate of a VGA monitor. When playing the game with GOG default settings, it usually isn't quite fast enough to reach that speed. Because the game's method of capping the number of executions is primitive (poll framerate only once per second, if it's above 70, apply VSync for the next second, then uncap framerate again for the next second etc.), in practice the main loop may be executed far more frequently. Thus if the CPU is fast enough to execute it 120 times per second, the average FPS will be (120 + 70 / 2 = 95).
    • The internal timers, however, have a resolution of 60/second, meaning anything that's tied to them (e.g. actor movement) can only happen at most 60 times per second.
    • If the graphics were drawn at 60 FPS, it would always be out of sync with a CRT monitor (you would get 35 FPS with VSync on, or tearing if it's turned off). Hence 70 FPS, the native refresh rate of a CRT monitor, is used.
    • The inventory is always VSynced. This has the negative effect that when playing the game with a CPU that's only moderately fast, it can make the rotation speed of the items displayed keep changing as it hops between 70 and 35 Hz. (demonstration) The "actions" object is too complex to render at 70 Hz at 11000 cycles, so it rotates twice slower.
  • The main loop of FitD is here and it corresponds to the AitD main loop almost 1 to 1. The pseudocode below is a sketch with the most important functions in the order in which they are processed during a frame of regular gameplay.
gameloop()
{
	readInput();
	if(inputAllowed) //eg: screen is not fading
	{
		if (key == esc) {
			mainmenu();
		}
		
		if (key == p) {
			savetimer();
			while(readInput() != 0) {
				//wait
			}
			while(readInput() != space) { 
				//wait
			}
			restoretimer();
		}	
		
		if (key == m) {
			changesmusicstatus(); //turn on/off
			showmessage();
		}
		
		if (key == s) {
			changesoundstatus(); //turn on/off
			showmessage();
		}
		
		if (key == enter || key == i) {	
			if(allowinventory) {
				inventory(); //execute FOUNDLIFE of object selected during inventory visit
			}
		}
	}
	
	updateInHand(); //process FOUNDLIFE of object INHAND
	
	foreach actor
	{
		reset COL_BY, HIT_BY, HIT, HARD_DEC, HARD_COL to -1 // don't touch COL[] yet
	}
	
	foreach actor 
	{
		processAnim(); 	//set COL[] now according to the actor's collisions (reset rest to -1) and execute FOUNDLIFEs of objects collected
 				//COL[] values are used later in this function for processing actor/actor collisions and setting COL_BY / HIT_BY / HIT.
		processTrigger();
		processAnimAction(); //HIT, FIRE, THROW actions
	}	
	
	foreach actor 
	{
		processlife(); //execute FOUNDLIFEs of objects picked up through FOUND or TAKE
	}
	
	if (changefloor)
	{
		loadfloor();
	}
	
	if (changeroom)
	{
		loadroom();
		setupCamera(); //load camera/background
	}
	else
	{
		processCameraSwitch();
		if (flaginitview) {
			setupCamera(); //load camera/background
		}
	}
	
	if(flagupdateAllActorsAndObjects) {
		updateAllActorAndObjects(); //move actors between actor<->object array (spawn or delete) based on current floor/room
	}
	
	if(flagCreateList) {
		createActorList(); //create a list of actors to be rendered
	}

	sortActorList(); //sort those actors based on their z-value
	
	if (flagRefreshBackground2) {
		renderBackground2(); //copy background to background2
	} 
	redraw(); //render everything
	
	processSoundAndMusic();
}
  • pause: If you hold down whatever button, then hit 'P', then release 'P' while still holding that other button, the game resumes. If you first start pressing 'P', then hit another button, then release 'P', the game remains paused until you hit SPACE. This suggests there is some small mistake in the loop as described above.
    • The game is also paused while in a pick-up prompt, the inventory or in the main menu.
  • It's possible to remove the VSync to see how the game behaves when running at an uncapped framerate: search the following bytes with Cheat Engine: BA DA 03 EC EB 00 (you have to select "Value type : Array of byte")and replace BA with CB. Two places need to be patched.
    • All the obvious effects of doing this (at a framerate of around 1000):
- Everything that rotates with the DO_MOVE command rotates faster than normal, e.g. the car in the intro goes around the in-curve.
- Rotation in the inventory and pick-up prompts becomes much faster than normal. This is because ShowBeta is decremented on every frame by the engine. Same with the back-and-forth motion of items in pick-up prompts.
- Inventory scrolling becomes very very fast when you hold down the button. Again, something to do with the inventory part of the main loop.
- You can STILL get the PC to run but it requires using two different buttons (keypad UP and regular UP) so you can give the second input fast enough.
- Flow actors rotate fast and disappear almost instantly. If you get some to spawn, it can crash the game very quickly (not usually the case).
- The trick that causes CHRONO-based events to happen instantly if the actors involved are serialized no longer works.
- Moving from floor to floor through staircases becomes very difficult in some cases because the TRACK ends earlier than normal and the PC ends the TRACK standing inside the trigger at the other end, causing them to turn around and go right back.
- Hodling down the keypad ENTER key especially can cause multiple things to happen very quickly because for some reason the input the game gets from that key tends to end by itself.
- Book pages turn faster. The fade-outs and fade-ins are very very fast.

File:Flow Actor Crash with Uncapped Framerate.avi

  • Another way to get the game to run faster (or slower) is to set a custom timer refresh rate the following way:
Search for the following value in Cheat Engine: 16.66636 (as Float).
There should be only one match. This is the interval in milliseconds for the timers.
Add that result to the list down the bottom (double click on it), then select "edit value".

Some examples :
33.3333 : 30 Hz
14.2857 : 70 Hz
1 : 1000 Hz
0.1 : 10000 Hz

Don't set it too low (e.g.: 0) as the interrupt handler (that updates the timers) will be called before it's finished and it will crash the game.


GAME MODES / GAME START

  • The intro and the main game are two "game modes". The outro is part of the second mode.
  • This is what the "new game" code looks like:
newgame() 
{
      playworld(0, 1, 7); (intro played in mode 0, start in E7R1)
      playworld(1, 0, 0); (main game played in mode 1, start in E0R0)
}

playworld(gameMode, floor, room)
{
      //init code
      ...
      loadfloor();
      setupCamera();

      mainloop(gameMode);

      //cleanup code
      ...
}
  • The difference between game modes is simply whether or not pressing a key will exit to main menu.
  • It seems that you should be able to enter the INV on the first main loop execution after the intro is over, before LIFE 562 has even had the time to place the "actions" item into it. The reason this doesn't work is FlagRedraw is set to a non-zero value by the setupCamera() function which is called explicitly (see above) after exiting the intro and before entering the main loop for the main game. It's only reset to 0 (required for inputs to do anything) by the redraw() function near the end of the first frame.
    • In the main loop. FlagRedraw is referred to as "inputAllowed".

MEMORY

  • A breakdown of the contents of the game's/DOSBox's memory at runtime (the image is taken from the CD version):

AITD Memory.png

  1. DOS/BIOS : exists before game is started. cannot be used by AITD.
  2. CODE (code segments) : x86 code used by the game. Should only be read, never written (unless there is self modifying code). If you look carefully, you can see game palette at the end.
  3. STATIC DATA (data segment) : all game static global variables (VARS, CVARS, active actors, timers, current floor/room/camera, renderer arrays, ...) It's about 43KB (in theory can be max 64KB). The pattern that repeat itself at the bottom are active actors (50 x 160 bytes)
  4. DYNAMIC DATA: dynamically allocated memory (can be greater than 64KB). Unlike static vars, the addresses of these variables can change from time to time (e.g.: between two AITD instantiations). It contains screen buffers (AUX, AUX2), cache (samples, anim, body, life), all actors (the gray area, right below lantern)... Some regions are allocated only for a short time, for specific things (e.g.: storing save game thumbnails) and then released right after.
  5. 640KB limit : in theory, AITD can only use the first 640KB. What is above (640KB - 1MB) is reserved and mapped to various devices like VGA/BIOS. The 640KB-1MB region is hidden in the MV.
  6. 1MB : in 16-bit real mode (which is what AITD use), only the first megabyte can be accessed by CPU (it has only 20 address lines. 2^20 = 1MB) However, some tricks like EMS allow to access that high memory part (called HMA) chunk by chunk (it's slow). AITD use EMS to store one of the screen buffers there.
0 - 640KB   Used by programs (e.g. AITD) for their own code/data. This is shown in the MV. 
            Programs cannot use it directly, they have to request memory blocks trough memory allocation. 
            The very first KBs are reserved for DOS/BIOS internal stuff.
            
640KB - 1MB Used for I/O (eg: VGA card), ROM, drivers, ... 
            AITD can read/write there (e.g. to send pixels to the VGA card) but cannot use it for its own purposes. 
            Reading in this zone mostly return zeroes so it's skipped in the MV.

> 1MB       Only accessible through EMS or XMS. 
            AITD use EMS to allocate a single buffer of 64000 + 320 bytes there (for a 320x200 screen that we call BACKGROUND and  
            one extra line). That buffer is shown in MV
  • At startup, AITD checks if EMS is available. If yes, it will use EMS to allocate one of the screen buffers. That screen buffer will end up above the 1MB region, leaving more room for the rest (what is inside the first 640KB region). This is what happen with AITD GOG version (which has 30MB of RAM and EMS enabled). If EMS is not available, it will not use it and the screen buffer will end up in the first 640KB.
  • AitD can write past the allowed limits during memory corruption caused by e.g. the 2D wraparound. If it writes past the address FFFF:FFFF, it will wrap around.
  • A detailed breakdown of runtime memory contents for the CD-ROM version is given here.
    • The exact layout of RAM may differ in a significant way between versions (floppy vs. CD-ROM).
    • Logical size and physical size are different. Logical means how much memory the game has requested, physical is how much was actually allocated. The memory allocator rounds up sizes to the closest multiple of 16 bytes.
    • There is 16 bytes between each block. Those are used by DOS to maintain the MCB block chain (and by extension by the MV to color the blocks). The 2D wraparound might corrupt that chain.
    • Some things may look out of place. This is due to reusing freed up blocks. E.g. the "samples cache header" should be right above "samples cache data" but ends up somewhere higher up.
    • The size requested for cache data (for PAK files) is always 300 bytes bigger than what it is supposed to be (E.g. the BODY cache is 65300 instead of 65000 bytes). What is shown in the CV is the size from cache headers (which is supposed to be what the game uses as well). Attempting to remove that extra 300 bytes (allocating less memory than requested) and it corrupts the game at some point so it's necessary. It might be used as a temporary buffer when doing some cache-internal operations.


CACHES/BUFFERS

  • The caches can be studied in detail with the Cache Viewer.
  • The game has separate caches/buffers for LIFEs, TRACKs, sound samples, BODYs and ANIMs, and the floppy versions have a music cache as well. They have the following maximum sizes in terms of number of entries and number of bytes:
    • LIFE, 100, 10000
    • TRACK, 10, 1000
    • SAMPLE, 30, 64000
    • BODY, 50, 65000
    • ANIM, 80, 65000
    • Every cache entry has a field called "timestamp" telling the game when the resource was last used.
    • If either limit is getting exceeded, the game in theory removes the least-recently-used entries for that cache until there is enough room. In practice this is bugged and sometimes the wrong entry is removed. See "cache item removal algorithm" for more.
      • Also during reloading, everything except for the sounds is flushed. The same is also done after the intro is over before the game starts. In addition, floor transitions cause BODYs and ANIMs to be flushed, and reading a book or other readable causes the samples to get flushed afterwards.
    • The flushing when reloading doesn't directly cause time losses since it's outside of timing, but obviously anything that wasn't immediately loaded back into the cache will cause additional loading lag during every segment.
  • The PAK files contain the data shown in this spreadsheet. The spreadsheet shows which ones are compressed and what their compressed and uncompressed sizes are. The second tab has some statistics, including more accurate measurements of the time to decompress LISTSAMP.PAK that were gathered on modern hardware and using the UnPAK algorithm, which might be different from what was used by the devs. In addition, slightly different statistics are listed below (the compressed size below compares the average of only the files that were compressed to the uncompressed versions of those files):
    • ANIMs (Both LISTANIM and LISTANI2) only have 1 ANIM uncompressed (#72). The compressed size is 33% of the uncompressed size.
    • BODYs (Both LISTBODY and LISTBOD2) are all compressed. The compressed size is 59% of the uncompressed size.
    • LIFEs have 213 compressed out of 563 (38% of them). The compressed size is 63% of the uncompressed size.
    • Music tracks (CD-ROM) are all compressed. The compressed size is 24% of the uncompressed size.
    • Samples are all compressed except for 6 of them. The compressed size is 73% of the uncompressed size.
    • TRACKs are all uncompressed except for 3 of them. The compressed size is 81% of the uncompressed size.
    • The files that are compressed load considerably slower than ones that are not, explaining most of the lag happening throughout the game. This indicates that it's mostly decompression that slows the game down instead of I/O operations. The logic of what files are compressed and which ones aren't is simply whether the compressed file was any smaller.
      • How long decompression will take depends on both the compressed and uncompressed file sizes but not the compression ratio.
  • The music file caching load times (for the floppy versions) were not tested much but they're midi files and as such have very small sizes.
  • The background graphics are unpacked into RAM but not cached for reusing.
  • In addition, there are two arrays for keeping actor data in. These are discussed under "actors".
  • The collider data is also kept in RAM for the current floor.
  • Upon starting a new game, and any time the game has been restarted, the game flushes the caches, including any actors from adjacent rooms you've just been to, and only loads in whatever is currently present. This means rebooting the game might lose you time unless you take the time to get every sound effect cached before you start recording any segment where they will be accessed.
  • Any cache entries could cause a compound time loss worse than just how long they loaded for initially if they're also forcing more entries to be recached later that were removed to make room for them.
  • It looks like the presence of a lot of dropped items in a room that you've either entered before since the last reloading or have not entered since then causes a difference in loading lag for that room/view of about 10 milliseconds per item (that has a BODY), which can probably be used as an estimate of how much of the lag owes to actors. Thus if you needed to go between rooms A-B-A, if you s/l'd in room B, it might cause you to lose some time this way compared to saving in room A then going to B and returning to A in one segment.
  • If you push a lightweight to another room, or an actor otherwise moves to another room further away from the one you're in, if you save and reload, it might disappear, suggesting that there is no flushing of actors unless the PC herself moves from room to room.
  • Objects that move too far away without the view changing (by being pushed from room to room for example) will be removed from memory only when the game gets reloaded the next time (returning from the inventory or menu).
  • In summary, make sure to cache as many relevant sound samples as you can when starting a new session. Other than that it may be difficult if not impossible to find chances to save time optimizing cache usage without losing it to the detours taken to accomplish it, except if you have some waiting time during which you can save a few frames by avoiding messing around causing new ANIMs or samples to play.
    • The most important part is avoiding any unnecessary triggers.
  • PRIORITY.ITD is always cached.


CACHE ITEM REMOVAL ALGORITHM

  • The code used to select which entries should be removed from the cache to make room for new entries in the respective cache is in principle very simple. Despite this, it has two bugs both of which can cause the wrong items to be removed.
  • The LRU Bug: The entry that should be removed next is always supposed to be the one Least Recently Used (LRU). The code should look like this:
max = 0
oldest = 0

for (i = 0 to entries.count - 1)
{
	if ((timer - entries[i].time) > max)
	{
		max = timer - entries[i].time
		oldest = i
	}
}

remove_block(entries[oldest])

It's really simple: you calculate how long each entry has been there, and if it's longer than what you have stored in "max", replace "oldest" with its index, otherwise just check the next one. After removing one entry, if there's still not enough room, the same code is run again, removing enough entries for the new entry to fit.

The code actually looks like this:

max = 0
oldest = 0

for (i = 0 to entries.count - 1)
{
	if ((timer - entries[i].time) > max)
	{
		max = timer - entries['''oldest'''].time
		oldest = i
	}
}

remove_block(entries[oldest])

The "max" value that is used as a reference will generally always be lagging one behind. This causes the following erroneous behavior:

    • If the oldest entry (with the first instance of the oldest timestamp) is the first in the list, it will be removed correctly.
      • (In practice, this can only be the case whenever some BODY hasn't gotten accessed after first entering the cache. Because the PC's BODY is never removed from cache, the only thing that can be the first entry other than it is the slot 0 actor's BODY. When you start the game, the chest gets that slot. So when you're in the loft, so long as the chest has not been refreshed, it will get correctly removed first. In every other place, the other actors will have IDs higher than 1, so even if you flush the cache (looks like just saving and reloading isn't enough to remove the PC's BODY from the cache, you have to end the current game first), the PC will be the first actor in the BODYs cache always.)
    • If the oldest entry is anywhere else in the list, if there are other entries after it that are older than the oldest entry that came before it, the first of such (not oldest) entries are deleted instead.
    • In the worst-case scenario this means the first entry that has the second-newest timestamp could get deleted if the only entries that came before the oldest all have the same newest timestamp.
    • This page allows you to test different sequences of timestamps to see which order they should be removed in (first output line) and which order the game will actually remove them in (second output line): https://jsfiddle.net/at2Le1ou/. Enter the values in the text area, separated by a line break, space or comma. Try inputting this sequence to see the worst-case scenario described above: 1, 99, 2, 3, 4, 5, 6, 7, 8.
    • It's possible to patch the game and fix the LRU bug by applying the following changes with Cheat Engine (or editing AITD.EXE with an hex editor):
      • Search for 57 06 26 1B 47 08 and replace it with 14 26 1B 44 02 90. Make sure the game is paused (press 'p' or enter the inventory).
  • Timestamp Underflow: Reason number two why the algorithm can fail is it uses Timer 1, which can occasionally revert to a previous time. Because of this, the time differences can turn into negative values, which are interpreted as large positive ones (underflow). This can cause an entry to be removed even if it's the last one cached.
    • What causes this is between the moment the old entry is deleted and the new entry is added, T1 has ticked (this happens asynchronously through an interrupt). The new entry will be created with a timestamp "in the future". Soon after, T1 is reverted back (because of the unfreeze command). Later, when the time of that new entry is compared with the current reverted time, and the underflow will occur.

Here is one complete example (this code is called when an entry is not in cache and must be loaded):

freezeTimer()    //12:52.51 (T1 is saved in a separate variable)

time = Timer1    //12:52.51 
while (cacheFreeSpace < newEntrySize) 
{
        max = 0, oldest = 0
	for (i = 0 to entries.count - 1)
	{
		if ((time - entries[i].time) > max) //might underflow
		{
			//...
		}
	}  
	deleteEntry(oldest);
}

loadPak() //load body, anim, sample... This take time. T1 might tick during that time.

time = Timer1    //12:52.53
createNewCacheEntry(time) //will be created with time in the future

unfreezeTimer()  //12:52.51 (Timer1 is restored)

For the glitch to happen you need code above to be called at least twice so first the new entry gets its underflowed timestamp, then next time the comparison causes the underflow.

    • The relevant code in FitD is here. It's commented out since the code used seems to have been simplified.
  • Note that the caches are not managed once at the end of every frame, but every time some asset is needed and not currently cached, which could happen at various points in the main loop. Thus cache item timestamps also get updated haphazardly, making it difficult to keep track of which entry will be removed next.
    • You can set up a breakpoint at 105A:0033, right before a cache entry is removed to catch the state the cache was in (as displayed by the CV) with the DOSBox debugger. Look for "debug version of DOSBox".


BACKGROUND1

  • The size of each view when compressed can be seen simply in the amount of data written into the BACKGROUND1 area at the start of changing views. The loading lag is mostly comprised of unpacking the view[s], loading in BODYs, LIFEs, ANIMs and TRACKs and drawing all the visible actors. During a floor transition, the collider data is also replaced.
  • The game seems to first copy the data into RAM for the shared view before unpacking it a bit later when going between rooms. There's also some data stored right above BACKGROUND1 for one frame right at the start of the transition.
    • The game even draws all the actors into the shared view before switching to the right one, which is completely pointless.
  • After testing 18 different view changes in E0—E2, there seems to be an average of around 12 frames of lag when loading in a new view with a range between 4 and 20 frames. If the view being loaded in is the same view that was already cached, the (seemingly unnecessary) process only takes about 30%-40% of that time for some reason.
    • The variance in loading lag seems to be the bigger the longer it takes, which is perhaps unsurprising.


SOUND CACHE

  • The samples cache is flushed after reading anything.
  • The approximate time sound samples load for in milliseconds (from the Room Viewer total delay meter)(one frame is approximately 16.7 milliseconds)(the global FPS during these measurements was around 35 in case that matters); also given the sample priority from PRIORITY.ITD.
Sample ID Description Approximate Loading Lag Priority
0 drawer opening 77 5
1 vase breaking 84 5
2 glass breaking 167 7
3 lock opening 83 6
4 door creaking 133 6
5 heavy object moving (wood) 67 5
6 thunderclap 200 10
7 taking a shot (house) 50 8
8 reloading 32 9
9 weapon click 17 6
10 Edward after flask 33 6
11 Emily after flask 32 6
12 Edward coughing 33 5
13 Emily coughing 34 5
14 hit sound 33 5
15 hit sound 2 33 5
16 Edward attacking 33 4
17 Emily attacking 18 4
18 Edward attacking 2 32 4
19 Emily attacking 2 18 4
20 Edward attacking 3 15 4
21 Emily attacking 3 0 4
22 Edward jumping/climbing 17 4
23 Emily jumping/climbing 17 4
24 Edward climbing 2 17 4
25 Emily climbing 2 16 4
26 Edward hit 67 6
27 Emily hit 16 6
28 bird bite 33 5
29 zombie groan 167 5
30 stepping on wood 33 1
31 stepping on wood 2 33 1
32 stepping on stone (house) 33 1
33 stepping on stone 2 (house) 33 1
34 Edward dying 0 11
35 Emily dying 50 11
36 doors slamming 50 7
37 door creaking 2 116 7
38 Edward falling through crack 166 9
39 Emily falling through crack 134 9
40 Edward falling into a pit 217 9
41 Emily falling into a pit 216 9
42 thud 34 2
43 thud 33 5
44 nightgaunt screech 67 5
45 nightgaunt dying 116 9
46 stepping on carpet 17 1
47 stepping on carpet_2 33 1
48 axe/arrow fired 33 4
49 page turning 67 1
50 sword clang 51 6
51 stepping on stone (house) 32 2
52 stepping on stone 2 (house) 33 2
53 water dripping 284 1
54 water dripping 2 66 1
55 water dripping 3 101 1
56 stepping on stone (underground) 34 2
57 stepping on stone 2 (underground) 34 2
58 aquaphone chime 151 3
59 eerie howl 117 3
60 taking a shot (underground) 50 8
61 drinking 16 5
62 heavy object moving (stone) 115 9
63 eldritch gurgle 234 5
64 back cracking 33 5
65 throwing 17 5
66 hoarse laughter 84 6
67 malicious laughter 49 6
68 deep laughter 81 6
69 drawer opening 50 8
70 wading through water 33 3
71 wading through water 2 50 3
72 water splash 116 11
73 rat squeal 83 6
74 car revving 67 2
75 car approaching 417 2
76 crickets and frogs 183 2
77 frog and car 217 2
78 slamming car door 34 2
79 opening car door 34 7
80 exiting car 50 7
81 stepping on gravel 17 2
82 stepping on gravel 2 15 2
83 wasp hoot 0 4
84 slimy slurp 49 5
85 slimy wheeze 83 7
86 Deep One growl 133 4
87 vortex chant 84 10
88 bird call 50 3
89 chimes tinkling 335 3
90 low rumble 100 10
91 saber breaking 99 8
92 beetle grunt 100 3
93 Cthonian cackle 184 5
94 pained moan 83 3
95 birds chirping 416 6
96 croaky laughter 49 8
97 Necronomicon whip 34 3
98 Vagabond growl 150 7
99 fireball explosion 101 6
100 fireball launch 67 5
    • The lag values are all within 1-2 ms from the discrete values you get when multiplying the length of one frame by different factors, as you'd expect.
    • The lag from playing samples is very closely a function of the sample's filesize (see the statistics in this spreadsheet. The variance between attempts looks to be around two frames generally so consider that the margin of error in the above information.
    • The worst samples to have to load in during a run (outside the intro/outro) are the talisman chimes (#89) and one of the random water dripping samples (#53) played in E5, both wasting around 300 ms directly and hogging up most of the cache, which causes more samples to have to be re-cached afterwards.
    • Most the of the samples are compressed but some are not. These samples are numbers 17, 19, 21, 24, 34, 83. Of these #34 (Edward dying) and #83 (wasp hoot) are ones that you would expect to cause some noticeable lag but they never do.
  • The fact that the oldest entry is removed means the game isn't particularly smart about what it's doing: it might end up removing every other sample when all it needed to do was remove one big one that couldn't ever fit in memory together with the new sample.
  • AitD can only play one sample at a time, called by the SAMPLE or SAMPLE_THEN command. Which sound gets preference is based on the values given in PRIORITY.ITD: if the new sound has an equal or higher priority, it stops the one that was playing before. It can even stop the other sound from being cached if it hadn't been yet.
    • Repeating samples can cause samples with lesser priority never to be played or cached, including other repeating ones. E.g. the vortex chant cancels out the talisman tinkle.
    • The logic with what sample gets what priority has not been studied very carefully, but it seems some of the more "trivial" and oft-repeating sounds like footsteps or water dripping were given a lower priority and more unique one-off sounds a higher one.
  • Hitting SPACE may sometimes result in a sample not playing in case it's taken you away from the main loop in LIFE 549 (skipping the ANIM_SAMPLE calls). Similarly entering a transition can do this.
    • You can suppress step sounds by holding down SPACE when you have the lamp out.
    • SAMPLE_THEN queues two sound samples back to back. It's only used a handful of times (e.g. when a door is unlocked and then opens with a creak) and was never really tested to see if the samples behave the same as those played by the SAMPLE command but it's very likely they do. If so, it should be possible to cancel both samples.
      • During a speedrun, the only time when this happens is walking down the stairs from E1 to E2 (library doors shut, then a laughter is heard). The player has no control during this time.
  • Some practical observations: By the time the game has cached all the step sounds from game start, the three sound samples played when you enter E2 (doors creaking, doors slamming, malicious laughter) will cause the carpet step sounds to be removed.
    • The game removes the door slam sound when the arrow is fired. The creek effect is reused when you open the E2R0 E doors.
    • The talisman chime sound sample takes up 45KB (70%) of the whole sample cache. It's played on repeat until the talisman is collected. This only leaves room for two step sounds. It has to be made sure the talisman chime sound isn't removed until it has been picked up or it has to be cached again. When it is picked up, the aquaphone chime effect is played, which will probably cause the other samples all to be wiped.
    • In E5R0, if you directly turn around and jump, looks like the jump sample isn't played or cached. This is because the PC enters LIFE 550 but the sample is played in 549.
    • In E5R0, after the desync, if you let Emily start the deadly fall, her scream will take up most of the sound cache. Otherwise, the four step samples and the three dripping samples can't all fit in memory at once, let alone them and the aquaphone effect which can still be played in E5-E6. You can, however, suppress them if either of VAR 20 (active monster count) or VAR 28 (player in a dangerous area) have a value other than 0. If you first visit E3R1, VAR 28 is indeed left at 1. After Pregzt has died, the effects aren't played at all either way.
      • Could try suppressing step sounds by holding SPACE.
    • Then in E5R4, the Cthonian starts cackling. This is also a big sample.
    • In E6R6, the two fireball samples will take a total of 30KB.
    • After Pregzt is dead, there will be a low rumble effect and the splashing when boulders hit the water, taking a total of 30KB. However, past this point until reaching the end no sample should be removed that was about to be reused.
  • When you move between floors, all currently playing samples are interrupted though the cache isn't flushed.

LIFES CACHE

  • The LIFE cache has size 10KB (100 entries) and the LIFEs have file sizes only going up to about 2KB, with most of them far far less. They should not cause any significant loading lag if any at all. There could, in theory, be about 50 of them in use at any time since that's the actor limit. The 10KB gets filled up before then, but it doesn't seem there's a lot of flushing LIFEs that were about to be used again, except when returning from the E5-E6 trip to E3.
    • FOUNDLIFEs are cached at times when they are executed, when the actor is collected, selected in the inventory, or on every frame when INHAND.


BODYS CACHE

  • The BODY cache has size 65KB (80 entries) and this seems to be adequate to hold every BODY through the entire floor when most of them are skipped in speedrun routes, unless you also had to scroll through a packed inventory. Any time saved from avoiding BODY loading will probably come from smart inventory visits.
    • BODYs have sizes ranging from about 0.2KB to 4KB. They are loaded not only when actors are loaded in but also during pickups and during inventory visits for all actors shown since those BODYs are different from the ones used in the world.
    • Doors and bridge segments at least will have the same exact BODY since they don't animate.
  • Situations in which the game redraws an actor, with accessing its BODY data causing its BODY entry timestamp in the cache to refresh:
    • Actors that are animating and are close enough are redrawn every frame.
    • Actors that are not animating and are close enough are redrawn when returning from menu, pick-up prompt or inventory.
    • Whenever anything gets pushed, all actors close enough are redrawn.
    • Whenever any actor moves between rooms, all actors close enough are redrawn.
    • Whenever an animating actor is behind another actor (2D-box overlap), the other actor is redrawn every frame.
    • Note that redrawing the current inventory item/actor does not cause the timestamp to refresh each frame because the inventory keeps re-using the pointer given by the cache. This is possible in this context because while the actor is being animated, nothing can cause it to flush unlike during the main loop.
    • Meanwhile, for whatever reason, the item/actor displayed in a pick-up prompt IS redrawn each frame instead of using the same trick with re-using a pointer.
  • Situations in which the game will remove BODY entries:
    • New BODY introduced to full BODY cache: remove least recent entry (buggy, see "LRU bug").
    • Move between floors: remove everything.
    • Reload save file: remove everything.
    • New game: remove everything.

ANIMS CACHE

  • The ANIM cache has size 65KB (80 entries) and this is plenty for holding all the ANIMs used in one floor during a speedrun. It doesn't even come close to the limits. One ANIM tends to have a size between 0.2KB and 1KB and as such takes only up to 1 frame to load.
    • You can only theoretically save time on ANIM loading lag by avoiding having to rotate both left and right (standing still) during a segment when you don't strictly have to. That and not aggroing monsters unnecessarily.


TRACKS CACHE

  • The TRACK cache has size 1KB (10 entries) and the TRACKs are so small they're very unlikely to cause lag at all.

SAVING/LOADING

  • Saving and loading is mostly perfectly implemented and not a lot of information goes missing. Some things will be recalculated during the loading process though. A sample of things saved in the save file:
    • current room
    • current floor
    • current camera
    • current camera target
    • current music
    • current object in hand
    • inventory objects
    • inventory allowed flag
    • shake screen flag
    • light status flag
    • game over flag
    • Timer 1 (but not Timer 2)
    • all vars
    • all cvars
    • all objects and their properties
    • all actors and their properties
    • There is a complete list in the objects worksheet.
  • When you reload, the music is reset.
  • The view the focused actor should be in is recalculated during reloading and so you might end up in a different view. More under "cameras".
  • The "game over" flag is only used right as the game starts fading back into the main menu after the game has ended, and set back to 0 when a new game is started. Thus it didn't really have a need to exist inside save files at all, because it will always have a value of 0.
  • Some things depend on what Timer 2 happens to be when the game has reloaded (Timer 2 is not saved in the save files).
    • More under "timers".
  • Loading a save file sends all ANIMs back to the start of the current keyframe, thus delaying things from happening quite as quickly. The BODY is visually frozen until it's caught up with how far into the ANIM the actor was before saving. This can help avoid taking hits from enemies like the knight by giving you just a tiny bit more time to move away (since the hitbox is tied to the hotpoint vertex position, more under "FIGHTING"). It also means you'll generally lose some time whenever saving and loading on a frame that's not the first in a given keyframe.
  • S/l-ing can also prolong the death animation of for example the nightgaunts causing them to block the way for a longer time.
    • When birds are crashing through windows loading and saving sending them further back can prevent them from getting through the wall at all. Specifically the E1 bird.
  • You can preserve running through a s/l by just buffering UP before and after.
  • When you s/l during walking, it sends you back to where the keyframe started just like running. However, soon after, you'll continue walking forwards a little ways because it seems you can't control the first several frames at all (could just be global lag?). This is true almost every time. While the extra movement is happening, the SPEED goes from 4 (during loading) to 3 to 2 to 1 to 0.
    • On the second load, you won't necessarily be sent all the way back at all, instead it could be 15 units less. It can be as far as 100+ units further ahead. The exact starting position will vary from load to load, probably owing to different amounts of lag.
    • There's an intermediate position you're placed at during the loading before the correct keyframe and frame are set. This might apply to all loading? Also, after the save has finished, you might be sent back or forwards a little bit with the wrong keyframe/frame (which could partially be the Viewer's limitations). There's usually or always a frame of ANIM 4 before it's switched for anything else.
  • Just saving the game causes flow actors to disappear. They're not present when you reload a save file either. This includes the smoke in E3R13, giving you more time to pass through unharmed.
  • When you s/l while opening doors inwards, you can end up being inside the door object since you're not moving as far back as expected.
  • The first time reloading a save file coming from the main menu has a fade-out fade-in. The fade-in also appears when reloading a save file where the focused actor is not inside a camera trigger. If the view is dark, this has to be accounted for in timing.


PAUSING

  • Pausing seems to be perfectly implemented with no anomalous effects noted when using it. Note that unpausing works differently in the CD-ROM and floppy releases. In the CD-ROM one, the game wants you to hit SPACE to unpause, whereas the floppy one will unpause at any keystroke.
  • Frame advance: You can get (near) frame advancing if you hit and hold 'p', then tap SPACE, then tap and hold pause again and alternate. Make sure to hold SPACE for a little bit before releasing each time in order for any potential loading lag or such not to disrupt the process.
    • Pausing in the middle of running with the lamp out, releasing all binds, tapping and holding 'P' again, then tapping SPACE doesn't interrupt running.
    • Should also be able to move the game just one frame (or so) ahead even if you don't want any lag to happen by hitting pause immediately while coming from the load menu.
    • Can also keep holding some key, then hit pause, and the game will unpause automatically whenever pause is released, but it's not possible to go frame-by-frame that way. The upside is you're not interrupting running by hitting SPACE. If you have a null action (lamp or after using the jug), hitting SPACE doesn't interrupt it though.
    • Sometimes the frame advance really is one frame at a time, but this is not guaranteed. Not sure what determines this.
    • The floppy version has a simpler frame advance possibility: just press the 'p' key itself to unpause.

LIFE SCRIPTS

  • LIFE is what the game calls its scripts. There are 563 LIFEs all contained in the LISTLIFE.PAK file, which you can extract using LIFEDISA. The scripts have no comments and are difficult to read unless the names for VARs, ANIMs etc. are replaced (see "LIFEDISA" for instructions).
    • Some LIFEs are tied to one actor's behaviour (acting as a state machine), some act as room scripts or floor scripts which are multi-purpose and relate to events inside some room or floor.
  • Each actor can have up to two different LIFEs, the other one being called the FOUNDLIFE. The FOUNDLIFE is executed when an actor is either picked up, used or equipped, or given via a FOUND command in a LIFE. Almost all actors have either just a FOUNDLIFE (if they're inventory objects) or just a LIFE, with object 2 ("actions") being the exception.
    • Both LIFEs and FOUNDLIFEs are in the same file and the only difference is which actor field their index is in. They're still only ever used as either LIFEs or FOUNDLIFEs and so the guide always refers to a LIFE as a FOUNDLIFE if that's what it's used for.
  • Every active actor that has a LIFE script field with a value other than -1 is causing the game to execute all the code in the script sequentially until reaching an ENDLIFE or RETURN instruction. Then the next actor's (in increasing order of their memory slot numbers) code is executed. Only when all LIFEs have been cycled through will the game draw the next frame and accept more inputs.
    • When a script calls for a different LIFE, it just updates the actor's LIFE field and proceeds normally with the current LIFE until the next frame, when the new LIFE gets executed instead. Thus if two LIFE instructions were issued on the same frame, only the latter one makes any difference.
    • Even though the code execution order may vary based on the exact memory slots for each actor, the game is coded in such a way that this should, in principle, never result in differences in its behaviour. This isn't really true in practice: when two contradicting instructions have been given on the same frame, the latter is what's left in effect. Here is an example of a LIFE that issues a command to move to another LIFE:
#510
xxx          //frame 1, 2, 3, 4
yyy          //frame 1, 2, 3, 4
IF END_ANIM  //frame 1, 2, 3, 4 
    BODY 7   //frame          4
    LIFE 521 //frame          4
END          //frame 1, 2, 3, 4
zzz          //frame 1, 2, 3, 4

#521 
xxx          //frame 5, 6, 7, ...
yyy          //frame 5, 6, 7, ...
zzz          //frame 5, 6, 7, ...
  • The order of execution every frame:
    1. Using/selecting something in the inventory (FOUNDLIFE, only one possible)
    2. Collecting an object off the floor (FOUNDLIFEs in order of increasing actor ID, can be multiple)
    3. LIFEs (in order of increasing actor ID, any FOUND commands cause the respective actor's FOUNDLIFE to be executed as a subroutine to the LIFE in question, which resumes after the player has made their choice of picking the item up or leaving it)
  • In some LIFEs there's missing cases in switches to look out for. One of the most obvious ones is there's no case for BODY 268 (flask) in LIFE 39 if VAR 29 is 1, so death by falling never happens.
  • The actors called "alone_in_the_dark" are dummies used to send the camera into a new view during cutscenes, and also to calculate the distance from Pregzt to make the fireballs disappear when they're getting too far.
  • The following explains some of the less obvious commands used in LIFEs. Note that if you used vars.txt, some of the number parameters were replaced with words:


  • OBJECT(...) == 1 means the object in question has been collected at least once and isn't currently flying through the air (this overlaps with the THROW field).
  • ISFOUND == 1 means an object is currently in the inventory.
  • FOUND gives the player a prompt for collecting a particular item off a table or such. Suppressed if that object's OBJECT is 1.


  • DEFAULT refers to a default case when no matching case exists in a switch.


  • Actor bounding box commands:
    • DEF_ZV: Defines a new custom bounding box for the actor around its ROOM coordinates. The six parameters are X1 X2, Y1 Y2 and Z1 Z2 (i.e. the box is defined by two opposing vertices). This command is usually, though not always, made to execute on every frame to override the default BODY bbox after the actor has been reinitiated. Actors can get stuck when this happens in case they were near a collider or another actor and the DEF_ZV bbox is bigger than the default.
    • DO_NORMAL_ZV: (Unused in AitD1) Resets the actor's bounding box with default values stored in every BODY (viewable through the Model Viewer). This is simply the smallest box that contains all vertices.
    • DO_ROT_ZV: The same except the NORMAL bbox vertices are rotated based on the actor's angle and a different orthogonal rectangle is built around the new vertex positions. This is generally used with doors and called on every frame when they're opening or closing.
    • DO_CARRE_ZV: Adjusts the bbox length and width so they're both the average of the default BODY values, creating a cuboid with a square horizontal cross-section. Leaves the height unaffected. Same as the CUBE zvType.
    • DO_MAX_ZV: Adjusts the bbox length and width so they're both as big as whichever one is bigger divided by two, creating a cuboid with a square horizontal cross-section. Leaves the height unaffected.
    • DO_REAL_ZV: Calculates the bounding box from the actor's current 3D vertex positions directly, taking into account the effects of rotation, translation and scaling. Very CPU-intensive. Mostly used before spawning the BUBBLES flow actor when a monster dies (to make the bubbles appear in the right place).


  • ANIM_ONCE; ANIM_ALL_ONCE: These both have two parameters which are ANIMs. The second one gets stored in NEXT_ANIM.
    • ANIM_ONCE: Plays two ANIMs once through that can be interrupted. If the first ANIM is interrupted, the second one gets flushed and replaced.
    • ANIM_ALL_ONCE: Plays one ANIM once through that cannot be interrupted, then another that can.
  • ANIM_REPEAT: Gets one parameter. Plays an ANIM on loop until interrupted.
  • ANIM_MOVE: If VAR 0 is 1 and TRACKMODE is MANUAL, hitting arrow keys causes ANIMs to be played from the list given as the parameters for this command, accompanied by a potential change in the player's SPEED. Only found in LIFEs 549 and 550. The rotation (change in the angle field) is an event separate from the rotation ANIMs though and only depends on TRACKMODE MANUAL.
    • ANIM_MOVE is not a value that's stored somewhere, it has to be executed every frame for basic movement to be available.
  • END_ANIM: A test for the current ANIM having just finished. This could be any ANIM, not just the one it is expected to be based on the context. Without this flag being set, any ANIM will repeat (see "ACTOR SERIALIZATION").
  • ANIM_SAMPLE: A sound sample (from LISTSAMP) is cached and played if the parameters given match the current ANIM and keyframe and the ENDFRAME flag is set. (So it's played at the very start of the keyframe)
  • DO_MOVE: Checks the actor's TRACKMODE and moves or rotates the actor accordingly. With TRACKMODE MANUAL, adjusts the actor's SPEED. Also causes a TRACK to advance after instructions are completed.


  • SAMPLE: Caches and plays a sound sample once. More under "sound cache".
  • SAMPLE_THEN: Caches and plays two sound samples once back to back.
  • REP_SAMPLE: Caches and plays a sound sample on repeat.


  • FADE_MUSIC: Causes the current music to fade away and then plays the music given in the parameter. Only used in the floppy release.


  • POSREL: Evaluates the position of the current actor (the one that executes the script) relative to another actor specified in the parameter. Note that the angle of the current actor is considered, not only the position.
    • The possible values are: 0 (any), 1 (left), 2 (right), 4 (front) and 8 (back).


  • PUT: Places an actor at the coordinates given in the first three parameters, in the room referenced by the fourth parameter, and facing the angle given in the last three. Differs from DROP by being forced regardless of space constraints.
  • PUT_AT: Places the actor referenced by the first parameter at the ROOM coordinates of the actor referenced by the second parameter. Differs from DROP by being forced regardless of space constraints.


  • SPECIAL: Spawns three kinds of particle effect actors with ID -2. A parameter of 0 makes the bubbles when a monster dies, 1 is blood and 4 is smoke from the ashtray.
  • CHANGEROOM: Sets actor floor and room (in that order). Does not change the 3D position.
  • SET_ALPHA/BETA: Rotation. Parameters are the angle to rotate to, and how much time this should take.


  • MARK: MARK is used to monitor what part of its TRACK the actor has reached. It's set by the engine when it's reading the TRACK file and encounters a MARK command there, and read by the scripts, often to change the actor's ANIM to the appropriate one.
  • TRACKMODE: Sets an actor's movement mode called TRACKMODE. The second parameter is called TRACKNUM. Actual movement related to these modes is done with the DO_MOVE command.
    • 0 = NONE. This is often used to lock the player's movement before doing something. This doesn't stop all movement though, just anything that would have happened through the other TRACKMODEs. It's still possible to move in a straight line (horizontally if the actor had an ANIM playing, or vertically if falling) and rotate through commands other than DO_MOVE.
    • 1 = MANUAL. LEFT and RIGHT are mapped to rotating the actor. This is obviously just for the PC. Forward and backward movement (and playing rotation ANIMs) require an ANIM_MOVE command to be looped and VAR0 to be 1.
    • 2 = FOLLOW. Follow another actor given in the TRACKNUM. The code used is the same as for TRACKMODE 3 except the target point is another actor and thus can move around. The rooms the two actors are in are taken into account in a primitive way.
    • 3 = TRACK. Follow a TRACK given in the TRACKNUM. More under "tracks".


  • HIT_OBJECT: Sets ACTIONTYPE to HIT_OBJ. This causes the actor to cause other actors' HIT_BY to be set to its ID during any collisions, used with monsters that are supposed to be dangerous to touch. The second parameter is the HITFORCE, the first one seems to be unused.
  • STOP_HIT_OBJECT: Cancels out HIT_OBJECT.


  • HARD_COLLIDER: Returns the ID of the first collider the actor is colliding with (HARD_COL). Only colliders with flag 0001 set affect this.
  • ACTOR_COLLIDER: Returns the ID of the first actor this actor is colliding with (COL[0]).
  • TRIGGER: Returns the ID of the first trigger the actor is inside (HARD_DEC). Unaffected by room triggers.
  • CONTACT: Returns COL[0] or (if empty) COL_BY.


  • GET_HARD_CLIP: Returns the bounding box of the first hard collider an actor is colliding with. Used for rendering the Vagabond: when rendering the individual pixels, any pixel which is inside that bounding box won't be rendered (it will get "clipped"). Because of this the Vagabond will progressively disappear when entering a wall, and then progressively reappear on the other side. The function is specific for the Vagabond. Without that special trick the vagabond would produce weird rendering glitches, similar to when the player is OOB.


  • RETURN: Skips the rest of the LIFE.


  • END_SEQUENCE: This suspends the script that calls it and draws all the art in END_SEQ.PAK file. This is the animation with Pregtz burning. When the animation is done, it resumes the script.
    • The end sequence looks the same regardless of CPU cycles or how fast the timers are ticking.


  • WATER 0, WATER 1: The game emulates there being water that obscures a part of the actors wading around in it at the bottom of E5 and E6 by using a variable that represents the elevation below which no vertices will be drawn. When it's 0, the elevation is set to 10000, when 1, it's -600. It's set to 1 whenever you're in E5 and E6.


HIT parameters:
1) ANIM while attacking
2) ANIM keyframe index when the hitbox should be created
3) hotpoint (indicates which 3D point of the actor model to use as the center of the attack)
4) hit range
5) HITFORCE
6) ANIM after attacking

THROW parameters:
1) ANIM when throwing
2) ANIM keyframe index when the object should be spawned
3) hotpoint index (indicates which 3D point of the actor model the object is spawned at)
4) object to throw
5) boolean (if 0, rotate the object to be throw by 90 degrees in the pitch direction to make it stand up)
6) HITFORCE
7) ANIM after throwing

FIRE parameters:
1) ANIM when firing
2) index of keyframe when the gun is fired
3) hotpoint
4) size of hitbox
5) HITFORCE
6) ANIM after firing


POSREL

0 any
1 left
2 right
4 front
8 back

TRACKMODE

0 none
1 manual
2 follow
3 track

KEYBOARD INPUT

0 nothing
1 UP
2 DOWN
4 LEFT
8 RIGHT

SPECIAL

0 bubbles
1 blood
4 smoke


IMPORTANT/SPECIAL LIFES

  • LIFE 11: Handles dropping items. Can send the player to the damage processing LIFE 553. The last clause that refers to the throwing_or_using_jug VAR isn't actually used.
  • LIFE 39: the death manager. The player is taken here at times when they're supposed to die. There is no way to escape it other than hitting a transition (taking you to #550) or if you had or got back INV.
  • LIFE 59: entered when drinking a flask. You can stay in this LIFE indefinitely if you had an action that is activated through hitting SPACE, and you hit SPACE during the ANIM. There is no way to get hurt while in LIFE 59.
  • LIFE 522, stuff that gets deleted when you defeat Pregzt:
    • E0 zombie, bird
    • E1 2 zombies, bird, 2 nightgaunts
    • E2 Vagabond, knight, ghost, bird
    • E3 jelly, pirate, 3 dancers, 5 spiders, kitchen zombie, 5 dining room zombies, ashtray
    • E4 3 rats
    • E5 Cthonian, bird, 2 wasps, white spider, purple spider, Deep One
    • E6 Deep One (through LIFE 517)
    • Doesn't delete the two portraits nor the crack. Note that it doesn't matter if the ghost/dancers are in their first form or second.
  • LIFE 549 is the one the PC normally has. It does the following:
    • IF player_hitpoints are ever <1, go to LIFE 39 (death manager) without doing anything else.
    • IF SPACE is not held and VAR 0 == 1:
      • INV 1 and ANIM_MOVE and ANIM_SAMPLEs according to which BODY the player has (flask BODY is missing). Also set player_moving_backwards == 1 if the player is taking steps backwards.
    • IF SPACE is not held and VAR 0 == 0:
      • INV 0
    • IF BODY == normal AND player_pushing_something == 1
      • IF ANIM is something other than pushing, or if END_ANIM, set player_pushing_something 0.
      • ANIM_SAMPLE for pushing.
    • IF ANIM == Necronomicon_effect, play ANIM_SAMPLEs in different parts of it.
    • IF ANIM == jumping:
      • IF it's the last keyframe, set flags 0141 (gravity on), otherwise, if not END_ANIM, flags 0041 (gravity off).
      • ANIM_SAMPLE for jumping according to which character was chosen.
    • IF ANIM == dying:
      • Go to LIFE 39 (death manager).
    • IF ANIM == getting hit and END_ANIM, set player_moving_backwards 0.
    • IF ANIM == throwing, play correct ANIM_SAMPLE.
    • IF FALLING == 1:
      • IF player is in E5 on top of one of the death pits, remove control and inventory, set TRACKMODE 0, set type of death 1, go to death_manager, play the right sound effect, set flags 0041 (aside from the first tier, the fall is cosmetic, so gravity is switched off).
      • Otherwise just ANIM falling, take away control and inventory but don't set TRACKMODE NONE, go to LIFE 551 (player_falling). Also set correct fall height based on player's Y-coordinate.
    • IF colliding with a climbable collider, ANIM is not jumping, player_moving_backwards == 0 and there is a space to climb into:
      • control off, inventory off, LIFE player_climbing, play climbing ANIM
    • IF hit by something, spawn blood, go to LIFE 553 (player_hit_by_something).
    • IF throwing or using jug and END_ANIM:
      • control 1, throwing_or_using_jug 0, allow inventory. This is also done in LIFE 11 the exact same way so this is probably just a failsafe.
  • LIFE 550 gives the instructions on how to handle floor transitions and trigger 18 in E3R1 (outside the front doors). The game automatically changes the player's LIFE to this when they've hit any floor trigger. The lower part of the LIFE has instructions for each individual trigger and the upper part extra instructions for the TRACKs the player will be put on as a result.
  • LIFE 553: the hit manager. It's where hitpoints are subtracted and a knockback ANIM is played.
  • LIFE 561 is about the actions you can select in the inventory. "actions" is actually just an actor like all the others you can pick up. It gets "equipped" like any weapon or such with the effect when hitting SPACE being based on the player_current_action VAR. It's initially "collected" in LIFE 562 (the TAKE command).
  • LIFE 562 has initial preparations at the start of a new game right after the intro has finished.
  • The majority of rooms have what we call room scripts. These are scripts that are loaded in only when the view is in that room that contain the code for the player hitting various triggers and colliding with or searching special colliders. The actors they're executed by can exist without a BODY or bbox so you can't see them in the RV at all necessarily.


VARS, CVARS, AND OTHER VARIABLES

  • All the variables are 16-bit signed values and thus can hold values between −32768 and 32767.
  • VARs are only read and set in scripts. CVARs can also be read or set by the engine.
  • Most VARs are boolean flags, and a majority of those are for doors being closed or open. They can generally be changed around without making the game unstable, except for the few sound sample pointers.
  • Most CVARs are important constants such as pointers (hence the name, "constant variable") but a few of them are actually used as flags during gameplay. Changing CVARs used as sample pointers, especially, can result in instability.
    • There are 16 CVARs used by the game even though the array they're in has size 44.
  • VAR 0 (player_has_control) being 1 is closely tied to the concept of the player being in-control of their actions. All SPACE-operated actions (e.g. searching, pushing, firing weapons) are locked behind this VAR, as is ALLOW_INVENTORY, which is matched with it every frame of LIFE 549 unless you're either holding SPACE which delays it, or are not in LIFE 549.
    • By contrast PLAYER.TRACKMODE is a command that can be found in the code somewhere. It governs whether or not your basic movement is available, while LIFE 549 governs which ANIMs are played when the player hits different movement keys.
    • The two are usually tied together but only because the coders kept setting both to 1 or 0 at the same time. There are ways to desync them, though this probably isn't terribly useful since there's no advantage to having less options or being stuck.
    • If PLAYER.TRACKMODE is NONE but VAR 0 is 1, you can still move if you have an action like pushing selected.
    • LIFE 550 (transitions) sets VAR 0 to 0 on every frame.
  • If you take a hit at any point when not having control, it will be returned to you immediately (VAR 0 == 1).
  • VAR 10 is set to 1 when throwing something or using the jug, and it causes control and INV to be restored in LIFE 549 afterwards.
  • VAR 20 (active monster count) is only used to control when scary noises, dripping in E5/E6 and certain music tracks are played by LIFEs 528 to 534. It is often left at an erroneous value on a given floor. It is, however, always reset to 0 by LIFE 550 during floor transitions.
    • Any monster becoming active increases the count and any monster either dying or being placated decreases it, except for the knight. The knight dying doesn't decrease it.
    • The rats can, in theory, be used to make the value become negative by killing them repeatedly, but this requires dealing more than 10 damage in one hit, which can only be done using memory corruption.
    • There are other ways of making this have the wrong value.
  • VAR 24 is set to 1 when you're moving backwards to differentiate between collisions with objects ahead of you and behind you. Mainly used for doors so you can only open them when facing them, but also for whether a climb should be started. It's erroneously left at 0 when shooting the rifle meaning you are able to open doors during that ANIM. If you rotate yourself to face the collider you're attempting to climb, you can also climb colliders after taking rifle shots.
    • Also set to 1 very briefly during knockback.
    • VAR 24 is possible to manipulate by holding SPACE just after changing your ANIM. Its value doesn't change if you timed it right, thus allowing to have the wrong value whether you're moving forwards or backwards.
      • This means you can open doors while facing away from them, or have them not open while you are facing them. The easiest way to do this is to just walk backwards into a door and tap SPACE and DOWN until it happens.
        • You can't seemingly get into a state where you're running but have VAR 24 be 1. For the ANIM to change from walking to running, you have to pass through the ANIM_MOVE command in LIFE 549 but this is also the same clause that will update VAR 24. Then again, if you can get walking with VAR 24 being 1, why can't you get running?
          • After a lot of trials, I couldn't make this happen. Could possibly use it for clipping through a door instead of opening it. E.g. the front doors, though there isn't room behind the front doors for clipping OOB like there would be if you first opened them.
        • You can get a very fast rotation of over 90 degrees by opening a door while facing away from it.
        • If you just needed to open some door outwards and keep moving, you could maybe save a bit of time by opening it while facing the direction you wanted to go. This could tie in with the idea of opening a door outwards in advance in case you later return to the same spot but have to open it inwards.
  • VAR 25 is 1 if you're in the intro. This is mainly used to suppress the scary noises or music changing, but also to avoid the player falling through the E1R1 crack.
  • VAR 28 is set to 1 whenever the player enters "dangerous" areas, e.g. E1R7, E2R5 and E3R1. It being 0 is a requirement for playing random scary noises, dripping sounds in E5/E6, or randomly changing the music in LIFEs 528 to 534.
    • The VAR is set to 1 when the player is in:
      • E1R7 when the Nightgaunts counter is not 0
      • E2R4
      • E3R1
      • E3R2
      • E3R3
      • E3R4 if the statue has been searched
    • The VAR is set to 0 when the player is in:
      • E1R1
      • E1R7 if the Nightgaunts counter is 0
      • E2R2
      • E2R4
      • E3R0: the floor scripts are erroneously only active in R0.
      • E3R4 if the statue has not been searched


  • VAR 33 is Pregzt being alive (0) or dead (1), affecting multiple areas in the game, most notably the front doors. CVAR 12 is also set when he dies.
  • VAR 105 is never set to other values than 1. If you use the Viewer to set it to 0 before opening the front doors, the jelly never attacks you and the doors never close.
  • VAR 205 is unused. It is set to 1 when the player is in E5 or E6 and 0 when they leave.
  • CVAR 11 is for reversing the direction of the lantern flying through the air so it bounces off Pregzt, presumably so it doesn't get lost. More precisely, it gives the ID of the actor that causes this to happen, but it's only used with Pregzt.
  • CVAR 12 is set to 1 when Pregzt dies. This prevents changes to the LIGHT variable and causes the related message not to be shown, and also changes the backgrounds that have the Pregzt tree in them.
  • CVAR 13 is used to store the ID of the actor in whose location the light circle should be placed when the screen is dark. This is done in the maze in E6. It's also erroneously shown in other dark areas when any blood or other flow actors have been spawned, because they pass the check for getting into the render loop (which is just !=-1) and their ID is set to -1 as they're deleted (the value CVAR 13 usually has) right before CVAR 13 is checked in the renderer.
    • The values aside from -1 CVAR 13 is set to in the dark maze are 1 (PC) and 13 (lamp if it was dropped).
  • CVAR 14 governs the smoke in E3R13 (see LIFE 427).
    • 0 : if player is in E3R13, create a special smoke actor using "SPECIAL 4" command
    • 1 : smoke animation
    • 2 : if player is in E3R13, play player cough sound and deduce health points
    • 3 : destroy smoke, then go back to 0
    • The CVAR 14 transitions are hard-coded in the game engine:
      • 0->1 happens immediately
      • 1->2 happens after 8 seconds
      • 2->3 happens immediately
      • 3->0 happens after 11 seconds
    • Though the 2->3 transition is supposed to happen instantly (handled by the renderer part of the engine for some reason), if you visit the inventory just as it's about to happen, the game skips rendering a frame and thus it never goes to 3. Thus the player takes an additional 5 damage from the smoke before the next frame has been rendered.
  • LIGHT is what controls whether the game draws the current room or not.
  • Other VARs etc. are discussed in specific parts of the guide.

SCRIPT INTERFERENCE

  • Under some conditions, executing a part of a script can be skipped in an unintended way. This generally involves there being a waiting time for some condition to have been met without the player having lost control completely, or something else happening on the same frame when control is lost. These conditions could be:
    • IF END_ANIM : every ANIM takes time to finish, if they get interrupted, this condition being met is delayed
    • IF FRAME : ANIMs take time to reach a given kf
    • IF MARK : reaching a particular part of a track
    • IF ALPHA/BETA/(GAMMA) : waiting for a rotation to finish, gamma rotations aren't ever checked for
    • IF CHRONO : waiting for a timer to reach some value
  • ANIMs themselves can be interrupted if the command used to set them was ANIM_ONCE or ANIM_REPEAT, or if it's the second ANIM given in an ANIM_ALL_ONCE command.
  • There are many non-ANIM_REPEAT commands that are coded in a way that makes them repeat (e.g. open/search), but unless ANIM_REPEAT was used, they do go to the second ANIM given as a parameter for a frame, which can have implications seeing as the second ANIM is almost always idling and the ANIM being idling is used for a lot of logic checks.
  • If you drop something, LIFE 11 gives you back INV and LIFE 549 at the same time. You can immediately go back to the inventory before LIFE 549 has been executed because the main loop has the inventory part before LIFEs. This way, you're preventing the falling ANIM and LIFE being entered, and can continue dropping items.
    • Another way to see this happening is manually setting the PC's health to 0 during the dropping: you're not sent to LIFE 39 while you're in this kind of loop.
  • The following examples give a general idea, though there are too many potential ways to interfere with a script to list out completely – in fact a lot of the unusual occurences mentioned in other parts of the guide can be explained by similar means.
  • The hover trick: When the player goes to LIFE 11 as they're trying to drop something, after the jump has finished, NEXT_ANIM is idling. There is a case in LIFE 11 for that ANIM that causes it to return straight to the normal LIFE 549 (which is why nothing is dropped either). The condition for jumping FRAME >= 7 isn't ever met in LIFE 549 and so the gravity flag isn't ever returned.
    • If the player passes between E5 and E6 during the jump, and only drops something after the transition, the following will happen:
      • LIFE 561 queues jumping ANIM then idle ANIM.
      • LIFE 549 sets gravity flag off.
      • Any of the triggers forces LIFE 550, which sets ALLOW_INVENTORY 0 (but does not remove control) and sends the player on the other side while also sending them back to LIFE 549.
      • Because the player has control, LIFE 549 sets ALLOW_INVENTORY 1 again.
      • Dropping anything sets INV and control 0 (in that object's LIFE). Also causes the player to go to LIFE 11. If not HIT_BY or throwing_or_using_jug, LIFE 11 does nothing until ANIM goes to idling. Then ALLOW_INVENTORY 1, player_has_control = 1, and sent back to LIFE 549.
      • Gravity flag is not returned because both ANIM == jumping and FRAME => 7 were not true at the same time. Player is left with control.
    • If the dropping happens BEFORE the transition:
      • LIFE 561 queues jumping ANIM then idle ANIM.
      • LIFE 549 sets gravity flag off.
      • Dropping anything sets INVENTORY and control 0 (in that object's LIFE). Also causes the player to go to LIFE 11. If not HIT_BY or throwing_or_using_jug, LIFE 11 does nothing.
      • Hitting the transition forces LIFE 550, which sets ALLOW_INVENTORY 0 (was already 0) and sends the player on the other side while also sending them back to LIFE 549.
      • The jump finishes, so gravity is set back on. No part of LIFE 549 can restore control or INV in this situation. The player is completely stuck unless something hits them.
  • The reason holding SPACE while descending or rising up stairs can result in being left in the wrong ANIM is because it doesn't allow the part of LIFE 549 to be accessed where ANIM_MOVE is called, and thus e.g. SPEED 0 goes into the idle ANIM 4 instead of whatever is appropriate for the current BODY. It requires you to have had a null SPACE action before or that action (and related ANIMs) get played instead.
    • The ANIM you're left with depends on the SPEED you're left with. Only going up and down between E3 and E5 doesn't set your SPEED to 0 right at the end, and so you're sometimes left with SPEED 4 instead. This corresponds to walking up/down the stairs.
    • The transitions from E3-E2 and E4-E3 both end (in LIFE 550) with ANIM_ONCE walking idling. This seems to be why holding SPACE as they're ending causes exactly one cycle of the walking ANIM. Again, only works if the SPACE action is null.
    • There are details here that are not understood, like why the infinite walk between E3-E5-E3 doesn't work consistently.
  • Why is the player left with the flask body after trying to drink the flask while also entering a transition?
    • This only seems to happen if there was a frame or more between starting to drink and hitting the transition. If there is none, LIFE 58/161 sets PLAYER.LIFE to 59 but it gets overwritten on the same frame by LIFE 550 and so no code from 59 is executed. Only the hitpoints are added.
    • With a frame in-between, the BODY is changed and the ANIM starts to play out but is obviously interrupted.
  • You can avoid returning to LIFE 549 after dropping something. This only requires buffering ENTER after the drop. This works because INV comes before LIFE execution in the main loop and dropping something overwrites the LIFE you were going to with 11. Thus you can chain drop items and avoid anything happening that normally happens in LIFE 549 indefinitely.
    • The most obvious effect of doing this is avoiding any deaths by falling.
  • When you're falling during vertical desyncing, you don't enter the falling ANIM because you're in LIFE 550. LIFE 549 is supposed to do all of this: set falling ANIM, player control 0 and inventory 0 (already 0), LIFE player_falling (which also sets falling ANIM and sends it to LIFE player_landing after FALLING = 0), set fall height. Because this stuff doesn't happen immediately, if you lag it so you fall to the ground instantly, it may never have time to happen. Hence there's no landing ANIM and you don't take damage.
  • There should always be a one-frame window after doing something has set VAR 0 to 0 but not removed the inventory access before the inventory actually becomes unavailable, at least if LIFE 549 was executed before whatever set VAR 0 to 0 in the frame.
  • If some script is waiting for END_ANIM on an ANIM that's normally going to play out in full, but you've found a way to interrupt it and have control, you can keep changing your ANIM making sure no ANIM ever finishes to delay the effect actually taking place. E.g. restoring VAR 0 and INV to 1 in LIFE 549 if VAR 10 is 1.

SYSTEMATICALLY STUDYING LOGICAL COMBINATIONS IN LIFES

  • There was a modest attempt made to systematize studying the scripts in a way that accounts for every possibility in how different conditional clauses can be combined within the same LIFE, with only the pure logical constraints as a primary concern.
    • Example, LIFE 11:
]if A
	if B
		...
		if C
			...
		else if D
			...
		end
		...
	end
else if X
	...
end
if Y
	...
end
if Z
	...
end
    • In this LIFE there are a total of four simultaneously true clauses if we count A and B and the same clause (just A by itself doesn't cause any code to be executed). For example ABCYZ would be one such combination. Two rules must be adhered to: 1) if a condition is true, the parent conditions must be true as well. 2) if a condition is true and part of an IF-ELSE block, the previous conditions must be false. This is a full list sorted by the number of blocks of code executed (the number on the left):
4 ABCYZ
4 ABDYZ
3 ABCY
3 ABCZ
3 ABDY
3 ABDZ
3 ABYZ
3 XYZ
2 ABC
2 ABD
2 ABY
2 ABZ
2 XY
2 XZ
2 YZ
1 AB
1 X
1 Y
1 Z
    • Now, if we want to make any use of this list, we should try to simplify it and pare it down to a minimal version that still contains all the actually interesting combinations. First we might simplify it so AB is replaced with just B. We might as well remove C, too, since all it does is displays a message, which we're assuming isn't any more interesting than the same combination without the message. We're left with this:
4 BDYZ
3 BDY
3 BDZ
3 BYZ
3 XYZ
2 BD
2 BY
2 BZ
2 XY
2 XZ
2 YZ
1 B
1 X
1 Y
1 Z
    • Next, we might reorder the combinations according to what LIFE they lead into:
Stay in #11
3 BDZ
2 BD
2 BZ
1 B
1 Z

Go to #549
2 XZ
1 X

Go to #553
4 BDYZ
3 BDY
3 BYZ
3 XYZ
2 BY
2 XY
2 YZ
1 Y
    • Next, there's more combinations that don't seem interesting (because they happen all the time or correspond with another combination in their effects). This rather intuitive paring down results in this list:
Stay in #11
3 BDZ
2 BZ

To #549
2 XZ

To #553
4 BDYZ
    • This could be seen as some kind of master list of the combinations that are different from what normally tends to happen in a potentially interesting way.
    • This approach would have to be refined quite a lot for it to become the go-to when hunting for oddities and opportunities in the scripts. It only takes into approach what happens in exactly one LIFE both in a horizontal sense (on different frames) and in a vertical one as well (different LIFEs on the same frame). This by itself wouldn't have been enough to reveal the potential for the Null Focused Actor Glitch between LIFEs 6 and 16.
  • To test a specific combination, aside from the obvious method of just trying to accomplish this in regular gameplay, it's possible to use a tool like Cheat Engine to edit RAM using the method described below.
    • You start by pausing the game's execution. Then you make changes to the script in question, advance by one frame, revert the script the way it was, and see what the outcome was. (Pausing and frame advancing are possible using, e.g. the DOSBox Debugger.
    • LifeDISA prints the byte data of the scripts together with the humanly readable version. This byte data can be searched for in the RAM editor. If you search for e.g. LIFE 5:
      • The two IF conditions can be reversed by changing 05 to 04 (line 124) and 04 to 05 (line 131).
Here is the table of opcodes for the IFs

0400 == 
0500 <>
0600 >=
0700 >
0800 <=
0900 <

SYNTAX COLOR IN NOTEPAD++

Script should have ".vb" extension, Notepad++ should automatically recognize it. It not, do the following :

In Notepad++, click on "Language" > "V" > "Visual Basic".

RANDOMNESS / RANDOM NUMBER GENERATOR / RNG

  • To determine outcomes of supposedly random events, a function is used that creates a long sequence of numbers that loops without any numbers ever repeating. The first seed comes from the system date and time. The word "seed" is used to mean either the first seed when RNG is initiated, or whichever number is being used at any given time.
  • There are only a few places where AitD uses a RNG:
    • Choosing Emily or Edward in the attract mode.
    • With special/flow actors' particles (to initiate positions, angles, speeds, ...).
    • The RAND function in the LIFEs (used for scary noises/music changing, the pirate's attacks, and the falling boulder positions after defeating Pregzt).
    • The RND_FREQ command.
    • Noise textures do not utilize RNG.
  • Manipulating the RNG: If you know the last value, the next value that comes out of the RNG can be predicted. However, which purpose it will be used for will be very difficult to keep track of or predict a long time in advance. This is because RNG tends to be called even multiple times every frame by various LIFEs and the order of execution (which depends on the details of what the player has done in the playthrough) determines which seed is used for what.
    • Under typical circumstances, the game will use x + 1 or x + 2 RNG values per second. X is the current framerate (number of main loop executions, including processing the LIFEs). X number of calls are from a RND_FREQ command in LIFE 549 (for all PC BODYs except for the flask BODY), and 1 or 2 go to the RAND function in the random noises script that is currently active, e.g. LIFE 532. If SPACE is held while in LIFE 549, the RND_FREQ command is skipped and the frequency of RNG calls falls to just the 1 or 2. This is also true if the PC is in a different LIFE.
  • The RNG in AitD uses a LCG implemented like this:
rand() {
   seed = (seed * 22695477 + 1); //will wrap around 2^32 since seed is 32-bit
   return (seed >> 16) & 0x7FFF; //return a value between 0 and 32767
}
    • The output value can be further processed to get some specific range:
rand(min, max) {
   return min + rand() % (max - min);
}
      • Note that because of using the mod function like this, the same seed will give a different result in different RNG functions.
    • The seed is initialized during start-up, using time(), which returns how many seconds have elapsed since 01/01/1970. However. it only uses the lower 16-bit part of it, meaning it wraps around every 65536 seconds (18 hours 12 minutes). There are 86400 seconds in a day, and so some values can be reached in two different ways.
seed = time() & 0xFFFF
    • You can reverse seed (retrieve the previous seed from a given seed) using this formula:
seed = (seed - 1) * 690295837

The above constant can be calculated like this:

(javascript):

function extendedEuclid(a, b) {
	if (b == 0) {
		return [1, 0];
	}
	else {
		var q = Math.floor(a / b);
		var r = a - b * q;
		var [s, t] = extendedEuclid(b, r);		
		return [t, s - q * t];
	}
}
extendedEuclid(22695477, 4294967296)[0];
  • To know what system time will give a specific RNG seed at game start, you can use the following javascript code:
(() => { 
	var seed = parseInt(prompt('Seed ? (0000-FFFF)'),16);
	if (isNaN(seed) || seed < 0 || seed > 65535) return;
	var date = new Date();
	var tmz = 18000 - date.getTimezoneOffset() * 60;
	var time = Math.floor(date.getTime() / 1000) + tmz;
	var delta = seed - (time % 65536);
	if (delta < 0) {
		delta += 65536;
	}
	alert(new Date((time - tmz + delta) * 1000));
})();
    • To run the code, you can e.g. use Chrome (press CTRL+SHIFT+I, then paste the code into the "console" tab and press enter) or use JSFiddle. The code has been tested across different time zones.
  • Avoiding random noises: Uncached sound samples always come with at least one frame of lag with two exceptions (more under "Sound Cache"). Given that only the random values 0, 1, and 2 ever cause random noises to be played (in the RAND 300 calls used in E0 through E4 the value 2 only changes the music without causing lag), if these specific values were possible to avoid completely, this would save time in the order of frames or a few seconds over an entire run.
    • For RAND 300, the RNG seed that gives the longest string of values other than 0 and 1 is seed 0x9276. It avoids those values for the first 1701 calls. During normal gameplay, this string will last about 28 seconds. Without the RNG calls in LIFE 549, it would be enough to guarantee over 28 minutes of silence.
    • For RAND 15, the longest string without 0, 1 or 2 is the one starting from seed 0xAAAB. It only lasts 44 calls, and as such can in theory be extended to 44 seconds. However, since RAND 15 is only ever used paired up with another RAND 300 call on the same frame, at least two seeds are used per frame, resulting in just 22 seconds of silence.
    • There are longer sequences but they require waiting in-game first, because only seeds of the form 0xXXXX are available from the start. E.g. the longest possible sequence that avoids the values 0, 1 and 2 for RAND 300 starts from the value 2C88E366, which is fastest to reach from the initial seed 0x7826 after 184512 seeds have been used, which amounts to roughly 51 real-time minutes of waiting.
    • To remove the LIFE 549 RND_FREQ calls, you could either get the flask BODY or hold down SPACE. The flask BODY requires picking up a flask or jug, and generally prevents running. Holding SPACE will normally overwrite the running ANIM due to the actions FOUNDLIFE 561. However, if INHAND is one of a few specific actors, this won't be the case. Specifically, it might be available to keep the lamp INHAND to avoid SPACE interfering with running (and simultaneously allowing lamp turns and lamp snaps, see the relevant passages).
      • In principle, if you were able to memorize/predict when the next noise-producing random value was coming, you could then release SPACE and hope it gets used by LIFE 549 instead, which is likely. Under TAS conditions, this could probably be used to avoid random noises completely.
    • These calculations have a fair margin of error because of the fact that the precise number of RNG calls per second is difficult to predict. The game targets 70 FPS, but in practice will run at less than 60 most of the time at 11000 cycles, and thus LIFE executions will be less frequent as well, and so even with LIFE 549 active, the noises will be less frequent than estimated here.
    • Instead of trying to remove all random noises, you could also choose a seed that gives the least average amount of noises over some stretch of time. The following figures assume 60 FPS:
      • Just RAND 300 in E0–E4: The chances of getting a noise to play on average are 2 / 300 = 0.67% per call. To reduce the chances as much as possible for the first 18000 calls (five minutes at 60 FPS), seed 0x71C2 can be used, dropping it to 0.46%. The worst possible seed for that would be 0x9C8A, which gives a 1.05% likelihood instead.
      • RAND 15 and RAND 300 in E5–E6: The RAND 15 by itself gives a noise with an average likelihood of 3/15 = 20% per call. The RAND 300 has likelihood of 0.67% per call. The combined likelihood of getting at least one noise (getting two would in practice only cache one sample and so is exactly as bad) is 20.5%. The best seed to drop the number of noise-producing calls in the first 18000 RAND 15 / RAND 300 calls is 0x2883, which gives 19.2% likelihood of noise per RAND call (3471/18000).
    • Because what seeds are best depends on the particulars of your run, more detailed discussions of RNG-based lag reduction should be had in the notes for the relevant category.
  • RND_FREQ returns a random value that is used to apply a pitch shift on all samples played. Generally turned back to 0 after it's been used with a specific sample but can still cause other samples to be affected than the one it was intended for in some situations.
    • See more details under "TECHNICAL NOTES".

GAME WORLD

  • The game world is a cube of size 65536 (-32768 to 32767) that wraps around in all directions.
  • Coordinates – X: West-East, Y: vertical (inverted), Z: North-South.
  • AitD uses extrinsic rotations (i.e. rotating around fixed axes instead of ones that rotate together with the actor) probably done in the order alpha-beta-gamma (X-Y-Z). Alpha starts at 0 when the actor is horizontal and causes it to somersault backwards as it increases. Beta is 0 when the actor faces S (in the Model Viewer, this is where the camera is) and the actor spins counter-clockwise when it increases. Gamma is 0 when there is no roll and as it increases, the actor's W side tilts up and E side down. The actors start facing 0 alpha, beta and gamma in the Model Viewer.
    • The rotation axes are different for cameras.
    • The angles given in the OBJETS.ITD file correspond with how actors are initially oriented in the game world during the intro (i.e. the E2R0 W doors are open etc.). Some actors get their initial angles set by the scripts instead.
    • The bow in E1R0 might look like it's facing E but it's really facing S as given in the file.
  • Each room has 3D floor coordinates, e.g. E1R0 = (5750, 0, -3280). This defines how far from the floor's center the room is located to make sure collisions between actors in different rooms work right. It's somewhere near each room's center but not exactly at it. The following image shows the rooms of E1 first all placed at their own origins, then moved to their floor coordinates.

Room Coordinates.png

  • The three kinds of actor coordinates are:
    • ROOM: Local coordinates relative to the floor coordinates of the room the actor is in. Used to detect collisions between actors and colliders and trigger collisions, including camera triggers and background mask triggers.
    • WORLD: Coordinates relative to the floor coordinates of the room the focused actor is currently in. Used when comparing the positions of two actors. This is also where actors are rendered.
    • ZV: Bounding box coordinates relative to the floor coordinates of the room the actor in question is in.
    • ROOM and WORLD coordinates are horizontally centered and at the actor's feet while ZV (as given in the RV) is the centerpoint of their bbox, and thus in the air, for most non-rotating actors (there's a few exceptional ZV positions lower down which are most likely erroneous). While the RV shows these as one point in space, in reality, the game defines bboxes as two points on opposite sides of the bbox cuboid. Because of rounding, the ZV coordinates shown in the RV may be off by one unit along any axis. Actors that rotate often rotate not around their centerpoints but rather around some vertex (e.g. doors). Those actors have their ZV position adjusted accordingly by the RV so it's centered at that vertex since all rotations in AitD must happen around (0, 0, 0). However, this gives an incorrect bbox which does not correspond with the one used by the game for collision checks.
  • world_pos = local_pos + room_pos
  • When checking for collision between two actors A and B which are in different rooms, calculations like this are done: bounding_boxB = bounding_boxB + (roomB_pos - roomA_pos)
  • Because of some kind of rounding, sometimes positions will be one off from what you'd expect. In the guide, this has sometimes been rounded back up or down to the nearest round number in contexts where the difference is clearly not permanent (so 1699, 1700 and 1701 could all amount to the same thing).
  • The rooms aren't really connected in the way suggested by how they're displayed in the Viewer. The room the focused actor is in is always placed in the middle of the plane (its center at the origin). If an actor's ROOM coordinates hit a room trigger, it causes the game to stop checking for collisions with static things in the previous room and start checking for those in the new room. Thus the whole floor (the rest of the colliders, triggers and most actors) effectively doesn't exist all at once, and for this reason moving around OOB, you still have to pass through every individual room along the path to make the final destination reachable.
    • Most but not all actors are loaded in on a basis of whether they could be seen from the present camera view or not. This usually roughly translates to being in the same room as the view or any adjacent one. What determines this is the actor's LIFEMODE. More under "actors".
    • Actor-actor collisions and hit/fire collisions are checked regardless of which room each actor is in.
    • Dropped or thrown actors cannot move between rooms even if they end up inside room triggers.
  • Floors: The game has several areas that are completely zoned out from each other. They're referred to as "etages" (Étage is French for "floor") in the game's files.
    • E0: loft
    • E1: 3rd floor
    • E2: 2nd floor
    • E3: 1st floor
    • E4: cellar
    • E5: caverns
    • E6: end
    • E7: outside (only used in cutscenes)
    • When a new floor is entered, the game loads in all of the colliders for that floor into RAM at once.
  • What your relative position to another actor or collider is (left, right, top, bottom) depends on your orientation as well as position. This is checked e.g. when searching certain colliders for items.
  • All bounding boxes etc. are axis-aligned, i.e. they're rectangles with orthogonally aligned sides. The only exception is camera triggers (and 2D masking triggers, not shown in the RV).
  • The actor being followed is referred to as CURRENT_TARGET in the code and the "focused actor" in this guide.
  • The compass directions (South, West, North, East) are shown correctly by the Room Viewer's default view which also corresponds to the map given in one of the books (Story of a Louisana Plantation). Thus the stairs down from the loft are on the E side of the room etc.
    • Abbreviations are used in the guide: S, W, N and E.
  • Every time the game has some kind of proximity check, it seems it uses a primitive method of summing the X-difference and Z-difference (and presumably Y-difference) between the coordinates instead of calculating the real distance by Pythagora's.
  • There's a few rooms cut from the shipped versions of the game:
    • E2R9 – some kind of connecting passage between R7 and R10 that also has two camera views, neither of which is fully drawn-out. One of these can be reached in-game. Instructions under "Camera views".
    • E3R6 – a room in which the staircase leading to the cellar turns towards the S.
    • E4R1 – what looks like another, perhaps hidden, stairwell exit.
    • In addition, E5R2 has a partially blocked-off E branch that's still accessible by running through what looks like a part of the tunnel wall in R5. You can clip out the other end to get back to the worm tunnels.
  • Some rooms have colliders in the corners whose sole purpose is to prevent the player from entering a blind area where they can't see themselves in any camera view. That or to prevent clipping into the walls.
  • The walls are 3750 units tall inside the house and 2000 units underground. If you're exactly 3750 units above the floor level inside the house, you can still trigger triggers but can pass over the colliders.
  • There are four main tiers across E5-E6 (with some walls reaching a fifth one) where the player's ROOM coordinates are at 0, -2000, -4000 and -6000 units respectively, with the axis being inverted so 0 means the lowest level.
  • When you enter a new room, if a door is loaded in around you but you landed inside the room trigger exactly with no further movement, you can avoid the door opening, or more generally colliding with the thing you're inside or right next to.
  • Most wall colliders overlap at the corners. This might have simply been the easiest way to edit them in but it does have the effect of preventing sideways clipping through them. More under "sideways clipping".


COLLIDER FLAGS

  • Collider flag uses:
    • Flag 0001: Does nothing by itself.
    • Flag 0002: With FLAGS 3 (both 0001 and 0002 and nothing else), when collided with the game sets the colliding actor's HARD_COLLIDER variable to the value 255, which is used in the scripts to identify climbable colliders (in E5 and E6).
    • Flag 0008: With FLAGS 9 (both 0001 and 0008 set and nothing else), when collided with the game sets the colliding actor's HARD_COLLIDER variable to the ID of the collider in question. This is used in some scripts. In addition, colliders with FLAGS 9 are sometimes used for instantiating actors that have the room scripts for that room/floor. Those actors will have zvType COLLIDER with the COL ID field (the next one in OBJETS.ITD) showing which collider to use.
    • Flag 0004: With FLAGS 4 (flag 0004 set and nothing else), the collider is used by the TRACKMODE FOLLOW algorithm to find entrances to other rooms. More under "TRACKMODE FOLLOW".
    • In the RV, colliders with flag 0008 are rendered in blue. Colliders with flag 0004 are rendered in cyan. Colliders with flag 0002 are rendered in dark gray. Other colliders are shown in light gray. Colliders that don't have either flag 0004 or 0008 all have ID 0, which is not shown in the RV.
  • There is also a secondary use of the 0001 flag: it seems generally any collider that has it belongs to something that is supposed to be visible in the game's graphics. The colliders that don't have it are typically the ones used to fill gaps and prevent the player from entering blind corners. Thus this flag was probably used when drawing the 2D background images so such dummy colliders don't obscure the walls, and/or to tell the artists no 2D mask is required for that collider.

TRIGGERS

  • Shown in the Viewer in red (room triggers), yellow (floor triggers), orange (effect triggers) and a mosaic of camera triggers.
  • Room trigger IDs are unique per floor and correspond with which room the trigger sends an actor into. Floor triggers all have unique IDs and their effects are determined by LIFE 550. Effect triggers are unique per room and their effects are defined in the room's script[s]. Camera trigger IDs are unique by floor.
  • There's no real reason for the room triggers to be as big as they generally are.
  • If you enter a room trigger that causes another trigger to load in where you're stood, you instantly trigger that one as well without being able to do anything in-between. If you enter two room triggers at once, the same one always takes preference.
  • The effects of floor triggers (0-18) are defined in LIFE 550, which the player automatically enters after hitting any such trigger.
    • If you hit a floor trigger, and a LIFE other than your own is polling for PLAYER.TRIGGER_COLLIDER, if the floor trigger happens to have the ID the LIFE is looking for, it will have the same effect as standing in an effect trigger with that ID. This only seems to be relevant in E6 where leaving the floor through the E5R11 side can cause a fireball to be fired (more under "PREGZT"). There are no floor triggers anywhere else that can trigger anything other than a transition.
    • Hitting a camera trigger doesn't affect PLAYER.TRIGGER_COLLIDER.
  • All the triggers are activated based on the actor's ROOM (not WORLD or ZV) coordinates. A ROOM coordinate of 0 is still high enough to touch triggers in E5 and E6. 1 is too low.
  • The player is usually the only actor that can trigger non-room triggers. However, camera triggers are triggered by whichever actor the game is following (not always the player).
  • If an actor hits multiple room triggers at once, the priority is dictated by the order of those triggers in the ETAGE game data file (not based on their IDs).
    • Because an actor can only store one trigger collider in the TRIGGER_COLLIDER field, whenever there are overlapping triggers, this can cause some effects that are supposed to be simultaneous to become mutually exclusive instead.
      • This causes the mat in E1R2 to soften the step sounds only when the PC is not also standing inside the other trigger.
      • The nested triggers in E5R0 work as intended because trigger 0 is entirely inside trigger 1, and being in trigger 0 is treated in LIFE 457 the same as being inside both of them.
  • The actor 0040 flag is what determines if collisions with room, floor and effect triggers can occur. This is set to 0 during all transitions based on an instruction in the TRACK in question. Camera triggers can still be entered.
    • The related code is completely skipped. There is no special usage of the HARD_DEC (trigger collision variable) field like with the HARD_COL field when collider collisions have been disabled. HARD_DEC is simply -1 if trigger collisions are disabled.
    • The 0040 flag is never changed directly by the engine.
  • Room triggers have no flags set. Effect triggers have flags 1 and 8. Floor triggers have flags 2 and 8.
    • It seems flag 8 the one that causes the game to change the player's TRIGGER_COLLIDER. This is why room triggers don't set this, and so when you enter the R1 trigger, this doesn't do the same thing as entering the effect trigger with that ID.
    • Camera triggers work differently from the others and don't have a field for flags.
  • Looks like the game ignores collisions with other floor triggers during a floor transition (TRACKMODE TRACK) specifically even after the step where "enable triggers" has been called. This was tested by making the PC fall down due to gravity and hit another floor trigger (in E3R5) while also having hit the target position for being sent up to E2.
    • Alternatively the game didn't have time to react to the new TRACK calls.

TRACKS / TRACKMODE TRACK / TRACKMODE FOLLOW

  • The file called "LISTTRAK.PAK" contains all the so-called TRACK instructions which guide actors moving along a set path. This covers things like birds crashing in through windows and the player moving up and down stairs. The Viewer shows the actor's TRACK and TRACKPOS. The latter tells you which instruction is being executed. (TRACKPOS is the same field that stores collectible items' weights).
  • The numbers on the left in the TRACK file are byte offsets for each instruction. Some instructions require extra parameters, which is data stored in the next few bytes. This is why some numbers are skipped. The game also reads the track instructions based on byte offset numbers.
  • TRACK scripts require calls to DO_MOVE to move to the next step. Some commands are executed in one frame, others (like rotating) happen bit by bit on every DO_MOVE call. Multiple commands are never executed on the same frame, unlike LIFE scripts, which causes TRACKS to be a bit "sluggish" and allows potential exploitations of this slowness.
  • Some of the less obvious commands:
    • disable triggers (disable the 0040 flag): disables being able to hit triggers other than camera triggers so things like entering staircases don't happen multiple times.
    • disable collisions (TEST_COL 0): makes sure the actors don't get caught on colliders or other actors (except they can still be caught on pushables). HARD_COL will still be set to 0 when moving and not colliding with anything or 1 when moving and colliding with something (while stationary, HARD_COL is left at -1) while setting COL and COL_BY values normally, which allows things like picking up items and opening doors. Whenever HARD_COL has values 0 or 1, this counts as colliding with the interactable collider with that ID in the scripts, which can erroneously cause things like picking up an item that you're far away from.
    • mark X: a marker that associated LIFEs can make use of to know when the TRACK has reached a certain position (via an IF MARK == n clause or such). Has no other function.
    • rewind: starts the TRACK again from step 0. Used for creating patrol paths and the dancers' endless dancing.
    • walk stairs on X/Z: explained below.
    • rotate: these commands are absolute and use the same 360-degree circle as the Viewer, e.g. "rotate 90" means the actor turns to face due E, 180 is due N etc.
      • if the command has one parameter, it happens slowly, if it's three parameters, it's instantaneous.
    • speed: sets the actor's SPEED parameter (only used with the PC), used to get them into the right ANIM. More under "speed".
    • store position: records the actor's current coordinates for use with walk stairs commands.
  • "goto position" instructions: Guides the actor to a target point in the target room. The actor following the instruction can either turn left, turn right, or do nothing if it's already facing the target point with a 4-unit (1.4-degree) threshold on either side. This threshold is there to prevent the actor from shaking left and right on every frame in some cases. For the PC, the appropriate special ANIMs are given by the ANIM_MOVE command in LIFE 550 based on the SPEED variable. For other actors, which don't have the SPEED parameter, they stay in the ANIM they had. Thus the goto command only rotates actors, it doesn't move them.
    • These goto commands assume there's a direct path to the target destination, i.e. the logic that is used to navigate from room to room when an actor has TRACKMODE FOLLOW is not used. This is why actors that start their TRACKs at an unintended time tend to get stuck. If the actor in question has no collisions, it can cause them to move to where the ROOM indicated by the command is but without passing through the room triggers to actually change its current room.
  • TRACKMODE FOLLOW uses almost the exact same code as goto commands in TRACKs. The same 4-unit/1.4-degree threshold is used to determine if the actor should be rotated. This means if you want an actor following you to face a particular angle, you might not have to be in the very most exact position. The error margin is tan(4/1024*360 deg) * 1000 = 24.5486 units per 1000 units of distance from the actor in question.
    • The FOLLOW command does take rooms into account instead of assuming the actor is already in the correct one. The way this happens is the game loops through all colliders with FLAGS 4 (which are all placed at points where rooms are connected) and sees if one of them has an ID that's the same as the room number of the room the followed actor is in. If such a collider is found, the chasing actor starts moving towards the center of that collider. If there are no matching IDs, the game chooses the last such collider in the list and uses that one, which is why actors can often get stuck in loops when following something that's more than one room away. If no colliders with FLAGS 4 are found at all, the game chooses the first collider in the room (doesn't actually happen since there are no enemies that follow the PC in E6).
  • It looks like the ROOM coordinates are generally used for reaching waypoints in a TRACK, including in a staircase, based on the TRACK ending earlier if those are nudged closer to the end point.
    • There's something quite unclear about how this works in a ROOM/WORLD coordinates desync. During such a time, the PC is unable to reach stairs waypoints, just circling around them with the wrong Y-coordinate. To see this effect, load the save file called "Hold Up For No Focused Actor.ITD" and just hold UP when it resumes so the game both moves the focus to and deletes the S Nightgaunt at the same time. Works most of the time, at least in the CD-ROM version.
  • The Indecision Bug: You can get an actor with TRACKMODE FOLLOW or TRACK to move endlessly in the wrong direction. The requirement is that it faces exactly away from its target and it also generally has to be orthogonally aligned with it. This is easiest to see if you enter E3R2 from R1 with the pirate in its initial position and with you being at or near X 100 in R1, then waiting until the pirate is around Z 2000, then pulling back to R1. This should cause the pirate to go straight to step 4 of TRACK 6 and because it's facing exactly S while the target point is due N, it won't rotate at all and instead walk out of the room. The same can be done with actors that are facing non-orthogonal angles, however due to the granularity of positions, the bug tends to end quickly, unless the angle is a precisely diagonal one.
    • If the alignment is non-orthogonal, the trick works in principle, but the conditions are usually broken soon after as the actor's movement causes it to no longer be facing a perfectly opposite direction (due to being aligned to a grid). The exception is the 45-degree diagonal which seems to work for at least stationary monsters.
    • Facing due W doesn't lead into moving due W, instead being forced to move a little bit S as well due to some rounding effect. Thus the trick works very poorly with a monster facing that way. You'd have to be able to match its Z-coordinate every frame to keep it moving W.
    • With TRACKMODE FOLLOW, some ways to set this up:
      • Get the actor to warp past the actor it's following. Since there is no threshold to this, the warp distance can be much shorter. Thus at least the arrows can be gotten to fly straight by such means.
      • Get the actor to only enter TRACKMODE FOLLOW when it's in the right relative position compared to the actor it's going to be following. This could mean e.g. approaching a patrolling monster from straight behind.
      • Get the actor facing the right angle and moving in a straight line. Now go around it but manipulate Timer 2 so it goes back to facing that angle instead of following your direction. When you're behind its back, manipulate it once more.
    • With TRACKMODE FOLLOW, you can also imagine a situation where two actors, both following each other with one facing away from the other, are locked in motion along an arbitrary line due to moving the exact same distance on every frame. Even if this could happen usefully, the actors should have to have the same offsets from keyframe to keyframe as well to continue to be in this entangled state. It is assumed no such pairs exist.
    • With TRACKMODE TRACK, the ways to set this up:
      • Get the actor to approach a waypoint in the TRACK from such an angle that the next waypoint will be exactly behind its back.
      • Get the actor to warp past the waypoint it's currently homing in on so it's left behind its back. This doesn't seem to be possible since the threshold for hitting a waypoint is 400 units (combined X- and Z-difference) and thus the warp would have to move it forwards by twice that much.
      • Get the actor to only enter TRACKMODE TRACK when it's in the right relative position compared to the waypoint it should get stuck trying to get to. This could be the first waypoint in the TRACK but it could also be the second one if it's already right where the first one will be when the TRACK starts.
    • Generally manipulating Timer 2 might help with this as well.
    • The mathematical reason only the orthogonal and interorthogonal angles should work for this trick is that 0 and 1 are the only rational roots of the tan function. In theory, there could still be something else that works due to the nature of all the numbers being represented as approximations but it's highly unlikely any other practical solutions exist due to how slowly, relatively speaking, the actors ever move.
  • "walk stairs on X/Z" is the same as "goto position", but the actor's Y-position is being interpolated between wherever they were when the TRACK's "store position" command was executed and the point indicated in the walk stairs command itself. Except it only looks at the X- or Z-distance between the points, depending on which command was used.
    • The same indecision bug works with this command. This can be seen if you walk backwards into the N stairs in E1R7 with Z-position being 3674 (or the S stairs with -4326). The other transitions will all first rotate you and so this is the only one where this works, unless you've managed to lag past or otherwise bypass the target point (Y-threshold) completely in which case the situation becomes analogous.
    • More under "stairs".
  • The "rotate" command doesn't have the same indecision bug if the actor was facing directly away from the target angle.
  • The warp/goto commands don't interrupt an uninterruptable ANIM like a knockback or jumping so the player can "cheat" on the supposed starting positions.
  • There is exactly one instance of a warp command that has five parameters. The fifth one is time (in frames) that the warp should take, i.e. it's not really a warp. This is done with the zombie in E0 as it's rising from the floor. Other similar cases are handled through playing ANIMs but ANIMs can't cause actors to rise or fall.
  • When determining whether target locations have been reached for the goto command, the primitive underlying maths look at the combined X- and Z-difference between the actor's position and the target one (the sum must not exceed 400). Thus it counts if the actor's actual position (i.e. ROOM coordinates, not taking MOD values into account, more under "MOVEMENT") is within a square that has been turned 45 degrees, has a diagonal of 800 units and is centered on the target coordinates. This is why warp commands are often used to ensure the position ends up being exactly right at the end.
  • After a TRACK is finished, TRACKPOS remains at its last value, but this shouldn't make any difference for the next TRACK.

STAIRS/TRANSITIONS

  • Stairs/transition movement is based on instructions given in the LISTTRAK file (see the section called "tracks"). Whenever the player has entered a floor trigger, the engine sets the player's LIFE to 550. This LIFE returns camera focus on the player and removes inventory access. It also contains further instructions for each individual trigger on which TRACK to send the player into and what else needs to be done.
  • "Goto" istructions use normal walk and rotate movement to guide the player into a new position, only the target position is not landed on exactly. This is why warp commands are often used to ensure the position ends up being exactly right.
  • If you're already facing the right exact direction entering stairs, this skips the slow "rotate" command if there was one.
    • This also works if you were turning at the time when you enter, landing on the right angle right as you hit the trigger, or if you finish turning the quadrant after hitting the stairs.
  • Stairs and transitions have a lot of subtleties going into their exact workings. These stem from the order of execution of TRACK commands and other phenomena. The gist is there's ways to manipulate your position after hitting a stairs trigger that may make it a bit faster to complete the TRACK, or potentially allow further exploitation. Multiple phenomena play into the mechanics. Here are some common denominators.
    • You can lag into a transition trigger to get closer to the destination with faster movement, since the transition ANIMs are all relatively slow (slower than walking). This may be worthwhile if you can combine it with an inventory action.
    • You can also use lag right at the start of a transition, but this has to be done with the menu method. This way you can get another full keyframe of running, in theory. Any such movement fails if there are colliders in the way and collisions have not yet been switched off.
      • You can also get a keyframe of walking directly following this, if your SPEED is still 5, since the SPEED 5 ANIM in transitions is the walking ANIM. This might relate to the game being unable to end an ANIM short (and thus prevent the movement) when your SPEED is 4 or 5, but this is not clear. If you're walking backwards (SPEED -1), this will not work since the new ANIM_MOVE has the idling ANIM for that SPEED.
      • Whenever your SPEED is set from 4 or 5 to 0, there is a small bit of extra movement (e.g. 15 units). Thus the total lag snap distance can exceed the keyframe offsets. A snap of 370 for kf0 of walking has been observed (346 + 24).
      • If you walk into the stairs instead, SPEED 4 means the ANIM_MOVE goes to the walk_stairs ANIM (or crawling) and thus the snap will be even smaller (e.g. 222 + 15).
      • Can't for some reason get SPEED 5 snaps in E1R6 or E1R7 at all. This testing was done in E3R11 but the other TRACKs can behave differently by the looks of it.
  • When collisions are switched off, it doesn't stop picking things up based on the HARD_COL value being set to 0 or 1 while in that state, nor interacting with doors. It's not clear how this could be used to one's advantage. In theory, if there was some such interaction that affected the player's trigger collision flag, this could be used to regain the ability to move between rooms after abusing a transition to get vertical desync, but there is no such interactions as only the transitions themselves disable and enable the flag.
  • There are only a few TRACKS where in theory, there's a chance to interrupt it before it has finished by hitting another trigger. This is numbers 31, 32 (E0-E1-E0), 37 (E3-E2) and 39 (E4-E3). The first two have "enable collisions" and "enable triggers" in the opposite order and the latter two have a rotate command as the very last command. However, even if you somehow could enter another floor trigger during that frame, that would lead into another TRACK that re-enables collisions anyway. It's also not clear whether or not the TRACK can actually be interrupted this way. (After some tests it seems unlikely)
  • The steps in staircases are merely cosmetic. In reality, the game saves your starting position ("store position") and interpolates your Y-coordinate between that and the target value. The commands "walk stairs on X" and "walk stairs on Z" in the TRACK file determine whether the game interpolates the Y-value based on proximity to the next waypoint along the X or Z axis. This explains why you don't have to be very close at all to the actual waypoints along the other axis for them to trigger.
    • The walk stairs command ends when the player's WORLD-Y is within a 100-unit threshold of the target Y-position on either side. The X- and Z-positions don't matter at all at that point. There seems to be some leeway because there are often frames when the position is e.g. 704 or 896 with a target of 800 without the instruction ending.
    • Stairs interpolation doesn't use MOD values at all, instead changing the actor's coordinates directly.
  • In single-segmented runs especially, hitting approximately the center of a staircase might be fastest since it stops staggering left and right. At least if the staircase has a waypoint. The general "transition strategy" should be considered, though in AitD 1 only the E1R7 stairs seem to have a lot of variation.
  • Any time you're in the middle of a turn and you enter staircases, most of them will complete the turn before anything else happens. This is all the staircases where there's an initial slow rotation to face the right angle. It doesn't matter if you were turning in the right direction or wrong and whether or not you had the lamp out or were holding SPACE. There may be a small snap turn first before gradual turning resumes.
    • This means hitting a staircase at the right exact angle but being in the middle of a turn does not skip the rotation command because the rotation will have continued before getting to it.
      • This also applies if the turn was a delayed one (e.g. SPACE turn).
    • Any time the current rotation ends at exactly the right angle, the rotation command will be skipped.
    • None of this works when turning to the right in E1R7 in either staircase. You can only turn to the left in E1R7 if your relative facing to the waypoint target (presumably) is on its right.
    • There's no evidence of manual SPACE turns performed on the first frames after hitting a trigger. This seems to be impossible.
    • Can't get any kind of SPACE turns to happen during the E1R6 transition.
  • The staircase rotate command takes 2 seconds to finish regardless of how large the angle to cover is. This means it's never faster than manual turning, unless you were using the push action to move.
  • You can speed up or revert the TRACK-governed turns with s/l manipulation like with rotating doors etc. More under "timers".
  • The PC always turns around the way that's faster when needing to rotate to go up or down stairs, after any preceding turns have finished.
  • The disabled collisions in stairs doesn't prevent collisions with pushable objects, meaning you could get stuck in E3R11 for example if you put a chair inside the stairwell. Doing that doesn't seem to have any interesting effects anyway.
  • Taking the N stairs or S stairs down from E2 has exactly the same effect.
  • When observing the Y-position at a given horizontal position when entering the staircase in the same way multiple times, slight differences are seen in the exact positions. Probably to do with lag.
  • Jumping into a staircase (and generally moving into them with uninterruptable ANIMs) makes the player move deeper in before assuming normal stair-climbing movement. This means you can jump through the E5R0 trigger to clear the stairs quickly (and without risk of getting turned back). The keyframe you were on when you hit the trigger is incremented and the movement resumes from the start of the next keyframe, meaning you're losing a bit of time if you don't skip most of any slow keyframe that you had just entered.
    • The same applies more broadly on any transition, but the other critical example is E5 going through to E4 which is also slow if you don't jump through.
  • Extra stairs movement from lag: If you lag the game on the last few frames right at the end of the transition, it causes you to move forwards further compared to no lagging. This is because of the last few commands in the TRACK normally being executed very quickly but this way you're drawing them out.
    • A difference of around 300 units has been observed, though this may vary.
  • If you're holding DOWN, RIGHT or LEFT when jumping up the stairs from E5, the game rotates you cw at the top. This effect is the same if you're just walking up and getting a SPACE snap or just starting to turn half-way through. The transition seems to always start this rotation right as you're reaching the end since you're not perfectly aligned with the target point, and holding the rotation bind just makes that turn continue.
    • If you try doing a slt during the erroneous turn, you can complete the quadrant by holding the same wrong (LEFT) button down whereas holding the other one doesn't do anything.
  • Sometimes you'll be sent back down the E3R11 stairs after climbing them.
    • Sometimes you get a 30-unit offset from (850, 0, 50) to (820, 0, 49) before the climb has started when coming up the E3R11 stairs. This happens on the frame the TRACK advanced to position 1 (it has just disabled triggers). Because the start position is not stored until after this has happened, it in part explains why you'll end up at different positions at the end. There's probably things like small amounts of global lag also involved though, and possibly which SPEED you had when entering the staircase. Other staircases probably behave in a similar way, but E3R11 just happens to allow the climb to finish on (900, 0, 50) where you're inside the upper stairs trigger sending you back down.
    • You're sent back to E5 the more frequently the higher the framerate regardless of anything, since this means you're less likely to skip a position due to lag.
    • If the framerate is high enough, the E1R6 stairs can behave similarly.
  • There is a way to interrupt a transition. It's based on this: in the middle of a transition, you hit a door (use lag and Timer 2 manipulation to get to one). Collisions with actors can still happen and so it should cause the door to open. Afterwards, you are restored control and so TRACKMODE changes. The TRACK is never completed. You're left with the wrong ANIM_MOVE and no INV but you might also be left without collisions. This is basically useless without triggers being active though, and only going between E0 and E1 are there transitions where triggers are re-enabled before collisions, meaning you could, in theory, interrupt the TRACK just before collisions are back on to no-clip around afterwards.
    • If you get past the target point to turn back around (especially doable with gravity on in many places), you can definitely reach nearby doors unless the falling prevents the right kind of collision from happening. After hitting the door, if you need to get back up or go further down to reach the target point just in time, you can just manipulate Timer 2 to prevent the door from fully opening too soon.
    • You can't do anything with this in E3R11 seeing as how are you going to get to E5 if the track doesn't complete? TRACKPOS -1 is what triggers the room warp.
    • The only other seemingly useful effect is being left higher up or down than usual. If you have triggers left on, you could then hit certain triggers like the E3R11 stairs trigger despite the desk being there, but this is about as good as it gets, unless you can get up to ROOM Y -3750 exactly (!) at which point you can pass over walls inside the house but still activate triggers.
    • The better approach to this is probably to start opening a door first, then get knocked back so you're freed. Now trigger the transition while delaying the door opening until you're on a suitable step of the TRACK. There are no TRACKs that disable collisions before triggers unfortunately so you can't noclip this way by interrupting the transition at the start.
    • If you could somehow get into this no-clipping state with triggers left on, because this is implemented in its strange way (see "TEST_COL"), you could at least make the game think you're hitting collider 0 and e.g. pick up the talisman this way.
  • Moving between E5 and E6 doesn't set your facing like the other transitions do.
  • If you have control but don't lose the TRACK movement in a staircase, you can sort of control your direction if you have a MAN_ROT action and hold SPACE while a turn has just started.
    • To get into this kind of state without cheating is probably impossible. LIFE 550 sets INV and VAR 0 to 0 at the same time as well as switching to TRACKMODE TRACK so you'd have to be able to affect just VAR 0 while in LIFE 550 without also setting TRACKMODE. This doesn't seem to ever happen. Using the INV + control restoration after throwing in LIFE 549 is no good because then you're not in LIFE 550.
  • You can lag the game when you enter E5 through E4, before you've rotated S, you can get Emily to enter the wall on the side. If you move directly to the E, it seems you can get to X 125. This leaves you inside the trigger and sends you back to E4. However, if you could avoid being in the trigger (easy if you were facing an angle like 89 degrees) and also got to X 152, you could now run through the wall OOB, presumably acting as a shortcut compared to other routes.
    • The X-position always ends up being 125 at furthest (the offset is 425, on kf2 or kf3 of crawling). If SPEED was 5, the ANIM would be walking instead, but that ANIM's kf's all have a smaller offset anyway.
    • Several frames will pass between SPEED being set to 4 in TRACK 40, and the player getting to E5, thus you can't possibly be in your running ANIM after the transition. It doesn't look possible to get further into the wall. Going the other way around, the distances to the walls are even greater with similar code preventing having SPEED 5.
    • If you jump through to E4 instead, if you have kf5 after the warp and an angle of 180 (+-3.2) degrees, you can lag the game to get to Z 5925. This isn't quite enough to run through the N wall, for which you'd need Z 5953. There seems to be no way to get any further as you're immediately turned to face the W afterwards.
    • What does work is if you first make sure the barrels have been moved, jump through to E4, start to rotate as soon as you can (kf3 works), do two slt's to face E while keeping the same kf, and lagging the game once again. You may have to face due E as you go through but possibly not. Now you just lag after the transition and you should go far enough into the wall to get OOB. You can even drop something at the end of the jump to get the hover if desired.
  • After entering a staircase, if the warp is immediate, the residual movement from the previous ANIM can cause you to move sideways after the warp, which can in turn cause you to enter a rotation during the transition. This could enable you to end it OOB even if it otherwise wasn't possible. It might be better to walk into the trigger instead of running because more residual movement is kept this way as the SPEED rolls down to 3-2-1-0. The movement you get also depends on what part of the walking ANIM you were in at the time.
    • E0-E1: Because the stairs descent ANIM only has a speed of 238 (all kf's), the player can only just barely get (1-unit margin) far enough into the thick E wall to pass through if they start from a position right next to the wall. During the transition, even if you use the residual movement to get to X 15 (next to the wall), you now surely cannot turn to face the E during the transition without at some point being W of the target X point (0). Thus this should be impossible to do.
    • E1-E0: Because of the warp command at the foot of the stairs, your starting position seems to be set in stone more or less. So there should probably be no sideways movement available during the climb (SPEED 0 is even issued before the warp).
    • E1-E2: Doesn't seem to work because you're always put at Z 250 no matter what.
    • E2-R1: The target points seem too far away from the walls to end up OOB.
    • E2-E3: Can be done easily but there's nowhere to lag OOB to near the foot of the stairs.
    • E3-E2: No walk stairs command in E2, there's a rotation first, then warp, so no chance.
    • E3-E4: Here it IS possible to use a long series of lagging (and timer manipulation) to eventually end up at the foot of the stairs but outside the room.
    • E4-R3: No walk stairs command in E3, there's a rotation first, then warp, so no chance.
    • E3-E5: Can get some residual movement this way but seemingly only towards the SE.
    • E5-E3: Your position at the foot of the stairs is set.
    • E5-E6 (either exit): Collision flag is never switched off anyway.
    • E6-E5 (either exit): Collision flag is never switched off anyway.
  • In the E3-E5 transition, you could bypass the locked door. The method would involve using the natural rotation at the top of the stairs (since your position and angle are otherwise set), and just lagging and timer-reverting a lot of times so you keep moving directly N without the transition quite being able to finish. This seems extremely slow.
  • When running into the E1 stairs, if you save on the frame after the next floor has been loaded in, reload, and immediately lag the game, you can get a snap that wouldn't otherwise be possible. This isn't really useful since you just need a little bit of offset at the top of the stairs to manipulate turning to the side near the bottom, which is enough to get OOB (even if it wasn't possible otherwise).
    • Possible:
      • E2-E3 (don't even have to save)
      • E3-E4
      • E4-E5 (don't even have to save but doesn't get you far enough by itself)
    • Not possible:
      • E1-E2
      • E2-E1
      • E3-E5
      • E5-E3 (you're turned before the warp)
      • E5-E4 (don't even have to save but doesn't get you far enough)
  • If you could interrupt the transition from E4 to E5, you could probably get the gravity flag to stay on in E4 this way. Furthermore, this should result in jumping being possible inside the house. However, this should be impossible to do since there's no doors in E4 and seemingly no other code that could interrupt the transition.
  • You can use a lowered framerate to cause goto and walk stairs commands to end a bit further regardless of how quickly the last commands in the TRACK are executed afterwards (if the first position that could have cause this to happen is skipped due to lag).
    • The theoretical limit to the horizontal difference in the position you end up in going up or down stairs should be no greater than the horizontal movement that corresponds to a Y-difference that makes you completely miss the target point (past this, you'll simply turn back or walk forwards indefinitely due to the indecision bug). Of course you'd need the slope to be very steep for this to be possible. In practice a difference of around 350 has been observed after brief testing.
    • You shouldn't be able to do anything with this, presumably, that wasn't possibly by lagging the game at specific times, but this is difficult to prove.
  • Stairs don't use modX/Y/Z values, instead setting the coordinates directly. This suggests that any sudden [forwards] snap, such as a SPACE snap, shouldn't be possible in them, at least not based on the same mechanism.
  • The stairs ANIMs have a "pulse" of about 80 frames so each step takes 0.75 seconds.
  • If you're holding SPACE at the end of a transition, the PC may get stuck in the transition ANIM, presumably due to this causing the ANIM_MOVE in LIFE 549 to be skipped. Doesn't always work. Probably doesn't have any interesting consequences since the ANIM is cosmetic.

DOORS

  • All doors/door halves have about five associated LIFEs for handling all the different combinations of opening and closing it from both sides between its open and closed states. There is one main LIFE the door is usually in, and another two pairs of LIFEs it's sent to to achieve the state changes.
  • The door's state is reflected on in an associated VAR, one for each door/door half, so that 0 means closed (can only be opened), anything else (usually 1) means open (can only be closed). Some doors also have extra logic connected to them, for example the door closing in E1R2 spawns in the zombie.
  • Doors open and close within exactly 120 frames. Opening a door inwards takes longer than this (about 4 seconds) since the player first has to complete an ANIM before this time starts. A slt can cut the rotation time at the cost of the time to save.
  • Door opening/closing may progress while you're in the inventory if the timers revert too early. Save/loading (while waiting in-between) also causes the rotation to advance without reverting back unless the timer is manipulated.
    • See "TIMERS" for more information.
    • You can run into a door or such and enter the inventory on the last frame, making use of the inventory time this way. However, if the door was inwards-opening, it doesn't start to open then, although other effects may take place (jelly is spawned for example). Also the door you're opening has to have the lower slot number of the two doors or this won't work.
  • The reason you can open doors while shooting the rifle (moving backwards) is because only walking backwards or getting hit actually sets VAR 24 to 1, which is what normally prevents it.
  • Hitting a door that opens forwards further away from its hinges is slightly better at least in single-segment so you don't get caught on it during the ANIM.
  • If a door is loaded in where you are after hitting a room trigger but you're most of the way through it, even if you're facing a direction that should cause the LIFE to be entered that corresponds to opening it from that direction, it might count as opening it from the other side instead.
    • If you landed inside the room trigger exactly with no further movement afterwards, you can avoid the door immediately opening.
  • If you enter E3R12 so you back off where you came from before the door has closed, open it again, and close it again (e.g. one half of the double door) you will end up having just one half locked and the other wide open (though in principle locked). If you close both of them, you start the zombie sequence with a delay.
  • The PC will turn to face the door only when the doors open inwards. In all cases, the player's rotation and movement are locked, except with the medicine cabinet in E1R3 which allows rotation.
  • Door opening/closing sets VAR 0 to 0 and TRACKMODE to NONE until the rotation is finished. Conversely a door opening can be used to set these back to 1 and MANUAL respectively. If you take a hit or otherwise get interrupted, you can be freed while the door is rotating, and then manipulate the rotation to finish at a delay so as to give control and TRACKMODE MANUAL at another time when you're not supposed to have them. With double doors, both halves opening have the same effect regardless of which one you're opening.
    • Double doors can be made to start opening and closing one frame apart, so you can first make one open fully with a s/l on the exact right frame, then save the other one's effect for later. You can also do the same by lagging the game on the frame when the first door has started to open.
    • The lag could also come from visiting the inventory on that frame, or probably also from stepping into a room trigger.
    • E.g. when the E3R1-R2 double door is opened on the N side (LIFE 240), it first starts ANIM 30 (ONCE) for the player and sends the E door into LIFE 243 to get the player to rotate and after that it plays the creak sound and goes into LIFE 241 while also sending the W door to LIFE 242 and makes a new ANIM start for the player (31, ONCE). Thus both doors are sent to the LIFEs that get them to open simultaneously, however, if the door you're opening has the lower slot number, it'll execute the other door's new LIFE on the same frame while the other one has to wait one frame. Thus the doors open one frame apart.
      • If the same door is opened on the S side, the player is sent to ANIM 29 (REPEAT), the creak sound is played immediately and both doors are sent into their respective opening LIFEs. Thus the same principle applies: if you're opening the door with the lower slot number, it should open the other door on the same frame.
    • This means it doesn't matter which side of the door you're on so long as it's the door with the lower slot number, and that the door that opens fast is always the other one.
  • Things that set VAR 0 to 0 (which can be reverted by a door opening):
	opening chests/wardrobe/cabinets
	opening or closing doors
	dropping (but you're in LIFE 11)
	throwing
	crack (but you're in LIFE 39)
	going to E6R6 after dying (set to 0 both in LIFE 39 and 555)
	reading Necronomicon
	reading De Vermis (but you're in LIFE 39)
	using jug (empty or filled)
	hit by jelly (but you're in LIFE 39)
	entering cutscenes in E6R6
	falling (but you're in LIFE 551)
	dying by falling (but you're in LIFE 39)
	climbing (but you're in LIFE 552/559)
	transitions (but you're in LIFE 550)
    • Seeing as VAR 0 being 1 at least allows SPACE actions, this could allow some kind of movement when otherwise there isn't any. If you're in LIFE 549, it also gives back inventory access. Thus you could try things like this:
      • Throwing another thing in the middle of throwing the first one. However, this doesn't cause the second thing to be thrown, just the PC getting stuck for a brief moment.
      • Instead, doing anything that doesn't require an ANIM to play should work, including using an item in the middle of throwing it.
      • It seems to be possible to throw a flask and also then drink it. This is despite the fact the throw ANIM is uninterruptable. It's unclear why this is possible while throwing a second thing isn't. Because of the BODY change?
      • Using jug: sets VAR 10 to 1 which can in turn set VAR 0 and INV to 1 if in LIFE 549 (or 11); because the opening door interrupts the jug ANIM, it never finishes and thus VAR 10 is kept at 1 until the next END_ANIM.
      • Reading Necronomicon: ANIM is skipped.
      • Falling: transitions are interrupted w/ or without having gotten desync before then, but you're always stuck.
      • Dropping: interrupted because door opening sets ANIM to idling which sends you back to LIFE 549.
      • Dying (type 0): shouldn't have any effect because LIFE 39 (VAR 0 set to 0 only in LIFE 555 at which point you're in the E6R6 death cutscene and nothing can give it back).
      • Dying (type 2): uninterruptable, shouldn't have any effect because LIFE 39.
      • Dying (type 3): can result in saving yourself because you can SPACE action your way out before the doors close, technically. See "JELLY".
    • The list of things that set TRACKMODE to something other than MANUAL only contains items from the list above and thus doesn't have to be considered separately.
    • In summary, you could: get a few things to happen with the wrong BODY (throwing something into e.g. equipping a weapon); cut the Necronomicon ANIM short; use it as part of saving yourself from the jelly death (see jelly); store the jug effect (VAR 10); both throwing and using the same item (doesn't cause any special effects).
  • If you were in the middle of opening a door outwards (repeating ANIM) and don't let it finish, if you somehow switch VAR 0 to 1, because you had SPEED 5 before, it resumes checking for the right movement in LIFE 549 based on SPEED, and so you'll go into the running ANIM. Now you can combine the SPACE action (push) with this to be able to run and turn. This seems to carry no benefit unless you were somehow forced into a state with TRACKMODE NONE that you can't otherwise escape.
  • The E2R0-R2 door on the S side is strange: it's not perfectly aligned, starting at an angle of 89.6 degrees instead of 90. Also when it opens it goes through an angle of 449.6 (89.6 more than 360) though this is probably a mid-frame memory read by the Viewer.
  • Door Wrong Rotation: If you start opening or closing a door so that you're first rotated and moved backwards, but you get interrupted with a knockback (or another door opening at a delay or possibly something else), the door is left in a LIFE that calls PLAYER.SET_BETA on every frame. It still proceeds to open the door but only on the conditions that your angle is the right one and PLAYER.END_ANIM is 1. Sometimes you can avoid these from being true at the same time and the door is left in that LIFE indefinitely. This prevents you from turning far but you could then in theory get the door to open at a delay without reliance on Timer 2 manipulation, getting the VAR 0 = 1 and TRACKMODE MANUAL effects at a delay as well. This also means you might be able to get a "wrong" kind of rotation to happen.
    • When you're being SET_BETA'd, it seems to be hijacking your normal rotation but it only happens when you're holding either LEFT or RIGHT. The rotation is slow initially, one unit at a time, but if the button is held, it speeds up and starts to oscillate.
    • You CAN still get any kind of quick turn but it's quite precise. You have to start the turn while facing the target direction exactly (otherwise the snap turn will have the target angle set by the door's LIFE, i.e. towards the door), then turn with SPACE held for the desired time making sure you haven't caused the ANIM to repeat yet. Using the kind of "wrong turn speed" (see "ROTATION") you can get a 90-degree turn while e.g. pushing within half a second without causing the ANIM to finish. You can also just use an slt.
      • After getting the quick turn, so long as you release LEFT and RIGHT, you can keep running straight ahead. This means that while you're getting SET_BETA's, you're still free to move in any direction within a 180-degree arc, at least.
    • In this state, if you're also getting rotated by a different command, strange behaviours will arise.
      • If the rotation is from another SET_BETA command (e.g. another door), it results in only being able to turn in one direction and only across a certain arc with the rate of rotation slowing down the further you've turned, after which you're stuck facing the same direction, which might be something like -3.2 degrees from an orthogonal direction. In the first observed case, this is 86.8 degrees away from the door whose LIFE was getting executed first (E3R8-R9 door rotating the player W) and 3.2 degrees away from facing due S (as per the E3R5-R8 door). When just waiting around with no inputs, suddenly on a seemingly completely random frame (before Timer 2 had had time to wrap around, which shouldn't have any effects anyway) the direction changed to being -6.0 degrees, further in the first door's direction. This might be caused by some lag spike or such but it indicates that a theoretical possibility of turning back the other way might still exist if you could slt on such a frame.
        • This random turn happened again later and the new facing was the same. After some more waiting it increased to -8.4.
        • After moving further away from the two doors, I could turn further towards the S up to due S. There was some evidence that distance from the door or doors may have been a factor but it's difficult to tell what exactly causes the difference. Also sometimes after firing the rifle a minimal ccw rotation was possible.
        • Getting yet another door to start opening (cellar door, also towards the W) caused getting westwards rotations to become a bit easier though not completely reliable. This LIFE was being executed in-between the other two. Also random rotations became considerably more common. Adding yet another door (towards the N, executed before any of the others) didn't significantly alter the situation though the "terminal angle" that you can't get past changed.
        • Hitting a floor trigger caused getting stuck in the stairs because it wouldn't turn me facing the right way. Even using the Viewer to set the angle wasn't enough to get me climbing up.
          • The second time trying to climb a staircase, I slt'd right as I got on and managed to start the climb which was normal other than a slight deviation in the angle faced during the climb. When coming back down the stairs (E2-E3), despite the ANGLE command after the warp, the facing was left at 266.8 and I proceeded to climb indefinitely in the wrong direction.
      • If the other rotation is from the ANGLE command in LIFE 39 (or maybe a transition), it causes movement in a direction in-between the two target ones. It's possible to further manipulate this so you're [mainly] only moving in the target direction of the door rotation by lagging the game every time you're facing that way.
        • If you get hit by the jelly while multiple doors are rotating you, this further confuses the game as to which way you should be moving and causes haphazard movement in multiple directions. The ANGLE command in LIFE 39 definitely plays a part as well as sucked_into_jelly ANIM's large offsets.
        • It seems that the main target direction will be determined by the LIFE that's executed last. It's not clear what determines the side on which you get stuck around that angle (cw or ccw from it).
        • It doesn't seem to cause anything particularly different to happen if you have two doors turning you in the same direction.
    • If you come back to an area where one of the doors is in its "about to open" LIFE, if it's a transition like the one between E4 and E3, you're stuck because the LIFE sets your TRACKMODE to NONE and VAR 0 to 0 again. If it's a transition like E2-E3, you can manipulate where you'll end up no-clipping over to when the TRACK is proceeding by lagging the game a lot at different times. However, since triggers are off, you can't move to other rooms like this. There's a very theoretical chance to make some transition faster if you need to move back and forth between areas. There is hardly anywhere OOB that you couldn't get to faster with other techniques. You can get through the study door coming up from E5 but only if it's the study door that you started opening in the first place (other doors are too far to be loaded in).
    • Using the Room Viewer cheat that moves Timer 2 back 5 seconds has caused a door to finish opening despite seemingly not facing the right angle. It's possible doing this manually could have a similar effect.
      • (Not sure if this means "despite the player not facing the right angle, but assuming so. Couldn't reproduce.)
    • MANUAL_ROT doesn't change the way this state behaves except you'll rotate within a smaller arc, which makes sense since MANUAL_ROT is slower.
    • If you proceed to open another door outwards, it causes you to then rotate in the target direction of the first door causing that door to open at the end of the ANIM.
  • There's a frame or several when you start opening a door inwards when you can still go to the inventory. If you then select some action, the door starts to open after the action has finished (provided you're facing the right direction). The following was tested on a single door.
    • Throwing something causes the player to run forwards automatically after the throw (or walk if you were walking, or stand still if you had SPEED 0, SPEED is left at whatever it was before), with no rotation being possible, until the doors have fully opened. There's a frame when you can go to the inventory before the door starts to open. You can use SPACE instead of running. You can even be left with SPEED -1 if you just hit DOWN right before colliding with the door and entering the inventory, causing you to back away further than normal. For the rest of the actions listed here, it's assumed your SPEED was left at 5.
    • Dropping something first makes you afterwards move backwards a little bit, then run forwards until the doors have fully opened.
    • Using the jug is the same as throwing but you run forwards further. There's a frame when you can go to the inventory before the door starts to open.
    • Reading the Necronomicon causes the Necronomicon ANIM to play out fully, then the doors start to open but you have full control during it.
    • Reading De Vermis doesn't do anything special (you go straight to LIFE 39, the door never opens).
    • Drinking a flask doesn't do anything special but there's a frame when you can go to the inventory before the door starts to open. Drinking a water jug is the same.
    • If you go to the inventory to select another action after the door has actually started to open, this results in the same outcomes as if the door had originally started to open outwards.
    • Some of this stuff could be useful in a single-segment run to make door-opening slightly faster.
    • If you are left with an action like open/search, or a weapon, you can hit SPACE to rotate when it's otherwise impossible. You can't SPACE turn.
    • The same stuff should work with closing doors as well.
  • If you try the same on a door that's opening outwards, the results won't be quite the same, partially because in case the timers aren't stopped in the inventory, you might get control back by virtue of the door opening instantly, and partially because there's no separate LIFE that makes the player rotate and changes the ANIM before the doors start opening. The following stuff was tested on a single door.
    • If you drop something, the dropping may be interrupted because once the door has opened, your ANIM is set to 4 and so you're sent back to LIFE 549 from LIFE 11.
    • Throwing something will succeed, after which you'll move forwards.
    • Using the jug will also succeed with a bit of forwards movement before the door has fully opened.
    • Reading the Necronomicon is interrupted as your ANIM goes to 4.
    • Reading De Vermis doesn't do anything special.
    • Drinking a flask is interrupted in an unusual way on a frame when VARs 70 and 204 change and you get control and the TRACKMODE back, but then it resumes. Drinking the water from the jug is the same except only VAR 0 and TRACKMODE change, suggesting one of those two might be responsible.
    • If you did any of this on a double door, presumably just more actions would get interrupted faster in case the other door had already started to rotate while you entered the inventory. If it didn't, the outcomes should be similar to those listed above.
  • If you knock yourself back into a door to open it, then go to the inventory when it's still possible (tested on a single door):
    • Throwing fails, you end up with fighting INHAND.
    • Dropping also fails.
    • Using the jug fails.
    • Drinking something will happen normally with an interruption when the door finishes opening.
    • Necronomicon takes away 5 hp but the ANIM isn't played out.
    • De Vermis gets you stuck in LIFE 39 without INV.
  • In Single-segment runs, when running into a door to open it inwards, it's actually slightly faster if you go to the inventory as the opening has just started, and select to throw something (the top item being the fastest). The item won't be picked up when you run over it. If you're right on the opposite end to where the hinges are – so you don't get caught on the door for as long – and you needed to move straight forwards after opening it, you might save a second.
    • The first door in E1R0 is an obvious place where you could throw the empty oil can, or after using it to inventory lag over the crack, you could throw it when opening the R4 door, although maybe this takes you too far into the room.
    • You could also consider throwing the oil lamp and then picking it up again, if you can, so as to leave it in an undousable state.
    • It's even faster if you use the jug. Throwing is 80 frames whereas the jug ANIM (open/search or close) is 60.
    • Getting hit could also be faster (also 60 frames), however you might move backwards during it.
  • You can also go to the inventory while opening a chest/wardrobe/cabinet if you time it well, and while the time you lose control for is very short, if for some reason you benefit from throwing something or so, you can now perform that action during time you couldn't have utilized in any way.
    • You can also start some long interruptable ANIM like reading the Necronomicon and be freed quickly.
  • You can go to the inventory and select another action in the middle of the last keyframe of the open/search ANIM and still get a door in front of you to open.
    • Closing a door works the same.
    • You can start pushing something and select another basic action in the inventory and the push continues to the end of the keyframe (though the first half of that ANIM is faster so it might be better to just keep resetting it). Doesn't work with dropping or throwing or choosing anything else seemingly.
    • It seems to be a requirement that the ANIM (or relatedly, BODY) do not change as a result of what you've selected in the inventory, making for a pretty big constraint.
    • It doesn't seem to be possible to first, e.g. use the water-filled jug (closing ANIM), then switch to the open/search ANIM to juke the game into thinking that's the ANIM that just finished. Not even if the first ANIM was running and the second one searching so you don't have to go to the inventory in-between, just hold SPACE to switch it.
      • I.e. it doesn't seem possible for an ANIM to change without resetting the END_ANIM flag for the previous ANIM.
    • You can, however, switch from open/search into using the empty jug (so you're really just switching to the same ANIM, so probably the game doesn't do anything) which will still change your current INHAND item while completing the open/search action.
  • You can simultaneously clip through a door and start opening the door as if from the other side (i.e. without having to turn around to do that). Observed with N door in E3R13.


FRONT DOORS/JELLY

  • The jelly's main LIFE is #239, but it is referenced in LIFEs 214 and 224 (front door LIFEs that spawn jelly), 215 (sends it to LIFE 220), and 218 (deletes it).
  • The jelly's attack starts on kf1 of ANIM 96. On kf2 it thrusts forwards continuing in kf3. On kf4 is starts to retract. On the first time, it starts from the beginning of ANIM 95 which lasts for 210 frames. It can only attack after this ANIM has finished. After the attack, it'll continue idling in ANIM 95 for a little while before the doors have closed. This leaves it on kf3. The next time it's loaded in, it resumes the same ANIM and keyframe, which is reset to the start. That's why it attacks you much quicker: 100 frames in.
    • Because the attack happens so much quicker, the doors are still opening. This is why you might not get hit at all yourself (the animating actor absorbs the hit, more under "[NOT] DYING". Also if you open the doors and quickly close one of them, the closing door can absorb the hit. Of course any animating actor will work the same.
  • You can get OOB through the front door. To get the jelly to disappear before the doors have shut you just need to close the left-side (N) door early. For example you can s/l to open the doors or one half quickly, then immediately go and close the door and the attack will get absorbed into it, leaving you intact, and the right-side door is left open.
    • Also managed to somehow get both doors to be open without the jelly being there (VAR 33 == 0) but can't remember how.
  • Drinking a flask on the last possible frame before having been hit (removing inventory access) makes you visit the flask LIFE (LIFE 59, sent there by e.g. LIFE 58) as evidenced by a change in VAR 204 but you're then immediately sent to LIFE 39 afterwards. You can see the LIFE going from 549 to 59 to 39 during the same frame.
    • You can drink a flask while the jelly is attacking (need to use rifle knockback and drink it before hitting the door to be fast enough) and sometimes you'll survive for some reason.
    • If you lag the sequence where you get sucked into the jelly, (after rifling yourself into the doors and drinking a flask), it never finishes opening the doors when it's already closing them again. This is because the player's ANIM never finishes until after being sucked in, and ANIM_END is a prerequisite for the doors opening.
  • The trigger behind the doors is responsible for killing the player off if VAR 33 is 0, or sending them to the ending if it is 1. It's there regardless of if the jelly is. How it works is it sets the focused actor as 288, which is a PC dummy waiting outside. It already has the right LIFE set to start the end sequence.
    • The trigger simply sends the player to the death manager with whatever type_of_death they currently have (so it's expecting that variable to 3 which is set when the jelly hits the PC). If it's 0, it plays the death ANIM after the player has left the trigger for whatever reason.
      • The death animation slightly nudges you forwards, which is enough to get back inside the trigger to interrupt the whole game over sequence so long as you were right next to the trigger when you died, and faced it.
      • If you die while pushing or aiming you can still continue to rotate during the death animation. Presumably because VAR 0 being 0 doesn't affect SPACE-bound rotation.
  • When you get hit by the jelly, it sets your COL_FLAG to 0 so you can pass through it as you get sucked in. This has the further effect that your COLLIDER (i.e. what you're colliding with currently, if anything) is set to 0 whenever you're not colliding with anything, and 1 whenever you are. Because of this, if you're in E3R13 and the jelly hits you (i.e. you've opened the front doors while in that room), it counts as having collided with the table in R13 (ID 0), thus picking up the lighter.
    • There's nothing else of note that can be done with this trick around the jelly.
  • If you try to throw something just before getting hit by the jelly, because the ANIM is uninterruptable, you'll go to LIFE 39 but won't change ANIMs. The game ends when the doors have closed. If you get just one of the doors open to free yourself, then time the other door opening after you've thrown the thing and been hit, you'll be left with control and manual movement (you can't turn though) but no inventory access. Collisions are off.
    • If you make sure you were facing a useful direction before getting hit, you can get into R13. Here you'll instantly pick up the lighter if it's still there. You have to keep reverting Timer 2 so the front doors don't close. While in LIFE 39, you can't get hit by anything or otherwise get back to another LIFE until you've hit a transition. You can slowly move through R13 towards R11 and hit the one there. If you run into a door to open it outwards, it just changes your ANIM to door-opening which repeats until you hit SPACE again. Opening and closing doors can change your direction though.
  • You can be left with control after getting sucked into the jelly simply by delaying the doors opening until you've been hit. This leaves SPACE actions available though not rotation unless the action has MANUAL_ROT (the guns or pushing). You can't move normally because you're not in LIFE 549. You could now shoot yourself backwards until you hit the stairs trigger to restore your state to normal. Collisions are off so you could also visit R13 to pick up the lighter.
    • If you could somehow get the sucked_into_jelly ANIM to be queued instead of playing immediately, you might be able to get something else to happen in-between getting hit and your angle being set to 90 degrees (256) that causes it never to be set at all. If you record a video of the event happening, it looks like the order in which it happens (according to the Room Viewer) is BODY changes, then one frame later everything else except the ANIM change happens, then several frames after that the ANIM changes causing the angle to also change to 90 degrees because of LIFE 39, even though it's all supposed to happen on the same frame. If there was any way to get the ANIM change to happen one frame before the angle updates (should be the case if the player's LIFE is played first), leaving LIFE 39 on that frame might cause the angle never to update so you could get sucked into another direction instead.
      • These kinds of delays in the Viewer are believed to be an artifact of the way it reads the game's memory. Everything is supposed to happen sequentially on the same frame exactly as it looks like in the code.
    • Jelly does this:
IF HIT == PLAYER
	PLAYER.BODY player_normal
	PLAYER.ANIM_ALL_ONCE sucked_into_jelly -1
	SET type_of_death = 3
	SET player_has_control = 0
	PLAYER.TRACKMODE NONE -1
	PLAYER.LIFE death_manager
	PLAYER.TEST_COL 0
	ALLOW_INVENTORY 0
	SET_BETA 768 -1 (beta already was 768 / 270 degrees)
	DEF_ZV 0 0 0 0 0 0 (shrunk into zero-size box)
    • LIFE 39 does this to the PC:
CASE 3
IF ANIM == sucked_into_jelly
	ANGLE 0 256 0
	TEST_COL 0
	SHAKING 0
	WATER 0
	LIGHT 1
	IF C_VAR8 == 0
		ANIM_SAMPLE Edward_dying sucked_into_jelly 4
	ELSE
		ANIM_SAMPLE Emily_dying sucked_into_jelly 4
    • Thus you can see it's not necessary to go to LIFE 39 to get TEST_COL 0. In theory, you could get LIFE 39 to switch collisions off wherever after deathtype has been set to 3 but it also requires the sucked_into_jelly ANIM which is only played when the jelly hits the player.
    • In order to get full movement back after getting hit by the jelly, you have to get back to LIFE 549. Otherwise the slow movement of pushing or searching makes a jelly route slower for sure (running is >4000/s while open/search/close 178/s; pushing 424.5/s; shooting seems out of the question; getting the ANIM of getting sucked into the jelly to happen in another direction is challenging, so long as type_of_death is 3, it's set every frame (actually, it's possible, see below where it says "door wrong rotation" for how to get to R11 into the transition this way); you can't get hit with no collisions; don't really have access to the transition ANIMs either; can't climb or jump let alone land). You can't get any of the player's LIFEs themselves to restore LIFE 549 (if the player's LIFE is executed after the jelly's LIFE, the player's LIFE is LIFE 39 already, if it's executed before the jelly's LIFE, you haven't been taken to LIFE 39 yet). Thus the only thing you can hope for is getting a LIFE other than the player's one to change the player's LIFE. The only LIFEs that can do that are
    • dropping or placing something
    • dying
    • drinking flask or jug
    • zombie dragging the player

Dropping and drinking things are FOUNDLIFEs which means they get executed first before any LIFEs. Because of this, they can't undo going to LIFE 39 before the player has actually been moved there. Because the jelly takes away your inventory access, you can't retroactively go and do one of these things to go back to 549 (a lot of effort has gone into trying to produce this effect). Meanwhile the other two are useless as well. Because of this, it doesn't seem possible for any LIFE to save the player from initially going to LIFE 39. Now you have to somehow gain inventory access back to be able to get the LIFE changed to restore movement. It can be done like this but it's very likely slower because of the arrow detour and needing to save the game a lot for Timer 2 manipulation:

	get the bow from E1R0
	get an arrow from E3R4 (really just needs the arrow pointer to be 1, 2, or 3)
	equip the bow
	start opening the front doors so just one side opens fast
	fire the bow making sure the other side doesn't open fully yet
	get hit by the jelly
	let the other door half open, giving back manual movement
	the firing animation should now also end, giving back inventory access
	drop something, keep manipulating the doors so the N one never closes
	you should have no collisions (so long as you don't enter a transition) but be able to move normally
      • The save file called "No Collisions.ITD" has the PC in this state, with no collisions but otherwise normal.
    • In order to be able to replace the bow and arrow, you'd need to get inventory access from one of the following:
	dropping (if ANIM == idling)
	throwing (in LIFE 549 if VAR 10 is 1 and END_ANIM)
	drinking flask/jug
	first E6R6 cutscene
	whenever VAR 0 is 1 in LIFE 549 and SPACE is not held
	climbing
	landing
      • Dropping can't give it back because firstly, it's a player LIFE and thus will not get executed once LIFE 39 has been called, and secondly because it requires ANIM to be idling, but the jelly changes it to something else anyway. Drinking a flask or jug and LIFE 549 are also player LIFEs. The others are inaccessible. The only way to be able to shoot the bow is if the arrow pointer is 1, 2 or 3. The only thing that sets this initially is collecting an arrow. The only way to collect an arrow is to search collider 0 in E3R4 (actor 155 has LIFE 304 that has the code for this has LIFEMODE 1 and so is only in memory when the view is in that room; no way to desync the room and view is known outside of E2; doesn't seem possible to switch collisions off for the player to make them touch collider 0 either). Going to the room with the arrows, E3R4, seems to take too long (probably 8 seconds with slt's). In addition you have to fire the bow etc. around the front door which takes a little bit of time as well.
    • Another idea would be to allow the player to go to LIFE 39 but somehow change type_of_death to 0, 1, or 2. Once it's changed to 3, there's no code to change it back to 0. 1 requires hitting the crack or falling in specific places. 2 requires reading De Vermis which in turn requires inventory access and getting the book of course. It also switches inventory access back off. It seems getting hit by the jelly first will prevent reading the book just as it prevents dropping something (and if it doesn't, it's easier to just drop something). Thus this option is probably unavailable.
    • You can pick up anything from any collider 1 as well (whenever colliding with a wall or actor), open/searching if need be, however there doesn't seem to be anything worth picking up this way along the way from R13 to R11.
  • There's the idea of combining the sucked_into_jelly ANIM with a door wrong rotation. This requires to get interrupted in the middle of the door-opening (before it's started to open/close), hit by the jelly possibly. Now during the ANIM, LIFE 39 is trying to turn you to face E while the door's LIFE tries to turn you whichever way is appropriate for opening or closing that door.
    • If you try to use the front door itself, all the LIFEs that rotate the PC (217, 219, 225 and 227) make them face E. This obviously isn't useful.
    • The doors leading to R2 can be opened or closed from the N side. This leaves you at an awkward distance from the front doors so possibly opening one of the front doors first is preferred. The pirate is very slow to lumber across the room so this method of being freed is probably not ideal.
    • You can open the N front door on the W side first while in E3R0, getting freed by the other half and delaying the N one, then get into position in R1 so you spawn inside the R2 doors. If you land perfectly on the trigger without further movement you can be facing the door without it immediately opening. Then you turn around so you can back up through the door and open it from the N side but this should be interrupted by the N front door finally opening. Now you're in the glitched rotation state getting rotated towards the S where the E5 trigger is with the only problem being you're on the wrong side of the doors that are not about to open.
      • If you could somehow get through, or if the time wasted opening and closing a door wasn't too big, you would only have to run far enough S and wait for the jelly to hit you, using the knockback at the start of the ANIM to get as far W as possible, then manipulate the ANIM to take you S to the transition trigger.
      • There's a problem when you enter R11 though: the R1-R2 doors will be gone. It's not for sure you can get the last snap that you'll need in R11 to happen towards the S. In fact the doors disappear if you even enter view 16 in R13. Thus even if you managed to clip through the R1-R2 doors, it looks impossible to make it into the stairs trigger.
      • You could also do the same with the R1-R13 doors if you first open them, then close one of them from the N. These doors are also in memory in view 24 in R11 so you could go all the way using them. In fact this has been done. It's very slow because of all the lagging and slt's.
      • When you come back using either method, the doors won't stop turning you when they're returned to memory until the conditions have been met meaning they might slow you down a little bit. In addition, if the front doors are closing, you'll have to keep manipulating them while moving towards the trigger behind them so the N door doesn't close. There is no way to set type_of_death to anything other than 3 while in E5 unless you've brought De Vermis with you: falling to your death is a very irreversible process and it only changes your death flag at the same time as moving you to LIFE 39 and removing inventory access.
    • During this kind of hybrid movement, there are sometimes really big snaps that are like SPACE snaps (because of the way movement is handled, see "MOVEMENT"), causing to e.g. enter the E3R11 trigger then snapping back towards the N and E so far it completely avoids the R13 trigger.
    • It's difficult to utilize just slt's during this hybrid movement because of snapbacks that keep sending you back. You seemingly have to lag snap to get movement just in the desired direction.
    • This type of strategy has been tested and it's still quite slow:

File:E3 Sucked into Jelly Route Optimized Mock-up.avi

  • You can always delay the jelly's attack indefinitely while running because run keyframes all last 15 frames while the jelly's two ANIMs both have keyframes that last longer than that.

running 255 (15, 15, 15 ,15) jelly 95 (30, 30, 50, 50, 50) goes into jelly 96 (30, 50, 10, 20, 40, 50)

  • Trying to sync up the flask with the jelly: finishing to drink it on the frame when you're getting hit (sent back to LIFE 549).
		ANIMs: 	drinking, ANIM 277, kfs (40, 60, 40, 60, 90)
			jelly, ANIM 95,	(30, 30, 50, 50, 50) goes into
			jelly, ANIM 96,	(30, 50, 10, 20, 40, 50)
		Start on drink kf0, jelly 95 kf3
		Wait 40 frames, s/l
			drink kf1, jelly 95 kf3
		Wait 60 frames, s/l
			drink kf2, jelly 95 kf4
		Wait 40 frames, s/l
			drink kf3, jelly 95 kf4
		Wait 60 frames, s/l
			drink kf4, jelly 96 kf0
		Now drink kf4 and jelly 96 kf2 will end at the same time.
    • If you do this, it still won't send you to any LIFE other than 39, because the flask's FOUNDLIFE is executed before the jelly's LIFE.
    • Drinking from the jug should work exactly the same as drinking a flask re. trying to free yourself from going to LIFE 39: it's just another FOUNDLIFE.
  • When you're in the trigger around the front door, you're sent back and forth between LIFE 39 and 550, which tends to flash very briefly. This is because hitting that particular trigger doesn't disable trigger collisions. There's the idea of trying to leave the trigger on a frame when you're in 550, but even if you could, this would still result in a softlock since no code is there to return you to LIFE 549.
    • This flashing also happens while you're being sucked through the trigger.
    • Can't get the game to pause on a frame where the LIFE is 550 so it's very likely this is just the RV reading RAM mid-frame.
  • Drinking flask while about to run into front door:
    • Hit ENTER in inventory
    • Several frames later, VAR 21 (player health) is updated and inventory access is removed, both of which happen in the flask's FOUNDLIFE. On the same frame, VAR 204 is updated (11-->12) which happens when the player is in LIFE 59. Thus what must have happened is LIFE 58 (flask's FOUNDLIFE) was executed, changing the player's LIFE to 59 before the player's LIFE had been executed that frame. Indeed the player's LIFE also changes on this frame, along with her BODY, though not ANIM. There is also some offset on this frame (moving up like sliding against the door, forwards, not a snapback) and the keyframe advances.
      • Thus a preceding LIFE can change which LIFE is executed on the same frame for some other actor so long as the order is correct.
    • On the next frame, VAR 0 = 0 and TRACKMODE 0. These commands must have been issued by the door's LIFE (the player clearly collided with it on the previous frame).
    • Several frames later, the jelly is spawned. Looks like Emily's ROOM_CHRONO is rolled back as well (possibly also CHRONO).
    • Several frames later, ANIM (-->27 opening door), nextAnim and animType are updated.
    • Next frame, VARs 101 (jelly has attacked) and 204 (12-->268) are updated.
    • Two more frames and the game is drawn.
    • Few more frames and ANIM goes to 277 (drinking flask), caused by player's LIFE 59.
    • Assuming that there's no real reason why some of these intervals are longer than one frame other than delays with the Room Viewer itself reading the changed values. It's a mystery why the player's ANIM would only update a lot later than their BODY and VAR 204 when all three should be updated by LIFE 59 on the same frame.

CAMERAS

CAMERA VIEWS

  • This file contains information about the game's camera views, including positions, angles and dimensions, albeit not how they're linked to different rooms: File:AITD cameras.zip
    • The data in blue is from the game's files, what's in green is based on calculations.
  • The camera IDs are unique per floor. Thus they can be referred to as either e.g. E1R0 C1 or just E1C1.
  • Camera positions are defined by nine fields:
    • position, XYZ
    • angles, alpha/beta/gamma
      • gamma is almost always 0 except in just a few views, incl. C38 in E2R1.
    • focal, XYZ
      • XFocal and YFocal are the horizontal and vertical field of vision respectively while ZFocal is a projection in the direction the camera is facing that doesn't change the FOV: i.e. it offsets the camera forwards or backwards from its given coordinates (probably used for convenience to fine-tune the camera positions). The aspect ratio is always 4/3 except in E7R2 C6 where it's a bit wider than normal. This view is used for the close-up on the chauffeur.
  • The camera angles go as follows: alpha starts horizontal and causes the camera to somersault forwards when increasing; beta starts facing S and causes the camera to spin ccw when increasing; gamma starts level and causes the camera to roll left when increasing.
  • As part of the process of rendering actors, the game calculates the positions of each actor's every vertex relative to the current camera position. There are three coordinates: left-right (X), up-down (Y) and depth (Z, the perpendicular distance in the direction the camera is facing). These coordinates are stored in 16-bit variables, meaning the camera is the center of a 16-bit cube (values from −32768 to 32767). The system works well so far as the rendered actors stay within a reasonable distance from the camera. When the distances start to approach the limits (which only happens when actors are OOB), artifacts start to appear. More under "CAMERA WRAPAROUNDS".
  • Each room has a list of cameras defined, e.g. E1R1 has the following camera IDs:
    • [1,2,3,4,9,8,10,12,6,7,14].
      • Some are inside the room: these are shown by the RV when you choose to show cameras for E1R1 only
      • Some are outside the room: these are cameras adjacent to the doors of that room. This seems to be intended to make the transitions between rooms somehow easier, though the implementation may be a little bit shoddy. These are shown in the RV if you choose the "also adjacent" option.
    • AitD does not track the current camera using the camera ID as shown in the RV. It uses the slot index of that camera in these lists.
    • For example when you switch from E1R0 to E1R1 and C1 is active:
      • E1R0 [0,1,3]
      • CurrentCameraSlotID == 1
      • E1R1 [1,2,3,4,9,8,10,12,6,7,14]
      • CurrentCameraSlotID == 0
  • Each camera also has a list of rooms defined. For a given camera, it defines which rooms are visible (directly or through doors), for example:
    • E1C1: [0, 1] (E1R0 and E1R1 are visible).
    • This information, along with the room trigger IDs, allows the game to build a room/view visibility list for the current camera. This is used to move actors with LIFEMODE -1 or 2 between the actors' and objects' arrays based on the active camera, and also determines which actors are supposed to be getting rendered.
      • It's possible for even actors with LIFEMODE 0 to be somewhere where they should be visible in the current view but aren't because they're not in the list of visible views. This is easy to set up if you throw something OOB and move a couple of rooms away.
      • Even if an actor isn't being rendered, it can still be interacted with (e.g. picked up).
  • The followed/focused actor's object ID is stored in the currentCameraTarget global field and also its slot number in the currentActorCameraTarget global field.
  • The current view is controlled by camera triggers that the focused actor enters. These are displayed in many colors in the Viewer and are the only thing that isn't orthogonal. When a save file is loaded, the game looks for which camera triggers the actor being followed is inside and chooses one based on the order in which they are in in the CAMERAxx.PAK file for that floor (always the same one at the same location).
    • This means in a segmented run, you may be able to segment somewhere that causes a camera loading delay to be untimed since the switch happens between segments.
  • The camera triggers ignore the focused actor's Y-position completely. Thus they can be entered as high up or low down as you like.
  • Moving into a position where the camera triggers overlap doesn't cause the camera to switch until the actor is completely off the previous trigger.
  • When you enter a new room, the following happens:
    • If the current view is one shared between the rooms, it's kept though it's still unpacked again.
      • If you're standing in a camera trigger that's not for the view you're already in, the view now shifts to that one.
    • If you're not in a view that's shared between the rooms, it goes directly to the first view in the list of cameras for the new room. This is the camera that's in slot 0.
      • If you're standing in a camera trigger that's not for the view that was just loaded in, the view now shifts to that one.
    • During these double-switches, the middle view is never actually moved to BACKGROUND1 or BACKBUFFER but the unpacking still wastes time.
  • The code exists for determining which view is better if the player enters two camera triggers at once based on which angle they're facing (a camera with a similar beta angle should be chosen), but it doesn't actually seem to make any difference. The one that's listed higher up in the file is used invariably.
  • The way game detects an actor is inside a camera trigger is by shooting rays on the left and the right side. If both rays hit one of its edges, the actor is seen as being inside the trigger. Whenever the trigger shape is concave and the actor is inside one of the indentations (inside the pocket formed on the outside of a concave corner), and specifically one where a horizontal exists where both the rays will hit a specific camera trigger, this will result in a false positive. For this reason, the camera triggers are shaped such as to avoid this (except where the concavities have colliders inside that prevent entry), but a few exceptions exist.
    • A situation where there is a split trigger and two separate parts of the trigger are close enough to be touched by the rays at once won't cause the erroneous switch. (E.g. E1C10)
    • Most or all triggers that can be activated erroneously like this:
E0C3
E4C2, E4C3 (E4C0 looks like it should work, but maybe it's in two parts)
E5C1, E5C2, E5C11, E5C24
    • The rays have a length of 100000 units (on both sides), so you might think there are potential wrapping issues but there aren't any. Everything is divided by 10 before the computations so if the PC is at X = 7000 it's actually checking with X1 = 700-10000 = -9300 and X2 = 700+10000 = 17000 and both are well inside the allowed range [-32768; 32767]. Dividing by 10 reduces precision, but it's acceptable. The calculations could wrap around if having camera triggers and the PC very close to the edges but that shouldn't happen ever (the ROOMs are usually centered against [0: 0]), and so the system seems pretty robust.
  • Actors with LIFEMODE -1 have the same visibility as those with LIFEMODE 2. This LIFEMODE is not shown in the RV.
  • Despite the current camera view being stored in the currentCamera field and saved and restored correctly during a s/l, it's immediately overwritten with -1 after reloading. This is why the view can change to a different one afterwards.
  • If you go to the edge of the game world plane, you can hit camera triggers because of the game thinking your center point is somewhere halfway between the opposite sides of your split bbox, i.e. around the center of the gameworld. This doesn't work for other kinds of triggers.
  • To get to the obscure E2R9 camera view that is referenced in the CAMERA.PAK, it requires first getting a camera view to be shown that is not directly connected (adjacent to) E2R10, and then entering R10 from the OOB so you're not directly entering a camera view in that room either. This is easiest done starting from E2R2 in one of the three ways described below.

1) Clip OOB in R2. Run to the edge along either axis so you're aligned with one of the other two camera triggers (causes new camera view to load). Enter R10. 2) Clip OOB in R2, enter R10. Now run to the edge so you're aligned with one of the other two camera triggers (causes new camera view to load). Enter R2 (gives you the R2 default camera), then R10. 3) (fastest) Lag snap OOB going into R4. Go around and hit the R2 trigger on the E side. Enter R10. Demonstrated in this video. File:Entering E2R9 Hidden View (Fastest Method).avi

    • Each time you enter a new room in these paths, make sure not to be inside a camera trigger or it might fail.
    • There's a save file called "Entering the Hidden E2R9 View.ITD" to easily access it.
    • There is another unused view between R7 and R10 that's tied to the missing R9. It can't be reached in-game without RAM editing.
  • The reason why sometimes reloading a save file causes there to be a fade-in, sometimes not, seems to have to do with camera triggers. If you're not standing inside a camera trigger, the game, for unknown reasons, does the fade-in instead of just rendering the first frame.
    • You'll also always get the fade-in for any save file that's the first you've loaded during the session.
    • Time doesn't start until the first frame is fully rendered but this might be problematic if you're in the dark. The time between the screen going dark after selecting a save file to load and when the fade-out has finished is 30 frames (at 60fps). Thus half a second should be removed from the start of such dark segments with an invisible fade-in. Make sure to remind the person doing the timing about this.
  • There are places where camera triggers can be avoided to avoid the lag. Some examples:
    • If going from E1R0 to R1, open the door on the left side to avoid entering the view towards the stairs again.
    • Passing through E4, the best way seems to be to turn after the first wine rack then go straight to the block. This avoids at least one camera transition comparing to any other path. Another alternative is to turn BEFORE the first wine cabinet but this might give a more awkward trajectory.
    • When leaving E4 for E3 and going towards the outside door, if you travel by a path that touches the corner of the staircase you avoid getting the angle that shows you the kitchen door.
  • If the game doesn't seem to be rendering (the screen is black/dark when it shouldn't be), this only seems to indicate the output is not being sent to the screen. Everything looks normal in the RAM and the game doesn't run any faster. This state is escapable by simply visiting the menu or inventory again.

CAMERA DEFOCUS TRICK

  • When the camera is not following the player, actors around the player may not be loaded in, allowing running right through where they would have been and generally skipping logic associated with them. The various times when the player is defocused:
    • E0 when the bird and zombie enter, but the whole floor is "monolithic" and everything is always loaded in (for the exception, see "NULL FOCUSED ACTOR GLITCH").
    • E1 when the bird crashes in (allows running to the R7 stairs) and also if you kill the nightgaunts the camera stays in R7 for a while, switching from one nightgaunt to the other.
    • E2 when the camera is on an arrow or axe. Can be used to remove the secret room door etc.
      • You can get the talisman like this: trigger an axe first, then quickly an arrow. You have to get the arrow into orbit in R7. Now run into R0 and towards R8. Run through while the door isn't there. Now simply let the axe hit you and you regain focus in R8.
        • Or lure an arrow through to R2 so it ends up flying into R10 for long enough to allow you to go through to R8, then hitting the knight to give you back focus. Alternatively get it to hit the jellyfish in R5.
    • E6, the two short cutscenes. It's obviously not enough time to do anything.
  • If the player is not standing inside a camera trigger when the game starts following them again, and the room they're in has some shared view with where the focused actor was when the defocus ended, the view will stay in that view until the player moves inside a camera trigger or into a room with no such shared view. This has potential to at least delay code from getting executed if not getting somewhere you couldn't have otherwise by hitting a room trigger from another angle you couldn't have reached because an actor was in the way.
    • There doesn't really seem to be anything useful you can do like this in AitD.
  • You can in theory use a related trick to get other actors following you through places where there's normally some door or such if the door is gone because you're far from it yourself.
  • What happens if you try to hit a transition on the same frame as losing focus? Where can you do this?
    • E0 (bird, zombie) doesn't seem to work, probably because the transition immediately moves you to a different room while the focus is on you, preventing the bird's or zombie's LIFE from getting executed that frame.
    • E1 bird doesn't work simply because it's not active while you're in either R6 or R7. It gets LIFEMODE 0 only on the same frame as focus is shifted on it. Also it's warped to a position in R4 at the start of its TRACK (so one frame later). This means that if you first warp it to R7, then get yourself into the transition on that one frame, you CAN use it to defocus yourself. This causes the player to vanish if you reach the bottom of the R7 stairs before gaining back focus. After regaining focus, you're stuck on the upper end of the stairs in E2R2, having been left with SPEED 0 instead of 4, apparently as a side effect of being removed from the active actors array (more under "ACTOR SERIALIZATION"). Also your new slot will be 0.
      • If you regain focus before reaching the bottom of the R7 stairs, the TRACK proceeds normally. Also if you do the same with the R6 stairs, the TRACK will set your SPEED correctly in E0 and thus no anomalies there.
    • E1 nightgaunts work, either of them and using either of the staircases. Causes the player's WORLD coords to become desynced and they can't reach the waypoint in the stairs though it looks like they're circling the right spot. The view was sent to C8 in R4 of all places (the view from outside), which might be some kind of default view.
      • The desync between ROOM and WORLD is (-6330, 1120, 200) so they're desynced even vertically. Warping the player in the RV so the waypoints are reached causes the PC to vanish (as you'd expect) and the game is left in limbo, though the menu still works. Saving and loading doesn't restore the view. Could potentially time this in such a way that the R4 bird gives you back focus afterwards but the expectation is the desync is removed when focus is given back as tends to happen.
      • The reason the PC can't reach the waypoint seems to be the stairs interpolation simply isn't taking them far down enough, suggesting something goes wrong when storing the starting coordinates for the "walk stairs" command, though even then the end coordinates should be all that matters. It could be both the WORLD and ROOM coords are used in some way.
    • E2 arrows and axes require you to be in R1 for focus to shift onto them, or if you first do a room/view desync and hit the wrong trigger (see "ROOM/VIEW DESYNC"), you'd still have to be in R0.
    • The first E6R6 cutscene requires you to be standing in the trigger but in theory you could throw the lamp at Pregzt from OOB for the other one. The second one you can't seemingly trigger in a way that causes anomalies even if you warp to a transition trigger just as the lamp hits Pregzt, but this is to be expected for the same reasons as with the E0 transition.
  • If you are not focused when dying, you'll have a ROOM/WORLD desync when you're sent to E6. Most of the time, this makes you invisible during that cutscene (the game is following the door not the player) until you're teleported to the sarcophagus after which the game ends normally.
    • This only seems to depend on which room the followed actor was in when the view changed to E6. The camera trigger or view it was in doesn't matter.
	E0R0 24576, 23296, -24576
	E1R4 6500, 0, 300
	E1R7 13400, 0, 3200
	E2R0 1600, 0, 7300
	E2R1 -3400, 0, 7500
	E2R2 8600, 0, 7200
	E2R3 9000, 0, -1500
	E2R4 3000, 0, 500
	E2R5 4600, 0, -1600
	E2R6 0, 0, 0
	E2R7 200, 0, 15900
	E2R8 4000, 0, 15400
	E2R10 8300, 0, 15600
    • It's not known what these desyncs are based on. None of them seem to produce the 2D wraparound while the player's body is moving around OOB. It's difficult to imagine other effects they could have since the WORLD coords are not used for triggering triggers.
  • There's no ROOM/WORLD desync if the view is in a dark place. Right before you die, the game lights up and the desync can appear though.
  • You could, in theory, get one of the two birds in E0 or E1 or the E0 zombie physically stuck so their TRACKs never finish and focus is never returned to you. In E0 this obviously doesn't do anything and in E1, you have enough time to reach the stairs anyway (and the setup would at least require moving the stool and wedging it in place with a dropped object). The arrows and axes move too fast to be held back by this method. No other similar defocuses exit.

WRONG PICKUPS/WRONG TRIGGERS

  • When the game is not focused on the player, different actors are loaded into memory than normal for the view/room you're in. These actors may run code for picking up items or achieving other effects that rely on either colliding with or searching a collider with a specific ID or standing inside a trigger with a specific ID. Because collider IDs and effect trigger IDs are only unique to the current room, any of them will do for activating the code, regardless of which room it happens in. This is what the "wrong pickup" or "wrong trigger" is based on.
  • The main way to abuse this is with the arrows or axes flying around in E2 but relatedly, a room/view desync may work as well. An example of how to use it goes like this:
    • Get an arrow to fly into E2R7. This despawns the R8 door so you can go there yourself.
    • The actor that gives you the study key is loaded in. It requires searching the collider with ID 1. There is a collider with that ID in R8.
    • Just search that collider and the game offers you the study key instead of what's normally inside of it.
  • You could also use the arrow for picking up the Necronomicon quickly (collider ID 0) if it had some use.
  • Wrong trigger: You can remotely touch triggers in a different room where the view is if you're standing inside one with the same ID. In AitD 1, can seemingly only be used to:
    • remotely place the mirrors in E1R7 (ID 0 and 1)(this is purely theoretical)
    • launch an axe from E2R0 or R8 (ID 0)
    • place the false book from E2R1 or R8 (ID 0)


ROOM/VIEW DESYNC

  • Unlike with the temporary camera defocus trick, there's a way of getting the room you're in and the views you're shown to desync indefinitely until moving into another room, or saving and loading: the room triggers are the ones for the room you're in, but views are from another room. Doing this can have the effect, as before, of despawning actors and thus removing scripts. Your WORLD coordinates and ROOM ones are desynced when you do this and you can cause further view changing to happen between the views in the other room.
    • Looks like this is done like this: you have to make sure you save the game (entering the menu should be enough) on the same frame when the arrow both hits you and enters the room that you want the view to stay in. You can't get more arrows to come to you because the actor that spawns them is not loaded in (unless you put the view in E1R1), or you're not inside the trigger yourself.
    • You can also just make yourself move into the next room on the frame when the arrow enters the room you're coming from, making the view stay there.
    • The only vague use I could see is some of the actors in the library are despawned with the view in the adjacent rooms. This allows for clipping towards the secret room off the E side as well as the W side, however it's a much longer clip from the E. The secret room door itself never despawns regardless of which view you're inside in R1 or R2. There doesn't seem to be any way to getting the view further away than the next adjacent rooms like this. I'm assuming there's nothing you can do in AitD 1 that actually helps in any way.
    • The desync doesn't work if there's a shared view between the two rooms, possibly.
  • Being ROOM/VIEW desynced can cause the hotpoint of an attack not to move. This is because the actor in question is too far from the view to be getting animated, and so because the hotpoint vertex isn't moving, neither is the hitbox.

OBJECTS AND ACTORS

  • The objects spreadsheet file contains information about the objects from OBJETS.ITD but also various other information.

OBJECTS

  • "Objects" refers to the information stored in and unpacked from the OBJETS.ITD file. Actors could be thought of as "live" versions of objects loaded into the cache (more under "ACTORS" and "CACHE"). Some fields are only present for one or the other, with actors having more of them than objects. The process is intended to reduce the amount of time it takes to load actors into the cache and to serialize them into the objects array again, and to store relevant short-term data into the actors themselves for quick access while they're active.
    • The same object is never instantiated into multiple actors.
  • Objects are referenced based on their index inside the file, i.e. their offset from the start. They don't have separate "object IDs".
  • Some fields are present only for objects but can be referenced by the actor ID (which is just a copy of the same object's index). Fields only objects have:
    • foundLife, foundBody, foundName: These change so seldom (in the scripts) that it's not necessary to give actors these fields at all.
    • flags2: Whether an object has been found or thrown, and which actions are available for it in the inventory. Also don't change so frequently.
    • field_24: Which LIFE to execute if the actor runs into a floor trigger (for the PC); Also used to indicate which VAR to show next to certain items like the weapons (how much ammo is left). Never modified.
    • mark: This also changes very seldom, during TRACKs.
    • zvType: doesn't change. Has some impact on how exactly an actor's bbox is calculated based on its BODY. The types are COLLIDER, ROT, NORMAL, CUBE, MAX.
      • Actors that don't have a BODY or zvType COLLIDER when initiated get a default bbox with size (200, 2000, 200), with position (0, 1000, 0), essentially a thin stick-like shape. Only actors 2 ("actions" actor which makes LIFE 562 be executed at game start), 87 (the E1R4 bird waiting outside) and 224 (E5R6 spider before it starts to climb) are like this.
  • There doesn't seem to be any particular reason why four of the objects are called "Alone in the Dark". Also the ones called "Manip Debug" don't seem to have any obvious special properties. It's probably all vestigial. The one called "You cannot carry anything else" could be where that line is read from but it's not clear.

ACTORS/ACTIONTYPES/COLLISIONS

Displayed in green in the Room Viewer, except for the player character and the actor that has slot 0, which are rendered in white. The RV starts showing each actor as soon as its ID has been set (isn't -1 anymore), which happens near the end of actor initialization.

  • Actors could be thought of as "live" versions of objects, stored in a small cache for quick access to data for monsters etc. that are currently active. Not all actors represent tangible things, however.
  • Every actor is initially stored in the objects array which is initialized from OBJETS.ITD every time a new game has been started, with about 300 entries. Every new actually active actor gets allocated the next free memory slot (shown in the Viewer) in the actors array, which has 50 slots, with the lowest ID actors getting lower slots, each actor's information copied over from the objects array. The slot is freed immediately when the actor has been removed, and the majority of its information is copied to the objects array again. This can happen when the focused actor moves too far away, onto another floor, or the actor is issued the DELETE command on. Thus the exact slot numbers are variable, though the PC will always have slot 1. Both the objects array and the actors array are stored in all save files, but the game never reads the data in the objects array directly when executing code.
    • When saving the game, both arrays are serialized. Saving and loading can change the slots around by reordering all actors by increasing ID instead of the haphazard slot order they tend to have after playing the game for a while. Also actors that are far away might not get loaded in again at all even though they were present when saving the game.
    • The two arrays are mostly similar except there are more fields for actors than objects. The reason there are two such arrays in the RAM is probably mostly that this way the active actors fit in one memory segment. Also it's a way of controlling which LIFEs are getting executed when: only those of active actors' are.
    • When an actor is instantiated, it gets the ID that's the index for its parent object inside OBJETS.ITD (and thus the ID is unique for every actor), and the object gets its ownerID field replaced by the actor's slot number.
    • Note that actor additions and deletions are only done when the main loop reaches the updateAllActorAndObjects() method. Thus no mid-frame replacements of actors should be possible to cause "wrong actor" effects. (See "WRONG ACTORS").
  • Schematic for how actors are loaded into the actors array (showing eight memory slots):
[41] [ 5] [  ] [23] [  ] [  ] [  ] [  ] //example initial state (three active actors)
[41] [ 5] [ 8] [23] [  ] [  ] [  ] [  ] //actor 8 is instantiated 
[41] [ 5] [ 8] [23] [12] [  ] [  ] [  ] //actor 12 is instantiated
[41] [  ] [  ] [23] [12] [  ] [  ] [  ] //actor 5 and 8 are removed
[41] [ 4] [11] [23] [12] [  ] [  ] [  ] //actors 4 and 11 are instantiated on the same frame, 4 gets priority because lower ID
[41] [ 4] [11] [23] [12] [ 5] [  ] [  ] //actor 5 is instantiated
  • Every actor has one or two LIFEs associated with it. The first one (stored in an actor field called LIFE) is executed fully every frame whenever the actor is present in RAM. The other field (stored in the linked object) is FOUNDLIFE and it defines which script will be executed in any of these cases:
  1. you collect the actor off the floor
  2. the actor is getting selected in the inventory, or
  3. you have the actor INHAND. Also if a LIFE contains FOUND or TAKE instructions, those cause the FOUNDLIFE to be executed as a subroutine.
    • Check the LIFE and FOUNDLIFE columns in the worksheet file for the starting values. The LIFE field value can only be changed by the LIFEs themselves, with one exception: the engine changes it if the PC has entered a floor trigger (PC's LIFE becomes 550). It seems the FOUNDLIFEs cannot be changed in the LIFEs, nor are they ever changed in general.
    • The function updateInHand() that executes the FOUNDLIFES calls the LIFE engine which expects objects to have an instantiated actor, but that might not the case (e.g.: it's an object in the inventory). If there is no such actor, it temporarily uses the first available slot but searches for it in reverse starting from slot 49. If it cannot find any free slots at all, it erases whatever is in slot 49. The temporary slot usage only lasts a very short time until the FOUNDLIFE has been executed. In practice, this only has to be done for actors that are INHAND (once every frame) and for those that you've just selected to do something with from inside the inventory. Actors being collected off the floor are obviously already instantiated.
  • An actor's LIFEMODE (LIFE_MODE in the scripts) determines when it will be in the actors array. This is also defined in OBJECT.ITD.
    • -1 = never (the RV shows nothing)
    • 0 = whenever the focused actor is on the same floor (shown as FLOOR)
    • 1 = whenever the focused actor is in the same room (shown as ROOM)
    • 2 = whenever the focused actor is in a camera view close enough (shown as CAMERA), generally in the same room or an adjacent one (i.e. this actor could be visible in the current view)
    • The focused actor is obviously usually the PC but not necessarily.
    • Actors that are supposed to be loaded in and removed are only checked when the focused actor moves to another view or room, or the game is refreshed by visiting the menu or inventory. For this reason, actors can enter another room or view without instantly disappearing even if they "should" based on those criteria.
    • If an actor has a particular LIFEMODE given in OBJETS.ITD but doesn't actually have a LIFE, it seems the game assigns it LIFEMODE -1 instead until it first gets some LIFE. This can be seen with zombie 1 (ID 176) in E3R12 which has LIFEMODE 1 in OBJETS.ITD.
  • On every frame, the game loops through all active actors twice. The first time is to process animation, movement, checking for collisions etc. The second loop is just to execute LIFEs. During the first loop, to do collision checks, the game loops through all other actors before moving into the next actor in line to check for its collisions. The order is always determined by slot positions.
  • Actors that are far away from the room you're in don't get rendered even if they have LIFEMODE 0. The easiest way to confirm this is to lead vortices far away from E3R3, then return to R3 yourself and play the Dance of Death, at which point they'll try to come back to R3 but usually fail being left in different rooms. This makes them invisible while you can still get hit by them.
  • Some of the actors don't have BODYs. The ones that don't are only there to hold LIFE scripts, either for some individual colliders, the whole room, or even the whole floor, with LIFEMODEs set accordingly.
  • An actor's flags2 field shows the game which actions are possible to perform on it in the inventory.
  • The player's FOUNDFLAG field is used to set your available actions so jumping is only available in E5/E6. In the scripts, the player's action modes are represented by an item called "actions" that's automatically picked up at the start of a new game, and stays at the top of the inventory based on the logic in the engine.
  • Unlike other forms of resources, the 50 actor slots are enough for any normal in-game situation so the game doesn't have a mechanism of removing the least-recently-used actor. In fact, if the limit is ever exceeded (which is not difficult to achieve even without cheating), the game often handles such cases fairly ungracefully, either overwriting existing actors or even causing a crash.
    • There may be a version difference between the English and French debug CD versions in what happens in these situations, though there shouldn't be.
    • When enough actor array slots have been filled, items can no longer be spawned in by dropping them or throwing them, or by other means. Dropping them always yields the message "There is no room". Trying to throw them plays the animation but nothing is thrown or removed from the inventory. Depending where you're at, it seems this starts to happen when the number of actors present ranges from 40 to 48.
      • It's possible that firing a gun could behave differently from other means of spawning actors since the debris flow actor is first placed temporarily in the lowest free slot, then put into the next free slot above that. In general, the debris actor is correctly not spawned when there are no more free slots though.
    • When the actor limit is being exceeded, it is unclear which actors the game will prioritise: it doesn't seem that only the actors with the lowest IDs will be kept for example, nor is it the actors furthest away from the center of the current room. Basically anything (though probably not the PC) can vanish, including dummy logic actors, doors and enemies. It could be only actors from slot 49 disappear.
    • Actors caused to vanish this way never reappear unless there is some code that spawns them again, e.g. the Deep Ones and arrows/axes.
    • The last slot, number 49, is overwritten by the updateInHand() command if the array is full to make space for the INHAND actor's FOUNDLIFE to be executed.
    • The save called "Missing Doors.ITD" has permanently missing doors and other actors for a demonstration of a situation where too many actors were present at once. For further experimentation with that save file, note that there are some items stored in E3R5, some in E5R0.
  • Flow actors: Actors with ID -2 are "flow", particle effects spawned by the SPECIAL command or the FIRE command. They occupy the same slots as other actors. They have LIFEMODE -1. Under some circumstances, they can glitch out and crash the game. More under "flow actors".
  • The PC actor starts out with and always has slot 1 under any ordinary circumstances.
  • When you move between floors, or when you come from certain views in the adjacent rooms, it looks like the game first loads in more actors, then removes ones with LIFEMODE 2 that are too far away, based on what's shown in the Viewer.
    • Generally moving between rooms, there's inconsistencies with whether the old actors are removed from memory first or the new ones loaded in. E.g. going from E3R9 to R12 causes the R12 actors to be loaded in first.
      • There's even some cases where some actors are first removed when leaving a room, then loaded in again (probably because they had LIFEMODE 2 and they're considered visible from both rooms). Seen going from E2R4 to R2.
    • It's not clear if this stuff can have practical implications other than longer loading times (presumably) if some actors are loaded in then removed again, and which exact slots everything will end up inside. Slot numbers can thus be manipulated just by moving between views.
    • The actors' scripts are probably not getting run at all unless they stay in memory after all the shuffling.
  • You can cause an actor (with LIFEMODE other than 0) that you've gotten to move into a different room vanish just by going to the menu and coming back instead of having to save/load.
  • If you try to drop or throw (or otherwise produce) an item with LIFEMODE 2 while the view is too far, it looks like nothing happens. When the view is brought back, you will see thrown objects in the air (similar to if you threw them, then went away yourself before they'd hit anything) and dropped ones on the ground. You still can't drop anything into a collider.
  • Many actors don't have exactly matching horizontal ZV and WORLD/ROOM coordinates. This shouldn't have virtually any impact on anything.
  • Actor DYNFLAGS, aka COL_FLAGS in the Viewer, are set by scripts (TEST_COL 0/1) and by the TRACKs (disable/enable collisions). When an actor is thrown, the engine sets collisions off then as well (a different collision check is used with thrown actors).
  • Actor vertices each have coordinates stored in the BODY.
    • When inside the game world, the coordinates are relative to the actor's ROOM position. Thus a vertex with the relative position (100, 100, 100) when the actor is placed at ROOM (100, 0, 100) will be placed at (200, 100, 200) etc.
    • If you have two identical actors one of which has all its vertices shifted to a different position relative to the BODY origin, the only two differences between them are where the actor is drawn relative to its ROOM position, and the effects of rotations. All rotations in AitD are applied around the BODY origin. So if the origin is at the center of the BODY (true for most BODY's, including all those used by actors that move around the game world), it rotates like the PC does. For actors like doors, the origin is at the corner where the hinge is supposed to be instead. The bounding box shape doesn't influence rotations at all but the opposite can be true (for actors rotating, often the bbox is calculated based on rotated vertices).
  • Actor rotations illustrated:

Actor Rotations 1.png

    • (0,0,0) is the origin. All rotations will occur around that origin. Most rotations in AitD are around the Y (beta) axis. In the above picture, rotations will occur around the center of the chest. In these pictures, the bbox is shown from top down in the top right corner.
    • If you want the rotations to occur around something else (e.g. a corner of the chest), the vertices of the chest need to be placed differently:

Actor Rotations 2.png

    • The model vertices have been translated by a given offset. In theory, you could have a bbox not matching the model at all (in either position or size):

Actor Rotations 3.png

    • In that case too, collisions will still be against the bbox (the chest model is effectively ghost-like) and the rotations will still occur around (0,0,0). This never happens in AitD (the bbox is always 1:1 with the model), which can be seen in the Model Viewer.
  • Actor bounding boxes: When actors are initiated, the kind of bounding box they will get depends on the object's zvType field (not copied into the actor data). This field can take the following values: COLLIDER, ROT, NORMAL, CUBE, MAX. Other than COLLIDER, each of these types has a corresponding command in the scripts that sets a new bbox in the same way (look for "actor bounding box commands"). The CUBE type corresponds to the DO_CARRE_ZV command.
    • The COLLIDER zvType causes the actor to have the same bbox as some collider with flag 0008 whose ID is given in the next object field.
  • The fields that actors have to store the information about things they're interacting with are as follows:
    • COL[3]: This is an array of up to three other actors the given actor is colliding with in order of increasing slot. If there are more than that, the rest are ignored (the ones with the highest slot numbers). All the actors inside the COL array get their COL_BY set accordingly, as well as their HIT_BY if the colliding actor had ACTIONTYPE HIT_OBJ.
    • COL_BY: This field has one actor that is colliding with the given actor. It is completely symmetric with the COL field except has just one value. This is set after the colliding actor's COL array has been filled. If multiple actors are colliding with this actor, the actor with the highest slot takes preference. Also if the same actor is colliding with more than three other actors, the actor that doesn't fit into its COL array also doesn't have its COL_BY set to this actor's ID.
    • HARD_DEC: Trigger the given actor is inside. The order in which collisions are checked is based on the triggers' positions in the file. The first collision counts and the rest are ignored. The trigger ID has nothing to do with it.
    • HARD_COL: Collider the given actor is colliding with. Requires flags 0001 + 0002 (0003) or 0001 + 0008 (0009) to be set. The order in which collisions are checked is based on the colliders' positions in the file, and the last entry overwrites the others. The collider ID doesn't have anything to do with it.
    • HIT: Actor that the given actor's attack has collided with. If multiple targets are hit on the same frame, the lowest ID actor is prioritized and the rest ignored.
    • HIT_BY: The actor that has attacked this actor. The actor with the highest slot that hits a given actor on the same frame overrides any previous ones. If this is set by a HIT_OBJ actor, the lowest three actors by slots are the ones that get their HIT_BY set based on collisions.
    • All these fields apart from COL[] are reset (to -1) at the same time every frame for all actors regardless of their flags. COL[] fields are set/reset for each actor a little bit later.
    • In many cases, either the colliding actor's COL[0] or the collided-with actor's COL_BY could be used for the same logic, each with their limitations as described above.
  • Most entities in the game are so-called Axis-Aligned Bounding Boxes (AABB), which means boxes with every edge aligned with an orthogonal axis. Collisions between them are checked for like this:
(a.x2 > b.x1) AND 
(a.x1 < b.x2) AND 
(a.y2 > b.y1) AND 
(a.y1 < b.y2) AND
(a.z2 > b.z1) AND 
(a.z1 < b.z2)
    • Illustration of two bboxes colliding in top-down view:

Collision Detection.jpg

    • "a" and "b" are two actors (or something else) and x1/x2, y1/y2 and z1/z2 represent the positions of each edge. x1 is assumed to be smaller than x2 and similarly with y1 and z1.
    • AABBs can also be rotated even though they're aligned to the axes. Some ways the game might use in different situations: rotate the existing bbox vertices, create new bbox around that (tends to make it bigger); first rotate the model's vertices, then create a new bbox based on the new vertex positions (tends to keep it smaller, and also the vertices have to be rotated anyway if the actor is supposed to be redrawn after the rotation). This is illustrated here:

Bounding Box Rotations.png

Green = model vertices.
Gray = bounding box.
Dark gray = bounding box recomputed after rotating original bounding box by 45 degrees.
Blue = Same but by using rotated vertices.
    • An actor reaching the edge of the plane behaves oddly in some ways. It can't push other actors, get hit by them or pick anything up. An actor being pushed behaves normally until it has reached the edge, at which point collision detection fails because of the side that has wrapped around, and it can no longer be pushed.
    • The reason for failing collision checks is the actor's x1/y1/z1 edge is no longer smaller than x2/y2/z2 (depending on the direction of the wraparound). This causes all collision checks to automatically fail. Wrapping around in several directions doesn't help, and also both the actors/bboxes wrapping around makes it even worse.
  • The game always checks all 3 axes when determining if there are collisions. Collision handling is a different process which depends on what kinds of collisions are being checked. For example, collisions against colliders require cancelling movement along some axis. X/Z collisions require checking which side the colliding actor is on, whereas Y-collisions do not since all Y-collisions are assumed to be from the top while falling.
  • Pushables colliding with other actors don't cause their own COL[] or the other actors' COL_BY values to change. Pushable collision checks are different from regular ones. More under "PUSHING".
  • Whenever the HIT == PLAYER check is used by any actor for some logic, if the player is inside another actor with a slot number higher than theirs, the condition won't be met. This is obvious if you stand inside some actor in E2 while getting hit by an axe. This obviously goes for any actors, not just the PC.
    • The ACTOR_COLLIDER check on the other hand works the opposite way around: only an actor with a slot number lower than the player's (so 0) can prevent the player getting hit. This is the case with the arrows.
  • If an object on the floor is completely enclosed inside another actor, it may not be possible to pick it up. Has to do with the first actor collided with always being the same one.
  • The game checks collisions between a given actor and the colliders in the room they're in first, then between that actor and other active actors afterwards. This explains why you can collide with colliders through other colliders (e.g. to pick up the lamp in E0) but you can't collide with actors through colliders since at that point, the collider collision has already caused an ejection to happen away from the collider.
  • If there's a COL check run on a rotating actor and the game is lagged so the rotation finishes, COL is never set even if this puts the rotating actor inside another actor. In AitD, this is only anomalous in the case of the hatch in E0 since the other rotating actors don't stop rotating anyway even if they're colliding with something.
  • The way the RV shows collisions taking place and the way they take place in the game don't seem to always coincide. There is often a few frames between when a certain collision should have started or ended (like if you're pushing a chair into a wall while running so the collision ends near the end of the keyframe when your offset is bigger than the chair's width) and when it shows it having started or ended. On the other hand, when the zombie in LIFE 17 collides with the chest in E0, it is clearly erased immediately on the same frame when the collision is reported. This discrepancy seems to remain even when the game is run at 0.1x speed.
  • animActionType: The ACTIONTYPE of an actor is a state machine field that can take the following eight values.
PRE_HIT => HIT
PRE_FIRE => FIRE
PRE_THROW => THROW
DURING_THROW
HIT_OBJ
    • The PRE states make the actor check for the right ANIM frame before changing to the actual HIT/FIRE/THROW states which are very short-lasting. The PRE_THROW one also checks for space around the actor (more under "THROWING").
    • DURING_THROW is the state the object being thrown gets that causes it to behave like it should.
    • HIT_OBJ is the state entered when HIT_OBJECT has been called in the scripts, and it makes a collision with another actor set the HIT_OBJ actor's HIT and that actor's HIT_BY and HITFORCE fields immediately. Essentially the same as HIT but with no separate hitbox. Used with things like the vortices.
    • The HIT ACTIONTYPE should not be confused with the HIT command or the HIT field that has an ID of whatever the actor's attack has just hit. That field is set if the attacking actor has ACTIONTYPE HIT_OBJ, FIRE, HIT or DURING_THROW.
  • Any ACTIONTYPEs seem to be cleared at the start of a transition, possibly due to the ANIM change.
  • The actors called "alone_in_the_dark" are dummies used to send the camera into a new view during cutscenes, and also to calculate the distance from Pregzt to make the fireballs disappear when they're getting too far.

WAYS TO ABUSE THE COLLISION FIELDS

  • You can abuse the fact the actor_collider command only checks for COL[0] by making there be a second collision at the same time, with an actor with a lower slot than the one the code is actually there to keep track of. The slot 0 actor is required to confuse the game when the PC is involved (in slot 1 always).
    • The 0 slot actor can stop some enemies from attacking the PC or change their behavior a bit (vagabond, pirate, arrow, zombies).
    • Enemies may continue attacking the PC non-stop instead of being in the recovery ANIM after hitting them.
    • Enemies may continue moving towards the PC after the PC is dead instead of standing still next to the body.
    • Zombie 1 in E3R12 can't sit in its chair.
    • The Cthonian doesn't stop past the boulders due to colliding with actor 219 to the S (having the shape of the wall). It will still stop when reaching the end of the TRACK.
  • COL_BY: Since only the highest colliding actor's slot is stored in this variable, here are the anomalous effects two or more actors colliding with the same actor at once could have if the slot order is right:
    • The PC is unable to search, close, push etc. something.
    • The PC is unable to open doors. This is worth considering separately because it might make some unlocked doors possible to clip through that aren't normally possible to.
      • Single doors that are inset (there's a tiny nook) can't be used for clipping even if they stop opening when the PC collides with them. Single doors that protrude can already be used for clipping without triggering them to open. Thus the only doors that can be affected by this is the double doors, and specifically clipping directly through the middle of them. Even if there were enemies near enough to have it save time, there aren't any interesting opportunities granted by clipping through in-between any unlocked double doors.
    • Might be able to clip inside the Nightgaunts more easily if you get a bird to also collide with them at the same time, which could be tricky of course.
    • The fireballs might not hurt the PC if also colliding with a Deep One.
    • Enemies that aggro when collided with by the PC won't aggro.
    • The boulders could become resistant to being deleted by the Cthonian if there was an actor with a slot higher than it also colliding with one of them.
  • Collision clipping: see the separate topic by this name.

HIT_OBJECT / HIT_OBJ ACTORS

  • HIT_OBJECT is the command called to set an actor into the HIT_OBJ ACTIONTYPE. When it has that type, the first three collisions (based on lowest actor slots) immediately cause the other actors' HIT_BY and HITFORCE to be set accordingly, with the rest of the collisions being ignored. The HIT_OBJ actor will be left with the highest slot actor of the three in its HIT field but this doesn't seem to make any difference.
    • Because the PC is in slot 1, this doesn't offer a way to shield oneself from such attacks.
  • The HIT_OBJECT command's second parameter is the HITFORCE. The first parameter doesn't seem to be used at all, though it seems to be the bigger the bigger the monster that uses it, suggesting it could have been intended as the range of the attack. Clearly the actor's bbox is used for the collision checks as normal, so such a value wouldn't have any reason to exist.
  • HIT_OBJ glitch: Sometimes HIT_OBJ actors with collisions can't hurt the thing they're running into. This can last from a few frames to becoming a permanent state so long as you don't move. In this state, one of the HIT_OBJ actor's COL fields has the other actor's ID and that actor's COL_BY has the HIT_OBJ actor's ID, but the HIT and HIT_BY fields don't show anything for some reason.
    • The save files called "Why Can't the Spider Hurt Emily.ITD" are very good examples of this. In the first one, the spider is colliding with the book on the floor twice, but it can't hurt Emily even if you warp the book away, at which point it clearly is colliding with the PC. In the second one, there are no other actors present at all, though there is still a floor collider underneath.
    • Of the other HIT_OBJ actors, the little spiders only seem to have short-lasting glitching, the Cthonian is difficult to assess because of how it immediately starts its attack when you're near the front, the barrels don't seem to have it and the rest weren't tested. The glitch isn't really understood.
    • While the glitched state tends to continue when you move perpendicularly to the HIT_OBJ actor, it will not continue indefinitely if the angle the HIT_OBJ actor is facing changes too much, perhaps because this changes some other property of the collision (POSREL).
    • The glitch can not be directly related to slot numbers since the big spider has been in both slot 0 and other slots when this happened.

File:Why Can't the Spider Hurt Emily.avi

  • Sometimes HIT_OBJ actors that have collisions collide with the same actor lying on the floor twice. This seems to happen a lot (perhaps exclusively) if they're stuck against the PC (or probably any animated actor will do).
    • At least works with the little spiders and big spider. It's easier to do with the big one.
    • In theory, this could be used to shield yourself from such an actor more easily.
  • Actors that bleed getting hit by HIT_OBJ monsters continuously spawns a lot of flow actors which is liable to result in memory corruption (see "FLOW GLITCH").


FLOW ACTORS

  • Flow actors: Actors with ID -2 are "flow", particle effects spawned by the SPECIAL command or the FIRE command. They occupy the same slots as other actors. They have LIFEMODE -1.
    • The five kinds are bubbles, blood, debris, muzzle flashes and smoke. The bubbles are what appears after something has died. The debris is the white stuff that appears upon firing guns.
    • What type a flow actor is is stored inside their ANIM field. While there are five distinct types (e.g.: the engine can call initSpecialActor(BLOOD) or initSpecialActor(GUN)), blood and debris will both be stored as ANIM = 1. The game does not need to make a difference since the only difference is color (stored in the _MEMORY_ cache entry). The ID of the related cache entry (as shown in CV) is stored in KEYFRAME.
    • The debris actor is for some reason first put into the lowest slot during its travel, then into the next free slot above that when it arrives. This is different from the other flow actors which all go in the lowest free slot like normal.
    • Aside from taking up an actors' array slot, flow actors also get an entry in the _MEMORY_ cache which has a size of 10000B and 50 slots. Each entry holds the data of all the many particles (velocity and position) involved. The _MEMORY_ entries are removed when some condition is met, e.g. all the particles have reached 0 Y. The _MEMORY_ IDs are given not based on what the lowest free slot is but rather there's just a 16-bit counter that increments by one whenever a new entry is added. More under "FLOW GLITCH".
    • Bubbles, blood and debris all take 304 bytes, the smoke 246 bytes of _MEMORY_ space.
      • 10000 bytes fits 32 entries of size 304 bytes or 40 entries of size 246. So there should be no theoretical way the number of entries in _MEMORY_ can ever be higher than 40 without corruption.
    • Only HIT_OBJ actors with no collisions can collide with or HIT flow actors. This doesn't show in the flow actors' COL_BY fields though, though HIT_BY is set.
    • Bubbles are spawned where the dying actor was. Blood is spawned where the hitbox was when it disappeared, or with HIT_OBJ collisions and thrown actors, where the HIT_OBJ actor or thrown actor (or fired arrow) is during the collision. The muzzle flash is spawned where the gun's muzzle is. The smoke is spawned wherever the ashtray is. Debris is spawned where the projectiles hit a collider (or reach the maximum distance from origin).
      • If you're defocused and shoot a gun, the flash flow actor is left hanging in the air and you can actually collide with it or even use it for sideways clipping. It disappears if you e.g. save and load or if the focused actor comes sufficiently close to it.
    • Flow actors don't seem to be rendered if they're too far from the camera, similar to when other actors are removed from the active array. The bubbles and smoke will still be rendered if the focused actor moves close enough (i.e. they can be drawn at a delay). Meanwhile the debris and blood are deleted immediately if not in view. LIGHT being 0 or just being outside the camera frustum doesn't affect rendering flow actors.
    • Flow actors disappear when you just save the game, and will be missing after reloading any save file.
  • More details on each type of flow:
0: bubbles (30 particles, 304 bytes)
The cache entry is removed once the size of all bubbles is zero.
Each bubble's size starts randomly between 150-300 and is decreased by 5 units every frame.
Thus the total duration is at most 300/5 = 60 frames = 1 second.

1: blood (30 particles, 304 bytes)
The cache entry is removed when all particles have reached a position on the floor (y >= 0).

2: debris (30 particles, 304 bytes)
The same as blood. The only difference is color.

3: muzzle flash
Only present for one frame after firing a gun (unless the PC is defocused in which case it can stay around for longer than that).
Has no particles and thus no related _MEMORY_ cache entry.

4: cigar smoke (20 particles, 246 bytes)
The cache entry is removed after 11 seconds.
See "CVAR 14" for more.

FLOW GLITCH / FLOW CRASH

  • The Flow Glitch: Sometimes flow actors stop spawning until a new game has been started.
    • It's possible to get a lot of flow actors to be spawned in quick succession, flooding the _MEMORY_ cache. The best way is to increase the PC's hitpoints total, then getting hit by a HIT_OBJ actor continuously. The resulting blood (each blood flow actor is 304B) will fill out the 10KB cache after 32 have been spawned. These actors will, however, generally be deleted in due course and more space be freed.
    • The actual reason why sometimes the game will stop spawning flow actors (or some types of them) at all is an accumulation of entries in _MEMORY_ that cannot be erased. Such actors are left there whenever there isn't a free actor slot available (e.g. if there already were 28 actors when the blood started to be spawned) when the game tries to spawn a flow actor since it still creates a _MEMORY_ entry each time, whenever the game is reloaded or a new game is started, which leaves all _MEMORY_ entries the way they were, or whenever the focused actor moves further from a flow actor (which have LIFEMODE 2) causing it to be deleted. In none of these cases does the game have a mechanism for cleaning up those entries afterwards since usually the related actor does that after some condition has been met. Saving the game correctly deletes both the flow actors and the _MEMORY_ entries.
    • This way the _MEMORY_ cache will eventually run out of space for new flow actors in a permanent way, although whether another one can be spawned in still depends on the size of that particular kind of actor. Specifically smoke actors are smaller than the other kinds and so can still be spawned after blood/debris/vortex actors cannot. Also muzzle flashes don't get a _MEMORY_ entry at all.
    • If the game ever attempts to spawn the smoke in E3R13 without there being room in _MEMORY_ for it, the ashtray flag (CVAR 14) will be left at 1 permanently, which doesn't have any code associated with it in LIFE 427. The game will never spawn any more smoke actors even if room was later cleared for them.
  • The Flow Crash: Related to the flow glitch, there are at least a few ways the game can softlock or crash under circumstances where the _MEMORY_ cache is getting flooded.
    • The first one is the game softlocks if you try to read a readable in the CD-ROM version. The sound file that holds the voice-overs for that readable gets stored in the _MEMORY_ cache but there is no contingency if it doesn't have room for it. Jeremy's notebook in E2R3 for example takes up over half of the cache. (The readables are correctly deleted from the cache after reading.)
      • This is also the reason why starting a new game can cause a softlock since it tries to cache the PC's introduction, sometimes with an analogue TV noise -like effect seen in RAM in a variable location. There's some examples of this softlock here:

File:Flow Glitch New Game Crashing Examples.avi

    • The second one is more complicated. It results from the actors' array filling out and the last actor's data being overwritten combined with sometimes not refreshing the list of actors needing to be redrawn. There's a more detailed explanation later.
      • This second kind of crash quite consistently results in corruption in the VARs, which can even keep getting overwritten with the same exact values every time.
      • One location where this can happen very easily is in E5R6 if you turn your back on the big spider and let it hit you, spawning lots of blood actors and sometimes causing the crash without even reloading the game once. This location is good for this since there's already lots of actors present (all the bridge segments). The spider is also at Y -4000, which is higher than any other HIT_OBJ actor, meaning the blood particles will take a long time to reach the floor, meaning the actors' array will get filled out more easily. Regardless, even the E2R10 vortex can be used (with there being a much lesser number of actors around that room). Also if you fill almost every slot first, this doesn't guarantee the glitch will happen. Instead the game may just stop spawning actors after the actor limit has been hit.
      • If the _MEMORY_ cache was already full, the flow crash cannot occur. On the other hand, it should only require a single flow actor to cause it, the rest are just useful for filling out the actors' array.
      • There is often a Not Enough Memory Error given by DOSBox during the flow crash.
  • Here are examples of the flow crash. The first two files show crashes after no extra actors had been spawned. The resulting corruption looks identical, at least in the VARs themselves. The third file has several crashes with other objects in the vicinity, showing slightly different VAR corruption (e.g. the VARs from 124 to 139. VARs 4, 5, 21 and 24 are expected to revert to normal values after the initial corruption).

Flow Crash Without Extra Actors.png

File:Flow Crash Without Extra Actors.avi

File:Flow Crash Examples.avi

  • There are usually several frames before the game crashes (you can see the PC's health keeps ticking down a few more points), suggesting you could save the game before the crash, which might stabilize it while keeping the memory corruption. In fact, this was done in the video called "Flow Crash Without Extra Actors.avi". This immediately resulted in a crash during the saving process, but the game still produced a save file, which is also available, called "Flow Crash State.ITD". It might obviously not have all of the corruption intact.
  • The corrupt VARs have a very wide range of values that don't at all resemble other crashes that have been studied. A few of them repeat outside of all the -1s and 0s. There are also notably several 1s written into them, though VAR 33 specifically has never been overwritten with that or any other value. It is reasonable to think the data could relate to the actors or something else that's mostly quite stable but not quite.
  • This shows the _MEMORY_ data cache in the MV during a flow crash while the PC is colliding with a HIT_OBJ actor (slowed down):

File:Flow Crash MEMORY Cache.gif

  1. The cache is being filled.
  2. Some entries are being removed and it becomes stable (keeping a 2/3 fill ratio).
  3. The numbers grow again.
  4. It reaches the maximum but does not overflow.
  5. The game crashes. There is no overflow, however, with more free space remaining before the next block.
  • Detailed explanation of the flow crash in this kind of situation: When there is a lot of flow, all 50 actor slots are likely to be in use. To execute a script for what you have INHAND, the game will temporarily use the last slot if no slot is free. Once the script is executed, it will set most fields of that last actor slot to -1 (e.g. ID or ANIM). FLAGS is set to 0. Right after, the game will try to create a new flow actor in that slot but it will fail because the _MEMORY_ cache is full. It will leave that slot with ID -1, ANIM -1 and FLAGS 0x20. That flag will later make the renderer think it's a flow actor.
    • Here is the issue: before rendering, the game computes a list of visible actors sorted by camera distance. Since computing that list is expensive, the game will only rebuild it when needed (e.g. an actor is removed or added). The list will be reused from frame to frame. If the list is not updated between two frames and LIFE 561 (or another INHAND FOUNDLIFE) is executed in-between, the renderer will be asked to render an actor with some fields (like BODY or ANIM) set to -1.
    • If that actor is flow, since its ANIM field (used to identify the kind of flow) has -1, it won't match any valid type so nothing will be drawn. The 2D bounding box will stay as it was initialized with X1 = 319 and X2 = 0 and similar for Y1 and Y2 (this is normally followed by looping through the 2D-projected particle positions and replacing those values with the correct ones, with the process being repeated every frame). That invalid bounding box will be added to the list of boxes to track anyway. Those boxes are tracked to know what parts of the screen need to be erased before actors can be redrawn the next frame. When the 2D bbox is processed, it will copy an invalid portion of BACKGROUND2 to BACKBUFFER. This will corrupt the MCB chain and some other sensitive blocks as well (like the SB driver).
    • Additionally, if there are no more free actor slots before creating a special actor, nothing is done (the creation is aborted). Before the crash, some special actors that were put in the last slot will probably get their fields set to -1. The crash might not occur yet because some other special actors could get removed (the condition of the Y position of all particles > 0 being met) which causes the list of visible actors to be rebuilt and the invalid actor to be flushed from it. The related _MEMORY_ cache entry of those flow actors will stay in the cache forever (their fields have been set to -1 so there is no way the game can update the cache entries, the link between actor and _MEMORY_ entry is broken).
    • The crash only happens when the cache is full probably because at that times a lot of entries may have been permanently locked by the mechanism explained above, which makes it stable (the list of visible actors is not being rebuilt). In the .gif linked above you can see that there is little activity right before the crash occurs. Also this is why the crash only occurs when the flow actors are actually getting rendered and not somewhere too far away.
    • Regular actors that are sent to the renderer but are not drawn (for whatever reason) do not cause glitching because the game will detect such cases (based on the flags) and will set the bounding box to
X1 =  32000
X2 = -32000 
Y1 =  32000
Y2 = -32000

Since it's outside the (0, 0) (319, 199) screen region, the renderer will ignore that bounding box. Other than that, the fact that the problem actor has ID -1 (i.e. the actor has been deleted and the slot is free) doesn't help since the renderer does no checks on actor ID.

    • This is what the renderer does frame-by-frame, with the copy_pixels command ending up copying bad data because of the invalid bbox of the degenerate actor:
renderAll()
{
    foreach (bbox in old_bboxes)
        copy_pixels(bbox.coordinates, BACKGROUND2 => BACKBUFFER);

    foreach (actor in visiblelist)
         render(actor); //this will compute new bboxes

    old_bboxes = new_bboxes;
}

WRONG ACTORS

  • Whenever the game stores values into the four actor collision fields (COL[], COL_BY, HIT, HIT_BY), it stores slot numbers not actor IDs. It seems there are ways to replace the actor that is in each slot mid-frame when actors get deleted by either the focused actor moving between views or rooms, being picked up or through LIFEs, and new ones spawned either by moving between rooms or views, being thrown, dropped or through LIFEs. It would make sense the game only evaluates which actor has collided with which one if required by a COL_BY or HIT_BY check in the LIFEs, which should leave time for a swap to take place in-between the first part of the frame when movement is processed and collisions calculated, and when that particular LIFE is being executed. However, the game actually doesn't replace the actors in the slot array immediately, instead when they're deleted, their room and floor are set to -1. Actors are only actually removed or spawned when the updateAllActorAndObjects() method is called in the main loop. This happens after LIFEs have been processed. That's why either actors collide or hit something and will still be in that slot so the LIFEs see the same actor as the collision code did, or the actor is removed on the frame before it's hit anything.
    • The rest of this section details several experiments that confirm wrong actor effects shouldn't be possible.
  • Using the example of throwing something harmless at an enemy, with the replacing actor being a dagger, we can see that since HITFORCE is set when the connection is first detected, it seems even if the thrown actor was replaced, this wouldn't change the outcome unless there was special code that was executed only if the right object hits particular targets.
    • The only actors that will react to being HIT_BY a specific actor are the knight, the Indian, the pirate, and Pregzt. Of these, the pirate has to be hit by the player and you can't really replace the player with another actor in slot 1.
  • Trying to move from room to room to get another actor loaded into the slot of an actor that's just getting HIT_BY something doesn't seem to work. The new actor doesn't show anything in its HIT_BY.
  • If you warp out of E2R1 right as the portrait has gotten hit by an arrow, there's a frame when the RV is left showing it's been HIT_BY the arrow without clearing this data. No special effects. This is with slots 6 and 12 for the arrow and portrait. With slots 0 and 12, there doesn't seem to be any difference. Same with if the arrow has slot 0 and the portrait slot 2.
    • The next idea was to get the arrow into slot 0 and making it collide with the portrait while the player also moves into the arrow to pick it up right afterwards, all while also getting the vase to hit something and break on the same frame, spawning in an actor (the key) to take slot 0 to see if the portrait behaves differently. This presumably requires the portrait's slot to be higher than the vase's so the vase's LIFE is executed first. Also either the vase needs to hit its target one frame before (it takes a frame for it to go to LIFE 68) or it can first be prepared by throwing it and catching it which makes it be in LIFE 68 already. The following shows what is theorized to happen:
	PREPARATIONS

slot 0 - arrow
slot 1 - PC
slot N - vase
slot >N - portrait
	
	FRAME STARTS: MOVEMENT

1) arrow hits portrait, portrait's HIT_BY gets slot 0 (will show 26 in RV)(the arrow shouldn't be hitting the player here since this causes it to ignore collisions)
2) PC moves in from the side and collects the arrow, vacating slot 0, portrait's HIT_BY shown as -1
3) vase (in LIFE 68) hits wall, lands

	LIFES

4) vase LIFE 68 spawns the key, which goes to slot 0
5) portrait LIFE 124 does its HIT_BY check, which should evaluate to the vase ID 56
    • This experiment was never conducted in practice since even if the RV shows the portrait was HIT_BY 56, this doesn't necessarily mean the game behaves differently for it when there's no special effect of the portrait getting HIT_BY the key as opposed to nothing. If you wish to try it yourself, the save file titled "Wrong Actor Test Indian.ITD" has the right starting conditions.
  • Trying to kill the knight by firing an arrow at it and then throwing or dropping the statuette (ID 83) so the arrow gets deleted first (it has to have slot 0 presumably) doesn't seem to work either. (See save file titled "Wrong Actor Test Knight.ITD").
    • The arrow's LIFE 30 will cause it to be instantly deleted once it's hit something.
    • This test for some reason did NOT cause the knight to think it had been hit by the statuette. This is despite the RV showing for several frames that the knight was being hit by that object, which at least suggests the swap happened before the HIT_BY value had been cleared, which seems to be happening at the same time as the knight's LIFE has been executed (causing the ANIM change). The knight was moved forwards and backwards by one unit to consistently and predictably change the outcome of the event so sometimes the statuette is spawned after the arrow's slot has been freed and other times it takes some other slot.

File:Wrong Actor Test 1.avi

    • In this next video, it looks like HIT_BY goes directly to 83 but none of what RV shows here tells us what exactly happens internally.

File:Wrong Actor Test 2.avi

  • If you try firing an arrow at Pregzt, then throwing (not dropping so it's not doused) the lit lamp, only the RV shows as if he got hit by the lamp, just like with the knight if you try to make the game think he's been hit by the statuette. Pregzt is not set on fire.
  • Presumably the same outcome would be seen if the new actor was the key spawned when the vase breaks since that's also done in the LIFEs like dropping.
  • It's not possible to start throwing an actor and get knocked back into another actor [that's just landed] to free up a slot right as the thrown object is spawned since the throw happens on kf3 of the ANIM but there's another keyframe after and the ANIM is uninterruptable.

ACTOR SERIALIZATION INTO OBJECTS/OBJECT FIELDS/ACTOR FIELDS

ADD LINK TO OBJECTS TABLE

  • The game stores a part of the object data inside a separate array that is small enough to fit onto one memory segment. This means the game is faster at runtime but data does have to be copied between the objects array and the actors array. Whenever an actor is moved from the actors array into the objects array, some fields that were filled at runtime are not stored anywhere. Thus when the actor next returns to memory, such information will have been lost. This is a list of all those fields:
mark
zv
BBox3D1 
Bbox3D2
Bbox3D3
Bbox3D4
worldX
worldY
worldZ
CHRONO
ROOM_CHRONO
nextAnim
nextAnimType
nextAnimInfo
FRAME_COUNT
END_FRAME
END_ANIM
modX
modY
modZ
fall
falling
rotate
rotateTarget
speed
speedChange
COL[3]
COL_BY
HARD_DEC
HARD_COL
HIT
HIT_BY
animActionType
animActionANIM
animActionFRAME
animActionParam
hitForce
animActionHotpoint
hotPoint
hardMat
    • Some of these are always recalculated based on other data or simply copied from fields in the objects' array or elsewhere anyway, and so losing them shouldn't have a big impact. All the fields are discussed below.
  • mark: This is not an object field in AitD 1, though it is in the other games. If it's lost, in theory some logic could fail to execute in AitD 1 though it's difficult to find anything that's actually impacted.
    • Tried to leave E0 just as the bird had went into step 12 of its TRACK. On that frame, the bird's ANIM is changed by LIFE 20 to ANIM 20 based on MARK getting the value 2. While I can enter the menu or save the game at a time when it's in step 12 but the ANIM hasn't changed yet, can't seem to be able to leave E0 at that time. It always changes the bird's ANIM first. Thus the order of execution might prevent the idea of flushing MARK so the ANIM never changes from being possible... at least if you do it like this. If it did work, what should happen is the bird crashes through but its ANIM stays the same through everything.
      • This was also later tested with the bird in slot 0 but this doesn't make a difference.
    • The other way to do this is if the actor has LIFEMODE 2, it can sometimes leave memory when you save and load (and the view changes). However, most actors that are on a TRACK have LIFEMODE 0, and the wasps in E5, which have LIFEMODE 2, don't actually do anything with their MARKs so it wouldn't have any visible effect if they were wrong. There's no other actors that have MARKs in their TRACKs with LIFEMODE 2 so if this works in theory, it's difficult to test in AitD 1.
  • zv: An actor's bbox can change if they leave memory and return. This is evident with e.g. the vortex in E2 and all but one of the E3R12 zombies, which all share the fact their bboxes are initially shrunk down directly after loading them in (this needs a special instruction to be done). After they've been alerted, they go to a different LIFE in which no such instruction exists, and thus the initial BODY-based bbox (or ROT bbox) is used the next time it's recalculated when they return to memory. As the bbox expands when they return to the active array, it can cause the bigger bboxes to clip inside colliders or other actors, but this presumably shouldn't allow them to clip through (the distance they have to move to get through has only increased). If the bbox shrinks, this also shouldn't really have any special impact.
    • Other than this, the bbox is recalculated from the BODY data and thus there's no need to store it in the objects. The ANIM is important. If you serialize the wardrobe in E0 when it's in the middle of opening or closing, when it returns to memory, it has a bbox that seems to correspond with how large it is when the doors are fully open. It's clearly recalculating the bbox based on the ANIM data but why it's not consistent (maximum bbox if serialized when opening and minimum bbox when closing) is not clear. It does go back to its regular size if serialized after it's fully closed.
  • BBox3D1...BBox3D4: 2D-bounding box (shown as 2DBOX in the RV) vertices used to determine which parts of the screen actually need redrawing around animating and overlapping actors. Given in the order Xmin, Xmax; Ymin, Ymax. It's not clear what impact losing this information could have since this is recalculated for animating actors every frame anyway. In theory, it could cause graphical glitching or even memory corruption, but nothing of this sort has ever been observed.
  • worldX, Y, Z: Whenever there's a desync between the ROOM coordinates and the other two, this is corrected if the actor leaves memory and returns. Only the ROOM coordinates (+MODs) get stored and the other two are calculated on their (and the BODY's) basis. Thus this can be used to undo any amount of desyncing. See "CAMERA DEFOCUS TRICK" for ways to actually achieve this for the PC.
    • Actors that already were desynced to begin with won't be affected. This might have something to do with the zvType field that governs how the actor's bbox is calculated.
    • Undesyncing may cause the whole actor to move in some direction by an amount greater than the desyncing was. Only observed when the desyncing was done using cheating.
  • CHRONO/ROOM_CHRONO: These are reset to zero but usually there is a bug that causes them to underflow, making whatever they were being used for to happen instantly. More under "timers".
  • nextAnim[Type/Info/Param]: These are set whenever any ANIM command is issued, and also commands like HIT or THROW in LIFEs and other places, and copied into an actor's ANIM, animType and animInfo. The copying only happens on the frame after though (see main loop: nextXXX variables are set in processlife() but only copied into the animXXX fields in processAnim()). For this reason, if the actor is serialized before that has happened, and if the same command isn't called again the next frame (generally whenever the actor moves to a different LIFE on the same frame with the ANIM switch), the new ANIM information will be lost. ANIM_ONCE -1 -1 is the only exception because it happens instantly and cannot be prevented this way. If the actor is in slot 0, the ANIM flush can be achieved by moving to any kind of trigger that causes the serialization. If the actor is in a slot higher than the PC, this cannot be done with floor triggers in specific since the PC warping away is tied to the DO_MOVE command in the PC's LIFE. The floor trigger processing happens in the PC's LIFE 550 and actors are serialized during the transition before a non-slot-0 actor has had time to have its LIFE executed. If you try, the ANIM is just played again when you return due to resetting the END_ANIM flag. Generally the effects of this trick are not very drastic.
    • This trick doesn't work for ANIMs that were already queued since the next ANIM is stored inside animInfo.
    • Warping the PC to the trigger and lagging to the end of manipulated actor's ANIM is the easiest way to cause these effects in case there was an ANIM_END condition to changing the ANIM.
    • Any time you try to do this trick while an actor is in an ANIM SWITCH (generally when in their "roaming" LIFEs), mostly the only effect is to delay entering the appropriate next ANIM by a keyframe or ANIM cycle depending, since they almost never move away from those LIFEs. The only exceptions are (1) if the actor was about to hit the PC, in which case the PC could have moved out of range before the actor returns (and the hit ANIM is never played), or (2) if the actor is hit, in which case either if they don't die to it, the knockback ANIM is never played, or if they do die, they will never enter the dying ANIM and thus don't go to the case where they are deleted. Instead, they will continue "roaming" except with TRACKMODE 0 so they continue to move in a straight line unable to follow the PC until they're hit again. All of this works the same with more or less all enemies and so these ideas are not listed separately below for any of them.
    • To do anything with the PC's ANIMs, you'd have to also defocus the PC and remove them from the floor as well (or alternatively get the focused actor to be on another floor). This either cannot be done without cheating or it gets the PC stuck.
    • It shouldn't be possible to do anything with this trick relating to doors: they don't animate, and they only set the PC's ANIMs.
    • All of the other theoretical and practical uses are listed below. Whenever a floor transition has to be used (either you're in E0 or the actor has LIFEMODE 0), assume that most of the time the RV has to be used to force slot 0 for that actor.
  • Uses of ANIM flushing:
    • E0:
      • Chest: If you warp the PC in the middle of opening it, the chest can be gotten to give you the rifle but with the opening ANIM resetting when you come back to E0. If you had already picked the rifle up, the "nothing important" message doesn't seem to have time to display. The wardrobe works the same.
      • Floor hatch: If you force the floor hatch into slot 0 and leave just as it's finished opening, when you come back, it doesn't animate when closing. It no longer has the 1 flag, having the 8 flag instead. That always happens with actors that have BODYs, it's just that LIFE 6 doesn't have a command to set flags 0001 like LIFE 5 (which it has just left) does.
      • Zombie: Forcing the zombie to slot 0 and leaving just as its CHRONO reaches 20 causes it to not have an ANIM. It gets to POS 6 in TRACK 0 where it's supposed to be warped but the warp command doesn't ever execute and so the zombie never finishes the TRACK. This warp is the only one that has five parameters, the last one expressing the length of time the warp should take. Clearly something goes wrong with this warp command.
        • If you only leave when this POS 6 warp has finished (POS is 12, MARK should be 1) the ANIM change in the LIFE is lost and the zombie is left standing there since the goto command uses whatever ANIM the actor happens to have.
      • Bird: The analogous trick works with the bird for getting it stuck at the start of the TRACK. If you wait until it's in POS 10 (this should set MARK to 1), when you come back, its LIFE does successfully change the ANIM to 20. This happens with any combination of POS 10 and POS 12 and slot 0 or a slot higher than the PC's. That suggests there's an order of execution thing that prevents the trick from working with MARKs. Maybe MARK will still be 1 or will be set back to 1 when you return, causing the MARK clause in LIFE 20 to be executed again.
    • E1:
      • R2: The chest still gives you the saber if you try to warp away (you have to warp to R0 or R4) right as it's opening. This is simply because there's a frame during which the PC is placed in R1 and is not yet far enough. If you try to warp away at any point during the chest's opening ANIM's last keyframe, when you come back, as per usual, the keyframe is advanced by one and the saber is given instantly.
        • You can also get the saber to be given on a frame when you're already in R0 or R4 but the game hasn't removed the extra actors yet.
        • The trick doesn't seem to work with trying to get the chest to stay in its closing ANIM for longer, with BODY 35 (chest closed) instead of BODY 36 (chest open).
      • R4: The bird in doesn't seem to be possible to get to stay in ANIM 50 past the part in LIFE 73 with MARK == 1.
      • R7: The nightgaunts can be made to repeat any of their attacks this way. If you do this with the 3rd attack, it also stops them from following the PC during it.
        • You can also throw something at one of them and warp away when they're getting hit to cause them to forget about their first attack completely. This only seems to be possible to do if you didn't pause the game. The same can presumably be done to make them forget one of the other attacks as well.
    • E2:
      • R0: You can get the vagabond stuck without an ANIM if you leave the room immediately after entering. In practice this seems to require either warping, or getting a focused arrow or axe to briefly pass through the trigger hitting you immediately after so focus comes back outside the room.
      • R2: The knight looks like you should be able to get it caught in TRACKMODE 1 but with no ANIM (supposed to be set by the HIT command) if you warp to E3 right after colliding with it, based on LIFE 131, but this doesn't seem to be possible regardless of slot order. You CAN throw something at it and warp away to make the sound be played (it's immediately interrupted) but the ANIM not to change.
        • It might be possible to get it stuck in ANIM 76 (roaming) if it's possible to skip both the ANIM change that happens when MARK is 1 and the one that happens when TRACK is -1. At least the MARK skip is possible if the knight was in slot 0. At this point you can go back and forth between E2 and E3 to make its rotation never finish, and it will keep wandering forwards. It doesn't seem you can prevent the ANIM change when TRACK is -1.
      • R3: The bird can be gotten stuck without an ANIM if you warp yourself out of the room while the notebook pick-up prompt is up, but only if it's in a slot lower than actor 85 (the one that gives you the notebook).
      • R5: The jellyfish can't really be gotten into any anomalous states like this because of the way it's scripted.
      • R10: The ghost has LIFEMODE 0. If it's in slot 0 (which doesn't seem to be possible in any simple way), the END_ANIM clause in LIFE 202 is executed except the ANIM starts again. This leads to the vortex getting completely stuck since the "E2R10_ghost_aggroed" VAR is now 1.
    • E3:
      • R1: The jelly starts in ANIM 95 (idling) and LIFE 220 based on OBJETS.ITD. Also it has LIFEMODE 2 so you can't even warp far enough away to serialize it on the first frame after colliding with the door to spawn it. You can probably stop the HIT command setting the ANIM to 96 but the resulting state is the same as before and so this presumably just causes another cycle of idling.
      • R2: The pirate probably can't be gotten to have no ANIM when entering LIFE 268 since there's no axes or arrows to use for entering the room for just one frame. Warping away as fast as possible after entering doesn't seem to work regardless of the pirate's slot. Meanwhile, you can wait until the pirate is just returning to LIFE 266 and serialize it to make it stay in its roaming ANIM regardless of slot order. The resulting state is exactly the same as with the indecision bug except it's in a different LIFE.
      • R3: The dancers could in theory be gotten stuck without an ANIM if you could leave the floor on the second frame after touching one of them while it's in slot 0. Similarly getting them to LIFE 288, where they normally start chasing you, without an ANIM is theoretically possible. Getting them to dance without an ANIM is likely not to be possible since this involves coming back from the inventory after which there always seems to be lag.
      • R4: The script that spawns the spiders is in ROOM mode whereas the spiders themselves are in CAMERA LIFEMODE so they certainly can't be made not to have ANIMs initially.
      • R12: The zombies have a lot going on:
        • Zombie 1 (the one that's standing) can be in either LIFEMODE 0 or 1 when it starts homing towards the chair. This depends on if it first entered its roaming LIFE (it's in LIFEMODE 0) or if you placed the pot before the doors finished closing. If it's in LIFEMODE 0, it can be gotten stuck in its idle ANIM instead of sitting simply by waiting outside the room for it to reach the chair and to finish the rotation before sitting. This causes it to go to LIFEMODE 1 and so it will effectively flush itself before the ANIM has changed. If it's in LIFEMODE 1 already before reaching the chair (it has that LIFEMODE based on OBJETS.ITD so that's the LIFEMODE it gets when it's given a LIFE), this is still possible but requires good timing.
          • If it has LIFEMODE 1 and you time it well, you can also first get it stuck in the roaming ANIM after colliding with the chair (LIE 418), and then to skip the other ANIM switch in LIFE 419 as well so it never stops roaming. If it could be gotten to slot 0, this should be possible even if it's in LIFEMODE 0 but requires moving the chair sufficiently close to a floor trigger so the zombie can be vanished while colliding with the chair.
        • Zombie 1 can't be gotten to get stuck without an ANIM when placing the pot (before the doors have closed) since you're coming back from the inventory. The ANIM is set by the pot LIFE 352. Also the zombie gets the same ANIM again in its own LIFE 353 on the next frame. If you first wait for the doors to close, its LIFEMODE goes to 0 (in LIFE 421) at the same time as the ANIM changes. Thus you can't serialized it then either. If you wait for the zombie to be aggroed, then place the pot, the result is essentially the same as placing it before the doors close. It doesn't even matter if the zombie has less than 1 hp at this point (would be doable by using an ANIM flush on its dying ANIM except LIFEMODE is 0 and its ID is too high to get to slot 0) seeing as that only stops the pot LIFE from changing its ANIM and LIFE to the same ones it already had.
          • If you solve the room by aggroing/killing the other zombies instead, zombie 1 is still already in the same ANIM 125.
        • When any of the other zombies stand up, they go from ROOM to FLOOR LIFEMODE and as such can't be serialized at that time.
          • Any of the sitting zombies in E3R12 can be caused to never change ANIMs if you shoot them while immediately leaving the room. The END_ANIM clause in LIFE 399 (and analogous LIFEs) is immediately met when you return though and they're deleted instantly. This is probably just because the sitting ANIM (162) has a length of 0 and it looks like the END_FRAME and END_ANIM flags are always set.
            • If you instead attack them with a melee weapon (HITFORCE less than 6), LIFEMODE is set to 0 so nothing special will happen. Zombie 1 is the exception: if it's HIT_BY anything while sitting, it always dies.
          • The ANIM change that happens in LIFE 396 (etc.) after the zombie has been pacified is from one ANIM to the same ANIM (135) so trying to stop it doesn't change anything.
          • The ANIM change to 139 in LIFE 397 (etc.) could in theory be stopped with a floor transition if it's in slot 0. If the zombie in question could be gotten to slot 0 (probably impossible), reaching a floor trigger would be possible, since the previous ANIM 136 has long enough keyframes that you can keep moving yourself while resetting its keyframe.
    • E4:
      • The barrels can be gotten to repeat their ANIM as usual so long as they're not in slot 0. If they are, the ANIM_ONCE -1 -1 command will play and wipe their ANIM fields instantly.
        • Collecting the block causes the barrel roll ANIMs to start but since there's a prompt, the lag afterwards should prevent even warping the PC to the transition.
      • The rats can't be done anything special with.
    • E5:
      • R2: The Cthonian starts with ANIM 193 from OBJETS.ITD. If you approach it from R0, it's given the same ANIM. If from E4, it gets a different ANIM. In either case it happens in the player's LIFE 550 during the transition to E5 without the player being able to leave soon enough for the ANIM to be flushed.
        • If the Cthonian is moving north, the ANIM switch at the end of the TRACK never happens since the TRACK never finishes.
        • If the Cthonian is facing south, since it has LIFEMODE 2, the ANIM switch in LIFE 460 can't be stopped.
        • In LIFE 461, the idling ANIM will be set after either its TRACK has ended, or it has collided with actor 219 (the wall by R7). Looks like most of the time the TRACK will finish just before the collision. You can flush this ANIM to make it keep moving, which happens anyway if you aggro it after it's stopped but before it has turned the corner.
        • The ANIM change in LIFE 465 doesn't seem to be possible to stop since you have to be in the trigger in R4 but the Cthonian has LIFEMODE 2.
        • In LIFE 462, the game repeats the same ANIM command again even if it's flushed the first time and so no wrong ANIMs can be left.
      • R4: The bird can't be done anything special with.
      • R6: The spider is alerted when the view changes to C27. This also sets the spider's LIFEMODE to 2, so you definitely can't get away fast enough to flush the ANIM set in LIFE 469. You also can't get away fast enough to flush the one in 470, and even if you could, LIFE 469 queues the same ANIM after the first one anyway.
      • R7/R8: The E5 Deep One (the same actor is used for both rooms) have LIFEMODE 0. There are no floor triggers near the triggers that spawn the Deep One and so the first ANIM can't be flushed. Flushing any of the other ones doesn't do anything very special.
      • R8: The wasp can be gotten stuck if you warp away as it's getting initiated. This probably isn't possible without warping. It can also be gotten to fall down below the water level without entering the dying ANIM after taking mortal damage, at least if it's in slot 0. If this is done, it continues to patrol underneath all the colliders, though it may be possible it doesn't always fall that far.
      • R9: The hook door (and analogous door in E6) can be gotten to start opening with the hook but never actually open if it's in slot 0 and you warp into the floor trigger.
      • R9: The beetle starts in ANIM 216 (idling) and LIFEMODE 1. This changes to ANIM 217 and LIFEMODE 0 when the PC enters the room. These are supposed to change back to 216 and 1 afterwards but they never do because it can't actually complete its TRACK. Thus if it can't be gotten to slot 0, it should not be possible to cause that or any other ANIM change to be flushed even with warping. The room actor has LIFEMODE 1 and a lower ID so getting the beetle to slot 0 when first entering R9 should be impossible. If you've already been to R9 and go to E6 and come back, another actor with a lower ID also has LIFEMODE 0 and thus gets a lower slot.
      • R10: This wasp behaves similarly to the R8 one.
    • E6:
      • R0: The hook door (and analogous door in E5) can be gotten to start opening with the hook but never actually open if it's in slot 0 and you warp into the floor trigger.
      • R5: The gem door shouldn't be possible to do anything with since it's got LIFEMODE 2 and it's nowhere near a floor trigger. It's set to open in LIFE 505.
      • R6: The hook door could be gotten to start opening without actually opening if it could be gotten into slot 0 and you warp into the floor trigger. However, the condition of colliding with the door is not met after the warp meaning this doesn't work.
      • R6: The E6 Deep One has LIFEMODE 1. If you first get it to emerge once, then try to flush it the second time making use of the CHRONO delay before it reappears, it will have the submerging ANIM from before when it's sent to LIFE 515 then 517, and this doesn't cause any unusual behaviour other than disappearing sooner. If you try to leave the room right as the Deep One spawns for the first time, regardless of if you run out to the R5 trigger from the W wall (you have to be inside one of the triggers for it to spawn) or if you warp to R5, this doesn't seem to ever flush the first ANIM. This might have something to do with lag as it's spawning as it always seems to have the ANIM already set when you move to the other room. The two Deep Ones are the only actors that are spawned in and given an ANIM at the same time. If the first ANIM could be flushed, it should cause the Deep One to be stuck in LIFE 517 in a state where it can only be hit by things or despawn if you are both in a different trigger than it and 4500 or more units away. Flushing one of the other ANIMs shouldn't do anything special.
        • In LIFE 522, after Pregzt has been killed, it sets the Deep One's ANIM to "dying". If this is flushed, the Deep One is left alive. Doing this in practice is possible if you first place the talisman, then go OOB on the SW side of R6, hit the trigger to spawn the Deep One, lure it towards the SW corner making sure it's never too far to be despawned, throw the lit lamp through the wall S of the R5 trigger so it avoids all the colliders and hits Pregzt, with you going to R5 at the same time.
      • The R6 fireballs also have LIFEMODE 1. They are spawned with the same conditions as the Deep One except they don't care which exact trigger the PC is in, and also the ANIM is only set a frame later in LIFE 519/520. Despite that, it seems similarly impossible to flush the fireballs' first ANIM. However, the second ANIM (exploding) can be flushed like normal, and this causes the fireball to be stuck in LIFE 524/526 with the wrong BODY and without collisions so it just flies around indefinitely. Once a fireball is in this state, it can't ever be fired again because that requires its ROOM to be other than 6 but fireballs can't move from room to room and it can't ever get deleted since that requires the ANIM that was flushed to complete.
        • The "mystery fireballs" don't get their ANIM flushed as the player leaves E6 because it only gets set in the next LIFE instead of the one that spawns them.


  • FRAME_COUNT: The number of keyframes in the current ANIM. Will be reobtained from the ANIM itself.
  • END_FRAME: END_FRAME is set to 1 when the actor is restored, but if the actor has an ANIM, it is set to 0 again before the next frame. However, it looks like for some reason, if the actor despawned was not on a different floor, its keyframes are actually advanced by one every time. Thus you can theoretically speed up an ANIM finishing if it has keyframes that last longer than the loading times and movement between rooms or views that cause it to disappear and reappear. This isn't really possible to utilize with anything you'd have to wait a long time for in AitD.
  • END_ANIM: This is also set to 1 when the actor is restored and similarly set to 0 again. Thus any related logic does not activate instantly without the ANIM actually finishing, regardless of whether the actor was on the same floor or different.
  • modX/Y/Z: These are added to roomX/Y/Z. The rest of the keyframe's movement should be lost and so the ANIM will finish earlier (spatially) than it would have without the serialization.
    • The loading times when moving back and forth between rooms or views are generally too long to make an actor's ANIM finish a lot faster this way.
  • fall/falling: An actor that was falling may get severely desynced if removed and returned to memory, possibly due to the flushing of the "fall" field ("falling" is only used so the LIFEs can check if the actor is falling, and only done with the E5R11 chest and the PC). This is very difficult to do anything with since the only falling actors start falling when the player is close to them, either having been pushed off a ledge, bridge segment gotten stepped on or the arrows being picked up in E3R4 (you have to go two rooms away to get the spiders to despawn). However there are some ideas.
    • You could probably lure something close to an edge and leave the area so it falls off when you're far enough. Should work with something like the spider in E5R6 (though it's deleted if it falls below -2000).
    • You can probably shoot the E5R8 wasp and quickly move to R5 then R2. For some reason, the wasp doesn't get desynced though, but because the ANIM progresses fast, it gets deleted higher up.
    • This could in theory combine with getting the player defocused, if you got gravity first, then entered the E1R7 stairs backwards to fall off as a nightgaunt is about to die. However, the nightgaunts are deleted when you hit the stairs so you can't first go to E5 for the gravity flag.
    • If the "bridge segment stepped on" flags (e.g. VAR 160) are set to something other than 0, the bridge segment will fall immediately after the two-second delay, making it possible to glitch it into weird positions. If the bridge segment in question is in E5R7 or R10, the splash sound is played on repeat.
    • The "falling" field getting flushed could also have some effects separate from the "fall" field, but this is difficult to ascertain. It's only used with the PC and the E5R11 chest that behaves oddly even when it's staying in memory during the fall.
    • If you push the E5R11 chest so it's just hanging off the edge, it won't fall until the next frame it's pushed (probably because its animated flag is off). If you go to E6 (or otherwise flush it) first, it clearly causes the falling not to happen until another frame of pushing it onto thin air. This could, in theory, be used to push some actor an indefinite distance through the air (using lagging to get further each time) at least if it's scripted the same way as the chest.
      • If you first get the chest to start falling normally, then flush it, it gets stuck wherever it had gotten to until given another push.
  • rotate/rotateTarget: There might be some theoretical small effect of this info getting temporarily lost, but the game seems to be recalculating it every frame anyway, and nothing really weird has ever seemed to happen relating to rotations (other than the Indecision Bug, which is unrelated).
  • speed: SPEED is only relevant for the PC, who has been observed to get confused during TRACKs as a result, as SPEED is set to 0 when it's supposed to be something else. Not really beneficial.
    • It IS also used to make thrown objects spin. This is probably purely cosmetic though.
  • speedChange: this seems to be used to move an actor without a set ANIM in a smooth way. For actors without an ANIM, SPEED is used to directly specify forward movement for the actor (e.g. 15). If the speed of such an actor is set to 15 and it was 0 before, it will smoothly interpolate from 0 to 15 in 1 second.
    • The only usage in AitD seems to be setting the speed of a thrown object to 3000 at the start of its flight. However, it looks like the thrown object accelerates to this value almost instantly, so it could be speedChange is actually used to make sure its speed remains constant regardless of any lag frames.
    • An actor leaving memory while in a thrown state makes it freeze in the air. Not clear if losing speedChange has anything to do with it since it also loses the DURING_THROW ACTIONTYPE.
  • COL[3]/COL_BY/HARD_DEC/HARD_COL: Losing this information seems to abort any and all effects the collisions might have had, but these are checked for every frame so at most it seems to delay them by another frame. Could possibly be used to nudge an actor further and further until the collision (e.g. trigger one) is no longer happening, and thus effectively pass through the trigger completely. Difficult to check since the player is the only actor whose trigger collisions are checked for in the scripts. Generally nothing of interest happens with other actors and the COL/COL_BY checks done on them.
  • HIT/HIT_BY: Flushing this information could possibly allow an actor to sponge hits from another actor without actually getting hurt by them. Difficult to test since the hitbox seems to disappear, presumably because the HOTPOINT field was flushed.
  • animActionType: The states here are PRE_HIT, HIT, PRE_FIRE, FIRE, PRE_THROW, THROW, DURING_THROW or HIT_OBJ. It's generally quite difficult to know if these are the only effects of this information getting flushed (would require a careful look at the code probably).
    • PRE_HIT: The hitbox never appears (the attack is effectively aborted except it's already set the HITFORCE).
    • HIT: (Entered on the keyframe indicated in the second parameter of the HIT call.) The hitbox vanishes, which is probably caused by the ACTIONTYPE changing as opposed to something else.
    • PRE_FIRE: This is difficult to test since the PC is the only character that fires anything in AitD.
    • FIRE: This is similarly difficult to test.
    • PRE_THROW: This is difficult to test since the PC is the only character that throws anything in AitD.
    • THROW: (Entered on the keyframe indicated in the second parameter of the THROW call.) This is similarly difficult to test.
    • DURING_THROW: A thrown actor is left hanging, with flag 1 being dropped and flag 8 set.
    • HIT_OBJ: The actor can no longer hit anything just by colliding with it. Seen with the E2 axe and arrow.
  • animActionAnim/animActionFrame/animActionParam: This is the ANIM after the current HIT/THROW/FIRE action, the keyframe the action starts on, and an extra parameter that can be present. Since the whole ACTIONTYPE is getting flushed, this information being lost probably doesn't cause further anomalies.
  • animActionHotpoint/hotpoint: The first one is the index given in a HIT/THROW/FIRE instruction for which vertex in a BODY should act as the hotpoint for those actions. The latter is a shortcut that stores the coordinates for that vertex and is updated by the renderer each frame, since it has to access the BODY data anyway to render it. If the hotpoint field didn't exist, the game would have to access the BODY data (which can be slow) a second time to find this information. Again, losing this data is probably moot since ACTIONTYPE is also lost.
  • hitForce: HITFORCE being set to 0 could save the actor from taking damage from a hit of some kind, though possibly only the PC since the other actors take the damage on the same frame. Since the HITFORCE data doesn't seem to be reset when a new actor is loaded into some memory slot, it won't actually be 0 every time.
  • hardMat: This is only used in AitD2 (and 3?) so it should be checked later.
  • An actor's current keyframe moves forwards by one every time it's been removed and returned, though only if you came from another room on the same floor, not another floor. This is very likely based on the same CHRONO underflow as the instant E1R4 bird trick seeing as that trick has the same limitations, and also the game does store the keyframe during actor serialization. Using this trick, you could skip long keyframes of an ANIM by going back and forth between rooms or views.
    • In AitD 1, the only ANIMs you have to wait to finish are either the player's ANIMs, or the thing you're waiting for is right in front of you (the hook doors in E5/E6) and thus difficult to get to leave memory, or it has LIFEMODE 0 and thus won't leave memory at all. Thus it seems impossible to make any use of this.
    • If the Cthonian could be manipulated, this could be moderately useful.
    • Also in an in-bounds run, you could manipulate the nightgaunts to die faster this way except it requires first going back to E0 during the N nightgaunt's death ANIM.

BODYS

  • BODYs are the 3D models used to represent different tangible actors. They're contained in LISTBODY and LISTBOD2, which are 100% identical, expect in the first the player character models are for Edward, in the other for Emily.
  • BODYs contain:
    • a bounding box (shown in RV)
      • The default bboxes are not used a lot and often make little sense. These are shown in the model viewer.
    • vertices (3D points)
    • bones (used to animate the model). (demonstration)
      • Bones are linked to vertices. When moving a bone, all associated vertices are moved.
      • Bones are translated, scaled and rotated in real time (using an ANIM); no other actions are available.
      • Bones are hierarchical: each bone (except for the root) has a parent. Moving a bone will also move all related child bones (e.g. moving the torso will also move the arms).
    • 3D primitives built from vertices.
      • lines (2 vertices)
      • convex polygons (3-N vertices)
      • spheres (1 vertex + radius)
      • 1x1 pixel (1 vertex)
      • square (1 vertex)
  • 3D primitives can be filled using one of these shadings:
    • single color
    • transparent color
    • gradient (vertical/horizontal)
    • noise
    • a glitchly cel-shading style effect exists but isn't used
  • If an actor changes BODYs, the bbox is not updated without a separate instruction to do so or unless they leave memory and return in which case the new BODYs default bbox is used.
  • There are some spare bytes in BODYs that are used for storing ANIM keyframe start times at runtime. This is why, if the BODY changes, even if the ANIM doesn't change, the keyframe is reset. This can be used to, e.g. delay dying indefinitely. This could also be part of why every actor with ANIMs has to have its own BODY in the file instead of reusing the same one. Doors and bridge segments etc. do share the same BODYs with seemingly no anomalous behaviour even if you try e.g. to open several doors at once.

ANIMS/ANIMATIONS (GENERAL)

  • The animation instructions are called ANIMs. They are stored in LISTANIM and LISTANI2 for Edward and Emily respectively. Both packs contain all the game's ANIMs, with only the player-specific ones being different.
  • An ANIM contains N keyframes. Each keyframe has the following info:
    • Time: keyframe duration in frames
    • 3D offset: an offset that will be applied to the whole body during that keyframe (more under "MOVEMENT").
    • An offset, rotation or scaling to be applied to each specific bone during the current keyframe, animating the actor. The bones in the BODY and those mentioned in the ANIM are matched using indices.
    • The transformations are interpolated over the keyframe (using Timer 2) to make them smooth.
  • ANIMs are split into ones that can be interrupted by other ANIM commands and those that will play through to the end. This isn't a property of the ANIMs themselves but rather the command used to call for them (it gets stored in an actor field called animType), although in practice the same command will generally always be used to call the same ANIM. For the player, those that can't be interrupted (called with ANIM_ALL_ONCE):
    • jumping
    • knockback from any source: getting hit, firing rifle or placing talisman
    • climbing (two ANIMs)
    • landing from any height
    • any deaths: normal, falling, De Vermis, jelly
    • coughing
      • Uninterruptable ANIMs can be very useful if there is another ANIM you'd rather avoid. Other ANIM calls are simply ignored until the uninterruptable one has finished. This is part of getting into various glitch states.
  • For other actors, these can't be interrupted:
    • knockback
    • dying
    • Deep One submerging
  • OBJETS.ITD contains a field called animType that can take the values ANIM_ONCE, ANIM_REPEAT or ANIM_UNINTERRUPTABLE that's used when initiating actors, and some of them also get an ANIM that's given in the file. These values are only really used with some actors though, with most of them getting the right ANIM and ANIMTYPE through scripts.
  • Once ANIM_ALL_ONCE has been called, the first ANIM of the two has finish before any other ANIM calls will do anything. Such ANIMs are still possible to interrupt by:
    • calling "ANIM_ONCE -1 -1"
    • resetting the 0x0001 flag so the actor is no longer animating
    • if an actor being thrown has just hit something
    • the actor is serialized (but this data is stored so the ANIM will resume)
    • the actor is getting pushed (e.g. push a chest as it's closing)
    • the actor's BODY changes (?)
    • After the interruption, any ANIM calls will obviously set a new ANIM.
  • ANIM ONCE -1 -1 is only ever used after END_ANIM checks. It's also used in these LIFEs:
    • 51: Pointless.
    • 190: stops the talisman's repeating ANIM.
    • 310 etc.: stops the spiders' repeating ANIMs.
    • 538: stops the player's ANIM during intro, but probably unnecessary since it also changes the player's BODY. Maybe it stops some glitch.
    • 545/547: stops the car's repeating ANIM.
    • 562: stops the player character's repeating ANIM.
    • There don't seem to be any cases where it is used in the scripts to stop anything other than ANIMs that are already over or repeating ones.
  • If there is a second ANIM parameter, it gets stored inside the actor's data (field called animInfo) and represents the ANIM played after the first one has finished. If the parameter is -1, this means the game ignores it and no second ANIM is set, not that ANIM -1 is set.
    • The game checks for END_ANIM being 1 for all actors on each frame. If END_ANIM is 1, ANIMTYPE is not REPEAT and nextAnim is not equal to -1:
anim = nextAnim
animInfo = nextAnimInfo
nextAnim = -1 
nextAnimInfo = -1
  • If an actors starts an animation but is serialized, the ANIM starts again after it's returned. This happens even for ANIMs that have completed once already, even non-repeating ones, though usually such ANIMs get replaced by something else when they finish. This explains why the rocking horse plays its ANIM every time the player re-enters E0: it's never replaced with something else.
    • Related to this, the ANIM flag 0001 replaces flag 0008 when the ANIM starts over.
  • If there's a clause waiting for un uninterruptable ANIM to finish, it doesn't seem you can squeeze in something else, like an inventory visit, in-between it having finished and going to that clause.
  • Just one actor, the talisman (ID 110), has its ANIM and ANIMTYPE initialized from OBJETS.ITD (the file used for initializing all actors). This makes it possible to start its "bubbling" without relying on scripts.
  • ANIMs use Timer 2 to interpolate from the position of the vertices at the start of a keyframe to the position at the end of it. This is not the same as how fast a keyframe changes to the next one (which uses Timer 1). This is why if you save/load, the ANIM may look like it's already at the end of the current keyframe even though the timer has started over from the beginning of it.
    • You can even avoid some attack like this, or at least delay it, since the hitbox position is tied to a particular vertex.
    • More under "timers".
  • The player's BODYs and their ANIMs are closely tied in the game's scripts through the ANIM_MOVE command present in LIFE 549 and 550 to make different key presses change the ANIM to the appropriate one.
  • Whenever an actor leaves memory, it's reset to the beginning of the keyframe when it next appears, similar to s/l-ing, however the CHRONO underflow (at 11.000 cycles) will invariably cause the keyframe to end instantaneously.
    • It could be this is actually because END_FRAME is set to 1 after retrieving it. It happens even if the "dynamic" core and "max" cycles settings are used.
  • The game never starts the same ANIM over even if another instruction to go to it was issued.
  • ANIMs can be used for making actors such as the vortices and dancers look like they're rotating. However, this is only cosmetic and doesn't affect the actor's angle value or movement.


OTHER ANIM-RELATED NOTES

  • As you're entering a staircase, if you hold SPACE through the whole transition, you'll walk forwards with the wrong ANIM 254 (BODY 12 walk forwards) instead of 288 until reaching the end of that ANIM, at which point it goes into idling ANIM 4. This happens regardless of whether you walked into the stairs forwards or backwards or ran. Doesn't do anything when going (E3-E4, E2-E3). Sometimes it's ANIM 254 (E2-E3, E4-E3) or the walking downstairs ANIM 259 (E3-E5). Walking upstairs ANIM 260 (E5-E3). Doesn't nearly always work between E3 and E5 even when doing it the exact same way. Doesn't seem to matter if you're holding just SPACE or also an arrow key. E5-E6-E5 doesn't do anything special. Nor does E4-E5-E4, E0-E1-E0 nor E1-E2-E1, except you're left with idle ANIM 4 instead of 287.
    • Can't think of any way to abuse this. There's no special code for being in any of those ANIMs.
    • You can save/load and keep the wrong ANIM by just continuing to hold SPACE.
    • Explained by the fact there's simply no code getting executed in LIFE 549 while SPACE is held.
  • Even if you hold SPACE down, ANIM 2 (search/open) and ANIM 28 (close) stop for one frame after completing before looping. Might apply to many other ANIMs as well. This is probably since there was a second ANIM (idling) lined up.
  • If you use the jug with water in it, it plays ANIM 28 (close), but if without, ANIM 2 (open/search). Because of LIFE 549, it's exactly the same as performing those actions, except maybe you wouldn't need VAR 0 to be 1.
    • Thus you can actually search or close things this way.
    • It also sets VAR 10 as 1. This is the throwing objects VAR. It's never reset, at least not in LIFE 162 (the jug itself), but may be reset in LIFE 11 (dropping) or 549.
    • You can get VAR 10 to stay 1 if you use the jug before hitting a transition, at least coming from E5 to E4. This doesn't look to have any special outcomes.
  • The reason dropping sometimes gets ignored (in the middle of a jump for example) seems to be the dropping LIFE (11) has a case for if the ANIM is idling, and because the idle ANIM is what you return to after most such actions. The dropping LIFE itself does not set the player's ANIM at all unless the drop has completed.
  • If you get hit it sends you to LIFE 553 for one frame. It's not clear what happens if you get hit on that frame. When you return to LIFE 549, if you get hit again before the ANIM has finished, it just sends you back to 553 but doesn't reset the ANIM.
  • If you change BODYs through the inventory, it doesn't interrupt the current ANIM though resetting it to the start of the keyframe. It might be it'd interrupt the movement ANIMs because of ANIM_MOVE but that changes anyway because you can't continue running or walking, whichever you were doing, presumably based on the required UP input being interrupted.
  • Some of the interruptions will be explained by whether or not the player stayed in LIFE 549 or went to LIFE 39 (death manager) instead. This is probably why the De Vermis ANIM doesn't get interrupted by something hitting the player, but the Necronomicon effect can be. The difference can be seen when either reading the books right before Pregzt gets killed or when about to get hit by the jelly.
  • Some ANIMs can be canceled by taking damage, for example opening a door in single-segment runs is slightly faster this way (knockback ANIM lasts 55 frames while a door takes around 120 to open or close).


ACTOR FLAGS

    • 0001: animated – tells the renderer the area around the actor (in its 2D bbox) has to be redrawn every frame
    • 0002: (unused)
    • 0004: redraw – redraw once, then set this flag off (not shown in the RV since it often flickers constantly)
    • 0008: not currently animating (replaces flag 0001 if the actor has completed its current ANIM without entering a different ANIM)
    • 0010: pushable
    • 0020: collisions – game will check for collisions between this actor and other actors and colliders
    • 0040: triggers – game will check for collisions between this actor and any triggers
    • 0080: collectible – PC colliding with this actor issues a FOUND call
    • 0100: gravity – one of the requirements for falling
  • THROW in the scripts is a function used to check whether the flag 0x1000 is set for the actor's FLAGS2 column. That flag is updated by the engine during a throw (for the object thrown) and cleared later, but if the throw is interrupted (the player was too close to a wall or the actor left memory before it had landed), it's erroneously left at 1.
  • Looks like any actor with a BODY has flag 0001 set whenever it returns to memory and is in a view in or adjacent to the current room to ensure it will continue to animate correctly. If it has no ANIM, this is instantly replaced with 0008. This is used to make the rocking horse in E0 go through one cycle of its ANIM every time the player returns to the loft.
    • Any actor that's in memory but not in the current room or an adjacent view gets neither flag 0001 nor 0008.

PLAYER CHARACTER

  • The player character, or PC, behaves differently from the other actors in several respects, but also exactly the same in others. The actors used during the opening and ending cutscenes to represent the player are actually different actors, with the outside one being considerably smaller. The indoors one looks the same but cannot possibly be controlled since only the ID 1 actor has TRACKMODE MANUAL.
  • bbox bounds relative to ROOM coordinates:
    • Edward : (-266;266 0;1777 -266;266) – 532 units wide
    • Emily : (-285;285 0;1761 -285;285) – 570 units wide
    • Edward being thinner means he can fit into slightly smaller gaps than Emily. This hasn't been found to be critical for any tricks though.
  • The game world has no differences based on which character was chosen, though a few texts are different. The actors' ANIMs cause them to be quite distinct from each other, however, at least as far as speedrunning is concerned. Emily is noticeably faster in almost everything she does. This and other differences between Emily and Edward are discussed under "Emily and Edward Differences" and "Enemies/fighting".



PLAYER STATE

  • These lists were compiled to help theorycraft what can and can't be done with glitchy states.
  • The main ways in which the scripts affect what the player or some other actor is doing:
    • [PLAYER.]TRACKMODE
    • [PLAYER.]ANIM
    • [PLAYER.]SET_ALPHA/BETA/GAMMA
    • [PLAYER.]BODY
    • [PLAYER.]LIFE
    • [PLAYER.]ANGLE
    • DROP
    • THROW
    • FIRE
    • SET (VAR 0/player_has_control)
    • [PLAYER.]TEST_COL
    • CAMERA.TARGET
    • HIT
  • In addition, TRACKs can change the following:
    • hard collisions
    • trigger collisions
    • SPEED
    • angle
  • All the player's LIFEs:
    • 11 (dropping)
    • 39 (death manager)
    • 59 (drinking flask)
    • 164 (drinking water from jug)
    • 549 (normal)
    • 550 (floor transitions)
    • 551 (falling)
    • 552 (climbing)
    • 553 (hit by something)
    • 554 ("The End" after certain deaths)
    • 555 (changing room to E6R6 after death)
    • 556 (dragged by zombie)
    • 558 (dragged by zombie 2)
    • 559 (after climbing)
    • 560 (landing)
  • Outside of the player's own LIFEs, PLAYER.LIFE can be changed by:
    • dropping or placing something (in that actor's LIFE): to LIFE 11
    • hitting the crack (38): to 39
    • drinking a flask (in the flask's LIFE 58 or 161): to 59
    • drinking from jug (162): to 164
    • reading De Vermis (194): to 39
    • jelly hitting you (239): to 39
    • dragged by zombie after dying (557): to 554
  • Outside of the player's own LIFEs, PLAYER.BODY can be changed by (at least):
    • lighting the filled oil lamp (LIFE 205 or 429; for some reason the other matchbox, LIFE 351, doesn't do this)
    • dropping something (in that object's FOUNDLIFE)
    • throwing something (in that object's FOUNDLIFE)
    • equipping something (in that object's FOUNDLIFE)
    • drinking flask (LIFE 58 or 161; if you interrupt it with SPACE, it leaves the flask BODY)
    • getting hit by the jelly (LIFE 239)
    • collecting oil lamp (LIFE 9)
    • collecting arrow while bow in hand (LIFEs 305-307)
    • firing last arrow (LIFE 29)
    • changing actions (LIFE 561)
  • Outside of the player's own LIFEs, PLAYER.ANIM can be changed by:
    • opening or closing a door or chest (in that object's LIFE)
    • dropping or placing something (in that object's FOUNDLIFE)
    • throwing (in that object's FOUNDLIFE, as a parameter to the THROW command, also when firing arrows)
    • if BODY changes, causing LIFE 549 to change the ANIM_MOVE list: not directly though
    • aiming a gun (in the weapon's FOUNDLIFE)
    • shooting a gun (in the weapon's FOUNDLIFE, as a parameter to the FIRE command)
    • attacking or poised to attack (in the weapon's FOUNDLIFE or in the actions' LIFE 561, as a parameter to the HIT command)
    • reading Necronomicon or De Vermis (in the book's FOUNDLIFE)
    • using something like a key (in that object's FOUNDLIFE inside the ACTION switch)
    • hit by the jelly (LIFE 239)
    • knocked back after Pregzt dies (LIFE 522)
    • lying on sarcophagus after dying (LIFE 557)
    • pushing, open/search, closing, jumping (LIFE 561)
    • anytime some action finishes and there's a clause for putting the player back to the idling animation (the same LIFEs generally)
    • relatedly if there's another ANIM queued up after an ANIM_ONCE or ANIM_ALL_ONCE command
  • All the SPACE actions:
	aiming/shooting
	fighting
	pushing
	jumping
	open/search
	close
	using keys, jug, hook
  • Stuff that sets VAR 0 to 0:
	opening chest/wardrobe/cabinet
	opening or closing doors
	dropping
	throwing
	crack
	going to E6R6 after dying (both in LIFE 39 and 555)
	reading Necronomicon or De Vermis
	using jug (empty or filled)
	hit by jelly (not when hit by anything else)
	entering cutscenes in E6R6
	falling/dying by falling
	climbing
	transitions
  • Stuff that sets VAR 0 to 1:
	opening chest/wardrobe/cabinet
	opening or closing doors
	dropping (IF ANIM == idling)
	throwing (in LIFE 549 if VAR 10 is 1 and END_ANIM)
	using jug
	leaving cutscenes in E6R6
	reading Necronomicon
	transitions
	climbing
	getting hit (but not when hit by jelly)
	landing
  • Stuff that requires VAR 0 to be 1:
	inventory access (if you're in LIFE 549 and not holding SPACE)
	updating ANIM_MOVE based on the current BODY (if you're in LIFE 549 and not holding SPACE)
	setting VAR 24 when moving backwards (if you're in LIFE 549 and not holding SPACE)
	playing sounds associated with movement (if you're in LIFE 549 and not holding SPACE)
	aiming and shooting
	fighting
	using keys and hook
	all the actions
	moving with the arrow keys (but not rotating)
  • Stuff that sets PLAYER TRACKMODE to something other than MANUAL:
	opening chest/wardrobe/cabinet
	opening or closing doors
	crack
	reading Necronomicon or De Vermis
	hit by jelly
	dying by falling (not just falling)
	dragged by zombie
	transitions
  • Stuff that sets PLAYER TRACKMODE to MANUAL:
	opening chest/wardrobe/cabinet
	opening or closing doors
	reading Necronomicon
	transitions
	climbing
	getting hit (not by jelly)
	landing
  • Stuff that sets ALLOW_INVENTORY to 0:
	whenever VAR 0 is 0 in LIFE 549 and SPACE is not held
	dropping
	throwing
	firing bow
	crack
	death with deathtype 0 (after going to E6R6)
	drinking flask/jug
	using jug
	reading De Vermis
	hit by jelly
	first E6R6 cutscene
	falling
	climbing
	transitions
  • Stuff that sets ALLOW_INVENTORY to 1:
	whenever VAR 0 is 1 in LIFE 549 and SPACE is not held
	dropping (in LIFE 11 if ANIM == idling)
	throwing (in LIFE 549 if VAR 10 is 1 and END_ANIM; LIFE 11 is not actually used for throwing)
	firing bow (in the bow's LIFE 29)
	drinking flask/jug
	first E6R6 cutscene
	climbing
	landing
  • The player's uninterruptable ANIMs (i.e. ones that are called with ANIM_ONCE_ALL):
	dying (any type)
	throwing (also firing arrows)
	shooting
	coughing
	knockback (also after the second Pregzt cutscene)
	climbing
	landing
	jumping
  • Stuff that depends on player_hitpoints:
	most monsters attacking the player, though they still home in on the player
	the matches, cartridges and lamp being affected by water
	random noises in E6 specifically
	staying in LIFE 549 and executing any of its code
	the "You feel weak" message
  • LIFEs that rotate the player:
	any door (two LIFEs)
	39 going into E6R6
	39 deathtype 3
	550 going to E5R0 and back to E3R11
	555 dying and going to E6R6
	557 dragged by zombie
  • A summary of all the typical erroneous ways to regain VAR 0/INV/TRACKMODE MANUAL/LIFE 549:
	delayed door opening (VAR 0, TRACKMODE MANUAL)
	transition (VAR 0, TRACKMODE MANUAL, LIFE 549 -> INV)
	firing bow (INV, which can be used to regain LIFE 549)
	hit by something (VAR 0, TRACKMODE MANUAL, LIFE 549 -> INV)
	VAR 10 is 1 in LIFE 11 or 549 and END_ANIM caused by throwing or jug (VAR 0, INV)
		have to make sure END_ANIM is delayed (or you're not in LIFE 549) so the effect isn't wasted
	falling and landing (VAR 0, INV, TRACKMODE MANUAL, LIFE 549)
  • Things that affect PLAYER.TEST_COL:
    • Set to 0 by transitions; getting hit by jelly (and again in LIFE 39).
    • Set to 1 by transitions; opening the cabinet in E1R3 (for some reason); climbing (for some reason).

IN_HAND / INHAND

  • Shown by the RV.
  • The INHAND is whatever actor you're currently wielding or holding in your hands. This is changed by any item that isn't used instantly, including the jug and any of the keys (which can be used at a delay by hitting SPACE). If INHAND is 2, that means the "actions" object is selected, allowing to perform basic actions. Generally the INHAND actor's FOUNDLIFE is always cycled each frame, and this is the link between that actor and associated actions and ANIMs.
  • Even if you can't successfully complete a drop action, the item is still removed from INHAND unless its presence affects your BODY, e.g. a sword. This is just because of the way all those items were coded differently.
  • If you have the flask BODY and you start whichever action by briefly tapping SPACE, even if it's not uninterruptable, it'll play out completely because of there being no ANIM_MOVE to interrupt it.


BODY/INHAND DESYNC

  • Even though the PC's INHAND and BODY usually match each other (with objects like keys causing the normal BODY to be used), there are ways to "desync" them for some mostly trivial or negative effects.
  • You can get the PC to move with the flask body if you knock yourself back into a transition (not every one of them will work probably, observed going from E3 to E5) and drink water from the water jug (or just from the flask) before hitting it. She will actually move all on her own without SPACE held and thus you can rotate her as well. She keeps her INHAND so if you hit SPACE it'll get stuck in aiming the rifle unless you changed the item before the transition. Here's what changing to different items does in terms of whether you're left with the stairs movement, but they all have the inventory and their respective SPACE actions available.
    • oil lamp: stuck.
    • usable item like key: SPACE movement
    • revolver: stuck
    • melee weapons: can back up but can't actually hit things; might be the hotpoint vertices don't exist on the flask BODY
    • bow: stuck
    • If you do this and walk into the E5R0 pit, you can't cross it because you're so slow, but if you lag it you'll at least keep control when you're down there.
    • For some reason, this didn't work the next time I tried it, at all. Must have something to do with your timing because sometimes you're not even left with the flask body.
  • There's also ways to make the player have BODY 12 (normal) but have any object INHAND that usually has a different BODY associated with it. This causes the player to be able to e.g. "shoot the rifle" without it showing at all. This doesn't seem to have any interesting uses though, because there don't seem to be any places in the LIFEs or the engine code where the game checks for both of the variables at the same time, and thus you'll end up getting one of the normal outcomes anyway instead of some hybrid.
    • The only interesting use is keeping the flask BODY so you don't die to the pits or take hits (while moving around by jumping), described under "[NOT] DYING".
  • Reading the Necronomicon with any BODY leaves the current INHAND object INHAND with BODY 12.
  • You can get Emily to walk around with the flask BODY if you just knock yourself back into the stairs (E3-E5-E3 works at least) while drinking the water jug: need to keep the rifle INHAND though.
  • Just interrupting flask-drinking (by hitting SPACE while having a SPACE action) causes the flask BODY to be left due to the code in LIFE 59 (it stores the wrong BODY into VAR 204).


SPACE BAR/ACTION KEY

  • Whenever SPACE (or any equivalent inputs) are being held, LIFE 549 is not looping through the part where INV is set to 0 whenever VAR 0 has that value. This seems to explain some anomalous behaviour when SPACE is held. More is explained by other parts of the LIFE getting skipped.
    • Even if VAR 0 is 1, INV is still not going to be enabled if it wasn't already. Conversely, if VAR 0 goes to 0, INV doesn't get disabled, at least not based on LIFE 549.
    • DO_MOVE is skipped. It's not clear if this is tied to the effect of getting stuck in the current ANIM.
    • Any movement from holding arrow keys doesn't happen since ANIM_MOVE is also skipped. The related sound effects don't get played.
    • VAR 24 (moving backwards) is not getting updated, which at least has the effect of being able to open doors or climb up facing whichever direction.
    • Also, SPEED does not update either, which can also reflect on opening doors and other things.
    • Instead, SPACE causes whatever effects, if any, are given in the LIFE of the INHAND actor for "CASE == space".
  • If you hold SPACE while performing one of the following actions, you can avoid losing INV:
    • opening chest/wardrobe/cabinet (there's a little delay before it starts to open which is when you can enter the inventory)
    • opening or closing doors
    • reading the Necronomicon, but only if you hit SPACE to close the book instead of ESC
    • Because these are all actions that don't set INV to 0, they also don't restore INV afterwards even if restoring VAR 0, meaning you can't e.g. start opening a door, then read De Vermis and interrupt its ANIM after the door has opened by just doing something else in the inventory.
  • Holding down SPACE causes sound samples to be skipped in many cases, e.g. while having shot oneself into a staircase it won't play the reload sound. This could potentially eliminate a bit of lag in case that sample hadn't been cached yet.
    • This also suppresses movement-related samples. While the lamp is out, holding SPACE doesn't prevent running. Thus you could theoretically save a tiny bit of loading lag time if you manage to completely prevent them while running over a new surface, e.g. a carpet.
    • In a segmented run this can be done by pre-caching those samples anyway with a low likelihood of causing issues.
  • Any mentions of a "null" SPACE action refer to times when hitting SPACE doesn't actually visibly do anything, e.g. while holding out the lamp (there's nothing in its FOUNDLIFE that causes the PC to stop or enter another ANIM).

MOVEMENT

  • Methods of movement:
walk, run, walk backwards
punching (for Edward)
poised to fight (for Edward)
hit knockback
rifle knockback backwards (first part)
rifle knockback forwards (second part)
daggers/sword backwards
falling
backwards after falling
forwards after falling
climbing
snapback, lag snap
using jug
dying (nudged slightly forwards)
sucked into jelly
    • Of those, the ones that are activated through the inventory are:
      • snapback (when changing BODYs)
      • lag snap (when not changing BODYs)
      • using jug
    • As you can see, the only way to get movement from a standstill through the INV is to use the jug.
  • Movement for all actors is mostly done through ANIMs. Each ANIM has different numbers of keyframes within them each with their own offset and length. The offsets given refer to the distance that actor will cover within that keyframe (and so their frame-by-frame movement is the distance divided by the number of frames in that keyframe). Moving up and down is handled separately.
    • See the MODX/MODY/MODZ section for more details on this and the ANIMs section for more information on specific ANIMs.
  • Movement is calculated first before any changes in the actor's facing on a given frame, i.e. the angle rotating actor will be left facing is not the direction they just moved. This can be seen the most easily when using e.g. a SPACE snap.
  • For horizontal collisions, there are four possible directions: top/bottom/left/right.
  • The game uses discrete (a posteriori) collision detection: it allows movement into walls to happen and corrects it afterwards to produce a new valid state before processing the next frame. The game doesn't reset the "virtual" position until the actor's ANIM or keyframe has changed even if such corrections are being made to the position the actor is drawn at.
    • This is why sideways clipping and corner clipping work, as well as some other tricks.
    • On any given frame, your virtual position has moved further in the direction you were facing the previous frame.
  • The "virtual" position is used when determining whether colliders are being collided with, but only "real" collisions count between two actors so you can't e.g. pick up an item off the floor through a wall but you can off a table (where the collider collision is what causes it). Pushing works through other actors but not through colliders.
    • The reason virtual collisions don't count between actors is more likely because collider collisions are handled first and the actor thus gets ejected by the collider so there's no collision detected when it comes to actor collisions. Probably the order of operations also explains the pushing.
  • Under some circumstances, ANIM_MOVE might have to wait for the current ANIM keyframe to have ended before being allowed to change it if SPEED is 4 or 5.
  • If a frame within the current ANIM is skipped due to lag, the game compensates for this by adding together the offset from two or more frames up until the end of the current keyframe. If there are lag frames past the end of the keyframe, those frames are lost.
  • You get caught on any seam between perfectly aligned colliders or actors if you're facing it at less than 45 degrees (because the relative position of the collider that stopped you has changed and thus it starts ejecting you on a different side). No known applications. You can never get inside such seams with sideways clipping.
    • Can be used to get other actors following you stuck just as well.
  • Because whenever the SPEED is 4, it rolls down through SPEED 3-2-1 to 0 instead of going there immediately, if you walk into a trigger or such that normally stops your movement, you may be able to get a bit of residual movement that offsets you from wherever a warp command would have otherwise left you.
    • This is easy to see when entering staircases.
  • VAR 0 and PLAYER_TRACKMODE both control whether the player can move. PLAYER_TRACKMODE at (NONE -1) disables basic movement using the arrow keys, and VAR 0 does the same and more (see LIFEs for what kinds of effects it has).
    • Getting hit while VAR 0 is 0 sets it back to 1, meaning this could be a way to free yourself from a softlock.
  • If your BODY is 268 (flask), you can only rotate and use SPACE despite TRACKMODE MANUAL and having control because there's no clause in LIFE 549 that gives ANIM_MOVE for that BODY.
  • If you're in LIFE 550 and you gain TRACKMODE MANUAL and control, you can move around based on the ANIM_MOVE given in that LIFE: you can only really walk forwards (normal transition ANIM) or run (walking ANIM) and so it's very slow to move this way. You're also generally stuck because your trigger collisions are switched off.
  • Collisions being off means you won't be stopped by either any colliders or with static (normal) actors: only pushable ones will still hinder you because of their special code.
  • There's a short period of time after a room transition when the player's movement seems to be culled in an unusual way, keeping them further away from any walls or actors. This probably has to be caused by the player's virtual position moving too far during the loading lag and view changing when Timer 2 is supposed to be stopped. It might be a bug that only happens if both the room and view change.
    • It probably doesn't allow anything that can't be done by a regular lag snap, but if you can time a transition so you're at the start of a keyframe, you should be able to save frames that way (utilizing the loading lag) though having no control over the direction of this snap unlike with something like a SPACE snap.

File:Post-Transition Collision Oddity.avi

  • The movement for a given frame is given one frame after the frame has been entered: for running, for example, if kf1 (frame 0) is shown, the last movement was for kf0 (frame 14).
  • Whenever an actor moves S or E, their movement can be slightly faster, by 1 unit per keyframe, compared to N or W, presumably because the coordinate values are always rounded up (and S and E are both the positive directions).
  • It seems you cannot move due W. At 270 degrees, you will also creep towards the S very slowly. Moving back and forth at that angle means you're just moving S.
  • Because jumping has several keyframes of movement before the fastest keyframe (kf5 which has an offset of 527), it's usually never good to get through walls with it, unless the first keyframe with movement (kf1, 362) is fast enough to get through.
  • The mathematically derived units per second for every keyframe of running for Emily:
    • 0, 2: 476/15 = 31.7333 = 31 or 32
    • 1: 611/15 = 40.7333 = 40 or 41
    • 3: 633/15 = 42.2 = 42 or 43
  • If you count Emily's running keyframe displacements together, you get 2196. However, in practice, this movement could be anywhere between 2192 and 2200. This is probably because of rounding that happens on every keyframe because the game has to keep the position stored as integer values. How much it is exactly depends on your exact angle of movement.
    • There is a region from +3.2 to -3.2 of any orthogonal direction within which the movement along the closest axis is given completely (e.g. you're going as fast N/E/S/W as you could be) while also getting a little bit of sideways movement to complement it. This means such deviations don't inherently lose any time and thus you can always safely hug walls at up to an angle of 3.2 degrees into it. The amount of "extra" movement you're getting if moving sideways at an angle of 3.2 degrees is between 119 and 123 per cycle of the run ANIM.
    • The fastest run cycle is when you're moving at the angles 3.2 (just to the E of due S) or 86.8 (just to the S of due E) in which case you'll get X: 2196, Y: 123 (or vice versa), total of 2199.44.
  • Movement other than running kf3 can have a different maximum angle margin for keeping the maximum movement speed along an axis. These were all tested by finding what the last angle that allowed escaping a wall was when standing in the first position where it's possible with the ANIM (and keyframe) in question:
    • Running S or E kf0: offset 476 when angle is +-3.5
    • Running N or W kf0: offset 476 when angle is +-3.5
    • Running S or E kf1: offset 611 when angle is +-3.2
    • Running N or W kf1: offset 610 when angle is +-3.2
    • Running S or E kf3: offset 633 when angle is +-3.2
    • Running N or W kf3: offset 632 when angle is +-3.2
    • Walking S or E kf2: offset 384 when angle is +-3.9
    • Walking N or W kf2: offset 383 when angle is +-3.9
    • Jumping S or E kf5: offset 527 when angle is +-3.2
    • Jumping N or W kf5: offset 527 when angle is +-3.2
    • Pushing S or E kf3: offset 298 when angle is +-4.6
    • Pushing N or W kf3: offset 297 when angle is +-4.6
    • So generally speaking the movement along the axis could be one less than the keyframe offset (though the total still rounds to the full amount) and the angle margin seems to be the wider the slower the keyframe.
    • It doesn't seem there's a difference whether the angle is off on the left or right side of the axis.
  • The offset on the first frame of a keyframe of movement, at least for running, always seems to be one or two units smaller than the rest, making it a tiny bit more inefficient to stop in the middle of motion than it otherwise is.
    • Also whenever you've stopped moving and start moving again, or if your ANIM changes, you've lost a little bit of displacement due to using a rounded coordinate value instead of a calculated precise one for the starting point of that movement (you aren't ever snapped forwards so you must be losing the remainder every time). Thus if you move at an angle of 180.4 while continuously stopping and starting the ANIM again, you never move towards the W at all.
    • This seems to depend on which exact way you're facing, since moving at an angle of 0.4 degrees causes the E movement to happen instantly, presumably because the movement is rounded up going E or S. This means that if you were for some reason confined to moving at such an angle but you needed to move E as fast as possible, you could do this by starting the ANIM repeatedly.
  • Whenever an ANIM with movement is about to be interrupted, it will be more efficient if it was interrupted on a slower keyframe. With running, the difference might be something like one-half-to-four frames with the difference oscillating as a factor of time. So an interruption right after kf3 is the best, and the difference it can make might be four frames at most.
    • If you are about to lose the entire kf3 of running (worst case), resetting earlier to get to use the "extra" kf3 for a bigger corner clip or such might have been worthwhile.
    • Since four frames plus the bigger corner clip (another three frames or so) and bigger corner SPACE snap (another one frame) only amounts to less than ten frames, it seems an interruption will have to happen within ten frames to have any hope of being useful, unless further time saving is enabled by e.g. using a faster keyframe to enable some large snap (such as over the E1 crack) or pushing lightweights through walls faster, saving a whole cycle. These kinds of things seem impossible to come up with any exact formulae for and so each segment just has to be evaluated separately.
  • Lag makes curving around in a circle slightly slower because movement is calculated based on the direction you were facing the last frame, even though you'll (usually) get the extra rotation to compensate for it. Because there may be more lag when the PC is facing the camera compared to away from it, this would explain why their position will usually slightly shift if you're running or walking in a circle that you expect to take you to the exact same positions.
    • The second reason is the movement being slightly faster S or E due to rounding.


MOVEMENT INPUTS

  • Running is based on double-tapping UP within a given number of frames. If the game is running fast, the window for this shrinks. If the game is running slowly, the window grows (but the chances of inputs getting ignored also grows). Remember that you're not allowed to adjust the default CPU cycles for actual runs.
    • There are many places, esp. when OOB, where it's almost impossible to get running since the game might not be actively rendering any actors and thus runs really fast. A triple-tap might help.
    • Because of the way the game caps the framerate (see "MAIN LOOP"), it's guaranteed to run at no more than 70 fps every other second, which could further explain why starting to run is sometimes easier, sometimes more difficult.
  • If you buffer UP right as you're saving in a situation where you're not running, as you reload, if you hit UP again around the right frame (not too early), you can get yourself running immediately as the game resumes.
    • You can also start the run if you're walking, go to the menu or inventory and buffer UP as you're leaving it. It goes from SPEED 4 to 3 to 5 as you're returning to game. This is an efficient way to get moving quickly after a staircase or such.
    • You also get to keep running if you buffer UP during the lag after picking up an object.
    • Unfortunately looks like when coming back from the menu or inventory you can't keep running if you were running already. You can just double tap and hope to lose the least possible frames.
  • You can also get a s/l run with the same idea: buffer walking before saving, then hit UP again as you're reloading. Only with s/l'ing, you have to time the latter key press when it's almost done loading, you can't just buffer it.
    • This sometimes systematically fails depending on what state the game was in before you reloaded, not the save file itself. It can be fixed just by messing around a bit.
  • If you hold UP and DOWN at the same time, moving back takes preference. If you hold LEFT and RIGHT, they cancel each other out.
  • There might be times when it's easier to use the KP_7 and KP_9 keys to buffer both turning and forwards movement at the same time. You can also get the character to run by hitting any combination of KP_7, KP_9, and KP_8 or UP in sequence.
  • You can inch forwards or backwards very slowly by tapping SPACE while hitting the movement keys. Even if the game registers a button tap and temporarily changes your ANIM doesn't mean you'll get any movement on that frame though.
  • If you use an INHAND item (e.g. jug) with SPACE, the PC will continue running afterwards if you just hold UP.


MODX/MODY/MODZ

  • This is a more precise explanation of how movement is processed.
  • Actors have ROOM coordinates and fields called modX, modY and modZ that are shown in the RV.
  • Every keyframe of every ANIM has a duration and three offsets (all shown in the Model Viewer), one for each axis relative to the actor's facing. In practice, only the last offset (forwards/backwards) is used, at least in AitD 1.
    • If an ANIM has a keyframe with offset 346, a new position will be calculated on each frame of the ANIM based on interpolation between 0 and 346. So if the ANIM is on frame 7 of 15, the actor will have moved (7 / 15 x 346 = 161) units.
    • This value (161) is stored into modX and modZ, split into X- and Z-components, (or into modY if the movement is vertical) so e.g. if the angle is 45 degrees:

MODX = FORWARD_MOVEMENT * SIN(angle) = 161 * SIN(45 deg) = 113 MODZ = FORWARD_MOVEMENT * COS(angle) = 161 * COS(45 deg) = 113

    • During this time, the ROOM coordinates do not change at all, until the end of the keyframe at which point the mod values are added to them. Also done if the actor is serialized at any point. The actor is, however, always drawn at the coordinates (roomX + modX, roomY + modY, roomZ + modZ) to make it look like they're moving steadily forwards. Collision checks also work with the combined values.
  • If the actor is serialized before the keyframe has ended, the rest of the movement for that keyframe is essentially lost since the keyframe advances as the actor returns to memory. If a keyframe was really long, this could allow getting an actor through a slow keyframe instantly (apart from loading times) to make it e.g. reach a trigger faster. There aren't any useful cases in AitD 1 since the only ANIMs you tend to have to wait for are ones happening with actors that are too close (in LIFEMODE 2 generally) to easily get to despawn.
    • More about serializing actors under "actor serialization".
  • Note that rotating during a keyframe just changes the angle in the above calculations. The same reference/anchor point (the ROOM coordinates) is still used until the end of the keyframe. Essentially the player pivots around their ROOM coordinates, sliding left and right unnaturally with any change in angle. This is exactly why a SPACE snap (and other similar instant turns) can suddenly warp the player so far.
    • The pivoting effect is usually too subtle to easily notice. In the following demonstration, the PC's walking ANIM's first keyframe has been hacked to be both longer and have a bigger offset, so it can be seen more clearly.

File:ModXYZ Demonstration.mp4

    • Note that this means that it does not matter what point in the keyframe you do the rotating for the outcome of where you'll end up. Turning X degrees on the first frame counterintuitively yields the same final position as turning X degrees on the last frame. The only difference is whether you're avoiding some collision by doing it one way or the other as you're still passing through different points in the middle.
    • Because of this, if your angle is off by at most 22.5 degrees at the start of a keyframe of running, you still generally won't lose time to this since you have time during the keyframe to adjust it.
  • If a collision is detected, the game does not clamp the movement down to whatever would cause the actor to move to the furthest valid position (right next to the collider or actor). Instead, either the frame's whole offset is applied or the actor doesn't move at all, though this check is done separately for X- and Z-displacements.
    • The lower your FPS, the more likely it is movement near a collider will be prevented since the offsets are for more than one frame at a time.
  • Aside from these offsets, any other "movement" during ANIMs is cosmetic, e.g. falling into the crack or reading the occult books doesn't change the player's actual coordinates.
  • Stairs interpolation doesn't use MOD values at all, instead changing the actor's coordinates directly.

ANIMATION MOVEMENT SPEEDS / EMILY AND EDWARD DIFFERENCES

  • Any unlabeled data given here is for Emily.
  • Some ANIMs are longer for one or the other and may have different keyframe counts, e.g. Emily's ANIM 278 (idle with rifle/revolver) is only 200 frames in 4 keyframes whereas for Edward it's 560 frames in 7 keyframes. Other examples: Emily takes 40 frames longer to drop items. Edward's cheer in the ending is completely different from Emily's. Edward takes 360 frames to die normally moving forwards 200 units, Emily takes 320 frames and only moves 62 units forwards. Emily dies to De Vermis in 270 frames in 6 keyframes whereas Edward takes 320 frames in 7 keyframes.
    • Attack-related differences listed under "Enemies/Fighting".
  • Edward actually moves forwards when he throws punches. Both punches move him 11 units, or 258 if you interrupt a right hook after kf2, or 91 if you interrupt a left jab after kf2. Otherwise all the movement-related ANIMs have offsets that are around 7.1%-7.2% bigger for Emily, both forwards and backwards. Except Emily doesn't move as far forwards when opening doors with ANIM 26 (difference of 117 units).
  • Emily's running speed 2196 units/sec which is about 7% faster than Edward's 2049. This is the main reason Edward is very difficult to justify as the character of choice.
  • Emily's running keyframes have displacements: 476, 611, 476, 633. The faster keyframes are the ones when she takes a step, and the right step coincides with kf3 starting. The step sounds are played right at the start of those keyframes.
  • Edward: Jumping has 8 keyframes and gives you a displacement of 2392 over 126 frames = 19.0 units per frame. Running has 2049 over 60 = 34.2 units per frame suggesting every jump loses you 1910.9 units of displacement, 56.2 frames, about 0.94 seconds. However, if you can utilize the snap turns, this won't be as bad.
    • Emily: Jumping has 8 keyframes, displacement 2564 over 123 frames = 20.8 units per frame. Running has 2196 over 60 frames = 36.6 units per frame. Thus you lose 1938 units per jump, 52.9 frames or about 0.88 seconds lost.
    • Edward has a frame in his jumping ANIM where the offset is 338 (which is massive for a single frame). Even Emily will move 120 units per frame during kf1 of jumping. These kinds of offsets don't seem to enable anything that lag snaps don't.
  • Walking backwards actually has a keyframe that gives you 84% of the max running speed (Edward has -495, Emily -530). It's 20 frames long. The shooting knockback for Edward has the following: kf1, 10 frames, -140 offset; kf2, 20, -315; kf3, 20, -471; kf4, 20, 138. A total of -926 before kf4. Getting hit for Edward has (15, -285), (20, -377) and (20, -237) for a total of -899. Emily has these in proportion.
  • Kf6 of climbing is 30 frames long and moves you 764 units. Kf2 also moves you 222 units within 10 frames. These generally just get you closer to the wall that you're climbing because you don't move upwards until the whole ANIM is over.
  • Summary of the biggest one-keyframe offsets (for Emily) aside from running:
    • sucked into jelly kf8 goes up to 2137 in 20 frames and the rest are also fast
    • walking backwards has kf1 -530 in 20 frames
    • climbing kf8 is 819 in 30 (but the walls are also thicker in E5/E6)
    • jumping kf5 has 527
    • landing after a long fall kf1 is 698 in 8
    • landing after a fatal fall kf1 is 637 in 20
    • Everything else never goes up to 500. Even though the frame counts are given here, only the offset itself is usually relevant, since lag snaps can always be employed to get all the keyframe movement at once anyway. More under "lag snap".
  • Landing after a long fall has a keyframe of 698 within 8 frames. This is 127 more than EM's hitbox so enough for getting through a thin wall or door if you could do it in E0-E3 (E4-E6 walls are thicker). Kf0 even pushes you backwards so it would be very difficult, though not impossible, to orchestrate in AitD 1.
  • Edward is the only one who has an ANIM that actually has a Y-offset. This is the one used when he falls with the lamp BODY; it's ignored by the engine though even if you walk right over the crack with the lamp, even if the gravity flag is on.
    • If this actually worked, you could delay dying by doing the trick that lets you survive the crack (see "DYING"), then switching your BODY to something else when you wanted to fall. Might not work in staircases seeing as LIFE has to be 33.
  • It's faster to back up swinging a weapon than just walking.
  • Some ANIMs are best interrupted before they've finished playing to make them faster. It may be none of them ever come up in speedruns though. Also the following doesn't take into account any frames lost while resetting them.
    • You can reset walking backwards after kf1 (40 frames in) to make it faster but only by 4.7%.
      • Meanwhile it's better to let ANIM 40 (moving backwards while swinging sword or knife) play out completely.
    • Same goes for walking forwards and resetting after kf2 (60 frames in) which is 33% faster.
    • If you interrupt pushing after kf1 (every 40 frames), you're moving at 7.075 units per frame as opposed to 6.135 if you reset it after kf3 (170 frames in). The last kf4 is slow so you never want to let that one play out.


SPEED

  • The PC's SPEED value doesn't actually directly relate to their frame-by-frame movement speed, which is given in the ANIM data instead. Instead, it's a piece of meta-information used to simplify the code in the TRACKs. The game doesn't have to know which exact BODY/ANIM the player has and change the ANIM to the exact right one: this is handled by the engine when it's told what SPEED the player is supposed to be moving at, as set by the TRACK instructions. It might also be used by the engine for other purposes (some logic checks).
    • The only values that are used are 0 (standing still, basic actions), 4 (walking), 5, (running) and -1 (backing up). 3, 2 and 1 are briefly visited when the player rolls to a halt, but only after walking, presumably as a means of giving the player time to double-tap to get the PC to run. These values are believed to behave exactly the same as SPEED 4. Thus whenever SPEED 4 is mentioned in the guide, it stands for any between 1 and 4.
  • The information about which SPEED corresponds to which ANIM at a given time is given to the engine in the LIFEs by the ANIM_MOVE command. It also assigns an ANIM to rotating in either direction. Normally it looks something like this: ANIM_MOVE idling walking running idling walking_backwards rotating_cw rotating_ccw. In stairs it might look like this: idling walking_down_stairs walking_forwards idling idling idling idling.
    • The information is thus given in the order SPEED 0, SPEED 4, SPEED 5, (unused), SPEED -1, cw rotation ANIM, ccw rotation ANIM. The fourth one is supposed to be used when the player stops running to give a rolling stop but it's unused and always matches the SPEED 0 ANIM. The rotation ANIMs are set by ANIM_MOVE if SPEED is 0 and the player is rotating.
    • Obviously, while TRACKMODE is MANUAL, the player can affect their SPEED by pressing and releasing the arrow keys.
  • For the PC to start running, SPEED has to be greater than 0 and smaller than 4. After releasing UP while walking, SPEED is rolled down by one each frame, leaving about three frames for hitting it again. In practice this seems to be longer seeing as the game won't react to keypress events instantly. It might also mean you can hit the key again too early as well as too late.
    • SPEED State changes when hitting UP.
-1, 0 => 4 : you start walking
4 => 4 : you continue walking
1, 2, 3 => 5 : you start running
5 => 5 : you continue running
  • Whenever SPEED is set to 0, if you were coming from SPEED 4 or 5 at least, there can be a short snap. From SPEED 4 (walking) a snap of 15 units has been seen, from SPEED 5 (running) a snap of 24 units.
  • You can manipulate which SPEED you will have during events that take away MANUAL movement based on your last inputs before they happen. This makes it possible to even open a door with SPEED -1. If the event contains a TRACK, this can of course override the previous value.
  • If your TRACKMODE is NONE but VAR 0 is 1 and you're in LIFE 549 (or 550) and your SPEED is something other than 0, you can move straight forwards (or backwards with SPEED -1) at times when you normally shouldn't be able to. SPEED is often left at non-zero values during activities such as reading the Necronomicon or opening a door, but VAR 0 is 0 so there's no movement. Thus anything that restores VAR 0 enables making use of such movement.
    • In practice, the most obvious way to abuse this is by e.g. throwing something right after colliding with a door that's going to open inwards.
    • You can be left with SPEED -1 if you hit DOWN just as you're about to e.g. open a door, even though this isn't supposed to be possible (VAR 24 is supposed to change to 1 which should prevent opening doors). Can be used in conjunction with using an item after hitting the door to make the PC take extra steps backwards, if that was to be desired but it's probably not enough to ever be worthwhile.
  • Unlike when you back up, if you fire the rifle or get knocked back your speed stays at 0, not -1, explaining why you can open doors during those ANIMs.
    • Your SPEED also stays at 0 during any basic actions that move you around.
  • During falling, you're left with whatever SPEED you had before.
  • Whenever you hit SPACE while in LIFE 549, you preserve whatever SPEED you had before. TRACKs can still set it to a different value.
  • There seems to generally be a frame or so between SPEED changing and the ANIM reflecting on this, which is another reason you could see anomalous effects.
  • In the RV, SPEED is shown for all actors that have a TRACKMODE other than NONE.

CORNERING / 180° TURNS

  • A corner clip is simply running into some convex corner and letting the virtual position essentially move diagonally through it before the reset at the end of the current keyframe (when the MOD values are added to the ROOM coordinates). The biggest distance covered in a clip is obviously 633 units. At an angle of 45 degrees, this is 448 units along both axes. In effect, the clip only happens along the axis that was blocked before (unless there's another actor or collider on the side in which case you'd clip through both corners), and thus 448 units should be the maximum snap for a 45-degree clip (plus a little bit because of the last bit of movement along the unobstructed axis, up to 449).
    • If you're clipping at 45 degrees, the distance you (your center) can at most have from the corner at the start of the keyframe is 162 units (448 - 570 / 2, rounded down). More generally, the formula is (x-displacement if the clip itself will move you along the y-axis, or vice versa, minus half your bbox) where the x-displacement is (keyframe offset times the cosine of the angle away from the x-axis) and so (633 x cos(θ) - 285). For some reference angles (negative numbers mean the coordinates have to already be past the corner):
    • if theta = 75, distance from corner is -122 (kf3) or -127 (kf1) or -162 (kf0/2)
    • if theta = 60, distance from corner is 31 (kf3) or 20 (kf1) or -47 (kf0/2)
    • if theta = 45, distance from corner is 162 (kf3) or 147 (kf1) or 51 (kf0/2)
    • if theta = 30, distance from corner is 263 (kf3) or 244 (kf1) or 127 (kf0/2)
    • if theta = 15, distance from corner is 326 (kf3) or 305 (kf1) or 174 (kf0/2)
    • In practice, so long as you're through on the first keyframe of hitting the corner collider, you should be doing fine.
    • See "SPACE SNAP" for information on how to save even more time incorporating that trick into corner clipping.
    • This file shows an example of efficient cornering versus inefficient:

File:Good Clip vs Bad Clip.avi

  • How inefficient is manual turning for getting to face an angle of 45 degrees towards the corner? In the following calculations, we're using a movement speed of 633 on every keyframe to simplify it. Offsets are all rounded and exact calculations are basically impossible. This also doesn't take into account that moving at angles of 1.5-3.2 degrees doesn't slow your movement along the other axis.
    • If you turned manually for two keyframes, you'd lose this much speed. First keyframe: sum(seq(42.2 x cos(a), a, 1.5, 22.5, 1.5) where 'a' is the angle you've turned towards the wall and the rest are starting and ending angle and increment, gives 615 (97.2% of 633) and the next keyframe sum(seq(42.2 x cos(a), a, 24, 45, 1.5) = 518 (81.9%). Now you're facing 45 degrees and can clip the corner without losing any more speed on that kf. Of course you might have to turn again on the other side losing more time depending on which angle you actually want to face. If you want to keep turning for another 45 degrees afterwards, the situation is symmetric to before (almost, you'll drift away from the wall this time which may be possible to recover for free depending on how long the next stretch is), so you'll have another two keyframes with approximately 518 and 615 movement.
    • If you don't have to turn at all after the clip, the total efficiency is 615 + 518 + 633 / 3 frames / 633 x 100% = 589 / 633 x 100% = 93% efficiency compared to if we were able to utilize the full offsets from every keyframe (which would be the case if we were to use an slt twice). If you turn 45 degrees a second time, it's around 615 + 518 + 633 + 518 + 615 / 5 frames / 633 x 100% = 92%.
    • Thus a segmented run using slt's might, on average, be able to cut a corner in 0.92 times as much time, although a direct comparison is very difficult on any particular instance.
    • These calculations are actually also wrong in the sense that they don't account for the fact that only the angle at the end of the keyframe matters for where exactly you'll have moved during it (as explained under "MODX/MODY/MODZ". This means the difference is even smaller. Also it was first assumed the menu time going to the save menu wouldn't be counted and so that also was never factored into this.
  • Using a SPACE snap instead gives us a similar situation (on average) as using slt's, except there will be one keyframe whose offset is multiplied by sqrt(2) assuming wanting to do a 90-degree snap, and you don't lose the time spent saving the game. Thus you've gained (on kf3 snapping) 262 units, or something like 6-8 frames of movement. On kf0 or kf2 you've gained 197 units, or between 5 and 7 frames of movement.
    • The SPACE snap is just generally the most efficient when done on kf3 since a lamp corner clip is just a linear multiplier to the offset of a given keyframe. Therefore a run reset may actually be worthwhile to line this up if you also benefit from it in some other ways.
  • What is the best way to clip a corner while having the lamp?
    • If you're running parallel to a wall, you just need to hug the wall facing at an angle between 0.4 and 3.2 degrees (3.5 with kf0) towards it. If you have perfect alignment for the snap (i.e. you clip the corner just around when the keyframe is about to change), you just SPACE snap after the initial clip.
      • The snap distance (total displacement along the obstructed axis) should be the same regardless of which angle between 0.4 and 3.2 you had towards the wall, though the actual snap frame has a smaller displacement if the corner clip already moved you some ways in that direction.
    • If your last keyframe before the clip starts at an awkward time, you might consider plotting in a run reset earlier into the route. It's not clear if and how much time this could save though.
    • You can't face an angle of 0.0 degrees because then you never clip around the corner in a way that makes the ejection from it leave you on the side you were trying to get on. No SPACE snap is going to happen.
  • Run resetting: Ignoring the time lost to the reset itself, if you reset the run at the end of kf0:
    • Actual movement (minus what you would have gotten without the reset): 476 (-611), 611 (-476), 476 (-633), 633 (-476), 476 (-611)...
    • At the end of each subsequent kf, you're -135, 0, -157, 0, -135... from where you would have been.
    • Thus on average you're (-135 + 0 -157 + 0) / 4 = -107 units behind if you had not reset the run.
    • If this means you'll get a SPACE snap at 22.5 degrees on kf3 instead of kf0, you're still losing a few units overall because (476 / 633 x 137) = 103 which is only 34 units less than 137. Thus the reset has lost you (34 - 107) = -73 units.
      • In practice, because kf3 corner clipping is more efficient anyway, cutting the travelled path shorter than the other keyframes, this makes a reset more worthwhile. In particular, by doing the corner cut on kf3 instead of kf0, you can save ((633 - 476)/sqrt(2)) = 111 units. Thus you'd actually start to have offsets like: -24, 111, -46, 111... compared to not resetting, and so you might actually be ahead depending on when the next reset is, except for the time spent doing the reset itself.
  • Resetting at the end of kf1:
    • 476 (-476), 611 (-633), 476 (476), 633 (-611)
    • 0, -22, -22, 0
    • Thus you're (0 - 22 - 22 + 0) / 4 = -11 units behind on average compared to not resetting, except for the time spent doing the reset itself.
  • Resetting at the end of kf2:
    • 476 (-633), 611 (-476), 476 (-611), 633 (-476)
    • -157, -22, -157, 0
    • On average, you're -84 units behind not resetting, except for the time spent doing the reset itself.
  • You can clip an adjacent corner to avoid any thin triggers that are positioned like the E3R11 stairs trigger.
  • The most efficient way to do a 180 degree turn is simply to stand still for the first 90 degree rotation, then continue to stand still for another frame or so to start another fast rotation before immediately starting to run. A situation where a 180-turn is required occurs when picking up the talisman off the table in E2R8 (though you get a pick-up turn first, so it's not really 180 degrees).
  • When going around triggers, the positions in the middle of the keyframe also matter. For this reason, the theoretically best way to clip the corner of a trigger is curving around it so the line between your keyframe start and end positions passes through the trigger, or SPACE snapping or lag snapping through.

ROTATION

  • Actor rotation: All rotations in AitD are applied around the origin in the coordinate system the BODY's vertices are placed in when it is created. Most BODYs have the origin at the very center. For actors like doors, to make them rotate right, their vertices were first shifted so the origin is at the correct corner (where the hinge is supposed to be) instead. The bounding box shape doesn't influence rotations at all, though the opposite can be true.
  • There are 1024 angles that an actor can face, or 256 per quadrant. Thus one degree is 2.8444... or 128/45 in-game units and one in-game unit is 0.3515625 or 45/128 degrees. Thus the increments as shown in the RV are rounded to either 0.3 or 0.4 degrees.
  • When a rotation starts, the game stores values into four fields that exist for each actor: oldAngle, newAngle, rotationTimeLength and timeOfRotate. newAngle and rotationTimeLength come directly from the SET_BETA (etc.) command's parameters (in that order). oldAngle is just the angle when the command was issued. timeOfRotate is a snapshot of Timer 2 when the rotation started. The interpolation formula looks like this:
    • currentAngle = oldAngle + (newAngle - oldAngle) * (Timer2 - timeOfRotate) / rotationTimeLength
    • The only changing variable is the current Timer 2 value. There are sanity checks on Timer 2 so that if its value is ever outside of the range it should be in, the rotation completes instantly.
  • The state in which an actor in TRACKMODE FOLLOW or TRACK is in as far as rotations are concerned is one of four: if it's facing the right direction with a threshold of 1.4 degrees on either side, it continues to move forwards (this is to stop actors from constantly shaking left and right); if the target is closer to it on the left side, it will rotate ccw, if on the right side, it will rotate cw; if the target is exactly right behind it, due to the "indecision bug" it won't rotate at all.

Rotation States.png

  • If you place the pot a second time when Zombie 1 is rotating to sit down in E3R12, it causes the rotation to end. This is likely due to switching TRACKMODE to FOLLOW, so this at least seems to be a way to interrupt a SET_BETA rotation. Other ways may also exist.
  • DO_MOVE is the command responsible for enacting normal rotation on the basis of the actor's TRACKMODE and the PC's inputs. ANIM_MOVE is only responsible for making the right ANIMs play based on keyboard inputs, but rotation isn't directly tied to ANIMs. Sometimes the slower MANUAL_ROT rotation is used instead of DO_MOVE, when aiming or pushing for example.
  • The game handles basic actor rotation (DO_MOVE and MANUAL_ROT) in 90-degree/256-unit quadrants. The starting angle of each quadrant is stored whenever a new continuous rotation has started and that angle stays the same until that rotation has been completed or interrupted. If rotation continues after a 90-degree turn, a new starting angle is stored.
    • Other kinds of rotation, SET_ALPHA/BETA/GAMMA and TRACK rotate commands, are processed in one "chunk".
    • The most obvious way to see the difference is to try to slt actors: some can only be gotten to turn 90 degrees at most in one go.
  • Rotation speed:
    • If rotating because of DO_MOVE, 180 degrees or 512 units per second while SPEED is 0, and 90 degrees or 256 units per second when SPEED is not 0. This is 512/60 (rounds up to 9) and 256/60 (rounds down to 4) units per frame respectively.
    • If rotating because of MANUAL_ROT, 45 degrees or 128 units per second, or 128/60 (rounds down to 2) units per frame.
    • Despite what the ANIMs look like, rotation has a constant pace only affected by any lag right at the end of the quadrant, which causes any "overhanging" frames to be lost.
    • Note that there's believed to always be a frame in-between successive 90-degree rotations, which thus makes rotation slower than indicated above, and the slower the lower the framerate (frames last longer). Some examples of how long 10 full rotations at SPEED 0 will take at different framerates:
above 60 fps: 20.28s (this is less than expected, actually, you don't lose 40 frames of rotation; not understood why)
around 30 fps: 21.77s
around 15 fps: 23.75s
    • The other reason rotations will be slower than they should be is that the last frame of rotation may overshoot the rotation end time (rotationTimeLength), meaning only a tiny bit of rotation could happen on that frame.
  • The game does not calculate an average rotation speed per frame, then multiply it by how much time elapsed (that would be imprecise). Instead it multiplies how much time has elapsed by 256 (one quarter rotation) then divides it by rotation speed. E.g. if the angle is zero and you press LEFT for 20 frames, the new angle will be (20*256)/30 = 170. If you hold left arrow a second time for 15 frames: 170+(15*256)/30 = 298. The results are always rounded down.
  • Some experiments were made with running and turning at the same time after seeing some anomalies to try and understand them better. This image depicts the kind of movement this resulted in at different cycles / FPS counts:

Rotation Experiment.png

Cycles / FPS
 15000 / 70    7800 / 48    3600 / 23
  1200 /  8     600 /  4     200 /  1
    • At full speed, it's almost a perfect circle. Then it becomes more and more erratic and starts to deviate from the initial center of rotation. At 1 FPS, it's only doing 90-degree turns. At 1 FPS, the turns are smaller than expected, and at 4 FPS, larger.
    • The circles starting to deviate at least has to do with the different FPS counts from differing poly counts of the PC model depending on the direction she is facing and what part of the ANIM she is in. Surrounding static actors can also affect this if they sometimes have to be redrawn but sometimes don't. Also hitting camera triggers will cause lag. Possibly rounding errors might be a factor too.
    • The main reason for the big deviations is that the number of frames required to complete one rotation will depend on the exact frame lengths. A full quadrant (while running) takes 1 second to complete, so if frames sometimes last less than and sometimes longer than 1 second, there will be a different number of keyframes of running (0.25s each) that are completed in that time. Thus in the 1 FPS example, there are sometimes 2, sometimes 3 keyframes of the animation per 90-degree turn.
  • This line and this line are where a keyframe ending is detected and a new keyframe is started in the FitD code. Note that both will happen in the same frame. Meanwhile, for rotation, this line and this line are where the rotation ending is detected and a new rotation started, but this time they're in the opposite order, causing a frame's delay before the next rotation starts.
    • Because of the extra frame in-between (and the fact the rotation is bottlenecked at 90 degrees) it should be slightly easier to perform exact 90-degree turns than you might think, and all the easier the lower the FPS.
  • When going between DO_MOVE and MANUAL_ROT rotation (and generally any two kinds of rotation), it sometimes takes some time for the game to catch up on what the correct rotation speed is supposed to be due to the way it's implemented. This lasts until you're at the end of the current quadrant. Thus, starting to rotate during pushing (for example) could be faster than it should be (if the current quadrant of rotation started at a time when rotation was faster) while any rotation happening after stopping to push could be slower than it should be (until the last quadrant started during pushing has ended). Any of this can be reset at any time by interrupting the rotation.
    • The most obvious usage of this is starting a faster rotation when SPEED is 0, only then starting to run to get both fast movement and fast rotation. This gives a noticeable benefit.
  • If a rotation starts during a transition but the transition finishes, and you're holding either LEFT or RIGHT, the rotation will continue in the same direction until the end of the quadrant.
    • Observed coming up the E3R11 stairs. They cause a cw rotation to start right before the TRACK finishes.
  • Manipulating your exact angle:
    • When you tap the arrow keys while running/walking, the smallest increment you seem to be able to get is 1.4 degrees or 4 in-game units, which is what you'd expect since this is the per-frame rotation speed. You'd also expect all movement to come in increments of the same 4 units, but this isn't always the case, even if you're using the same kind of rotation. Two and a half frames' rotation has been observed while standing still, for example. It's not clear why.
    • Generally, to get angle changes of less than the minimum rotation, or other rotations not a multiple of the per-frame rotation for the current type of rotation, you can just wiggle left and right and since the values are always rounded down (rounded the same direction), this enables facing whichever angle instead of being stuck to a subset of angles.
  • Automatic rotation: Whenever the game calls for a special rotation command (SET_ALPHA or SET_BETA; SET_GAMMA isn't used), the object in question will start to rotate every frame (calling DO_ROT_ZV every frame makes its bbox keep updating during this process) until it reaches its target value, for which there's always a separate check in the LIFE in question.
    • The second parameter in these rotation commands is how long the rotation should last. If it is -1, it happens instantaneously and you can't seemingly get a larger or lesser rotation at all by manipulating Timer 2.
  • The reason you can still rotate even without control is the ANIM_MOVE in LIFE 549 (which doesn't execute if VAR 0 is 0) only governs which ANIMs will play when different arrow keys are held. The ANIM data doesn't relate to the rotation. You can rotate with the arrow keys whenever TRACKMODE is MANUAL.
    • If you have an action that calls MANUAL_ROT, this is another way to rotate even without TRACKMODE MANUAL, by holding down SPACE.
  • If a continuous rotation starts near the 360 degree mark and goes past it, the game will not instantly reset the angle back to 0. This doesn't cause issues because the mathematics related to rotation are modular.
  • For some reason, the PC's basic DO_MOVE and MANUAL_ROT rotations cannot be sped up by lagging during inventory and menu visits. Other actors are all affected normally. This could be because the inputs given, which might be lost during the inventory or menu visit, affect which ANIM you're in and if the ANIM changes, the rotation ends.
    • This doesn't even work when using the method of hitting ESC/ENTER quickly to leave the menu/inventory, then quickly switching to holding down the arrow key that was pressed to start the rotation.
  • There is a unique global variable in the game called "ShowBeta". When you are in the inventory, that variable is constantly decreased. Objects in the inventory are rendered with a beta/yaw rotation angle equal to that variable. The same variable is used for pick-up prompts and the copy protection screen (floppy only).
  • For different ways to manipulate Timer 2 to cause erroneous rotations, see "DELAY TURN".

ACTIONS

PUSHING

  • The two kinds of pushable objects will be referred to as lightweights (move when an actor runs into them) and heavyweights (only move when pushed with the push command). The only difference is whether the pushable flag 0010 is always 1 or only sometimes, as governed by the LIFEs. In fact, sometimes heavyweights can erroneously behave like lightweights.
  • There is a tiny bit of uncertainty about the exact mechanics of collisions between actors and pushables. It probably has a bug or two in it. This attempts to give a broad view on how it works.
  • When you push a pushable actor, the code used to determine what happens goes like this for collider collisions:
    • First, the game tries to move the object a little bit in the direction you're moving to see if it will collide with any colliders active for the player himself. This doesn't result in position correction to try to find an acceptable position, just a yes or no.
    • Next, if this test was passed, the game does the normal actor-collider and actor-actor collision tests for the pushable (using colliders from the room it's in) and also the player. This determines what movement actually takes place.
    • For actors, it seems the PC can push things through other actors without having to move himself, but only if he's not completely stuck. This is probably because if you're stuck, you'll fail the check for finding a position for the PC but if you're just stuck along one axis, the check is passed.
  • If the pushing actor is touching the wall that it's trying to push something along, this may allow sliding the pushable along that wall. The reason is because regular cancelling of the X or Z component of the pushing actor's movement results in there only being movement along the other axis, and thus the pushed actor isn't really colliding with anything.
  • Pushables can only be gotten to half-clip corners. That happens if the push is from a direction parallel to the wall the pushable is adjacent to. E.g. if the clip is from the W side of a collider to the N side of a collider, the pushing actor has to be on the S side, not W side of the pushable (unless the N distance around the corner is so small it is covered in one frame of pushing). Even when the clipping is successful, the pushable doesn't snap all the way around the corner.
    • For some reason, the pushing actor and pushable seem to often be stuck for several frames after the half-clip. This could just be lag, though it's not registered by the total delay counter.
    • A lag snap can be used to achieve a bigger clip the same as normal actors.
  • It doesn't seem to be possible to cause a pushable to clip sideways into a collider even if you take care to ensure the player's new position after such a push should also be a valid one. The test could be repeated in TAS conditions, but it seems likely this is because of the first condition of pushing never being met.
  • You can prevent actors from moving even when their regular collision test is off by making there be something pushable between them and yourself (or another actor), because the push test will fail so long as it would have put the pushable inside of you (or that actor). This is a way to e.g. prevent birds from coming through windows, and stopping non-clipping monsters. Also works with heavyweights so long as the pushable flag is erroneously left to 1 after you've pushed it.
  • You can also prevent an attack from connecting if there's a pushable in-between, at least if it's getting pushed during the attack so it's animated (flag 0001), because then it'll absorb the attack.
  • Pushables colliding with other actors don't cause their own COL[] or the other actors' COL_BY values to change. Pushable collision checks are different from regular ones.
  • If the pushable is being pushed by an actor with no collisions and colliding with something while being pushed, it sometimes results in these changes to actor fields (and nothing else):
    • The pushing actor's COL[] gets the value of the slot of the actor the pushable is colliding with.
    • The pushable itself has its COL_BY set to the pushing actor's value.
    • The actor the pushable is colliding with has nothing in any fields.
    • Easy to see if you place the stool between the zombie and the chest in E0 right as it starts to walk, causing it to die as if it touched the chest itself. This only works because the game uses a CONTACT check there instead of COL_BY.
    • An actor can even collide with itself because of this effect. It just needs to get into a position inside a pushable and attempt to push it. Now the pushable is being blocked by (colliding with) the pushing actor itself, so the pushing actor gets its own slot in the COL[0] field.
      • No actor has a CONTACT check (which checks COL[0] as well, not just COL_BY) that has the requirement to either collide with itself or with any whichever actor. They all require a specific other actor to be collided with. Therefore this doesn't really lead into any anomalous behavior.
  • Can an actor push another actor to indirectly collide with a third actor (shown in the pushing actor's COL field) in a way that actually causes something unusual to happen?
    • Since for the actor the pushable is colliding with, there are no collision fields set at all, any COL_BY checks won't be passed and thus the PC can't e.g. open doors or pick up items this way since those all use COL_BY checks. Instead, the only checks this can cause to pass in an anomalous way are CONTACT ones. Furthermore, it requires the CONTACT check to be done in the pushing actor's script, not the one pushed into. The only actors whose scripts meet these requirements in AitD 1 are the E0 zombie (CONTACT with chest through a pushable has the same effect as directly colliding with it) and the fireballs (but there's not pushables in E6).
  • When the player and the pushable are in different rooms and the player tries to push it through a wall that it's colliding with, if the pushable object is small enough so the player's maximum movement offset can move it through the wall, this will happen, because there (generally) isn't a wall there for the player to stop his movement.
    • This is the same as if the PC has no collisions at all, in which case pushing other actors around becomes quite a bit simpler.
  • Lag convex clip: If you lag the game while pushing something, the push code will only calculate what would happen if the player (and the pushable) both moved by the full lagged amount. Thus no pushing will happen even if there was some clearance ahead of them. Usable for getting a convex clip without needing to jam the pushable in place.
  • Some heavyweights behave differently from how they should. They're only supposed to have flag 0010 whenever the player is colliding with them and are in the pushing ANIM. However, with some of them, the flag resetting also requires the player to collide with them, and thus it is usually left on erroneously. This allows other actors to come in and push the heavyweight, or the player to push it (for one frame) without the ANIM requirement being met. You can use lag to cause the pushable to move further during that frame. The heavyweights that behave this way are:
    • The vanity in E1R5.
    • Grandfather clock in E2R7.
    • The boulder in E5R11.
  • An arrow could easily push a pushable through a wall with its high keyframe offset. The only pushable in E2 is the grandfather clock though.
  • You can prevent a pushable from moving by leaving something on the floor behind it (item collisions prevent pushables from moving). This may enable a convex clip.
  • Collisions with actors that can be pushed are more CPU-intensive and generate lots of lag. The game redraws all actors on the screen afterwards instead of just the animated ones, and the collision processing is probably a bit slower too.
  • Most of the pushables have ZV and ROOM/WORLD coordinates desynced along the X and Z-axes. It's assumed not to be big enough to cause weird effects.
    • If the desync was bigger than the pushable's bbox, you could maybe move the pushable to an adjacent room but make it end up on the other side of a wall. This doesn't seem to ever be even nearly the case.
  • You can push several pushables, lightweight or heavyweight, at once, but only in parallel, unless your virtual position moves far enough so you're colliding with another one that's past the first one in a line.
    • Fast Push: If you push two actors at once, one on the side (which needs to be a heavyweight) and able to move and one that's directly ahead and can't move (any kind of pushable), the actor that can move will be sent forwards much faster than how fast it would normally be pushed. See the save file called "Pushing Something Fast.ITD" for an example of this situation. The mechanism behind this phenomenon is unknown.
  • You can push lightweights that are on the other side of an actor, at least if the lightweight has a lower slot number than the actor you're pushing it through, indicating that the order of collision checks/resolutions is important. The times when the lightweight moves correspond with the PC reaching different maximal MOD offsets during different kfs. Can't be done through colliders.
    • If the lightweight's slot number is higher, you can seemingly only push it sideways like this.
  • Because the COL_BY field takes the value of the player for any successful heavyweight pushing (as per LIFEs like 502), it isn't supposed to be possible to push a heavyweight while another actor is colliding with it that has a slot number higher than the player's. However, if the player has already started pushing it, the push flag doesn't get reset until COL_BY takes the player's ID again, and thus the heavyweight is still pushable, by any actor.
  • Pushed objects don't seem to have a s/l snapback, suggesting their positions are updated directly without using any kind of field like modX.
  • If you take a pushable to the edge of the world, you can easily clip into it (collision checks will fail when vertices wrap around). This can also happen during a transition.
  • When the pushing actor and pushable overlap, depending on the direction of movement, when the pushing actor tries to move, one of two things will happen depending on the exact positions, sizes of the bboxes and direction of movement.
    • First the game tries to move the pushing actor: if MODX/MODZ are big enough, this can move it past the pushable's bbox. If it does, the pushing actor is separated from the pushable.
    • Next the game looks at the pushable: either the pushing actor has moved ahead and the pushable can't move into that space anymore, in which case the pushable is left where it was, or the pushing actor didn't find a space, in which case there is a push. If the push takes the pushable far enough to clear the pushing actor's bbox, the pushable will move instead. However, because of how pushing works, both the actors will move the same distance together. This is not subject to any additional collision checks. This is why they will end up overlapping in the new position again.
    • See this image for reference.

Pushing Actor and Pushable Overlap.png

  • In addition to the methods to get OOB using pushables listed under "OOB / CLIPPING", in theory, you could use a lightweight to enable some kind of OOB snap inside a trigger that would otherwise require a SPACE turn. This would happen by getting caught inside the pushable, positioning yourself so when you run (in a specific direction), the pushable is pushed together with you. This effectively quantizes all your movement. The reason this is not the same as just lag snapping from a specific location is it effectively "absorbs" keyframes of your movement, only allowing movement to take place during kf1/3 or just kf3 of running. If you can do a SPACE turn, you can just run into a nearby wall (or so) or generally get a running start, then SPACE turn to face the desired direction at the beginning of a fast keyframe, ready to lag snap afterwards. The pushable allows for the same without the SPACE turn.

JUMPING/HOVERING

  • Jumping is enabled and disabled when the player's flags2 field is updated in LIFE 550 during transitions between E3-E5 and E4-E5. There is no known way to avoid this happening though the player can still jump through the transition without it getting interrupted.
  • When you jump, the gravity flag (0100) is set off until the end of the jump, which is what prevents you from falling down during that time. The rest is cosmetic.
  • The hover trick: To hover, try to drop any object or drinking something while in the middle of a jump to be in a different LIFE than 549 until the jump ANIM has finished. Now you can avoid falling until you've either returned to E5 after visiting E3 or E4 (moving between E5 and E6 doesn't change the state), or jumped again. Climbing doesn't end the hover. The object is never actually dropped.
  • You can jump through triggers like the ones that take you to E3/E4 from E5 to make the transitions faster. If you don't drop something after the transition, you'll be left with the gravity on.
  • If you jump into the E6 trigger from E5R9 while doing the hover trick before you've hit it, you're left immobile aside from turning around. No INV either. Same coming back from E6. Instead, you should drop the item only after the transition, though if you get hit, this also restores control. Moving between E5 and E3-E4 it doesn't seem to matter.
  • It's better to get the hover before having to run out of a wall, not as you're doing that: jumping out loses several frames of movement unless 362 (the first keyframe offset) is enough to get out.
  • During a jump, just release SPACE and you get free rotation in the air. You can also start turning holding SPACE then release it to get a delay turn.
  • If you hold DOWN before jumping, and also hold SPACE the whole time, you'll have VAR 24 = 1 (like you're backing up). If you also drink a flask during the jump to be left with the flask BODY to avoid VAR 24 from reverting to 0.
  • There's a save file hacked so you can jump around inside the house (no known legit way to do this). The save file is called JUMP.ITD.
  • There's a frame on which you can jump right before falling. If you do this, the jumping ANIM is played (you will still fall) but you get stuck in the falling ANIM afterwards. You can't access the inventory through any of this.


CLIMBING

  • How climbing works in the LIFEs: there's a test in LIFE 549 (TEST_ZV_END_ANIM) for whether the player is in a situation where climbing should occur. If yes, it sends you to LIFE 552. You'll have ANIM climbing_start at this point and you are sent up. Afterwards, the ANIM changes to player_climbing_end and you're sent into LIFE 559. If you have any other ANIM in LIFE 552, you're just sent back to LIFE 549 (in case you had another uninterruptable ANIM already playing). After player_climbing_end is over, you're sent to LIFE 549 again. player_climbing_end is what moves you forwards so you don't fall straight back down. (There's a version difference between floppy and CD versions, more on that below.)
    • UP_COOR_Y is the command for actually shifting the player up. It's only present in LIFE 552, thus climbing is the only way to move upwards aside from being warped, and it always shifts all coordinates by exactly 2000 units in one go. The climbing ANIM doesn't have an offset and is cosmetic as far as going higher is concerned.
    • The command TEST_ZV_END_ANIM() tests (for a given animation) if the actor's bounding box will not be colliding with something after the animation is complete. The first parameter is the animation. The second parameter is the height difference to apply.

Also, for the check to succeed you need a second condition to be true: the bounding box needs to collide with something (usually the floor) at the height specified in parameter shifted by 100 units down. In other words to start climbing you need something flat approximately 2000 units above you.

  • There's a special ID (HARD_COLLIDER == 255) for every collider you're supposed to be able to climb. This was given to all the floor colliders in E5 and E6 (except for the one around Pregzt), not just the ones that it makes sense to have it.
  • For the TEST_ZV_END_ANIM test to be passed, your ZV Y has to be within a small 100-unit range of positions. -[X]979 is the first value that works. -[X}880 is the last value where X is a multiple of 2 (probably only because all the colliders have heights separated by 2000 units).
  • You can climb up things through a collider that seems to be in the way (a virtual collision is enough), but of course you'll fall right back down if you don't have the hover. Evident when trying to climb up colliders from OOB through a wall.
  • In principle, it is required that the PC actually faces the collider they want to climb for climbing to be possible and VAR 24 needs to be 0 (PC isn't moving backwards). However, VAR 24 doesn't track the player's movement direction very reliably (either getting knocked back or just holding space and walking back both enable keeping it at 0). Also, the climbable collider you're colliding with and the colliders you end up climbing don't have to be the same. Thus it is possible to climb up while in an ANIM that moves you backwards and to climb onto colliders that you weren't colliding with.
    • This enables climbing when you're either inside some collider, or climbing onto the wrong collider from a corner between two climbable colliders, or one behind and one in front. None of this doesn't seem to enable doing anything you couldn't otherwise have done.
  • The only ANIM that the game explicitly doesn't want the PC to have to start climbing is jumping.
  • If you can convince TEST_ZV_END_ANIM (267 -2000) (which is from LIFE 549) to see your projected position as okay, you can get inside walls. It looks like the test always gets the same params, 267 being the first climbing ANIM and -2000 the height gain.
    • The climbing ANIM has a small 222 offset on kf2 and another much bigger one on kf6 for 764 units. You only move up after the first climbing ANIM is done, which is why normally the 764 displacement is just you running into a wall. However, if you're in a corner and climbing diagonally, it's enough to make you clear an adjacent corner. This means you're not prevented from moving sideways while the other collider does push you back and you end up inside the corner for that reason. See "SIDEWAYS CLIPPING" for more on this trick.
  • Falling while climbing is possible in both versions. This just requires there to be both a pit and a climbable collider ahead and aiming the PC to face an angle that makes them step off the platform they're stood on during the climbing ANIM, which has a forwards offset.
  • Anomalous behaviors when falling while climbing in the floppy version, unlikely to be relevant for speedrunning, mainly because it seems to be superseded by the hover:
    • Chain climbing: After the climb finishes, provided the fall has proceeded down exactly one entire tier by then, this will leave the PC in a position where they're in LIFE 549 for a frame, with ANIM 268 moving them forward, and on the same frame, the UP_COOR_Y command will move them up 2000 units so they return briefly to the original height. Provided there's a climbable collider ahead now (two tiers above where the PC fell), this results in the same set of circumstances repeating until there's no longer a climbable collider ahead.
      • This trick is really no different from whenever there are multiple successive tiers of climbable colliders next to each other in a shape like a very steep staircase (e.g. in E5R10), except with the unusual falling happening at the same time.
      • During the trick, the PC will have a one-frame window of control and INV every time they enter LIFE 549. Entering it twice while holding down LEFT or RIGHT will cause a 90-degree rotation in that direction.
      • The trick is the easiest to explore in the E6R6 spider pit. A position halfway down (at Y = -2010) inside one of the four surrounding colliders is possible to clip into even without warping. The pit is concave in shape so the PC can go around the entire pit.
      • In theory, and again keeping in mind the hover can give you the same outcome, this could be applied in a situation where you want to climb up onto a collider but not in the most readily reachable position, instead "carrying" the climb through to a different part of the same collider or to a different collider of the same height. In order to delayedly complete the climb, you have to get lodged into another collider first to stop the falling, which can be done through sideways clipping as the offset for the climb ANIM kf6 is big enough. This is shown in the Floppy Version Climbing Weirdness demonstration when climbing out of the spider pit in E6R6.
    • You're able to throw something but with the throw failing, and picking it up again before the throw animation finishes because you fall through it.
    • Starting to aim a gun or perform some other SPACE action immediately after climbing causes the game to render you a tier up temporarily for some unknown reason.
    • Because in the floppy version, the player_climbing_end ANIM is interruptable (second parameter of an ANIM_ALL_ONCE command), once the PC's coordinates have been moved up in LIFE 552, if the ANIM is interrupted (as by doing something in the inventory), the PC will fall down again because nothing has nudged them actually onto the ledge ahead.
  • You get some free movement during the climb if you start climbing with some amount of free space in front of you. The movement will be parallel to the ledge you're climbing. Because of this, you have to think of the very most optimal lines when moving around such spaces as being something other than exactly straight.
    • If there's another collider on the side that ends where the collider you're climbing is, this movement can also be gotten at a delay, after the climb, simply by it adding up to being enough to take you past that collider then. The furthest angle at which this can still work is something a little past 45 degrees away from the ledge you're climbing.

GRAVITY/FALLING

  • The five conditions for falling are:
    • The actor's animated flag (0001) is set.
    • The actor's gravity flag (0100) is set.
    • The actor's ROOM Y is higher up than -10.
    • The actor is not colliding with a collider.
    • The actor is not colliding with an actor.
  • The collision checks are presumably done by trying to move the actor's bbox down a little bit and seeing if there's a collision then.
  • Falling is handled independently of the scripts by the engine. It's not dependent on entering the falling LIFE 551, that's just for handling it more gracefully including going to the landing LIFE 560 afterwards. Other actors don't have any similar scripts which is why their falling is so primitive by comparison.
    • The PC goes between LIFE 551 and 560 during the fall to handle any falls longer than one tier.
    • Falling while in a transition (with LIFE 550, see "VERTICAL DESYNCING") won't work quite the same. However, the movement itself is handled by the engine and so there should be only cosmetic differences.
  • You always fall for 2000 units before the conditions are checked again to see if you should continue falling or not. Nothing can stop the fall in-between. The falling speed is 2000 units per 40 ticks (at 60 ticks per second). The frame-by-frame movement is 50 units. If there's lag right at the end, it could be you lose some frames before the fall ends or continues.
    • The ZV Y value is being interpolated and is kept in the MODY field. The bbox is translated every frame by the MODY difference between the current and previous frames. The ROOM and WORLD coordinates are only updated at the end of each 2000 unit fall to stay in sync with the bbox. The RV shows those coordinates with MODY added to them, not how the game sees it.
  • Falling requires the 0001 (animated) flag to be set but it doesn't depend on actually having some ANIM.
  • Falling doesn't affect whatever desync you had before unless you've hit a transition trigger (see "VERTICAL DESYNCING").
  • In practice, you often have to go even higher than -11 above the current floor level in E5-E6 because of collisions with the collider or actor underneath. Setting your position 90 units above it is enough to always send you falling down a level, except if you're inside a collider when it sometimes will fail (e.g. at -1734, -2980, 6783 in E5R10).
    • If you go to E5R10 and stand on a bridge segment, going up 90 units doesn't cause you to fall, but going up 94 does. You're left stuck inside the bridge until you run out.
    • You can fall off the floor/water level by setting your ZV Y to -891. You can't reach triggers anymore. You can't fall again past that.
  • There seems to be no overlap between values that allow climbing and values that allow falling: e.g. -2979 is the last value that allows climbing and -2980 is the first value that allows falling.
  • There's a frame or two between landing a high fall and going to 0 hp before you've been "declared dead". You can access the inventory during this time and save yourself.
  • S/l-ing can save you from falling after a transition (while desynced) if you do it right before. There might be some other way to stop it as well.
  • When you fall in stairs without hitting a warp or transition, the fall simply continues until you've fallen down 2000 units. The stairs interpolation adjusts the Y position directly and the falling adjusts MODY. Because of the net effect of both, the Y position where the fall ends will not be 2000 units below where it started but somewhere a little higher than that instead.
    • If you do hit a warp during a fall, after the warp, the fall will resume normally until the timer has ticked up to the value when it's supposed to end, and then the stairs interpolation takes over.
  • If you fall from a height of -5000 or more, the fallheight VAR 206 becomes 3. -3000 to -5000 it's 2, otherwise 1. If your landing height is -3000 or more, subtract 2, if -1000 to -3000, subtract 1. A resulting value of 3 means the fall is deadly.
  • You can cancel the knockback at the end of a fall at least by going to the inventory.
  • Whenever you enter a new room with gravity on in E5/E6, if there's no floor underneath you where you touch the room trigger, you'll fall down a level. In one case at least, this can be used to get OOB but it's unlikely to be the simplest way.
  • There's an actor.FALLING flag, referenced in LIFE 549 at least.
  • The reason you only fall one level during a special falling death in E5 is the code in LIFE 549 sets flags 0041 (no gravity). The rest of the fall is cosmetic.
  • Quick falling: if you enter the inventory on the frame when you're first on thin air and come back, it'll skip a part of the fall (so long as Timer 2 was reverted early). If you throw or drop something on that frame, it can save you from fatal falls. You can also simply save and load the game (or lag it) to skip a part of the fall at a time, simultaneously confusing the game about the height of the fall so you won't die to a long fall.
    • Any of these techniques can also save you from dying to pits in E5R0 and R6. Not from the crack though since the falling is just cosmetic in that case.
  • If you fall into a floor trigger (only the E5-E3 and E5-E4 triggers are possible to fall into, the E5-E6 ones are too tall), it causes a softlock on the other end as you're left in LIFE 551. This can be avoided by quick falling.
  • When you push the chest off the ledge in E5R10, it won't fall straight away even after it's just passed the edge, instead hanging in the air and then falling instantly if you push it more. You can then drop down into it, after which climbing back up might cause it to fall another tier, but not all the way. Just in general, it behaves in quite an anomalous way. This is probably because the time the falling started is set on the frame the fall flag is set but there is obviously a long time between then and when the falling proceeds in this kind of scenario.
    • It probably doesn't fall straight away because it isn't animated unless it's pushed.
    • If it's flushed, it seems to lose the "falling" flag causing it not to instantly fall upon being pushed again.
  • The sacrophagus and hook: To pick up the hook while falling, you can either land inside the sacrophagus so you're mostly outside of it, or land directly on the hook itself.
  • You can stop falling simply by standing on objects in the air. This is because it counts as an actor collision.
  • There's a frame on which you can jump right before falling. If you do this, the jumping ANIM is played (you will still fall) but you get stuck in the falling ANIM afterwards. You can't access the inventory through any of this.
  • Falling while climbing is possible in both versions. This just requires there to be both a pit and a climbable collider ahead and aiming the PC to face an angle that makes them step off the platform they're stood on during the climbing ANIM, which has a forwards offset. (more under "CLIMBING")


VERTICAL DESYNCING

  • Normally the center of the PC's bbox (ZV position) is always 880 units above their ROOM coordinates, in the middle of where the 3D model is drawn. If you manage to get your ZV to move downwards by a sufficient amount (1761 in the house or 1771 underground), you'll be able to pass underneath all colliders causing new shortcuts to be possible. This is difficult to achieve and has downsides as well. This section details how you could try to go about achieving this effect.
  • All the observed ways to desync your vertical (Y) coordinates between the bbox and the ROOM position are both related to falling while a stairs transition is taking place. Thus all the conditions for falling have to be met: gravity flag, -10 ROOM position or higher, no collision with colliders, no collisions with actors.
    • To get the gravity flag to be on where the stairs all are, you have to enter E5 and jump back to E3 or E4, after which it will stay on until the next visit to E5.
    • To get the character to be above the ground level, this can be done by moving backwards from the upper landing of a staircase after starting a downwards transition, causing the normal stairs Y-interpolation to become extrapolation. This can be done in the E1R7 stairs (either side) or E3R11.
    • The other two conditions are generally not difficult to meet for the "main" ways of getting the desync: there tends to be no actor collisions and the only collider collision is with the top step of the stairs itself which you soon end up stepping off or being sufficiently higher than so there's no longer a collision.
    • This method is discussed further under "main method".
  • The desyncing happens right as you've reached the last target point threshold which sets your coordinates. It can only happen once per fall. The desync amount is dependent on your modY at that moment and should thus be between 50 and 1950. 1950 is the greatest observed desync as well. At 2000, modY should be added to your ROOM coordinates and a new fall may start, and thus the desync is a modular function of how high up the fall started. If it started 2100 units above the threshold, the desync should be 100 units.
    • The desync amount can be manipulated by s/l'ing or lagging during or right before the fall.
    • The stairs interpolation is fighting the fall and will often (especially after lagging) end the fall without any desyncing having happened.
  • Why the desyncing happens exactly is unknown but possibly has something to do with how when moving up or down stairs, modY isn't normally used at all and thus the game expects it to be 0 during all such transitions. Something causes modY to be added to the ZV Y but not ROOM Y. The warp during the transition clearly only moves the ROOM coordinates first, and the difference between old and new coordinates is stored somewhere with the same offset being applied to the ZV coordinates afterwards, which would be why the desync is never removed by transitions.
  • Note that the fall doesn't end when the desyncing happens, but instead often continues through the transition, causing you to fall down a tier afterwards. This can be stopped by s/l'ing at the right time or possibly by lagging.
  • There is no known way to cause the desyncing to happen in the opposite direction. This is likely to be impossible seeing as the modY value isn't used during climbing or warping, or indeed when walking up stairs. Of course you could keep going until you wrap around the bottom but this takes inordinate amounts of time.
    • Being above everything could be even better than below potentially since you're not caught in any floor colliders.
  • The reason you start falling off most staircases that you climb when gravity is on is simply because the interpolation takes you to positions where you're not standing on any of the steps. This should happen more often if you start your interpolation as far back as possible. You'll generally only ever fall off some steps "naturally" when going up stairs, not when going down them, unless you manipulate the path so you're clearly standing on thin air at some point. There is no other known explanation except that the path you trace when moving up and down might not be quite the same. This kind of falling while climbing doesn't have any special effects since the interpolation just yanks you back up every time and you can't fall into the walk stairs threshold since it's above not below (unless you first manage to lag past the target point).
  • Falling outside of transitions and climbing don't add to or remove desyncing.
  • While desynced, coming from E2, after enough tries, I got the game to actually stop the E3 "walking down stairs" animation completely and I simply landed on the floor (after a reload). It's possible this requires more desyncing than is necessary for the basic usage. It could be an instance of quick falling.
  • The most immediate effect of being vertically desynced is the game's background masking fails and you appear behind things you're supposed to be in front of. This should be cosmetic only though it could have some impact on the 2D wraparound (see "2D WRAPAROUND").
  • If you manipulate Timer 2, it is possible to go back up towards where the fall started and delay it from finishing after an initial desync has happened but it doesn't seem to be possible to get any more desyncing from the same fall for some reason. The desyncing that's happened will stay.
  • You can drop some object on the floor at the spot where you're about to fall from to cause the fall to happen a little bit later (presumably dependent on the object's bbox height) and thus get a little bit more desync from it.


MAIN METHOD (E1R7 and E3R11)

  • In E1R7, vertical desyncing is as simple as walking backwards into the stairs triggers. This works because there's no commands to set the player's position and direction before entering those stairs (TRACKs 33-34), just the "walk stairs" command itself, and so the PC starts to walk away from the TRACK waypoint, which causes upwards extrapolation of their Y-coordinates thus meeting the -10 ROOM position condition.
    • After the first desyncing in E1R7, you'll walk down the other half of the stairs in E2, but you'll almost always end up hitting the stairs trigger in E2R2 immediately and climb the stairs back up again. One round isn't enough to pass underneath colliders yet. Two rounds should be enough for that. To actually proceed past the E2 staircase, you can try s/l-ing and whatnot and eventually the PC should stop just short of the trigger in E2, leaving you with control.
    • The E1 stairs give more desync per fall than E3 but this is probably a moot point seeing how much further away this is from E5, unless you could somehow get the gravity flag without needing to jump first, which there doesn't seem to be any code for. Also jumping isn't enabled by anything other than going to E5.
    • You should even be able to get a one-round 1950 desync in E1 if you're exactly aligned with the waypoint when you enter the stairs facing exactly E, because of the indecision bug that causes you never to turn. Now you can manipulate when exactly you'll fall (and thus the fall height / modY) by lagging or s/l'ing.
  • After hitting the E3R11 stairs, you have two frames during which you can get menu lag to move further in whatever direction before SPEED is set to 0, allowing extending movement that's based on ANIM_MOVE (and not uninterruptable). However, the interpolation hasn't started yet, so all this does is the game stores a different starting position for it, which means it's not directly useful but does lead into a very useful further application that is detailed later. Also the TRACK's fourth command is for rotating the player, which seems to stop the player's movement completely, even if the ANIM was uninterruptable. The only way around this is if you're already facing due E in which case the rotate command is skipped. To be facing E but moving W, the only practicable ways to move backwards in E3 are walking backwards (fails, movement instantly stops) and getting knocked back by the recoil from the rifle (or by getting hit by a monster, but this is very slow to arrange). You obviously first have to get on the E side of the trigger by either lagging or sideways clipping, or even just clipping through the corner.
    • You can adjust what the desync will be like by standing further away from or closer to the trigger when you fire the rifle. Other factors seem to include inducing lag and s/l-ing at various times. Lag might be generally bad if you're already moving down and good if still going up, but this isn't clear and there might be cyclicity.
    • After one desync, you'll probably fall down a tier (can be prevented) but in either case you're stuck in the ground. You can still clip S or N though. The best way to continue and get more desync so it's enough to pass underneath things seems to be to clip N, then E, then go around back to the trigger and jump through so you can repeat the process. This detour plus the time to pick up the rifle is something like 30 seconds compared to ignoring the desync strategy altogether, and not enough time can be saved afterwards to make up for it.
  • You need to be at -900 to hit the E3R11 trigger from the W, -600 from the E.
  • If you try to get a SPACE snap to happen as you're entering the E3R11 staircase initially facing the right direction (due E) before it, with the idea of trying to juke the game to making you turn the wrong way and get extrapolated upwards, you do get the SPACE turn, but doesn't seem any timing allows for a snap to happen after hitting the trigger, even when clearly having released SPACE before even hitting the trigger (and making sure there's nothing hindering moving in the direction of the snap). There wouldn't be any upwards movement anyway since the interpolation hasn't started yet.
  • If you do get a big enough desync to pass under colliders and try to get through to E6 and finish the game like this, you'll find that this isn't enough: you can't climb out of the E6R0 floor collider and you're stuck there. You're even much slower to get out of the floor in E5R0. To be able to climb out of colliders, you have to be within a specific range of Y-positions. -X979 is the first value that works. -X880 is the last value where X is 0, 2, 4, or 6. Thus the range is just 100 units for each tier. The difference from the ROOM coords is 1130 to 1031 units. The difference between the normal E3 ZV Y and a climb-enabling one is 1901 to 2000 units, or 1911 to 2010 from what it should be in E5-E6 (your bbox being 10 units higher up than normal) meaning you'll need two desyncs of 955.5 to 1005 if they're both the same size. Since modY will change in 50-unit steps, the only values that will give you enough desync in one fall are 1950 and 2000, but it looks like when modY reaches 2000, it's immediately added to the coordinates and reset, so the only possible value is probably 1950.
    • The biggest desync recorded from using just the rifle is 1550 units. This was the absolute best result after a great number of attempts, and was only achieved after s/l'ing and lagging repeatedly at different times and in different amounts, presumably manipulating when exactly the fall starts. Thus this method is not sufficient for getting the one-fall 1950 desync.
  • You might think you can jump up the stairs from E5 to E3 and use the uninterruptable motion from the jump itself to somehow get falling and desynced. This fails for the simple reason that in order to get the gravity flag from the jump, you have to be in LIFE 549, but hitting the transition takes you to LIFE 550. Thus you have to first land the jump (reach kf7) to get gravity, and only then hit the trigger. Kf7 is the last one of the jump ANIM and it has no offset. Thus you can't even try use a s/l snapback to get extrapolated upwards. There are other reasons why this can't really work as well.
    • You could try to lag the jump near the top coming from a kf4 transition, and you can actually completely skip past the target point so Emily starts turning to move back towards the top of the stairs. You can't enter the E3-E5 trigger to start the interpolation before the old TRACK has ended, which only happens after you've been warped to 0 ROOM Y though. At this point you don't have any W movement left anyway.
    • For the record, you could use a s/l snapback to hit the trigger again after flying past it.
  • Another try would be to somehow get the PC to turn around while in the middle of the "walk stairs" routine. Even if there is a small deviation left or right from an initial due E facing, it seems abusing this by lagging or doing a slt can only ever theoretically take you to face due N or due S, which doesn't make you move further away along the X-axis like it would need to.
    • Actually, what CAN happen is you start facing due E but turn a little bit S or N during the walk because you're not facing the target point directly. After this there CAN be small oscillation left and right. During such oscillation, you CAN get you past the 90 degrees angles and thus moving further from the target point. This needs to happen quickly, within the first keyframe (they all have length 20), so you can s/l snapback to where the movement started.
    • It does sometimes start this kind of adjusting turn very early into the walk. It's very difficult to know when you need to s/l and lag it because this has to happen on the frame before the rotation (unless the rotation takes more than one frame). Furthermore, even if you lag it at such a time, multiple things can go wrong:
      • You're in the middle of the first 90-degree turn and you only get a due S/N facing.
      • You're facing the exact direction the game wants you to face and so there's no turning at all if attemping to cause one on that frame.
      • You're started to turn the opposite direction and thus can't even reach the 90 degree angles.
      • Even though you've managed to turn further away, the game still interpolates you towards the target instead of giving a stationary turn. It seems which way you end up facing doesn't determine the direction of the lag snap, however, sometimes this doesn't cause problems after all.
      • Even after you've gotten the right kind of early turn, you have to never s/l because this will likely cause a slt back towards the target.
    • Saving and loading without any rotation doesn't seem to cause any changes in what can potentially happen next (this hasn't been observed anyway). If this is true, then there might be a problem with combining the snapback with rotation.
    • The s/l snapback that happens if you've saved during this process can help you stay close to the start of the stairs but retain any rotation that had started (it will be kept).
    • There's no particular reason you have to initially face the right direction with this trick except it might save time when it skips the automated rotation.
    • Because the walk path is a bit shorter if you start on the E edge of the trigger, a shorter distance is required for moving up the 11 units required for the fall to start. If you're at the start of the walk (Y = 0), if you were around the E edge of the trigger, you have to set a facing of around 195 in the Room Viewer (15 degrees past due N/S) to get the fall to happen. If you're on the W edge instead, this looks to grow to something like 200 instead so it probably isn't necessary to position yourself very carefully. These angles are bigger than can occur "naturally" and thus moving the PC as far as possible on the side at the start of the walk abusing s/l turning and no-clipping is probably the best bet.
    • If you do get a rotation past the Z-axis, you could try to lag instantly and hope it lags you forwards, which has been observed.
      • If there is ever lag movement that you don't want, it might help to save/load again to advance the game by a frame.
  • In the above trick, you can also first get a 90-degree turn just to noclip further N or S without having moved E at all in case that serves better as a starting point. This seems to require turning quickly, then advancing through the first keyframe with lag, during which you'll sometimes not move E at all. This seems to allow angles like 75 degrees to be faced meaning you could get 15 or more degrees past the Z-axis. Sometimes saving and loading at the displaced position just causes another 90 degree turn towards the middle, resetting the trick.
  • A successful attempt of the above, dubbed EXP 1, happened like this:
  1. Hit the stairs trigger from the E side near the N.
  2. Wait for the rotation to start, s/l.
  3. Slt to face due S.
  4. Lag to move further S. (You're now on kf1)
  5. Let the first rotation start then slt to face E again.
  6. Slt the next rotation so it goes past the angle where you would have rotated.
  7. Slt carefully so you go back past the same angle by as little as possible.
  8. After the next slt, you should be facing an angle like 115.
  9. You may have to s/l once more here to avoid unwanted movement towards the E.
  10. Now just lag forwards and you should be able to get far enough to fall off.
    • This attempt generally gave a consistent 750 desync (to -4140 or -2140), which isn't quite enough (need 955.5 per go minimum). Schenanigans (another s/l) got 650 to 850 desyncs.
    • With this method, you'll keep walking the stairs at the same time as falling, just like with the rifle you keep getting knocked back while you fall. The interpolation can stop the falling and prevent any desyncing as well.
  • To maximize the above trick's potential, you'll also want to lag forwards further after hitting the stairs trigger (run through the corner first so you're on frame 0 when you hit the trigger).
    • Last point N side that doesn't touch the trigger is (-885, 401). First point inside that's also in the trigger is (-600, 115). X-distance 285, Z-distance 286, total distance 413.8, angle 45 degrees.
      • This information may not be directly applicable to how you need to clip the corner to get to that point, because it seems you'll have to first run forwards, then start going sideways as well. If you COULD get to that point at the start of kf3 of running, and then somehow lag forwards in the due E direction for around 600 units, the resulting initial angle seems to be around 75. Lagging backwards with an angle of 345 makes you go up to -200 Y, over double EXP 1. If you first lag S to the other side, you can get the angle 110, which gives -270.
    • You might bet better results if you maximize how far to the side you end up being after the first lagging instead of how close to the finish point. Being outside the confines of the stairs colliders makes your angle be better than anywhere inside them.
    • Furthermore, if you can get far enough to the side first so that after the lag in step 10 of EXP 1, you're still on the same side of the target point, you can do another little lagging backwards at the cost of more time.
  • TRACK 42's walk instruction goes down to (250, 800, 50). The amount of Y-interpolation if you started from X = -900 is (800/1150) 0.696 Y/ 1 X. Thus to get to -1200 (approximately where the 1950 desync came from), you'll need to go W 1725 units to X -2625. If you started from X -600, the ratio would be 0.941 Y / 1 X. Thus you'd need to move only 1275 units to X -1875. If you lag as you enter the trigger to start from X -300, the ratio is 1.455 Y / 1 X. You'd only have to move 825 units to -1125. Emily's stair climbing speed is 238/20 frames, thus 11.9/frame. If you could instantly turn to move due W, you'd have to move for 70 frames (so around 3.5 keyframes) to get that far. To move 1725 units, it takes 145 frames (7 keyframes). If you had to move at a SW or NW angle instead (225 degrees), it'd take 205 frames (10 keyframes) to move 1725, or 98 frames (5 keyframes) to move 825 units W.
    • Experimentally confirmed Y/X displacement ratios: 0.696, 0.929, 1.453. So the theory should hold. They even seem to be approximately right after warping in the Room Viewer.
    • Looks like you can get at least as far as -200 X as your starting position (first make sure to run about 40 units inside the trigger before hitting ESC, then as you lag you can initially get the 633 kf3 run lag movement and resume, you should get some SPEED offset as well from going to SPEED 0, all before TRACKPOS 6 has been reached. At -200 X, you get a ratio of 1.777 Y / 1 X, and so you have to go W only 675 units, 57 frames (3 keyframes) worth. If you're moving SW or NW, it should be 80 frames (4 keyframes).
    • Afterwards you can lag forwards and save again. Now you go back in time (Timer 2 manipulation) to where the rotation started and you can get an additional forwards snap.
  • You can simply run deep into the E3R11 stairs trigger by first getting caught on the collider next to it while running. If this happens on the last frame (second to last?), you can get a full lag snap afterwards.
    • You can even get a second lag snap (having SPEED 5 but the LIFE 550 ANIM_MOVE) for another 371 units before the TRACK has changed your SPEED to 0. This can actually get you past the target point completely, to X 404, which might eliminate the need to do one additional lag snap and generally make things easier since you can then do a slt u-turn, lag past the target point again and be left facing due W. The slope at this point is very steep and only a few extra lag snaps are required to get enough height for a 1950 desync fall.
    • You can't get a SPACE snap after hitting the trigger, at all. That means the last chance for any snaps is right as you're about to clip the corner, however this will always send you back in the Z-direction and you won't clip it at all. SPACE snaps are probably useless here, though it is known that you can get another full snap afterwards so long as it shows that you're at the start of the next keyframe after hitting ESC after the SPACE snap.
    • It might be possible to start turning right as you're crossing the threshold and get a 1.4 degree turn from 91.4 to 90 to skip the rotate command. This should be possible because movement is given as per what your angle was on the last frame, and also it takes a few frames to get to that command anyway.
  • The jump through the staircase: it probably doesn't matter which keyframe out of 2-4 (5) you skip with the transition lag so long as you skip the whole keyframe because they all have length 10. The one that gives the least movement is kf2 but you don't really need all that movement anyway.
    • Do a few slt's at the other end making sure you always save at the beginning of each keyframe (so as to not repeat frames of the ANIM since you want it to finish as fast as possible). Land in a spot that gives a good setup so you have to run some 4-5 frames before reaching the collider on either side. This suggests something like (-1050, -25) might do. You could pull out the lamp here since you can't lag snap while doing it anyway due to the BODY change.
    • Run through the corner so you hit the trigger at around (-600, -15) and turn at the last moment (SPACE snap?) to face 90 degrees. You have to be at the start of kf1 now (probably as good as kf3 but less waiting time to get there). Immediately menu lag to the end of the keyframe and you should be at around (50, -15). This takes into account the two SPEED change nudges.
    • You can now even lag past the target point and do a U-turn, then lag the other way past it as one idea (without lagging to the side you can't get a really good angle seemingly, unless you wait one more keyframe hoping not the hit the trigger). In this case the first lagging should take you to just below 900 Y (952 observed a lot, whenever it went to 904, it snapped back to 800 without any desync) and when you turn back you should be able to lag to Y = 4 or so. Now reload so you're moving almost due W and this can give you the 1950 desync.
      • This is a total of 3 laggings.
    • Second idea would be to make use of the frame on which the game sets your SPEED back up to 4 before position has been stored: could get another 238 units of movement.
  • Using the s/l snapback doesn't seem to change the outcome of stairs even with gravity on with the snap moving you backwards. You're not extrapolated upwards and you won't fall unless you were already falling. One reason why this fails may be the fact that the interpolation hasn't started yet if you're still in your running ANIM. If you try to counteract this by taking that save file and waiting for the ANIM to change, then causing another snapback, this still fails. This seems to be because the starting position for the interpolation isn't stored until when the ANIM is changing, meaning any snapbacks before then just cause that starting position to be set further back. This was tested both in E1R7 and E3R11 (facing the right way initially). It was also tested walking into the stairs instead of running.
    • The ANIM changes because SPEED is changed by the TRACKs before storing your position.
    • Even if you kept the erroneous walk_stairs ANIM (holding SPACE at the top) and somehow managed to turn around to get back into the E3R11 stairs, you have the wrong stairs ANIM and so it'll change anyway. Furthermore the SPEED during the erroneous walk is 0, not 4 or 5.
    • This might be possible if you had an uninterruptable ANIM when you entered the stairs so the ANIM will last long enough (independently of the current SPEED) to be past the position-storing step of the TRACK. None of those ANIMs have movement aside from the rifle/damage knockback (which go the right direction anyway) and dying to the jelly. In the latter case, if you first got gravity, you could in theory come back, get that ANIM while door turning (wrong turning) to R11, enter the stairs, and snapback to start falling though this hasn't been tested. (It is made obsolete by other methods anyway seeing how slow it will be)
  • You could jump up and use slt's to go directly back down the E5-R3 stairs during the same jump. This doesn't have any special effects though. Even though you can get the jump ANIM to take you all the way past the target point (with lagging) and cause the interpolation slope to be inverted (further E moves you up), you can't possibly keep the gravity flag and thus cause a fall to happen. This is because you're already in LIFE 550 and LIFE 549 is where the flag is set on at the end of the jump, on kf7.
    • The best case scenario is to land (going to kf7 of the jump) on the same frame or a frame later than entering the trigger. At that point, you won't get any initial lag movement to take you towards the target point before the interpolation start has been set and so the slope will be too gentle to be useful.

OTHER METHODS

  • In general, you can also get desynced with any walk stairs command regardless of which way you were going in the stairs. Goto commands don't work since they don't warp you after reaching some threshold.
  • If you can start a stair walk interpolation further back while having gravity, the slope will more often pass more than 10 units above any colliders (the steps) giving more of a chance of falling off in case that's relevant.
  • If you do the rifle kickback trick or get hit by a rat while entering the E4-E3 stairs trigger (have to position yourself inside the staircase in a way that may require warping) facing due N so you're sent S after touching the trigger, it does extrapolate you downwards. The falling it causes is very similar to what climbing stairs while gravity is on looks like anyway, and you just keep getting yanked back up. Nothing of interest is happening.
  • You will also fall automatically when arriving at the bottom of some staircases with gravity on, causing you to always get stuck in the "water" after such a fall because none of the triggers reach down below the floor level. This will happen coming down to E2 or E3, or after climbing up from E4 or to E0. These falls can be prevented just by s/l'ing when you've just fallen below ground level (or possibly with lag). You fall down instantly when coming down from E0 if you managed to stay on ground level there (climbing up with gravity on generally results in falling and getting stuck), making that transition end quickly and leaving you OOB. However, you may have fallen down two levels down to ROOM 2000, and you may be greatly desynced, but this can be stopped.
    • In stairs where this doesn't happen automatically, you can try lagging and s/l'ing while near the bottom.
  • You can fall off stairs when descending them with gravity on and skip a large portion. Works at least in E4R0, E3R5, E2R2 and E1R6. This can get you stuck though.
    • When going down the E4 stairs, you can lag on the first frame to change your trajectory into one that might enable falling off without getting stuck. The direction is inherited from when you went through. If you mess with it more you might even end up falling through the floor. This happens at least if you get out of the stairs colliders completely.
      • This has caused a small (700 unit) desync. It's probably possible to get a much bigger desync from any staircase where this happens if the circumstances allow falling from high up enough.
  • You can also get a desync from lagging past the target point of a staircase while ascending it (it helps if you started falling down right before so it never reaches the target point, though once past simply lagging on every frame so the fall never starts works fine). This allows for a 1950 desync for sure, but does take ages to do. Using the Indecision Bug will help to keep facing the same direction once past the target point.
    • This kind of desync is actually possible to easily utilize because you're not falling below the ground level on the floor you're climbing onto.

ITEMS/INVENTORY/MENUS

INVENTORY/ITEMS

  • There are three requirements for entering the inventory:
  1. GameMode is 1 (otherwise it will exit the main loop after any input).
  2. FlagRedraw is 0 (otherwise inputs are ignored).
  3. FlagInventory (i.e. INV) is 1.
    • FlagRedraw is set to 0 most of the time, to 1 shortly during a camera switch, and to 2 after exiting the inventory/main menu, during the fade-in/fade-out. These times when it's not 0 explain why there are times when it feels like you should be able to go to the inventory immediately after some kind of loading but before something else has happened, but you can't.
  • List of all collectable items:

https://drive.google.com/file/d/0B6VbXOZpnj6ManpzSkVBN0Zzbkk/view?usp=sharing LINK

  • Each actor has a weight variable, initiated in OBJETS.ITD. It's set at 20 by default for all actors but only used for collectable items in practice, whose weights range from 10 to 200. The maximum combined weight you can carry is 700. maximum number of objects you can carry is 30. That is the size of the inventory array in the game executable. However, in practice, the game will prevent you carrying more than 29. If the game code is modified to force 30 items into the inventory, it works fine. 31 items, however, crashes the game as it overwrites the variable that contains the number of items in the inventory (which is right after the slot of the last inventory item). Also "Actions" is always present, so in practice you can only carry 28 collectable objects.
    • If you aren't holding any very heavy objects (such as the gramophone, pot of soup or heavy statuette), the 700 weight limit will generally not be met before the 28-collected-actors limit will.
  • When an object is added to the inventory, it's added to slot 0 if the inventory is empty. If it's not empty, all objects below slot 0 are shifted by one position, then the new object is added to slot 1. This is what keeps "Actions" (which is added in by LIFE 562 when starting a new game) up top.
  • You may want to use items like the fuel canisters straight away before they get shoved further down by other pick-ups. You could manipulate the order by dropping and picking things up again but it's difficult to imagine this being a net time save under any normal circumstances if it doesn't also accomplish something else.
  • Each collectable object has a FOUND_FLAG field. The number inside describes which actions are available in the context menu (e.g.: use/eat/drop). This field is initialized from OBJETS.ITD at game start but it is also updated later in the scripts (e.g after you drink a flask, the FOUND_FLAG is modified so you cannot drink it again).
    • The player actor's FOUND_FLAG field is used to set the available actions.
  • When you select any command given for a particular item in the inventory, that actor's FOUNDLIFE is played. This could change your INHAND variable (equipping the item), shown in the RV, which in turn makes that actor's FOUNDLIFE be looped every frame to e.g. enable swinging a sword when holding SPACE (see LIFE 130). The PC's BODY isn't always changed, so this change can be invisible.
    • Dropping or throwing something leaves you with "actions" INHAND instead of whatever you had out before.
      • Even if you can't successfully drop something, it's still removed from INHAND (and INHAND becomes actions) unless its presence is reflected on in your BODY. This is just based on how the LIFEs tend to be written.
    • Selecting different basic actions just changes INHAND to "actions" and sets VAR 90 to the right value so the actions FOUNDLIFE 561 knows how to handle it.
    • When drinking flasks, the INHAND and BODY are temporarily changed, then reverted to what they were before (usually). When drinking or using the jug, the BODY changes to 12 afterwards.
    • Thinking about when the best time is to use various items is advisable so you e.g. use a key while it's near the top of the inventory if nothing that you do between then and unlocking the door changes INHAND.
  • There's a delay of 5 seconds before the game allows the same item to cause another pick-up prompt to appear. This extends to any item regardless of how it's getting collected. The items do not affect each other.
  • A matrix test of using various items either one after the other or combined with other interactions was conducted, though there is no guarantee some idea wasn't missed. The most critical findings were copied over into the rest of the guide. See extra notes in the matrix test sheet.

https://drive.google.com/open?id=1PO6Nfgp6nB7fpUcPDnaKNnCshpij1Rjd LINK

  • Here is a summary of various items that are placed in the game world in an unusual way or have something unusual about collecting them:
    • Indian cover, two mirrors, false book, pot of soup, talisman: placed with the PUT command.
      • If you make sure to first drop or throw items once that are placed into the game world with the PUT command, they retain the 0080 flag after being placed and so they can be picked up again. This may require clipping or going OOB to be able to touch them.
    • Records, saber blade, dresser's key, sword, key to the dance hall: placed with the PUT_AT command.
    • Indian cover: separately gets the 0080 flag in LIFE 14 after being placed for some reason, however, its flags after being placed are still 0008 unless it had been dropped first.
    • Saber, saber blade, gem: deleted when placed.
    • Talisman: The bbox is sent down to 10000 Y by LIFE 521 after it's been placed and the first cutscene has finished. If you've dropped it before, it can be picked up if you warp down there or have sufficient vertical desyncing, or if you collide with it before the cutscene has ended. Otherwise, you can also first simply serialize it which resets its position.
    • Hook: can be picked up directly as well as by colliding with the collider around it.
  • If you do pick up one of the items placed with the PUT command after placing them, placing them again may have slightly unusual effects:
    • Indian cover: when dropped after placing once, the new BODY makes it look weird.
    • mirrors: nothing special but the same mirror can be placed on both sides.
    • false book: sample is played again.
    • pot of soup: can have some unusual effects on zombie 1 depending on its current state. They're similar to the ones achievable with ANIM flushing since the ANIM is left as the wrong one in either case. List of states and state changes:
      • LIFE 353 (roaming, should only ever spend one frame in this LIFE after pot has been placed once): sent to same LIFE with same roaming ANIM.
      • LIFE 353 going to 418: delays going to 418 for another frame during which the zombie is in TRACKMODE 2; had same ANIM already. (This should be impossible because of lag coming from the inventory from the last time you placed the pot)
      • LIFE 416 (sitting): sends zombie 1 back to LIFE 353 (roaming) where it starts following the deleted chair, thus becoming awfully confused and unable to proceed past the next LIFE 418.
      • LIFE 418 (homing towards chair): sent back to 353 for a frame.
      • LIFE 418 going to 419: back to 353 with TRACKMODE 0, then given TRACKMODE 2 again and back to 418.
      • LIFE 419 (rotating to sit): sent back to 353 then to 418 to cause extra movement before colliding with chair again. The SET_BETA is interrupted.
      • LIFE 419 going to 420: BODY and LIFEMODE will have changed, active monster count will decrement, moves in a straight line and gets stuck in 418 since the chair has been deleted.
      • LIFE 420 (starting to sit): goes through 353 and gets stuck in 418 with a different bbox in addition to different BODY and LIFEMODE.
      • If at any point the zombie was right next to the PC when the pot was re-placed, it plays the attack ANIM once first. If the BODY has already changed, causes the hotpoint vertex to be an immobile one.
    • talisman: The bbox is left as the usual one if picked up before the cutscene ends or reverted to it if dropped again. (This is the same as if you simply serialized it after it's been placed.)
  • In SS runs as well, if you need to do something in the inventory you can combine it with getting a door to open if you time it well. Saves 2 seconds if the door opens outwards (because Timer 2 gets reverted too early). If the door opens inwards, here's a few ideas:
    • Can't do any of the things that don't give lag snaps (except using the jug because of its own movement) or you won't have collided with the door when you return from the inventory. This means:
      • reading Necronomicon or De Vermis
      • throwing
      • dropping
      • In addition, choosing any action that changes your current BODY except for drinking the flask or jug.
    • Leaves you with shooting the rifle (too slow), drinking or using the jug: causes that ANIM to play out first, then the door opening ANIM as well so you're not really cancelling that one out, however you do have inventory access after the drinking or jug-using has finished (you have VAR 0 as 1 in LIFE 549). This allows you to do complete another action to restore control early. This could be dropping or throwing something, or using the jug again. Other actions that cause another ANIM to play out seem to be too long. Any such resulting states allow you to use SPACE actions though not move until the door has opened.
    • If you wait one frame longer before going to the inventory (i.e. not buffering it), and it's the first inventory visit of the session, Timer 2 won't be stopped and you can do another inventory action while the door opens. However, because you could have just used the free first inventory visit at any other time, it doesn't seem useful.
    • Reading the Necronomicon will be interrupted by the door finishing to open.
    • Because none of the above techniques cause the PC's door-opening ANIM to be skipped, none of them seem to save any time in segmented runs either (with inwards-opening doors).
  • The full inventory loop in the FitD code is found here.
    • The inventory goes from state to state like this:

Inventory State Diagram.png

    • Only states 0 and -1 allow the item selection to go up or down. Transition from 0 to 1 will start the fast scroll timer (see "Inventory Fast Scroll").
    • When entering the inventory, freezeTime() is called here, and when a new BODY has been cached, unfreezeTime() is called here.
  • Inventory Fast Scroll: Normally when you press and hold DOWN/UP to change the selection in the inventory, there's is a delay of about 40 frames before the faster scrolling starts. This is here in the FitD code. There is a way to skip that delay that has the same explanation as the actor CHRONO underflow except it's Timer 1 that's used. Timer 1 underflows whenever an actor's BODY needs caching after DOWN has been pressed.
    • To be precise, the inventory starts scrolling fast whenever any button on the keyboard (except for those that map to 'exit' or 'select') has been held for 40 frames (0.666 seconds) continuously. The key being held doesn't have to be the same through the whole countdown. If another key than DOWN or UP is being held, this suppresses one-by-one scrolling with those two keys. The fast scrolling mode causes the items to change at a speed of one item/frame and continues for as long as some key is registered in the "key", "joy" or "click" variables.
    • The fast scroll cannot be started the first time you enter the inventory after a cache flush since this always causes the "actions" BODY to be cached too soon to be able to release the key used to enter it and get the game to respond to pressing DOWN.
    • The fast scroll can only be done once per inventory visit.
    • In theory, the BODY caching could happen so quickly the timer does not advance in that time but the glitch seems very consistent at even 100.000 cycles.
    • Here is a simple way to reproduce the glitch:
  1. Reload some save file where you have several items in the inventory. Reloading results in a cleared cache. You can also use the CV's cache-clearing function.
  2. Enter the inventory. (This causes the "actions" BODY to be cached).
  3. Exit the inventory.
  4. Enter the inventory once more. Press and hold DOWN.
  • Sometimes if you get a fast inventory initially, it'll hang when you get to the item at the bottom of the part of the list that is initially visible. The hang time seems similar to the normal initial hang, and so this probably ties in with that. This has not been reproduced and it isn't known how it works.
  • Practical notes on the Inventory Fast Scroll:
    • If you repeat the same actions and pass through the same rooms and views, the state of the game's cache should be constant at all points. Thus also the times when the fast scrolling can happen are constant.
    • The BODY cache is cleared when you either start a new game, reload a save file or move to a different floor. The latter two are therefore good times to consider if you can use the fast scroll to your advantage during the inventory visit after the next one.
    • The other option is moving through enough rooms and views so some inventory BODY will be removed as the least-recently-used as the cache fills out. The code that selects what BODY to remove is bugged so this isn't always what ends up happening. See "cache item removal algorithm" for more. Whenever the cache-clearing code works as it should, it's usually useless for the cache scroll since the item removed is the last one down the list, unless that item has since been e.g. dropped or thrown and picked up again to put it at the top.
  • Sometimes holding ESC or ENTER hangs the game indefinitely until you release them, sometimes it just resumes the game after a short delay.
    • If you press ESC soon enough while entering a menu, it closes it quickly instead of hanging. The lag obviously isn't big enough to achieve anything. (See more under "LAGGING")
    • Holding the regular ENTER key tends to keep the game hanging while holding the keypad one tends to cause it to resume after a while.
  • If you go to the menu, select Save Game, then hit ESC, it takes you back to the main menu. Now if you hit ESC again, instead of resuming, it goes back to the Save Game menu. Hitting ESC again now takes you directly back to the game after a fade-out.
    • Aside from a small graphical glitch in the save menu (the thumbnail for the first save gets replaced by the current game view), no special effects have been noted, though you can lag the game the same way you can when resuming from the main menu view.
  • You can read books quickly by just hitting ESC instead of flipping through the pages.
    • Alternatively you can hit SPACE [and hold], which has the added effect of the game acting as if SPACE was held when you return to game, which allows you to retain INV after reading the Necronomicon.
    • This means you can quickly hurt yourself by reading the book over and over without the ANIM being played.
  • There is a unique global variable in the game called "ShowBeta". When you are in the inventory, that variable is constantly decreased. Objects in the inventory are rendered with a beta/yaw rotation angle equal to that variable. The same variable is used for pick-up prompts and the copy protection screen (floppy only) and presumably the possum shown after launching the game as well.
  • Most actor weights are logical and unremarkable. They usually stay the same but some are adjusted during gameplay using the FOUND_WEIGHT command. Here's a few notes:
    • The two flasks have weight 0 initially but are set to 20 when removed from the first aid cases.
    • The first aid cases have weight 40 initially but set to 20 when the flask has been removed.
    • The revolver has weight 0 initially, but set to 20 after being removed from the shoe box.
    • The shoe box has weight 40 initially, but set to 20 after revolver has been removed.
    • The firearms don't have their weights adjusted when shooting or reloading.
    • The Dance of Death and Opus Posthumous weigh 10, but the Blue Danube 20.
    • The lamp and oil cans all have weight 30 initially, with the cans set to 20 after being emptied, though only if you "use" them directly. Using the lamp's "reload" command does not adjust their weight. The lamp's weight is never adjusted.
    • The saber has weight 30 initially but set to 15 after breaking.
    • The saber blade always has weight 15 (but is only spawned after the saber has broken).
    • The biscuit box has weight 30 which is not adjusted after eating the biscuits.
    • All the readables weight 10 (loose sheets), 20 (Jeremy's notebook), or 30 (the bound books); the false book is heavy, with a weight of 50, same as the Indian cover and the gem from the Pirate chest.

ITEMS

  • Arrows are the only items and perhaps actors in general that have a height, but their ZV is on the same level as their ROOM Y instead of at the middle point of their bboxes. When you attempt to drop things, or when they've hit something in flight, they're placed at the PC's normal ROOM height. Thus their ZV Y being higher up insures there's room for the part of the bbox that's underneath the middle point. Because arrows have it at the same height, and their bbox height is 60, you can only successfully drop them if you're 30 units above any floor colliders yourself. Thus if you throw arrows in E5/E6 they may get pushed down to the water level and shifted horizontally across ridiculous distances regardless of their orientation until the game finds a free spot to place them.
  • The lamp can be pulled out for free (no extra inventory visit) whenever you light it up, however, this only works with the lighter and the E2 matches, not the E3 matches.
  • To quickly select "leave" in a pick-up prompt, just hit ESC.


FLASKS

  • You can postpone the flask-drinking if you have a SPACE action of some kind.
  • Drinking a flask while being knocked down/up stairs, when timed right, causes the flask to stay in your hand. In this state you can only rotate and execute the search command until you've gotten hit or used another item, which removes the effect. Drinking another flask retains it.
    • Looks like one of the effects is VAR 24 is left at 1 because LIFE 549 doesn't have the case for BODY 268, meaning the game thinks you're backing up. This is why you can't get doors to open. You also shouldn't be able to climb anything.
      • You can also get the flask BODY to stay if you get hit while drinking (after dying)? While drinking it and jumping?
        • Getting hit now can "knock the flask off" (because of the default case in the switch in LIFE 553). After the flask was knocked off I could suddenly jump by tapping BACK! Wasn't holding any other keys.
          • This has not been reproduced and it's unclear what led to it but it 100% happened.
  • If you start drinking a flask and something causes your ANIM to change (e.g. a door has finished opening, you've just fired a weapon), it causes the first IF clause in LIFE 59 to be executed a second time. Because the player's BODY on that time is the flask BODY, that's the one that gets stored in VAR 204, and thus the one you're left with afterwards. This is why you get stuck with the flask BODY under such circumstances. The easiest way to achieve this is simply to hit SPACE while drinking and having some SPACE action.
  • Drinking a flask (or jug) while jumping causes gravity never to be restored so you hover.
  • See "[NOT] DYING" for more information on flasks.


THROWING THINGS

  • THROW parameters:
1) ANIM when throwing
2) ANIM keyframe index when the object should be spawned
3) hotpoint index (indicates which 3D point of the actor model the object is spawned at)
4) object to throw
5) boolean (if 0, rotate the object to be throw 270 degrees gamma first)
6) HITFORCE
7) ANIM after throwing
  • THROW has an uninterruptable first ANIM like ANIM_ALL_ONCE.
  • Throwing happens between the LIFE of the thrown actor and LIFE 549. LIFE 11 has nothing to do with it even though it has some vestigial code in the end that looks like it relates to throwing.
  • Throwing has multiple phases during which the thrown actor goes through several ACTIONTYPE states. Specifically:
    • PRE_THROW: Rotate the actor to have the same alpha and beta as the throwing actor. The angles are just set to those values instead of performing rotations relative to the game world's axes. All actors except the arrows when fired are also given a gamma/roll value of 270 degrees (768 units). For this to happen, the 5th THROW parameter needs to be 0. Next, check if there is space around the player (happens twice, on kf0 and kf2 of the ANIM). If not, drop the object on the floor immediately.
    • THROW: Prepare the throw by setting some variables like player animation and speed of projectile (3000 units/sec).
    • DURING_THROW: move the object at constant speed until there is a collision with something. Then drop the object on the floor. If its roll was changed before, it's set back to its usual value.
  • Collision checks for objects having been thrown are handled differently from regular collision checks. In summary, the thrown actor is first calculated a new (candidate) position for based on the throw speed of 3000 units/s and the time elapsed since the last check (lag frames accounted for). Then a hitscan test is used with a box 400x400x400 units in size that starts at the thrown actor's last position moving forward 100 units at a time until it's collided with either a collider or actor (using their regular bboxes as shown by the RV), has entered a room trigger or floor trigger, or has collided with the thrown actor's new position. To check against collisions with the actor's new position, the collision box has size 200x200x200 units instead. The thrown actor's bbox is calculated from current vertex positions, i.e. is the same as shown by the RV.

Throwing Hitscan 1.png

    • If a collision is detected with a collider or actor other than the original throwing actor (whose ID is stored in the thrown actor's object alpha rotation field), the thrown actor is stopped at the position where the hitscan box is at that time. Same with room and floor triggers. Otherwise, if no collisions are detected, the hitscan box is moved forward again.
    • If a collision is detected with the original throwing actor, the collision is ignored and the check is ended. This is the basic reason why the PC can get thrown actors to tunnel through walls and other actors. In other cases, these collision checks seem to successfully avoid the "tunneling effect" which could otherwise exist in cases where the framerate is low or there's a lot of lag.
    • The RV may show as if the thrown actor has passed through the actor or collider before being yanked back onto the other side but that's just the new candidate position that is getting stored temporarily during the calculations.
    • In practice, when the framerate is higher than something like 15 FPS, the hitscan check doesn't do anything useful since it just collides with the new candidate position on every frame without there being any room for additional checks in-between. It's really mostly useful during lag spikes such as when returning from the inventory.
    • Because this process is not the same as regular collisions between actors and colliders/other actors, there's no possibility of sideways clipping happening to a thrown actor.
  • If a thrown object is at any time colliding with the throwing actor, collision checks are ignored and thus it can pass through obstacles. This is why you can lag such an object OOB or back IB if you lag the game at the start of the flight (in practice, you will want to induce lag on several frames in a row starting around when the throw sound is played), but the object has to start on top of or behind the PC for the collision to be there. This trick seems to work despite the collision seemingly having happened on the PREVIOUS frame in many cases.
    • For this same reason, you can throw things through thin doors and walls even without lag when standing right next to them. Even big objects can pass through though sometimes they fail the check and fall on the floor. The same goes for shooting arrows.
    • Any room or floor triggers will stop the thrown object (or fired arrow) unless possibly if they start in or are lagged directly into one. This explains why they tend to stop when thrown from room to room.

File:Throwing Something OOB.avi

      • Note that the thrown object has a collision with the PC even though it looks to already be ahead of her.
    • Even thick walls in E5/E6 can be thrown things through although this is very difficult.
    • It looks like the successful attempts were all or most after lagging the game a lot on both kf3 and kf4. This makes the thrown object first appear slightly behind the player.
    • The throwing actor's ID is wiped from memory if the thrown actor is ever despawned and thus this effect will end. However, it also won't continue flying to begin with when returned to memory.
  • When a thrown actor reaches the world's edge, it will have a new candidate position either partially or completely wrapped around. If it wraps around completely (shown on the right in the picture), this should generally not cause any glitching. If it has wrapped around only partially (shown on the left), the stop condition of colliding with the new coordinates is never met (similar to how collisions stop working when any actor goes to the edge) and the thrown actor's hitscan test wraps around with the consequence of either eventually colliding with some distant actor or collider (causing it to mysteriously teleport to its location), or softlocking the game entirely.

Throwing Hitscan 2.png

    • There are actually two things wrapping around. The hitscan box's position is a sum: initial_throw_actor_position + offset. So first the sum itself wraps around to the other side of the world but later, the offset component of the sum will wrap around independently of it. At that point, the check will resume near where it started (though the 16-bit space doesn't divide evenly by 100, so in reality it should do 25 different sets of checks before returning to the starting coordinates, meaning there's still a small chance a collision will be detected after the first wrap-around).

Throwing Hitscan 3.png

    • Evidently, the fact that the hitscan box also wraps around doesn't help to enable it to collide with anything (consistent with other collision checks done between actors, one or more of which has wrapped around).
    • This behaviour around edges can cause lagging even when it doesn't result in a softlock.
    • The softlock can be ended with the RV by warping the PC or another actor in the thrown actor's path ahead of it or behind.
    • In this image, the thrown actor's hitscan test will collide with the PC after wrapping around the game world twice (only if the total distance isn't too long).

Throwing Hitscan 4.png

  • The throwing actor's ID is stored in the thrown actor's object alpha rotation field.
  • Actors that get rotated around the gamma axis will look different depending on the direction they're thrown, though this doesn't affect their bboxes. As such, this is only important if using them to cause a 2D wraparound.
  • It seems to be more important what's behind the player and on their right side, and not as much what's in front or on the left for whether an object gets successfully thrown, probably because of where the right arm is during the throw in case that's where the object's bbox is "fitted". Despite this, the thrown objects appear slightly towards the left of both characters.
  • Because the space check is done on kf0 and kf2 and, if it's passed, the object is only thrown on kf4, you can use the time in-between to rotate to face another direction, including one where the game wouldn't have let you throw the object if you had already been facing that way at the start of the throw. Nothing stops you from doing that using slt's.
  • Sometimes throwing something is blocked even when it really doesn't look like it will be. Try throwing the bow due S in the N part of the study at X=-925 or so.
    • Whether something will fly over something or not (at least for the bow) is not set in stone and may depend on where you're at when you throw it. Sometimes the bow passes over the desk, sometimes not.
  • During the throw, the size and shape of the bounding box shown in the Viewer does not match the 3D model rendered in AitD. It seems the game calculates a new bounding box volume internally during the throw but does not update the actor bbox yet (so the bbox size shown in the Viewer is possibly incorrect, though the game mostly behaves as if that was the one actually used). It's only after the collision that AitD updates the actor bbox volume and the Viewer sees that. This might explain some erratic behaviour.
  • You can throw something into a spot near another actor so when it lands, the game tries to put it inside that actor and because it can't, looks for the next possible place on the same side, effectively causing it to teleport through the actor completely. The way it moves is back towards where it was thrown from. This is made easier because the bbox tends to change after landing to make it bigger (horizontally) and thus more difficult to fit anywhere.
    • You can also get thrown objects to be dropped inside other actors if they can't find a place to land. This probably requires a successful throw first, and possibly there has to be a collider preventing them from moving backwards from a position inside the actor. Works, e.g. when standing in E1R7 at 889, 2635 and facing 197.9.
  • If you lag the game just as the item is spawned into the game world, it might appear in a different spot than it would have otherwise, potentially causing it to not fall down having hit a room trigger (which it normally does).
  • Sometimes after an aborted throw, the thrown object has had COL_FLAGS 1 instead of 0. This shouldn't affect anything since the object isn't moving. Not clear why and when it happens.
  • There's a small shift in the thrown object's Y-coordinate immediately after the frame it's been spawned. Might have something to do with the throwing actor's hotpoint shifting.
  • Placing an item down puts it in the same ROOM Y position as you, whereas throwing it makes it fall all the way down if it lands on thin air. If a throw is aborted, the object doesn't fall even if it's on thin air (same effect as dropping it).
  • If you throw arrows in E5/E6 they may get pushed down to the water level and shifted horizontally across ridiculous distances. This is because some of the items have a ZV_POS that's lower down than it's supposed to be (which is half-way up their bboxes). With arrows the ZV_POS is the same as their ROOM_POS.
    • Other similar actors: first aid boxes – ZV 10 units too low so you can drop them from e.g. -4010 or higher but after landing when thrown, they're shifted; vorpal dagger – same ZV and ROOM, like the arrows.
  • During flight, an object's ZV, ROOM and WORLD coordinates may all be slightly out of sync, different amounts depending on the object. This is unlike to cause any major glitching since they're used for different purposes anyway.
  • The thrown object will be moved forwards based on Timer 2. Thus you can manipulate it to fly forwards faster by s/l'ing. Each cycle lasts one second. After initially appearing, it moves a bit less than 3000 units during the first second then settling on 3000/s after that.
  • If you throw the bow but the throw is aborted, its bbox will have a dynamic shape (e.g. 774, 126, 814). It always starts having that bbox even during normal flight. At the end of a successful flight, the bbox is (900, 125, 225) which is W-E oriented. Other objects all or mostly all seem to always have the same exact bbox regardless of the direction of the throw and whether it was successful or not.
    • It's easy to get the bow caught inside other actors because of this special feature.
    • The hitbox is determined at the very start of the throw and isn't changed by turning around during it.
    • Throwing it due S gives it its standard hitbox even if it never flies off.
    • This seems to have to do with the zvType as given in OBJETS.ITD. The bow's zvType is ROT (bbox takes rotations into account), which only the bow and the small mirrors have out of all the items.
  • During throwing, LIFE 549 gives you back control and INV on the frame when the throwing ANIM has finished. Thus if you're not in LIFE 549 at that point, you might be getting stuck.
  • OBJECT is set to 0 when an actor is thrown and back to 1 when it has fallen on the floor.
    • The boomerang method: since the only requirement to do so is usually OBJECT == 0, you can collect a thrown object from afar if you activate the code that first allowed picking it up (potentially abusing a wrong pickup, see "WRONG PICKUPS"). There don't seem to be any real uses in AitD, since there aren't any items that you have to throw into some trigger or such. As expected, it doesn't seem to be possible to collect a thrown item this way after it's already collided with something. If it doesn't collide with anything, it doesn't seem to affect the game state in any useful ways unless it was used for a 2D wraparound but needed to be retrieved afterwards.
  • Since a thrown object's animated flag is on, it can absorb hits from enemies. Just the action of throwing also changes the PC's HITFORCE and can cancel knockbacks as well, but this requires good timing (see "[NOT] DYING" for more on these effects).
    • A FLOW actor such as the BLOOD or DEBRIS actors, can in theory absorb hits as well.
  • If you throw something, the game lets you instantly pick it up again, whereas dropping it causes it to be on a cooldown for a little while.
  • Thrown damage: First the HITFORCE of the throwing actor is set by the THROW command, and this value is copied to the actor that is thrown a little bit later when the throw ANIM is on the right keyframe. See "FIGHTING" for information on the damage values.
  • The THROW ACTIONTYPE happens at the start of kf4 of ANIM 11 and lasts a frame or two.
  • When the thrown actor has been spawned, there's a frame when it's ACTIONTYPE is NONE before turning to DURING_THROW. If you warp the PC somewhere during that frame so the actor disappears, when you come back, its position changes to one that's right in front of the position the PC was warped to, suggesting this is an adjustment that happens each time.
    • It could be the DURING_THROW ACTIONTYPE specifically that adds the extra collision checking for thrown actors.
  • The PC throwing something will still throw it if they were warped into a transition. The ANIM isn't interrupted and nothing particularly unusual seems to happen as a result.
  • THROW flag: Thrown actors have a flag called "THROW" (not shown in the RV; not to be confused with the ACTIONTYPE) that is 1 during the throw and 0 after a successful throw. If the throw is aborted because there's no room, if you collect a thrown actor mid-air (you have to be moving), or if the actor is serialized during the throw, the flag is left at 1. During serialization, the actor loses its DURING_THROW ACTIONTYPE, which is presumably what causes it to be left hanging in the air. It can still be picked up.
    • If you get the lamp serialized, which requires leaving the floor altogether, the lamp no longer gets doused even if it's in water, until thrown again. This is because the douse code checks, among other things, that the lamp isn't in the air before it goes ahead with dousing it.
    • THROW is only checked in the E5/E6 scripts to see if the lamp should be doused and nowhere else.
  • When something is thrown, it loses flag 8 and gains flags 1 (animated) and 4 (redraw). After one frame it loses flag 4. After hitting something it gets flag 8 and 4 back and loses flag 1. After one frame it loses flag 4. Nothing unusual here. The item always has flag 80 (collectible).
  • Pregzt will hurl any object thrown at him back at you until placing the talisman. The game sends the object on the other side of the tree if you were right next to the sarcophagus/colliders because it couldn't place it at your feet and looked for the next closest place in the direction the rebound came from.
  • You can kill stuff remotely by throwing something sharp at them while OOB and just stepping into the trigger to load them in. If the object hits them right after the transition, it becomes invisible but is left where you'd expect.
  • Thrown objects usually have their HIT and COL both set when they hit something, except if it's the PC in which case HIT is not set. If it was, the player could get hit by the actor they're throwing.
  • During the flight, HARDCOL is shown as 0 until a collision occurs with a collider with flag 0008, at which point it changes to 1. After the flight, the field is cleared.
  • Normally, thrown actors have LIFEMODE -1.
    • The lamp has LIFEMODE 0 always, including when thrown.
    • The vase has LIFEMODE 2 when thrown so it can be gotten to break after hitting something.
  • If you just pick up the vase in flight, it stays in LIFE 68 but the OBJECT condition is never met until the next time it's dropped anywhere, when it breaks instantly.
    • If the vase hits a collider on the same frame as the PC moves into it, the PC can still pick it up and it won't break, presumably because the OBJECT condition in its LIFE 68 is not met. This only works if its slot is 0.


FIRING THE BOW

  • Firing an arrow uses the same exact command as throwing so the mechanics should be the same as far as the arrow is concerned. There is a difference in the LIFEs though in the sense that when you fire an arrow, you're given back INV afterwards by the bow LIFE 29 instead of LIFE 549, which means you can regain it even when you're not in LIFE 549. See "FRONT DOOR" for an application.
  • The infinite bow trick: When you fire the bow the PC enters the firing ANIM at the end of any loop of the aiming ANIM if SPACE and UP are held (this is in LIFE 29). If you hold SPACE at the end of the firing ANIM, the game changes the arrow pointer according to which arrows if any you have left in your inventory so the arrows are fired in succession. The arrow LIFEs themselves (LIFEs 30—32) cause the arrows to be deleted after hitting something. However, if SPACE is not held at the end of the firing ANIM, the pointer is never adjusted. For this reason, the same arrow will be fired again and again.
    • If you fire the same arrow again before it has hit anything, this causes the arrow to collide with and deal its 3 damage on the PC. This presumably happens because the fire command warps the arrow where the player is but the flag that stops thrown objects from initially hitting the PC has the wrong value to prevent that. The PC is not knocked back as a result.
  • Firing an arrow causes it to be spawned higher up, around -1502 Y, which is about -563 units above the elevation of a thrown arrow (-939). Some enemies that are possible to hit with thrown objects are not possible to hit with the bow (birds) since they aren't tall enough to be hit unless they're falling from above.

DROPPING

  • Dropping is done half by the LIFE of the object being dropped and half by LIFE 11 where the PC is sent.
  • A dropped object's orientation is always the same. All the angles are 0.
  • When you try to drop something, some kind of check is done for space ahead of the PC. Even if the drop fails, the PC is left with actions INHAND unless the item's presence is reflected on in the current BODY because of how the individual objects' LIFEs handle this.

SHOOTING/FIRING A GUN

  • FIRE command parameters:
FIRE parameters:
1) ANIM when firing
2) index of keyframe when the gun is fired
3) hotpoint
4) size of hitbox
5) HITFORCE
6) ANIM after firing
    • Rifle parameters: 12 1 19 150 6 13
    • Revolver parameters: 128 1 17 150 6 127
  • FIRE has an uninterruptable first ANIM same as ANIM_ALL_ONCE.
  • How the hitscan check is done: The hitbox size parameter given to the FIRE command is the size of the "radius" of a square-shaped hitbox that gets moved forwards a distance equal to the same parameter repeatedly during the same frame until the shot goes outside the [-20000,-20000];[20000, 20000] range or has collided with something. The area scanned is thus 300 x 300 units in size.
    • Thus if the shot is not orthogonal, the areas affected will have a jagged edge. Because of this, you can't necessarily tell if you'll hit a shot just based on the relative positions of the PC and the target, but rather have to take the exact distance into account as well. This is illustrated below.

Shooting at 45 degrees.png

  • You can shoot OOB or IB through walls if you're close enough to them. Also through other actors.

FIGHTING

  • Aggroed enemies (with TRACKMODE FOLLOW) first try to get to the room the PC is in, and only then home in on exact coordinates. That's why they're difficult to hide from and don't get caught everywhere stupidly most of the time.
    • More specifically, the enemies will home in on the center of the room trigger they think they want to reach next, which is probably based on some kind of list somewhere in the game files. The room the enemies think they need to go to to reach you isn't necessarily always the best choice in reality. Seen especially in E3.
    • The vortices and other non-clipping enemies especially do sometimes get stuck of their own accord whenever multiple room triggers are close together like around E3R9.
    • The monsters that track you seem to only do a direction check every few frames so their turning looks jittery. Makes sense as having multiple monsters active lags the game as it is.
  • Melee attacks by any actor are based on a command called HIT which defines a cube-shaped hitbox around a given vertex "hotpoint" in the actor's BODY. The hitbox appears on a keyframe defined by the HIT parameters and disappears after the ANIM has finished or if it hits something. It can only hit one actor even if the hitbox overlaps multiple ones on the same frame.
    • Hotpoints can be placed in places like the fist, foot or tip of a blade. You can see where each attack's hitbox is in the Viewer (rendered in red).
    • Because ANIMs (and thus the movement of the hotpoint) are tied to Timer 2, it might be possible to avoid enemy attacks by reverting or advancing Timer 2 during it.
  • THROW and FIRE are also used for attacks by the PC (in AitD 1). More under "throwing" and "firing a gun".
  • The command HIT_OBJECT is also used. It acts the same as HIT except no special hitbox is defined and instead when the actor collides with another actor, this also sets HIT and HIT_BY for the actors respectively.
    • More under "HIT_OBJECT".
  • All the attack types look for collisions with any currently animated actors (flag 0001). Thus attacks can get absorbed by other actors that are moving or animating, e.g. doors.
  • There's a check (e.g. IF DIST(PLAYER) <= 1500) in every roaming enemy's LIFE to see if the enemy is close enough to the PC. If they are, they start an attack.
    • The check is done by taking a combined X-, Y- and Z- difference of coordinates. Thus you can be closer to an enemy diagonally than orthogonally before they attack you. This also means some enemies (at least the pirate) will start attacking you before you're even close enough to get hit if you're facing them orthogonally.
    • Conversely, you also have an easier time hitting enemies if they're diagonally away from you, not orthogonally.
  • If you're in a different room than the monster, it can still start attacking you if you're close enough. In that case it won't turn to face you though, instead attacking in the direction where it thinks it needs to go to reach you (i.e. towards some room trigger).
  • Some monsters have attacks that are easier to dodge on one side than the other. Generally anything that looks like a left-sided attack is a left-sided attack and vice versa but in addition, there are non-obvious cases.
    • Easier to dodge on their left side: Vagabond (only the first swing can hit you)
    • Easier to dodge on their right side: zombie, knight (though it's a right-handed swing)
  • The rats' attacks are perfectly centered and hit anything anywhere around them.
  • Your sword and saber swings can go past enemies completely (even without lag) because of how small the hitbox is at the tip of the sword.
  • The code in LIFE 261 suggests there's no difference in the radii for the two characters' attack hitboxes. However, the ANIMs themselves are different, could have different speed, and the attack keyframe could be reached quicker or slower. Also where exactly the hotpoint (fist, foot or blade tip) is located relative to the center of the BODY affects things. You can obviously just reset the ANIM after hitting something so the length of the rest of the ANIM isn't critical.
  • All of both the characters' basic attacks start from behind them so you can actually hit something that's behind your back. This also applies to most melee weapon attacks.
  • If you s/l in the middle of an attack (your own or an enemies'), you can manipulate Timer 2 so the ANIM moves to the position at the end of the keyframe, thus also moving the attack's hotpoint to that position. This way you can instantly hit whatever was there. In some cases might also allow avoiding an enemy attack if you're positioned just right and repeating the trick as necessary. If both actors are attacking each other, you could get your own attack ANIM to make progress through the keyframes while the enemy is stuck in the same repeating keyframe if their keyframe is longer than any of yours.
    • Speeding up an attack this way seems to sometimes fail (the hotpoint is there but disappears before hitting the monster around the end point).
    • Note that the RV doesn't necessarily have time to draw the hitbox if it disappears right away.
    • You'd think you could get the ANIM to go to an intermediate point between starting and ending but after brief testing, couldn't actually get this to happen. It always either disappeared or went all the way.
    • This might really be the fastest way to kill something with melee attacks seeing as you might not have to get quite as close to them, though you do of course lose the time spent s/l'ing.
  • Emily's attacks:
left jab – 35 frames, attack starts 25 frames in, short reach
right hook – 35 frames, attack starts 25 frames in, long reach
kick – 60 frames, attack starts 30 frames in, medium reach

left swing - 35 frames, attack starts 20 frames in, short reach
right swing - 45 frames, attack starts 30 frames in, long reach
overhead swing - 45 frames, attack starts 30 frames in, medium reach
backing up - 70 frames, attack starts 30 frames in, short reach and move 582 units backwards

rifle aiming - 30 frames
rifle shooting - 61 frames, attack immediately, move backwards 845 units in 41 frames, then forwards 148 units in 20 frames
bow aiming - 20 frames
bow shooting - 131 frames, attack 80 frames in
revolver aiming - 20 frames
revolver shooting - 31 frames, attack immediately
  • Edward's attacks:
left jab – 50 frames, attack starts 20 frames in, short reach but move 91 units forwards in 30 frames
right hook – 60 frames, attack starts 30 frames in, long reach and move 258 units forwards in 40 frames
kick – 76 frames, attack starts 40 frames in, medium reach

left swing - 35 frames, attack starts 20 frames in, short reach
right swing - 40 frames, attack starts 30 frames in, long reach
overhead swing - 45 frames, attack starts 30 frames in, medium reach
backing up - 70 frames, attack starts 30 frames in, short reach and move 552 units backwards

rifle aiming - 40 frames
rifle shooting - 71 frames, attack immediately, move backwards 826 units in 51 frames, then forwards 138 units in 20 frames
bow aiming - 20 frames
bow shooting - 111 frames, attack 100 frames in
revolver aiming - 15 frames
revolver shooting - 40 frames, attack immediately
    • Summary: If you just consider how soon the actual attack starts, compared to Edward, Emily's left jabs are 5 frames slower but her right hooks 5 frames faster and her kicks 10 frames faster. The fastest attack overall is Edward's left jab or either character's left-side swings which can be reset every 20 frames if they connect immediately.
    • Each of Emily's rifle shots is 20 frames faster than Edward's but she takes 20 more frames to shoot the bow. Emily shoots the revolver 4 frames faster.
  • Gun aim ANIMs have to finish every time before they can be shot again.
  • Edwards's hit recovery is 5 frames faster than Emily's (55 vs 60).
  • Even though that ANIMs are the same regardless of which kind of melee weapon you're fighting with, the sword and saber's hotpoints are further and so you have greater reach compared to the daggers and knives.
  • It's even faster to fight monsters if you can stand inside them (e.g. when a bird crashes in) so they don't get knocked back. Same thing as them being caught in a corner. This way your attacks also connect instantly.
  • Weapon damage:
unarmed (any type) - 1
saber - 4
saber (broken) - 2
knife/dagger - 2
sword - 3
bow - 3
revolver - 6
rifle - 6
  • Thrown damage for melee weapons is always 2, including the poker and loose saber blade. The heavy statuette is also 2 damage. A thrown arrow deals 1 damage. The other items deals no damage when thrown.
    • Even if the thrown item deals no damage, it can still be used to stun enemies susceptible to this.
  • The saber has by far the highest DPS (up to 12) until it breaks.
    • The saber starts with durability 5 and degrades by 1 after hitting any actor, not just monsters. It can hit the same actor multiple times per swing, causing even faster degradation.
  • If you're aiming a weapon but don't shoot as soon as able, the aiming ANIM will continue to loop and no shot will be taken unless it's at the end of the loop, meaning the times when you can take the shot are quantized. You can of course affect the timing by hitting SPACE a bit sooner or later.
    • With the revolver and bow, the whole looping "aiming" ANIM can be skipped altogether if you just hold down UP and SPACE at the end of the drawing/lifting to shoot immediately.
  • If you're defocused and far away from the camera view (probably you have to be in a view that's not in the focused actor's current room's camera IDs list), if you try to execute a melee attack, the hotpoint stays stuck in place, always on the same NE side regardless of your angle. This is very likely because such actors are simply not animated, even if it's the PC.


ENEMIES

  • What we call them unless we find more authoritative sources:
Zombie
Bird (though they resemble Byakhees)
Nightgaunt (E1R7, obviously inspired by Lovecraft)
Vagabond (E2R0)
Settler (E2R1)
Indian (E2R1)
Knight (E2R2)
Jellyfish (E2R5)
Ghost -> Vortex (E2R10)
Jelly (E3R1)
Pirate (E3R2)
Dancer -> Vortex (E3R3)
Small Spider (E3R4)
Ashtray (E3R13)
Rat (E4R0)
Cthonian (E5R2, obviously inspired by Lovecraft)
Wasp (E5R8, E5R10)
Deep One (E5R10, E6R6, obviously inspired by Lovecraft)
Big Spider (E5R6)
Beetle (E5R9)
Pregzt (E6R6)
    • The game calls the monster behind the front door "giant blob" which seems like a bad option.
Enemy hit points and HITFORCEs
Bird Zombie Nightgaunt Vagabond Settler Indian Knight Jellyfish Ghost/Vortex Jelly Pirate Dancer/Vortex Small Spider Ashtray Rat Cthonian Big Spider Deep One Wasp Beetle Pregzt
10hp 8hp (E3R12) / 10hp (others) 15hp 10hp (-1 damage taken per hit) 10hp 10hp
1 dmg 1 dmg 2/3/3 dmg (three attacks) 2 dmg 10 dmg 10 dmg 5 dmg 5 dmg 20 dmg (5 dmg) 3 dmg 20 dmg 1 dmg 5 dmg 1 dmg 50 dmg 1 dmg 2 dmg 1 dmg 2 dmg 3 dmg (fireballs)
  • In addition: barrels 5 dmg; falling bridge segments (not E5R0) 1/2/3 dmg depending on how far they fall (one has the wrong value); falling boulder 10 dmg. The jelly deals 5 damage when hitting other actors but the PC never takes this damage because they're sent to LIFE 39 before it can be dealt.
    • At least the Japanese (3DO) version has slighly different values for some of these. Smoke deals 7 dmg, rats 3, and the beetle 3 at least.

PREGZT / BOSS FIGHT

  • Pregzt is represented by the actor with the ID 40 and starts with LIFE 518.
  • Notes about the Pregzt fight:
    • You can normally only rotate but not move during the first cutscene that lasts 3 seconds (CHRONO > 2). All activities are halted altogether during the second (which is essentially just an animation that plays while the game is otherwise paused).
    • If you get hit by a fireball or the Deep One during the first cutscene, it gives you INV, control and TRACKMODE MANUAL back early.
    • If you get hit right before the latter cutscene (where Pregzt is lit up), if you're still in the knockback animation, it cancels the knockback that normally happens afterwards.
      • If you start climbing before the second cutscene it cancels the knockback animation then also and just proceeds with the climb. The same should be true of any uninterruptable ANIMs.
    • If you throw anything at Pregzt, if the talisman is not placed, it will bounce off (done by the engine based on what actor's ID is given in CVAR 11). If you were right next to him, it may get placed on the other side of the tree, simply because of how the algorithm for placing thrown objects works, with you being in the way yourself. With the talisman in place but without visiting R5 first, any item will fall in the water. If you did come straight from R5, a lit lamp will light Pregzt up, and anything else will still fall in the water. If the hook is on the sarcophagus, it will block the flying lamp if it's thrown from ROOM Y -10.
    • Throwing the lamp at Pregzt off one of the platforms makes you miss. The highest ROOM Y you can have so the lamp still hits him is -1260.
  • If you start climbing before the second cutscene and make sure you've lagged the game during the climb, you may end up going visually two levels up without having stepped onto the platform at all. You'll quickly fall down one level (really just the graphics catching up probably) and then actually fall down into the water. Then you'll be stuck without being able to visit the inventory either.
  • The fireballs (OBJ275 and OBJ276) get spawned and launched by LIFE 518 and their respective LIFEs. There are only two actors alternating.
  • The Pregzt actor will keep turning to face you whenever loaded in. The fireballs have the command to copy his angle once after being spawned. Whenever you re-enter the room from a new angle, the first fireball might be fired in the wrong direction. Also you can just manipulate Timer 2 to affect his rotation.
  • The fireballs, when in flight, have HIT_OBJ ACTIONTYPE set in LIFE 523/525, and they deal 3 damage with a radius of 200 units. This effect only ends if the fireballs pass a CONTACT check with the PC or collide with a collider a certain distance from Pregtz. This is why whenever they collide with a Deep One, they will continue hurting it on each frame without exploding (dealing 2 damage each time because of the Deep Ones' "damage reduction").
  • The first two fireballs fired will have a different, rectangular shape (bbox default).
  • "Mystery fireballs": Pregzt will fire single fireballs at the PC when they enter E6R6 sometimes, without ever entering the big chamber where the relevant triggers are.
    • The conditions for firing a fireball are: PLAYER.ROOM == 6, PLAYER.TRIGGER_COLLIDER >= 0, player_hitpoints > 0 and CHRONO > 2 (meaning it has to reach 3). In addition, the fireball whose turn it is to be fired must not currently exist in the game world (i.e. has to be outside R6).
    • The TRIGGER_COLLIDER test is passed when the player hits the floor trigger going back to E5, which explains why the fireball switch (VAR 199) is changed as you're leaving. This is unlikely to have other applications in the game because there aren't any other conditional statements that allow being inside any whichever trigger. Usually the game polls for specific ones.
    • If you come from the E5R9 side, it doesn't instantly load actor 40 into memory. If you move between E6R0 and R6, it doesn't do anything unless the fireball was already ready to be fired, even though the CHRONO values visibly underflow. This is because room triggers don't affect PLAYER.TRIGGER_COLLIDER.
    • In conclusion, every condition has been met when the player leaves E6 for E5R11 after waiting 3 seconds, and provided the Pregzt actor (40) was in slot 0 so its LIFE will be processed first (this will be true if you enter E6R6 through E5R11 and haven't dropped anything with a lower ID). This causes LIFE 518 to launch a fireball, but the game simply doesn't have time to actually change the fireballs' LIFEs and position until the next time the player enters E6. At that point the fireball already has updated values in the objects' array. Re-entering through E5R9 also works.
    • Note that this glitch does not rely on a CHRONO underflow since CHRONO is reset when changing floors.
  • Sometimes Pregtz has been seen firing two fireballs in rapid succession (might have happened when respawning him in with a CHRONO underflow while another fireball had already been "queued up").
  • If you collide with the sarcophagus with the hook on it on the frame when you're also placing the talisman, it brings up the pick-up prompt and either skips going into the Pregzt cutscene if you leave the hook, or plays it out but you're not locked in place. In either case the rest happens normally.
    • Sometimes the camera will be moved to Pregzt before the prompt shows up, but even then it goes right back to the player afterwards.
    • Skipping the cutscene is probably based on Timer 2 reverting too early during item pick-ups. See "TIMERS" for more.
  • If you collide with the sarcophagus right before Pregzt is lit up, instead of the red color fading away after the cutscene, it will stay on the screen until entering and leaving the menu, just like the black can stay sometimes. Saving the game also removes the effect, but loading doesn't unless there's a fade-in during it, so you could transfer it to other save files. The item pickup prompt is also there though you can't see it. This never skips the cutscene, however, and also doesn't affect room/camera lag nor has any other obvious effects.
    • There is evidence that the screen being left red can happen in a slightly different way as well.
  • Reading the Necronomicon right before the [second] cutscene leaves you in a frozen state. Reading De Vermis just kills you.
  • When you hit Pregzt with the lamp (or another thrown object), this sets his HITFORCE to a new value (it was 3), but seeing as the fireballs use HIT_OBJECT and not THROW, it shouldn't affect how much damage they deal even if it is still possible for another fireball to spawn then.
    • Seems to only work after placing the talisman anyway.
  • There is just one boulder actor that keeps getting teleported to one of various spots high up after hitting the water.
    • You only take damage from a boulder if it hits you from above (HIT_BY). You can safely run or jump into them from the side.
  • Pregzt can't be defeated without visiting R5 first if you come from E5R9 because of the order in which the actors are loaded into slots. When you enter E6, slots 0-5 will have actors in them. The actor that douses the lamp (in E6R0, invisible with ID 284 and LIFE 534) is placed into slot 5. When you enter R6, no slots are freed up since all the actors have LIFEMODE 0 or 2, and so the Pregzt actor, though having a lower ID, goes into slot 6. Thus the lamp will get doused first before LIFE 522 has time to trigger Pregzt's death. This is why you have to go two rooms away to R5 to despawn the door in R0 that had slot 2, though it's only removed for a short time during loading for whatever reason. Its slot 2 is inherited by the actor in R5 with ID 271 which has LIFEMODE 1, and thus when you return to R6, slot 2 is freed for Pregzt and his LIFE is executed first. Indeed, actor 271 is the only reason the boss fight normally works at all.
    • The detour to R5 compared to running straight to Pregzt seems to only be about 8 seconds more, which places very strict limits on methods of replacing it with another trick.
    • You also can't seemingly get hurt in E5 right next to the E6 trigger, get knocked back into it, and get the blood to spawn in E6 to temporarily take slot 0. The blood is spawned right away in E5. Similarly trying to fire the rifle doesn't cause the debris to spawn in E6.
    • What you could do is drop any object you've picked up inside the house when you get to E6R0. They should all have IDs lower than 284. Then you go back to E5 and return to E6 again. Now the dropped object probably has slot 0. Now you just pick it up and go to R6 and Pregzt inherits the freed slot 0. This does seem fairly slow to achieve since the stone door is in the way on both sides.
      • The actor that's INHAND has slot 49 and is thus useless here.
      • All of the actors that you drop or throw (or cause to otherwise spawn in the world, e.g. playing a record or if it's a SPECIAL ID -2 actor) seem to have LIFEMODE -1 (except for the vase with LIFEMODE 2, which means it behaves the same, and the lamp itself with LIFEMODE 0, meaning it's completely impossible to despawn in E6) and evidently can't be despawned any faster than going to R5. Some actors have a different LIFEMODE given in the OBJETS file, e.g. the bow has LIFEMODE 1 but this won't actually be used when initializing them. You also can't throw an actor into a room trigger to get it to go to a different room (e.g. E6R1), you have to run there yourself.
    • In conclusion, there's no clear way to use inventory items to manipulate the slots faster than just going to R5.
  • There's the idea of trying to prevent the lamp from dousing when it hits Pregzt when Pregzt is not in slot 0. The lamp is clearly placed down on the floor on the same frame as when it's hit something. The code where the dousing happens has the following conditions:
IF an_oil_lamp_13.STAGE == 6 AND OBJECT(an_oil_lamp_13) == 1 AND ISFOUND(an_oil_lamp_13) == 0 AND an_oil_lamp_13.ROOMY > -1000 AND THROW(an_oil_lamp_13) == 0 AND oil_lamp_lit == 1
	SET oil_lamp_lit = 0
    • an_oil_lamp_13.STAGE == 6
      • The lamp obviously has to be in E6. The only observed way in which actors can move between STAGEs is in the player's inventory.
    • OBJECT(an_oil_lamp_13) == 1
      • The lamp obviously has to have been collected once.
    • ISFOUND(an_oil_lamp_13) == 0
      • The lamp can't be in your inventory if you've thrown it. You can try to immediately pick it up as soon as it's reached the water and this CAN cause it never to get doused (because the ISFOUND == 0 check is failed at least as well as possibly the ROOMY one) but Pregzt still isn't set on fire. This is because FOUNDLIFEs (such as those when you pick something up) are processed before LIFEs and because the collision fields store slots, not actor IDs, and so when Pregzt's LIFE is run, the actor that hit him is evaluated to ID -1 (the lamp has been removed when you picked it up) and not 13.
        • Conversely, if you try to pick the lamp up as it hits Pregzt while Pregzt has a lower slot than actor 284, you can't. Pregzt is lit up normally, suggesting that whatever had to happen for this has already happened by the time the opportunity to pick the lamp up is given.
    • an_oil_lamp_13.ROOMY > -1000
      • The lamp's ROOM Y during throwing is tied to the PC's. When the PC is at -10 ROOM Y, the lamp will be at -949. This means this condition will always be true during a throw off the water level. The PC's ROOM coordinates have only ever been seen going up or down in 2000 unit increments, thus the next position will be at -2010 and the lamp will fly at -2949. This is much too high: you can only be at -1260, no higher, if you want the lamp to hit Pregzt. In either case, the lamp seems to instantly drop down to the water level after the impact before getting deleted so this condition won't stay false anyway.
    • THROW(an_oil_lamp_13) == 0
      • There is no known way to make this 1 after the impact has happened. The only obvious scenario where THROW is set to 1 is at the start of the throw and while it can be made to stay 1 if the throw was aborted, it's not known how to make it stay one after a successful flight. If you're right next to Pregzt and throw the lamp then, it won't count as hitting him.
    • oil_lamp_lit == 1
      • This is a requirement for Pregzt to be lit up anyway. Also, as expected, it doesn't seem you could throw it, collect it after the flight and immediately light it up to avoid the dousing code while getting Pregzt lit up. The lamp doesn't get doused if the throw was aborted but this doesn't help.
    • In summary, there doesn't seem to be any way to avoid all of these conditions being true right after the lamp has hit Pregzt.
  • Another trick that was attempted was lagging the game right at the start of the throw in case this should e.g. stop THROW from reverting to 0 after the collision but this doesn't work either.
  • If you try firing an arrow (in slot 0) at Pregzt, then throwing (not dropping so it's not doused) the lit lamp, only the RV shows as if he got hit by the lamp, just like with the knight if you try to make the game think he's been hit by the statuette, but Pregzt is not lit up. Read under "Wrong actors" for more on why this type of tricks should not be possible.

CTHONIAN (WORM)

  • The Cthonian has relatively complex logic in LIFEs 458-465. It can be gotten into several different "error" states. From a speedrun point of view, the most important property it has is removing the boulders from E5R4, which just requires it to collide with them before you've moved into R9 (or R8) since going to either of those rooms deletes it.
  • What stops the Cthonian moving further south when it gets to the corner by R7 is either its TRACK ending OR colliding with actor 219 (the wall by the corner). Looks like most of the time the TRACK will finish just before the collision.
  • The Cthonian will always return to its post upon re-entering E5 until Pregzt has been defeated. After you've once led it around the corner it will no longer attack you or harm you after being respawned from the R1 side. It's completely tame and will follow you down the tunnel. However, when approaching it from the E4 side it will be aggressive again.
    • If you enter E5 through E4, its collisions are set off. Now if you return to E5 through the study, they won't be set back on, causing it to ignore the colliders and actors that normally stop it and go on a long southwards journey.
  • The Cthonian starts a weird sidewinding if you get it to attack you while in position past the boulders, dodging the attack. It would presumably go on ad infinitum if it weren't for another actor that was blocking its way.
    • Actually it just does about two more clip steps and stops if you use the Viewer to remove the obstacle.
  • The Cthonian only has HIT_OBJ when it's not in its attack ANIM. This is why you can clip against it during an attack. See the clips table for more on this.
  • The Cthonian doesn't stop at the end of the tunnel in E5R3 because it has its collisions off and because it misses the two waypoints it's supposed to reach. It doesn't rotate to face them because of ANGLE instructions its LIFEs have that override the turning on every frame.
  • The Cthonian's attack is easier to dodge after it's rounded the corner (radius of 1500 vs. 2000).
  • The Cthonian has a different bbox shape and size depending on its state, set by various LIFEs. It's the only enemy that has a distinctly non-square-shaped bbox.
    • The default bbox size is 1886, 2266, 5200.
    • When moving N, it's 2000, 1500, 4000. This is set every frame by LIFE 459.
    • When moving S before the turn, it's 1900, 1500, 4000. This is set every frame by LIFE 461.
    • When moving W after the turn, it's 4000, 1500, 1800. This is set every frame by LIFE 462.
    • The only discrepancy therefore is before it's started to move when going S (default values from the BODY) and the slight difference in the X/Z-sizes depending where it is, even accounting for the rotation. It's not clear what would have happened if the shape was always the same.

E2 ARROWS/AXES

  • There's really just one arrow actor and one axe actor that gets teleported back to the portrait every time it's hit something. The actors inherit their movement direction from the "previous" one, i.e. it's never reset.
  • When saving and loading, the arrow gets sent back just like the player would. When lagging, it generally gets sent forwards, but the exact amount varies depending on its keyframe position. The keyframe's length is 15 frames. If it's near you, this makes its behaviour more erratic. The general rule is always it's sent forwards and it rotates whichever direction your WORLD/ROOM coordinates (or the room trigger it's homing in on) are at. Even when it's moving straight ahead, you're vacillating between being slightly on its left and slightly on its right (because of quantized directions), thus it can sometimes randomly turn away from you as well.
    • Obviously all of this applies more generally for every actor (including the axe) that's ever in FOLLOW or TRACK mode.
  • The arrow moves in increments of 83.333... which is 250/3. This makes its speed 5000 units/second. Its ANIM has one keyframe that lasts 15 frames (for 1250 movement).
  • When actors/arrows following the player move from room to room, there isn't initially any rotation happening, presumably because it hasn't determined which way it needs to turn yet until the first frame processed in the new room. This can be used to enable segmenting without messing up the direction the arrow is moving.
  • Just moving from room to room yourself can affect the projectiles' trajectories a little bit because of the lag. Even more so if you have the focus and switch views at precise times.
  • If you leave and reenter E2 while an axe is flying around, the axe can no longer hit you. If it was an arrow, it still can. The arrow doesn't hurt you though, just disappears. You can clip against the axe, but generally only if you lag snap into it: presumably because the axe's bbox isn't big enough to hold you if you're right next to it.
    • Unless you "tame" the axe by leaving and coming back first, you won't get repeated attempts for more than one clip step.
    • Probably possible to clip against the arrow and axe even without "taming" them first. Could't manage to get into the corner near the knight in R2 deep enough to run through but it felt like it might well be doable.
    • There's a tiny difference between the arrows and axes in LIFEs 126 and 128 respectively. One has a conditional clause with the condition IF ACTOR_COLLIDER == 1 and the other one with IF HIT == PLAYER. Since the HIT field isn't updated without the HIT_OBJ state being set, the condition is never met after the actors have once left memory (the ACTIONTYPE is flushed). The HIT_OBJECT command isn't being issued every frame like most such enemies and that's why neither causes you damage. Also HITFORCE is not kept when an actor is moved away from the active array, explaining why those values can be different when you return.
  • If you can manipulate the focused arrow or axe to hit another actor such as the knight, the view will return to you.
    • You could get an axe to follow you, trigger an arrow and manipulate it into R7, then go to R8 yourself while the door isn't present and allow the axe to hit you there, restoring focus to you even though the axe didn't have it. This would be to get the study key from R7 (collider ID 1) first, then picking up the talisman.
  • If you leave E2 while the camera is following an arrow or axe and it's just entering a dark room, those VARs can be left with the wrong values. 70 is left at 0 (area lit) and 71 at 1 (player is in E2R0). This makes the screen be dark though this doesn't affect loading times or what actors are in memory. The effect is cancelled after you've either re-entered E2 or left another dark room.
    • This can also happen if you get a room/view desync so the view ends up in the dark library. You can now move around E2 freely without the game being lit back up until you return to R1 or R2.
  • If you get the axe to hit both you and another actor (with a higher slot) on the same frame, the HIT == PLAYER condition isn't met and so the axe will hit you on every frame. The arrow will work the same but only if the other actor it's colliding with has slot 0.

OTHER ENEMIES

  • The information here is presented in order of which room the first of a given type of enemy is met in the game.
  • You can get a bird to go OOB through a thin wall or door because its attack ANIM has a keyframe with a larger offset than its bbox size.
  • You can simply take stools and push them against the windows while the birds are coming in. This should prevent the bird from getting far enough through and you may be able to get it to be stuck.
  • You can get the E1R0 zombie to follow you around instead of resuming its patrol.
  • The three ways to trigger the bird in E1R4 are:
    • Wait sixty seconds without the timer resetting (saving and loading).
    • Pick up the vase (there's a two-second delay).
    • Just enter the room twice. This is actually the same as the first case, because really what's happening is the timer rolls back too far and causes ROOM_CHRONO to underflow into a big positive integer, thus meeting the sixty-second criterion. Note that you can't save and load between the two visits or this doesn't work.
      • The E1 bird's LIFE and LIFEMODE etc. change when you LEAVE the room, not when you enter a second time. Despite having LIFEMODE 0, it's still removed from memory until you return.
    • If you wait for the bird in E1R4 for 60 seconds, when it shows up, the active monster count goes up by 1. However, if you trigger it by collecting the vase, this doesn't happen. This count (VAR 20) resets anyway during all transitions and thus doesn't change the rest of the game, but it does mean you can (erroneously) get random noises or the "exploration" music playing with the bird still alive in E1.
  • The fact the Vagabond can clip through things is due to a special flag enabled for that actor. That flag is set using TEST_COL (see LIFE 122). The engine will ignore collisions between an actor and hard colliders when that flag is set (but not actor/actor collisions, that's why you can't clip trough it). Despite this, if you run against it or lag the game when it's close to you, it can easily end up clipping inside your bbox for some reason.
    • The Vagabond can, in theory, be used for sideways clipping, but it seems this is very difficult to do for longer than one step presumably because of how it itself tends to clip into the PC.
  • The Vagabond can be defeated by anything hitting it so long as the PC has the vorpal dagger INHAND.
  • The Vagabond can't move from room to room because it doesn't have the 0040 trigger collision flag.
  • Getting the knight to follow: If you are colliding with the knight on the frame when its first attack animation has finished, that's when it keeps following you instead of going on patrol in front of the door.
  • The knight can only be hit and aggroed if the player is in E2R2 unless it's the statuette that's hit him.
    • The knight's LIFEMODE starts at 2 but stays 0 after he's been aggroed once.
  • The jellyfish has two different attack animations, for when you're closer and further than 3000 units away. The jellyfish can be stunned.
  • The pirate in E3R2 is best fought by allowing him to start an attack on you and then hitting him after it's too late for him to block it. Alternatively you can fake one attack (e.g. right swing) and when he's blocking that quickly switch to another attack (left swing is best). He automatically tries to block whatever attack you've chosen with the correct type of block and only if he's in the right blocking ANIM will the damage be blocked.
    • Throwing things at him or attacking him with something else just causes him to somersault backwards.
  • You can get the pirate to come out of his room (E3R2) into R1 and through to R13 where he'll be stuck against the table. He just keeps moving in a straight line endlessly. More under "indecision bug".
  • The middle dancer in E3R3, when stood still, will react to being hit by something, but the others will not... but after they've started dancing, the middle one is the only one that does NOT react to it. This is just because the LIFEs aren't quite symmetric. There's no actual reason for this.
  • You can get vortices to start dancing as well just by playing the Dance of Death in E3R3 at any time. Differences between vortices that are dancing and regular ones:
    • They're following their respective TRACKs instead of chasing the player.
    • Since the vortices are immediately given LIFEMODE 0, they're present wherever you go in E3 even if they're dancing.
    • Different ANIM: the dancing ANIM makes them move for 60 frames, then stop for 60 frames etc. The chasing ANIM is faster than dancing and has continuous motion. They often slip outside the ballroom when they have that ANIM.
    • Collisions are disabled also in the TRACKs themselves. The TRACKs never end because the last command is to rewind to the start.
    • If they started dancing before 5 seconds had elapsed, they can't hurt you. Instead, if you now hit them, they'll become dangerous after a new 5-second countdown (without stopping before it). Even after this, they'll continue to follow their TRACKs.
    • If you first get vortices to follow you somewhere else but return to R3 and play the Dance of Death, they will try to return to R3 in their dancing ANIM. This will often fail depending how far they were and they might end up being technically in the area where R3 is but invisible. The reason isn't understood.
  • Aggroed vortices (ones that can hit you) can turn other dancers into vortices as well.
  • If you leave R3 (i.e. enter R2 or R4) while the VARs 112-114 ("vortex or dead") are 0, the dancers die. Thus if one of them ever turned into a vortex, this doesn't happen to them.
  • The E3R12 puzzle can be solved by placing the soup on either end of the table (there's two different triggers) or killing all the zombies one by one. There's also a bug that allows you to solve the room by getting all the zombies aggroed and afterwards killing just one of them. The doors are unlocked and zombie 1 (if alive) will home towards its chair. The rest of them remain aggroed.
    • Why this happens: The last set of IF-ELSE clauses in LIFE 421 require zombie 1 to be aggroed which happens when the doors have closed. This also sets "zombies active in E3R12" to 1 but when another zombie dies, this is set back to 0 (the game assumes the zombies attack the player one by one) and the first IF is passed. Next there's a series of checks for zombies being dead. However, when you aggro a zombie that's sitting, this erroneously sets their "dead" flag to 1 even if they don't actually die to it (LIFEs 396 etc.). That's why all the checks are passed and the room is "solved".
    • None of the other zombies that have aggroed once will de-aggro even after placing the pot.
    • The sitting zombies can be one-shot with either weapon.
  • Zombie 1 in E3R12: This enemy can be left with either LIFEMODE 0 or 1 depending on if you place the pot before it has aggroed. If you place the pot first, this sets the zombie 1 LIFE to 353 and ANIM 125, but does not change VAR 158 (zombie 1 aggroed; this shouldn't matter). The LIFEMODE is set to 1 (OBJETS.ITD default) since it now has a LIFE. If it first aggroes, it's given LIFEMODE 0.
    • If you placed the pot first, since VAR 130 (E3R12 solved) is now 1, LIFE 353 immediately sets the zombie's LIFE to 418 and makes it FOLLOW the chair (193), with ANIM 125 set again. If you're within 1500 units of it, the zombie actually attacks you once before having moved to LIFE 418, but afterwards the state is the same. You CAN get it stuck in the taking damage ANIM though, just by starting an attack that connects on the next frame after placing the pot. It can be revived by hitting it again. If you deal some damage ahead of time, i.e. allow the doors to close, hit it until it has 1 health left, then place the pot and get another zombie or so to help you hit it on a frame before or after you've hit it, this causes it to first move back to LIFE 353 and also be HIT_BY something on that frame. This kills it, except that since it automatically returns to LIFE 418 again, the code that deletes it and creates the bubbles is never run and it just lies there instead, still following the chair.
    • Zombie 1 can also be gotten to move endlessly after the pot has been placed and it's homing in on the chair, by moving far enough so the chair disappears. This is because the zombie is given LIFEMODE 0 when it starts to chase the player and this only changes to 1 when it sits down.
    • If you solve the room by aggroing the other zombies and killing one of them, wait for zombie 1 to have sat down, then place the pot, provided it has 1 or more hp, it starts to roam again with the BODY that has the chair in it. Since the actor it's trying to follow has been deleted, it just moves in a straight line. Again, it can be hurt or killed with the aid of another actor.
  • When the rats in E4 have been aggroed, they never de-aggro. After being aggroed, if you can deliver a blow of HITFORCE 10 or greater on any of them, rat 1 gets deleted. This is normally impossible.
    • The rats' bboxes grow a little after they've been aggroed. It's not clear why this is done. This can easily cause them to get stuck in the colliders. The shape is restored for a very short time when you re-enter the area and then set by the same LIFE to the bigger bbox again, which can, again, get them stuck.
  • The bird in E5R4 can't be stunned like the other birds.
  • You can defeat the purple spider in the tunnels just by leading it back into its pit, either that or into the water in the pirate maze room. It won't follow you through on the other side, but you CAN lead it into the secret tunnel and it gets trapped since it can't clip out like you can.
  • The Deep Ones "absorb" one unit of damage (i.e. receive one hp back during the same frame) and thus you can't hurt them with just your unarmed attacks. If you throw something at them that deals no damage, this will actually heal them.
  • The Deep Ones might be the only monsters the player can stand on top of. The birds are too low so there's no collision check when there needs to be. So is the wasp in E5R8. You can't stay at -6010 going towards the Cthonian because the room triggers are too low. You can't stand on the Cthonian anyway.


[NOT] DYING

  • Aside from all that is mentioned below, also consult the section about damage manipulation.
  • Damage taken by the PC is dealt in LIFE 553 based on a HITFORCE value copied over to the receiving actor whenever something has hit it. The player only stays in LIFE 553 for one frame instantly returning to 549. During this time, TRACKMODE MANUAL and control are returned to the player: thus on the first frame of 549, you also regain INV and are able to take action to prevent further damage from another source.
    • Because of going back and forth between LIFEs 549 and 553, the player can only effectively take damage every other frame even when it's from a continuous (HIT_OBJ) source.
  • You're immune to damage and the damage knockback whenever you're not in LIFE 549 or 11 since no other LIFEs have the code to handle damage.
    • Immune: dying (LIFE 39), drinking (LIFEs 59, 161, 164), transitions (LIFE 550), falling (LIFEs 551, 560), climbing (LIFEs 552, 559), already having been hit (LIFE 553, but this is only visited for a single frame), landing (LIFE 560) and other miscellaneous LIFEs. See "PLAYER STATE" for a full list.
    • If multiple actors hit the PC at the same time, the damage from the actor with the higher slot number is the one actually applied. See "DAMAGE MANIPULATION".
  • Health items: the three items for healing yourself are the two flasks in the two bathrooms and the biscuits in the kitchen. They heal you for 10 (E1 bathroom), 20 (E2 bathroom), and 5 (biscuits) respectively. The game doesn't cap your health so you can use them pre-emptively. The maximum obtainable by normal means is thus the base hp 20 + an additional 35 = 55.
  • Death manager: LIFE 39 is the one that contains the code for handling events after the player's death.
    • There are two ways to reach the death manager from LIFE 549: having below 1 hp or if ANIM == dying. Also if falling into a death pit in E5. Can also reach it from LIFE 38 (falling through the crack), 194 (De Vermis), 239 (jelly) or 550 (front door trigger). Depending on how you got there, the game may have set the death type flag (VAR 29) to another value. 0 – default when you're on below 1 hp; 1 - falling into the crack or in E5R0 or R6; 2 - De Vermis; 3 - jelly. Hitting the front door trigger doesn't change the value, only having been hit by the jelly itself does.
    • If you've survived some form of death, the death type flag is kept at whatever value it now has going into a "normal" death or hitting the front door trigger. This is why the death manager goes into the wrong code upon such deaths.
  • If you ever have below 1 hp and you're in LIFE 549, it will immediately send you to LIFE 39 regardless of anything. LIFE 39 doesn't have code to send you back to anything else. This is why cheating death requires either staying at (or returning to) above 0 hp, and/or interrupting LIFE 39 by triggering something else to happen first, but you still normally have to heal up to restore normal functions. That something else is generally going to be a transition but it could also be reading the Necronomicon or De Vermis, so long as you've been left with your INV so you have some way to influence what happens next (it's easy to get into softlocks too). Similarly, you can just drink flasks and/or eat biscuits to get back to above 0 hp.
  • You can also block an incoming attack if you get hit on the frame when you're entering the inventory if you then proceed to drop or throw something. Drinking a flask also works, but that works regardless of the exact timing, and reading De Vermis also works by taking you to LIFE 39. More on this kind of stuff under "damage manipulation".
  • You can't take damage while drinking a flask because there's no code in the drinking LIFE 59 to take the player to LIFE 553.
  • You can make the drinking animation last longer by hitting/holding down SPACE. This causes the ANIM to alternate between whatever your SPACE action was before you started the drinking, and the drinking ANIM being set by LIFE 59. If you had jumping selected before then, the character will jump every time you hit SPACE. This means you can technically move around invulnerable while in E5/E6 but unfortunately you can't rotate until you've finished drinking barring some further shenanigans to restore that ability.
    • Having hit SPACE causes you to keep the flask BODY afterwards, which can be a problem seeing as this prevents basic movement. You have to e.g. drop something to regain it.
  • Going to below 1 hp causes CVAR 15 to change to 1. This only has the effect of disabling picking items up off the floor, obviously intended to prevent weird situations when you're dramatically slumping to the floor. This includes the wooden block in the cellar. There's no code that turns CVAR 15 back to 0 so the effect is permanent. This doesn't affect other ways to pick up items.
    • To "resurrect" yourself, the simplest thing is to just drink a flask. You will survive but be in a zombified state. To get out of it you have to select some action first.
    • This could very theoretically save time like this:
    • You die but instantly hit a transition to cancel the animation while also using healing (which happens instantly).
    • Now you can proceed to run over every other item in your path and not be bothered by pickup prompts. This obviously only works if you didn't need to pick up anything else by running over it.
    • Doing the resurrection with the lantern INHAND will make dark spaces be lit even if you're no longer holding it. Might require having been in a usually-dark room at the time.
  • Having PLAYER_TRACKMODE be 0 (as it often is in various "dead" states) means LIFE 549 doesn't give you options for movement. However, if you're holding SPACE, you can still move backwards with the help of a melee weapon or the rifle because those ANIMs happen based on the item's own LIFE, not LIFE 549.
  • "Slow" death abuse: after taking mortal damage you can still perform certain kinds of actions (instantaneous effect ones that don't require an ANIM to play out) such as wielding an item or placing an item into its slot (e.g. the saber into the coat of arms). No known uses.
  • The crack defy: There's one way to completely "shrug off" death by the crack in E1R1. Mostly when you hit it, you're sent into an ANIM for falling with whatever BODY you had at the time. The way to survive it is to either be drinking a flask when it happens (e.g. simply lag onto the crack through the inventory while selecting drinking it so you're already stood on it when you start drinking), equip a bow having an arrow in the inventory, or equip any dagger or knife. Any of these work because the game doesn't have a case in LIFE 39 where VAR 29 == 1 and PLAYER.BODY == 268 (flask). Nor for BODY == 31 (bow with arrows) or 118 (dagger/knife). The game gets stuck in the LIFE with nothing happening, except the current ANIM keeps playing. Thus, to restore control, first make sure the way is clear on either end of the corridor for you to reach any staircase, then you just have to be running or otherwise moving when you hit the crack (not possible with the flask method), and the ANIM can take you to the staircase. When you've climbed it, you can keep playing normally. The death type flag is left at 1 but the crack doesn't actually do anything to your hp.
  • For some reason, if you defy the crack, this causes the medical cabinet in E1R3 to open at a delay whenever searched, with the PC first going into ANIM 4 instead of straight to ANIM 8 like they're supposed to. No idea why. This doesn't seem to be possible to replicate by just changing either VAR 29 to 1 or CVAR 15 to 1. The other time when this effect is seen is if you open the cabinet door and leave the room before it has closed.
  • Aside from LIFE 39, there don't seem to be other LIFEs with missing BODYs causing unexpected effects. Only a few LIFEs (39, 549, 553) have a player BODY switch to begin with. In LIFE 553, it is missing the flask LIFE but it has a default case to cover that anyway.
  • Getting a normal death after defying the crack has interesting effects:
    • The general result is a death by base jumping right through the floor.
    • One example: Getting to 0 hp, then drinking a flask, the flask body will stay even though you have a dagger INHAND. Then getting hit, the flask will vanish. In this state, you can attack as if you had the dagger out but no hitbox is created, presumably because the correct hotpoint vertex is missing.
      • You can also get the normal BODY to be left with dagger INHAND.
      • Works the same with the sword INHAND.
    • If you interrupt the fall-through-floor animation with a flask, it just yanks you right back up to the start of the ANIM. Equipping a dagger mid-fall leaves you suspended at the lowest point (i.e. at then end of the ANIM). If you've done that, you can proceed from there into dropping or throwing something (the latter can only be done once). At any point, until some action causes you to lose inventory control, you can switch what's INHAND (including to the basic actions) and this causes you to reverse right back up, but you'll also proceed to fall again every time you do it. De Vermis just makes you freeze at the bottom.
      • Equipping the dagger, then reading De Vermis gives you the De Vermis death but behind a black screen... Necronomicon also gives a black screen but you fall to your death behind it. However, in the latter case you have inventory control and can save yourself.
    • If you switch actions while waiting for the falling to happen (e.g. you s/l during ANIM 266 then immediately switch), it instantly sends you to the game over screen. This might be related to quick falling.
  • Surviving the bottomless pits: Drinking the flask or dropping something while on the bridge works, rendering the fall harmless since you're not in LIFE 549 where the check for starting the lethal fall is. Doing either of those or s/l'ing a few times on the first frame when you've stepped onto thin air works as well, as does s/l'ing on every frame as the bridge segment is falling off from under you. Similar techniques work for the other pit in E5R6.
    • Surviving a long (6000-unit) fall: similar techniques will work. By s/l'ing during the fall, you reset the fall height each time and thus render it safe. It won't ever go into the landing ANIMs at all either. Also you can abuse the fact that the INV is usually available on the first frame after landing (at least) so you can go and drink a flask to save yourself (your hp will have went down to 0 but the flask brings it back above it while also sending you back to LIFE 549 even if you'd already moved to LIFE 39). The biscuits can also save you if you didn't go to LIFE 39 already.
  • You can technically survive being on zero or negative HP: this involves first getting the De Vermis (type 2) death without dying (e.g. read it while entering a staircase) after which you e.g. read the Necronomicon. Afterwards you should be left with the inventory, meaning you can select jump/push and move around the place like that even though you're locked out from basic movement.
    • In this state, monsters will generally not attack you, just follow you around. The exceptions include at least the jellyfish (doesn't do a hp check).
  • The default death ANIM slightly nudges you forwards, which can be used to get inside some trigger after collapsing on the floor, e.g. causing a transition.
  • One way to get custom "The End" screens: rifle yourself backwards or jump into the E5 stairs (any staircase really) – before transitioning, quickly read De Vermis. Now any time you die in the deathtype 0 ways it softlocks with "The End" on the screen. You can even access the inventory.
  • You can technically underflow your health in AitD: one simple way to do this is to get a flask and go to E3R13. Just start drinking the flask and interrupt it with SPACE so it never finishes. You keep losing 5 hp at a time to the smoke until you reach -32768 and wrap around.
  • If you're ever sent to E6 after dying (the zombie cutscene), you will unfortunately lose INV even if you had it while dying.
  • If you die right next to the sarcophagus and place down the talisman right before being placed on it, the cutscene is played out after which you get your INV, VAR0 and TRACKMODE back. If you now drink a flask, you can resurrect yourself. You're stuck in the sarcophagus though.
    • If you had enough vertical desync before this, you can get out of the sarcophagus but your ROOM coords are still below water level and thus you can't hit triggers, nor are you able to even hit Pregzt with the lamp.
  • The reason just eating the biscuits to bring yourself above 0 hp and dropping something doesn't restore you to a normal state is because you'll be stuck in LIFE 11 with the dying ANIM 261 which doesn't have any code associated with it.
  • If you change BODYs in the inventory during the dying ANIM, it keeps resetting the keyframe each time and delays death indefinitely. The same should be true of any ANIM.
  • You can avoid an attack by lagging the game through the last keyframe of the attacker's attack ANIM, which makes the hitbox instantly disappear usually.


HITFORCE MANIPULATION / DAMAGE MANIPULATION

  • The variable the game stores an attack's power inside is called HITFORCE. There's one for each actor. However, the same variable is used for damage the actor is about to deal as well as damage they're about to receive. Because of this, there is a possibility to cause the values to be wrong.
  • Ways to influence an actor's HITFORCE:
    • Actor starts a melee attack (HIT command)
    • Actor either starts a throw or is the object being thrown (THROW)
    • Actor starts to shoot (FIRE)
    • Actor is hit by anything, melee, thrown or shot (HIT_BY)
    • Thrown objects' HITFORCEs can be manipulated by the same means so you could e.g. make some object deal 50 damage if it first passes through the Cthonian's hitbox.
  • If you have three actors, A, B, and C, and their slot numbers correspond to that order, the collisions are checked like this: A-B, A-C, B-A, B-C, C-A, C-B. Any actor can only hit at most one other actor on the same frame. Remaining hits are not checked for. Thus if A hit B, A-C is not checked.
    • In this example, if A hits B and B hits C on the same frame, A deals its own HITFORCE of damage on B, but because the value was copied into B's HITFORCE, B hits C with A's HITFORCE worth of damage instead of whatever it was supposed to.
    • If you take two hits on the same frame, the last one (from the actor with the higher slot) is left in effect since that's the last one to overwrite HITFORCE.
  • When considering what exactly will happen when several actors hit each other around the same time, take into account that the PC is the only actor that will take damage only on the following frame because of going to LIFE 553 first.
  • The exact way in which HITFORCE can be replaced by a melee attack of your own goes like this:
    • You've been hit by something, which set the HITFORCE to their value. LIFE 549 detects this and sets LIFE to 553 for the next frame.
    • Next frame.
    • LIFE 561 is the ACTIONS FOUNDLIFE. FOUNDLIFEs are executed before LIFEs, and so if you're holding down the inputs to start an attack, VAR 0 is still 1 since LIFE 553 has not been executed yet and thus LIFE 561 will allow you to enter the clause where the HIT command is, thus overwriting the HITFORCE.
    • LIFE 561 is executed normally with the normal HITFORCE being used.
    • In case there's some kind of input delay, it could be you have to start holding the inputs a bit earlier.
  • Here are some practical observations about how damage can be avoided around the hit event:
    • If you are just getting hit and you're in the inventory on the frame when the hitbox overlaps with your bbox but has not disappeared yet:
      • leave with fighting selected or choose some melee weapon while buffering SPACE and an arrow key: starts a melee attack in time to replace HITFORCE
      • throwing doesn't work
      • dropping doesn't work
      • drinking works because there's no code for getting hit in the LIFEs this takes you to; similarly reading De Vermis though this takes you to LIFE 39
    • Coming from the inventory on the frame on which the hitbox has disappeared and your LIFE is 553:
      • melee attacks are too slow
      • throw something to replace HITFORCE
      • drop something to go to LIFE 11 instead of 553, but now you have to wait for the dropping ANIM to finish and are vulnerable during that time
      • drinking something still works
    • There is some chance some of this will work differently if the attacker's slot is 0.
  • You can cancel the damage from two attacks in a row by throwing something (harmless) on the frame that you've taken the first hit. The second one is cancelled when the thrown object hits the attacker (who has by then started a new attack), which means the throw can't be aborted due to proximity to other actors or colliders (see more under "THROWING"). The thrown object may end up falling behind the PC in search of a landing spot when this has succeeded.
    • Remember that since a thrown actor is an animating one, it can also simply absorb the attack by virtue of body blocking for you, doubly so if it gets slot 0.
  • If suiciding was ever beneficial, you could do it faster by manipulating the damage you're taking from enemies and hazards.
  • One theoretical way to manipulate HITFORCE is by causing an actor to leave and return to memory into a different slot, because it's not stored when the actor is serialized. This is observed when leaving E2 with the arrow and axe flying around, at least if another actor with a low enough slot was dropped so as to nudge the slots of those actors on the return.
    • Of course any ACTIONTYPE, including HIT_OBJ is flushed when actors leave memory and thus the attack will never be carried out anyway. More under "actor serialization".
    • HITFORCEs are never flushed from the memory slots when those are assigned to new actors, and so whatever value was there before will be inherited by the next actor loaded into them. This makes no difference since if the actor is going to do something that actually reads that information, the command (e.g. HIT) sets the value to the appropriate figure anyway. Also if the actor had the HIT_OBJ ACTIONTYPE, it, as any ACTIONTYPE, just gets flushed anyway and thus whatever HITFORCE it happens to get after the shuffling doesn't matter.
  • Note that HITFORCE being set to something doesn't by itself cause damage to actors (i.e. reduce their respective health point VARs). This is why most "living" actors have a clause in their LIFEs where the game checks for their HIT_BY and HITFORCE and acts accordingly. HIT_BY is reset every frame so the damage is only applied once per collision at most.

TIMERS / CHRONO / ROOM_CHRONO / TIMER MANIPULATION

  • The game has two global timers (Timer 1, Timer 2) and two per-actor variables (CHRONO, ROOM_CHRONO). Both the timers are updated 60 times per second. Only Timer 1 will ever roll back when saving and loading, explaining several effects related to Timer 2 like fast door rotations. Of the CHRONO variables, actors with no BODYs only use ROOM_CHRONO with the other one being reset constantly.
    • All timers should roll back after lag (such as when entering and leaving the menu or inventory) even though they keep ticking up during it, but in practice this doesn't always happen.
  • In the RV, the timers are frozen when their "frozen/saved" flag is set on even though in the game's memory, they keep ticking. A second timer is shown for each showing how long it's been frozen for.
  • ROOM_CHRONO and CHRONO as shown in the Viewer are really the calculated difference between Timer 1 and the actual static "roomChrono" and "chrono" field values which are set when an actor is initialized (copied from Timer 1). That difference is what the game uses whenever a script relies on either reaching some value. The values rounded down (counting full seconds) so e.g. if a script is waiting for CHRONO > 2, that means CHRONO has to reach 3.
    • Both CHRONO and ROOM_CHRONO are flushed if the actor is removed from the active array since objects don't have those fields, and thus no countdowns are resumed in such instances (but the countdown can finish instantly based on the Actor CHRONO Underflow, see below).
    • ROOM_CHRONO is also reset whenever the actor has moved into a new room though not when moving between floors. The PC's ROOM_CHRONO isn't used for anything anyway so this shouldn't matter.
    • ROOM_CHRONO is only used for the two birds in E0 and E1R4 to achieve a 60-second countdown.
  • The global timers seem to generally run a little bit ahead of the player's own CHRONO (and that of other actors), but there's no immediate effect to this seeing as the game never uses more than one timer value to accomplish any one task.
  • Timer 1 and the CHRONOs are stored in a 32-bit variable, Timer 2 in a 16-bit one. Because of this, if you wait 65536 / 60 frames per second = 1092 seconds = 18m 12s, Timer 2 will overflow back to zero. For Timer 1 it takes a lot longer, 828 days.
    • It seems that whenever either timer wraps around, it doesn't cause any anomalous behaviour. The game must have a way of handling these situations.
  • Even though Timer 1 is saved in save files, neither Timer is reset when you start a new game. Thus if you wanted to choose what value you wanted as you start the new game, you could. This shouldn't have any practical meaning though.
  • Timer 1 is mainly used for CHRONO-related things seen in the LIFEs. This is why those generally can't be sped up, except for the underflow trick (explained below).
    • Also seems to govern the smoke expansion (and maybe other similar effects). If you artificially move Timer 1 back, it causes everything to be covered in smoke for a little moment before the animation resets.
  • Timer 2 is used for moving actors (playing back ANIMs). The further the timer has advanced since the last check, the further the actor is moved (e.g. multiple frames at a time after lag). When you save and load, the actor is first placed in the right position (where the game was saved), and only then the animation engine is started, and the game sees that when the keyframe started, you were at position X a little bit behind you. Because the frames were reset, that is also the position where you are moved afterwards before resuming the game.
    • The actual ANIM (i.e. vertices visually moving) seems to advance to the end of the keyframe upon reloading, but it's paused until the full keyframe's worth of time has passed and thus you can't get ANIMs to move forwards any faster in practice, as far as movement is concerned.
    • Because an attack's hotpoint is tied to a particular vertex, Timer 2 manipulation can make that one move instantaneously.
    • Timer 2 is also used to determine how far a rotation of various sorts needs to have advanced. The actor itself stores the value of Timer 2 when the rotation started, the starting angle, ending angle, and how long the rotation should last, and interpolates between those angles from the starting time to the ending time. Saving and loading as this is happening causes only Timer 1 to roll back to the value when the game was saved whereas Timer 2 will not, and thus the rotation can normally only advance further with s/l'ing. This is why quick rotations work, and also explains slt's. Unlike ANIMs, there's no keyframe start parameter that gets reset, hence the difference.
    • The game considers rotations finished when Timer 2 has advanced past the end point of the rotation, but also if it's moved backwards to before the rotation started (because the difference is interpreted as a big positive number presumably). If Timer 2 is moved back just a little bit, so it's within the window of time between when the rotation started and when it's supposed to end, the rotation is continued from that position instead, meaning you can effectively delay it from finishing like this. This kind of manipulation can be done with a little effort, by saving the game, restarting it to reset the timers, then waiting in-game for long enough to be at nearly the time at which the rotation started before starting to reload the save file repeatedly until having hit the exact window.
  • Timer 2 is also used for interpolating falling.
  • In this hacked version of the game, Timer 2 has been set to increment at 1000 frames at a time instead of 1. All the keyframes are finished instantly and rotation happens in 90-degree turns only (the Room Viewer is used to adjust the angle at one point).

File:Timer 2 1000 Increments.avi

  • If you load the save file called "Crashes When Something Thrown.ITD", which has a lot of actors in the same place, it clearly takes more than a few frames for them all to load in. Here's a demo showing moving from room to room in E6 and then returning to E6 from E5.

File:Slow Actor Caching Demo.avi

    • Given how many frames it takes, it should be easily possible for T1 to increment several times during such a loading and not just because the background is loading for so long.
  • Theoretically, the timers can increment between any two instructions. However, it's very unlikely to happen. They're updated 60 times per second, or once every 16.666ms. During that time, if the game is running at 11000 cycles, the number of cycles (game instructions) that will pass is 11000 cycles/ms * 16.666 ms = 183326 cycles. So the timers are only updated once every 183326 cycles. Something like initializing a single actor field (e.g. FLAGS) takes 2-3 instructions (cycles) maximum.
    • Still, in principle, it's believed sometimes something like the actor CHRONO underflow (detailed below) could happen at a time when it wasn't expected due to a precisely timed timer increment.
  • Actor CHRONO underflow: Any time there's timed events such as the birds crashing through the windows, nightgaunts dying or Deep Ones spawning/despawning (anything that involves CHRONO or ROOM_CHRONO), if you leave the room/view to despawn the actors, then return, it generally causes those timed events to happen instantly because of those values underflowing for a frame or longer (will only happen on sufficiently slow execution, e.g. 11000 cycles). Meanwhile if you leave the floor, or save/load while the actor in question is not in the active array, this generally won't work (the countdown resets), and so you can't get the loft bird or zombie to spawn faster using this method.
    • A list of various effects this can have:
      • The most immediately useful effect is causing the bird to be initiated sooner in E1R4, allowing for the E1R4 bird camera defocus trick.
      • If you touch the ghost in E2R10 (or dancers in E3), it starts a 6-second timer (not 5 because the values are rounded down). The vortices have LIFEMODE 0, so you can't make the timer tick down instantly, even in theory. Meanwhile, you can abuse the resetting of the CHRONO every time you leave and re-enter the floor so it never finishes the 6-second countdown. This can be done even in practice with the E2 ghost.
      • The zombies in E3R12 have LIFEMODE 1 until they've started to move so they are possible to get moving faster.
      • The Deep One in E5 has LIFEMODE 0 so it's not possible, but the E6 one has LIFEMODE 1 so it is possible there. In fact, it happens automatically when you enter the room even for the first time (why this happens is explained below).
      • The two fireballs both have LIFEMODE 1 and sometimes you see two fireballs in quick succession, obviously caused by the underflow. Generally the fireball in rotation (using Pregzt's actor's CHRONO) will underflow when you enter the room even for the first time.
      • LIFE 518 and 521 are where the short cutscene is shown. There's a 2-second timer. If you could leave the room (the object that has the timer, Pregzt ID 40, has LIFEMODE 1) and return within 2 seconds, this could cause you to get control back sooner.
      • The oil lamp ticks down every 10 seconds. If you leave it somewhere, then leave the room and return, you'd think it could tick down instantly. However, it has LIFEMODE 0 meaning you'd have to leave the floor completely to make it disappear from memory. But that means it gets flushed from the cache as well, and then reinitialized, which is why this probably doesn't actually work.
  • More technical explanation of the Actor CHRONO Underflow: When you switch to a new room or view that causes new actors to be initialized, here is what happens most of the time, in the setupCamera() function called when either of those happens: The two timers get frozen and unfrozen multiple times by nested save/restore commands. The game has one flag for tracking if they're supposed to be frozen or not (although this doesn't actually stop them from incrementing, they actually keep ticking up the whole time, which is required for this glitch to happen), and one field for storing each of their values at the time when they were frozen. Specifically, the extra nested save/restore commands are called whenever a BODY or ANIM has to be cached during actor initiation.
save timer : oldtimer = timer (Timer 1, e.g. 0:01)

load background (takes a lot of time)

init actor 1
    body/anim (missing from cache)
        save timer (ignored because already frozen)
        load body/anim
        restore timer : timer = oldtimer (0:01)
    actor1.chrono = timer (0:01)

init actor 2
    body/anim (missing from cache)
        save timer : oldtimer = timer (0:01)
        load body/anim
        restore timer : timer = oldtimer (0:01)
    actor2.chrono = timer (0:01)

...

restore timer (ignored because already unfrozen)

when evaluating actor chronos in their lifes:
    timer - actor1.chrono = 0:01 - 0:01 = 0:00 //no glitch
    timer - actor2.chrono = 0:01 - 0:01 = 0:00 //no glitch etc.
    • So even though the code certainly doesn't behave in a logical way, it happens to avoid any problems (other than the timer not getting reverted like it's meant to after the loading is finished).
    • Because the "restore timer" and actorX.chrono = timer commands are right after one another, it's extremely likely they are executed on the same frame.
    • The above is simplified and is missing the same being done to the ROOM_CHRONO fields as well, and both a BODY and ANIM loading will have their separate save/restore timer events too.
    • The glitchy case where you see an underflow is when the ROOM/view switch doesn't cause any actors to be initiated that have a BODY or ANIM and didn't already have them cached.
save timer : oldtimer = timer (0:01)

load background (takes a lot of time)

init actor 1
    body/anim (already in cache in this case)
    actor.chrono = timer (has ticked up to e.g. 0:02)
init actor 2
    (same)

...

restore timer : timer = oldtimer (0:01)

when evaluating actor chrono:
    timer - actor1.chrono = 0:01 - 0:02 = -0:01 //glitch occurs here, same for actor 2 etc.
    • If the game is running much faster than normal (e.g. cycles = MAX and CPU = DYNAMIC), all the loading starts to happen during the same frame and the underflow becomes very unlikely.
    • You can also prevent the underflow by s/l'ing before entering the same ROOM/view again because this flushes the BODYs and ANIMs in the cache, or by making the BODY/ANIM be flushed after the caches run out of space.
    • The underflow occurs automatically when entering a few rooms that simply don't normally have any new actors that have a BODY or ANIM. This is the case when entering E2R6 (the room script actor underflows) and E6R6 (Pregzt and Deep One actors underflow) at least.
    • Aside from the two cases where either all actors need a BODY or ANIM cached, or none do, in practice you often have situations where just a part of the actors will need it. Generally speaking, we expect any actors that are loaded before the first caching-requiring actor to underflow and any that are loaded after it not to, though the more actors there are in a row that don't need caching, the more likely it becomes Timer 1 will tick up during loading them. It's still not massively likely since only the caching commands (especially background loading) are slow to execute. Here's a video showing a few instances of loading in a lot of actors during the same transition, demonstrating that at least if BODYs have to be cached, it won't all happen on the same frame.

File:Slow Actor Caching Demo.avi

    • It's also possible if an actor only underflows by one frame, the timers will advance by one frame before the LIFE is called and thus the underflow won't have time to cause anything after all. This might be happening all the time.
  • The function that initiates actors is called initObject(). It's where the actor's BODY and ANIM are cached (by a command called getCache()) and the CHRONOs are set. The function deleteObject() is the one that removes actors from the active array. Both of these are called inside updateAllActorAndObjects() which in turn is called by setupCamera().
  • Because the parts of the main loop that handle ROOM switching and view switching are in an IF-ELSE clause, the camera switch after moving into a new room actually only happens on the next frame. By the same mechanism that causes the Actor CHRONO Underflow, Timer 2 will also either revert normally if there aren't any new BODYs or ANIMs cached, or it will erroneously fail to revert if there are. In the latter case, there is a lag snap -like effect for every actor that was moving that will be the bigger the more data was cached.
  • Inventory Timer Glitch: When you enter a pick-up prompt (foundObject() function) or the inventory and a given item is displayed for the first time (i.e. without having been cached yet), it instantly causes the timers to revert to the value stored when you entered the inventory. Past this time, they're not reverted again, meaning the time you've spent in the inventory is effectively counted towards things like rotations finishing and the lag snap without needing to generate any extra lag while exiting necessarily.
    • This is caused in the same way as the CHRONO underflow since the inventory items require a BODY to be cached (and those are never shared between the in-game actor and the one that represents the inventory item, which are all scaled-up models).
    • Thus the first visit always works this way because of the "actions" model being shown. On the next visit, you have to scroll down to the second item to achieve this effect, on the third visit, the item below it etc. If you scroll past multiple items, they all lose their ability to cause this effect. However, if enough other BODYs get cached in-game, they can displace the inventory item BODYs from the cache, meaning the same item can cause the glitch again.
    • If there's any rotation-based event happening, it's a good time to pick up items since the rotations will continue automatically due to this glitch.
      • Any CHRONO-related events are also possible to make faster this way if you needed to go to the inventory or pick something up, for example if you pick up the hook after placing the talisman in E6R6, the pick-up prompt time will count towards the first cutscene finishing.
    • Menu visits do not result in this behaviour since no items are cached or drawn.
    • Simplified pseudocode showing what happens in the inventory (the pick-up prompt code is similar):
inventory()
{
	freezeTime();
	
	while(!exit) {
		readkeyboard();
		processinput();
		
		if (currentselection != previousSelection) { //pressed up or down
			currentbody = listbody.getCache(...);
		}
		
		render(currentbody);
		flipscreen();
	}
	
	unfreezeTime();	
}
    • The listbody.getCache() command is the same as what is found in the initActor() function. Thus the Actor CHRONO Underflow and the Inventory Timer Glitch are closely related evem though one of them relies on the getCache() command not being called and the other on it being called.


LAGGING/FRAMERATE

  • There's two kinds of lag: lag while loading something in (loading lag) and lag during execution because the CPU cycles aren't enough to run all the code at the target speed of 60 FPS (global lag). The difference between them is the game will try to compensate for global lag but not for loading lag, at least not in all cases.
    • The GOG version runs at a default of 11.000 CPU cycles which you're not allowed to adjust in runs. A slower setting makes getting the characters to run easier but there's more lag.
  • Just like while using lag snaps (see "LAG SNAPS") you can't get further than the end of the keyframe, your movement during global lag is also limited to that (it's assumed to be the same code). This means if you lag for two frames on the last frame of the keyframe, you're effectively losing one frame of motion instead of being fully compensated for it. If your FPS is 30, the chance of losing a frame like this is 50%. Thus per every 1 second of running, you're expected to lose 2 frames (1/30 of the movement). If it drops to 15 FPS, the chance to drop either 0 frames, 1, 2, or 3 frames is 25%. Thus the expected loss is 1.5/keyframe or 6 frames per 1 second of running (1/10 of the movement). For this reason, a higher FPS is slightly better for movement speed.
  • A typical camera transition will take something like 0.25-0.35 seconds. A room transition is also around 0.3 seconds but is usually coupled with a camera transition and thus about twice as long. A room transition's length depends on how many actors have to be removed from memory as well as how many are loaded in. You can start seeing this easily if you drop lots of items in the same place.
    • The lag coming back from an inventory and the lag when loading a new camera view are very similar, suggesting they might have the same exact cause.
  • If you pick up an item or return from the menu or inventory while also moving from one camera view or room to another, or stepping into a transition that immediately warps you onto another floor, you avoid one camera loading lag.
  • Collisions with actors that can be pushed are CPU-intensive and generate lots of global lag due to their special handling. The game redraws all actors on the screen afterwards instead of just the animated ones, and the collision processing is probably a bit slower too.
    • Simply running into a collider or actor doesn't seem to decrease the framerate almost at all. Presumably because all the same checks would have been run anyway.
  • The screen shake effect is also CPU-intensive. It causes the more lag the bigger the filesize of the background is. In E2C16, for example, it's much lesser than in most views because the walls are mostly made of the same color.
  • Another thing that generates lots of lag is if actors overlap in the game view so the game has to draw the ones in the foreground again.
  • Higher framerates might make combat easier because hits are checked for frame-by-frame. This means there's more of a chance of hitting something (but also of it hitting you) since the hotpoint is moving through more coordinates.
  • Choosing the low detail setting in the menu might reduce lag by a little bit, e.g. when scrolling through the inventory. It removes the transparency and noise effects.
  • Wandering near an enemy so it attacks you causing a sound sample to play could lose a few frames. Generally sound samples and every other asset (except for the music in the CD versions) all take a little bit to load in. More details under "caches". Of course avoiding the attack is very likely to require a detour that's longer than the sound sample loading.
  • If you wanted to see how the game plays with the least possible lag, you have to choose "cycles = max" or "core = dynamic" in the DOSBox config file. This has the additional effect of removing CHRONO underflowing at least most of the time.
  • The Room Viewer's FPS meter / lagometer will not count frames spent in dark rooms, not even when you have the lamp out in the maze. This is because the render code path is different from usual.
  • The reason you may have a more difficult time getting the PC running in many OOB areas is simply because the PC is behind the near camera plane and thus ignored by the renderer, increasing the frame count a lot (and running is bound to frames, see "SPEED" for more).
  • There can be a noticeable effect on the framerate if an actor simply turns around so more polygons are visible. With Emily standing around, if her back is towards the camera, there's more large polygons and the framerate might be 60. If she's facing the camera this could drop to 45, i.e. to 75%. Because this would only be relevant if there was waiting time, and only (?) if you were still moving around, there don't seem to exist any obvious circumstances in which this should be taken into account.
    • For some reason if you're far away from the camera (but still getting rendered), this seems to decrease, not increase, the framerate.
  • You can make there be more global lag by luring more enemies into the same area and making there be more collisions by e.g. running into several colliders or actors yourself.
    • The particle effects when something has died also cause lots of lag.
  • In general, lag reduction techniques and detours seem to be by and large secondary to anything else, but avoiding being in a collision especially with pushables for any longer than necessary seems to be a good idea nonetheless as well as using opportunities to segment in places where this changes the view into the next one you were going to enter.
  • If you even just stand around somewhere where no actors are getting drawn (somewhere behind the camera OOB), you can see the framerate fluctuating between 70 and 120 or so. This is because of the "slow" (soft) frame capping the game is doing.
  • If an actor goes into ANIM -1, BACKGROUND2 is redrawn with that actor in it causing a bit of lag. Similarly when an actor starts animating again, BACKGROUND2 is redrawn without it. This effect is possible to see with e.g. the pirate in E3R2 when you enter and leave the room again.


LAG SNAP

  • To artificially induce loading lag to get a sudden snap to happen to every actor moving around, you can either go to the menu and return to game (Menu Lag) or visit the inventory instead (Inventory Lag). In both cases the amount of lag and thus the distance of the snap you get can be influenced by the player.
    • If you could slow the game down to a low enough global framerate, you could achieve the exact same effects as with loading lag, they're not different in anything other than scale and context.
  • The technical explanation for lag snaps is this: There's a flag called "flag save timer" set to 1 every time the game has to perform a time-consuming operation that will require reverting the internal timers later on (the commands used are freezeTimer() and unfreezeTimer()). After reverting them, it's set back to 0. However, under some circumstances (like when entering the inventory), this can happen too early. The rest of the time before the game is resumed actually affects timed activities as if the game wasn't paused during that time.
  • Inventory lag is the best for speedrunning because it doesn't cause a long fade-out-fade-in like the menu does, though you can't always access the inventory. The amount of lag can usually be adjusted by how long you hold either ESC, SPACE or ENTER to leave it. You have to make sure you've let go of ENTER (that you pressed to get into the inventory) first before hitting ESC or you won't get much lag (the game is resumed without any extra delay). You can combine the inventory visit with doing something useful there. Not every selection causes you to get the snap from it though. In specific, any action that causes VAR 0 to be 0 results in there being no lag snap:
    • reading the Necronomicon or De Vermis
    • throwing
    • dropping
    • using the jug
    • In addition, choosing any action that changes your current BODY causes a snapback back to the start of the keyframe instead of lag snapping forwards, with the sole exception of drinking a flask, possibly because the BODY change is delayed by a frame.
    • If you keep holding numpad ENTER while selecting any item in the inventory, the game automatically resumes after a while. The waiting time seems to be about 32 frames from starting to press it so this is probably not the fastest, though it does guarantee getting a maximal snap (while running) in half a second if that's good enough. The big ENTER key doesn't behave this way.
      • This is NOT the same behaviour as if you held numpad ENTER to enter the inventory as well and didn't let go, in which case "fighting" would be selected after a while. The waiting time isn't constant in that case and it doesn't necessarily generate any extra lag.
    • The amount of lag seems to correlate with the amount of frames between starting to hold the input that takes you away from the inventory and when the movement happens, but the movement is a little bit less than expected, e.g. 14 frames could result in only 11 frames worth of movement.
    • The amount of extra waiting after the lag snap has visibly happened is probably dependent on the area you're in: in E0R0 and E3R1 both it seems to be 22-23 frames.
    • There's some chance some of this stuff depends on the keyboard you're using. Please report any deviations.
  • With menu lag, if you need more than the default amount, you just hold ESC/ENTER when resuming the game. The game is halted until releasing the key.
    • If instead you tap ESC/ENTER briefly and then immediately start holding another key down instead (or the same one again), this also causes extra lag time. It also acts you as having had that key depressed the whole time, so you can, e.g., complete an action by holding SPACE. The same works during the first inventory visit after reloading.
  • If you're lagging on the first frame of the first keyframe of the ANIM (i.e. right after it's looped around), the RV may not show any advancement in frames, however the snap seems to happen normally.
  • The best time to start the lagging is right at the start of a keyframe so you get the full keyframe offset's worth of movement so long as you caused enough frames of lag (unless of course you don't need that much). If you started it on, e.g. frame 3/15, you can only get up to 12 more frames of movement.
  • Looks like you always lose your run during lagging using either method. This SPEED/ANIM change is probably why you can't get two lag snaps one directly after the other, although there could be some obscure ways to use trigger SPEED changing to keep the same SPEED/ANIM.
  • You can get the inventory "caught" if you give an input right after hitting ESC to leave it, similar to how you can do this coming from the main menu. No known uses compared to just holding a key to delay the resuming.
    • In fact hitting SPACE to leave it into quickly hitting ESC can also work, though no method seems consistent.
  • When you enter the inventory for the first time after restoring a save file, Timer 2 doesn't revert afterwards so you're automatically converting all the time spent in the inventory into lag movement without having to hold any key down at all. It looks like the time that counts towards the lag snap starts on the first frame when the inventory has started being drawn and ends on the frame before when the actual movement happens.
  • You can't get lag snaps from item pickups off the floor (there may be a tiny nudge that's just "leftover" movement) if you're running because it ends running and thus changes your ANIM. You can get it from any uninterruptable ANIM like jumping, though there's no rotation.
  • You can lag out of a SPACE snap (more under "SPACE SNAP"). You get a full-keyframe lag so long as you've timed the snap so it happens just as the keyframe changes (so as you go to the menu/inv for the lagging, it shows the first frame of the next keyframe).
  • Lagging on kf3 of running seems the best on paper even if you don't need it for OOB'ing (see "LAG OOB"), however, in practice, you're going to pass through all the keyframes anyway so in most situations lagging on any keyframe should probably be equally good.
  • You can lag across the crack in E1R1 on kf1 or kf3.
  • You can use lag snaps to open doors (and generally collide with anything) from further away. Even though the game decides there's no room where your virtual position would have been, it still counts as having hit the door. The result is you open the door or perform the action from a greater distance.
    • This can be applied to the front doors. If you drink a flask as you've just hit it (there's one frame when you can do this), it causes the door not to open until you've finished drinking it or until you've been sucked into the jelly.
  • Lag snaps can be related to other things as well. They can happen when moving from room to room even when nothing is cached (which has a bug causing the Actor CHRONO Underflow, and the Timers not to revert properly), at least when the LIGHT 0 command is involved. In such a case, the explanation might go as follows:
    • The timer will get frozen three times: twice for loading the background and once because LIGHT 0 is called (e.g. in LIFE 172).
    • When LIGHT 0 is called, only the first step of what that causes the processSoundAndMusic() function to do makes sure to freeze the timer. The second step is fast, but the third step (restoring the palette) will take 2 frames at least.
    • The palette loading commands are slow because the game waits for vsync before modifying the palette. Also, it's done in two steps for some reason: first colors 0–127, then colors 128–255. In the end, it waits for vsync twice (this explain the two frames delay).
    • Aside from palette loading happening twice, the screen rendering will take another 1–2 frames. Thus a total of 5–6 frames have passed without a timer revert, explaining the lag snap.

SNAPBACK

  • The two ways you can get a snapback (the PC suddenly moving backwards) are saving and loading, or whenever you change your INHAND in a way that causes your BODY to change as well. In both cases you move back to the position where you started the current keyframe, but sometimes the RV may show that you were a few frames into the keyframe but you don't get any snapback at all, or you get e.g. only a 400-unit snapback even though you were on the last frame of a keyframe of running. Not sure what causes these discrepancies. The direction you're left facing after a s/l can be different (see "SAVE/LOAD TURN").
    • This trick was mostly tested using the s/l snapback but it's assumed the BODY snapback should work the same way.
  • The probable reason a changing BODY causes a snapback is the game stores how much time has elapsed since the beginning of the current keyframe inside the BODY (for easier access) so replacing the BODY might cause the game to reset the value to 0. The game sees that the player's frames just went back instead of forwards, and so there's a minus sign in the offset calculation.
    • Just in general, the game knows where the player was when the keyframe started, just not how long ago. Even if it stored that value, it couldn't use the current Timer 2 values to calculate where the player should be because that would mean you'd generally always move forwards every time you s/l and that wouldn't be as safe as moving the player back.
  • If you save/load in a situation where you were just about to collide with something if you resumed, the snapback can prevent this from happening.
  • You can't avoid the s/l snapback just by there being an object behind you on the floor. If there's a lightweight there, it gets pushed backwards if there's room. If there isn't, then neither of you will move, though the PC's virtual position might still be inside the lightweight until the end of the keyframe.
  • You can snap back to the previous room to save a room loading lag in a situation where you just needed to briefly visit another room for some reason (E1R4 being the obvious example for the bird defocus trick). Might save time combined with a slt despite the time spent reaching the save menu.
    • Since the position you're sent back to is where the keyframe started, you can't snap back into any trigger that you haven't already been in the location of, unless the trigger has just appeared, hence why it works with room triggers.
  • If you run into a trigger, then s/l and successfully snap back, the trigger's effect stays. E.g. you still die to the front door trigger.
  • The snapback after a s/l isn't always the same! Observed a situation (test run save "seg6-A0") where although usually the snapback caused me to return to E1R0, sometimes I would stay in R6 instead and was able to lag OOB.
  • Can't you just s/l to reset a fast keyframe after getting the sideways clip step so you clip again on the same keyframe?
    • If you go to the menu and return on the last frame of kf3, it clips you afterwards. If you save on that final frame before the clip and reload so you're still running, you do return to the start of the keyframe but without having clipped.
    • Looks like 15.1 is the last angle at which a kf3 clip will happen on the second to last frame.
    • You can't seemingly get a second clip off the same keyframe even if you use that angle and the first clip happens before you've s/l'd. It just gives you a tiny "complementary" step at the end of the keyframe.
    • This probably wouldn't save time anyway with the time spent reaching the save menu.
  • If you are in a situation where your virtual position is inside a wall (after a room has loaded in with a s/l snapback or something like this) but you're actually outside the wall getting ejected from it, if you reset the run ANIM this could be faster than waiting for it to reach a point where you're free to start moving away from the wall.
    • If you've just run out of the wall, you can't just reset kf3 with a s/l to get the faster movement again since this just sends your virtual position is still in the wall (s/l is too slow anyway, at least unless combined with a slt).
    • If you start rotating, you CAN move sideways if there's no obstacles along that axis.
    • Resetting the ANIM can't be used to cancel a s/l snapback on open ground: you get the snapback regardless of which keys you're buffering.
  • Can you s/l on kf3 usefully if you combine coming out through a wall with a) facing a non-orthogonal angle or b) slt?
    • a) It snaps you back along the axis that it can, parallel to the wall, but you're still stuck to the wall because of your virtual position.
    • b) You can actually get right back through the wall if you start with a big slt and keep turning back where you came from (doing two slt's works as well). Similar to the lamp transgression except doesn't have prerequisites. Can save you from getting trapped in e.g. the bookcase in E2R0 when entering from R2. The functional difference between this and the lamp transgression seems to be that you are actually losing frames of movement because of the snapback (the SPACE turn retains the offset amount, just changes the angle), but with a SPACE turn you also can't go for turns of over 90 degrees, meaning the slt could be the only way.
      • Unable to combine this with lagging (lag snap -> s/l snapback) because it's seemingly impossible to keep the run ANIM after lagging.
  • Using SPACE while loading a save file prevents the game from snapping the PC back like it normally does, so long as you don't have a null SPACE action. This can affect whether a certain effect takes place or not, such as being about to open a door.
    • It can also have the opposite effect: causing you to keep moving forwards and e.g. hitting a door that you then open.
    • Buffering ENTER gives you the inventory as expected, and this also combines with SPACE: hitting both has the same effect as just SPACE but only after returning from the inventory, meaning you can do stuff in the inventory but the game sees you as having snapped forwards. This can be used to e.g stop right in front of the front doors (with a null action) and s/l holding both SPACE and ENTER: you will be able to drink a flask, which causes you to stand in place but the game sees you having hit the door: thus the jelly is spawned (even thought the doors remain closed) and attacks you through the closed door.
    • The reason the doors never open is probably because the first door open ANIM never finishes as it's been interrupted by the flask.
  • You can get the E1R4 bird stuck outside the window just by saving the game right as its TRACK has finished (while it's still in kf2 of ANIM 71). It gets collisions back in the last step while it's still half-way in the window frame. Upon reloading, it's sent back too far to be able to get through.
    • It doesn't seem other birds can be gotten stuck like this.

DELAY TURN / DELAY SNAP

  • Refers to any kind of turn where a temporary state – typically holding down SPACE while performing various actions – locks rotation so that buffering the rotation before the action and releasing SPACE causes a sudden turn to happen similar to slt's. The angle covered is the same as if you hadn't been holding SPACE, usually up to 90 degrees. A full 90-degree turn while running, for example, takes 1 second.
  • Some delay turns come with a snap when the game corrects your position after turning you. The game gives you however many frames of movement you were into the current keyframe, with the starting point where the last keyframe started, aka the "anchor point", going the new direction after the turn has happened. So if you pass through (0, 0) on kf0 of running, run for 15 frames W to (-476, 0), then turn 90 degrees to face the N, your new position will be at (0, 476) and thus the offset will be shown in the RV as 673. If you did the same on kf3, the offset would be 895, which is thus the highest value you can get. If you run for 1 frame during kf0, the offset would only be about 45.
    • Seen an offset of over 950 while using a SPACE snap to instantly bounce back from another room. Might have something to do with cancelling some lag effect (not quite clear).
  • Delay turns can happen during:
    • fighting
    • searching
    • pickups
    • jumping
    • knockback
    • opening doors outwards
    • opening chests
    • landing
    • climbing
  • Delay snaps only happen if your ANIM doesn't change as a result of ending the lag, generally as a result of releasing SPACE. Thus during these:
    • jumping
    • knockback
    • pickups (that didn't involve searching)


PICKUP TURN / PICKUP SNAP

  • A kind of delay turn/snap that works with items picked up off tables etc. (not the floor): buffer a rotation key before getting a pickup dialog (whether or not this involved using open/search), and again after inputting your choice for a turn that's typically 90 degrees (or to the end of the current quadrant), but if you're quick enough with the input, could be less. If the pickup doesn't involve searching, you can also buffer UP to get a snap in the direction you were headed before the turn, making it potentially important to choose the right angle of approach so you get a bigger snap in a direction where you're not getting blocked
    • Getting the snap will only work if it's the first pickup prompt for the session since last reloading (Timer 2 is not paused).
  • The pickup turn and snap don't happen if there's more than one item in the same stack.
  • If you were walking forwards instead of running, you CAN get a pickup snap of sorts (but not turn) afterwards. This might have something to do with SPEED rolling down 3-2-1-0 but it's not clear.
    • Being knocked back or jumping also work.


SPACE TURN / SPACE SNAP

  • A subtype of delay turns / snaps where the effect is enabled by holding SPACE. This is a special case because it can be done without interrupting running at any point. This trick requires having an item INHAND that has no associated SPACE action. In AitD, the two obvious ways to get into that kind of state are choosing either the lamp or the jug (empty or filled). In this guide, it is assumed you're using the lamp. You simply press a rotation key with the lamp held out, then hit SPACE and release it when you want to turn.
    • The limitations are: the angle of the potential turn gets reset every time you hit a staircase etc. affecting rotation. Also you obviously can't turn any faster overall from a given starting state than manual turning, and thus it's only helpful if there's a run-up.
    • SPACE turns/snaps are better than slt's in not losing time to getting to the save menu. You're also guaranteed not to drop frames due to the s/l snapback. In fact you can gain time due to the snap itself moving you in a desirable direction. The downside is you're obviously going to have to find a suitable time when these benefits will outweight the time invested into getting the lamp out in the first place, plus you're not left with a segment break if you wanted one.
    • A SPACE snap can also replace a lag snap for purposes of e.g. getting OOB. It has the advantage that you only have to visit the inventory once, but this is only if it's efficient to be facing the wrong direction when arriving at the OOB spot, since the snap happens at an angle to your direction of movement.
  • The SPACE snap happens a frame after the SPACE turn. Since you don't move backwards in the keyframe, the sudden turn simply causes you to move to a new position that's located as far away from where the last keyframe started (the "anchor point") as before the snap, but off in a new direction. So if you pass through (0, 0) on kf0 of running, run for 15 frames W to (-476, 0), then turn 90 degrees with a SPACE snap to face the N, your new position will be at (0, 476) and thus the offset will be shown as 673. If you did the same on kf3, the offset would be 895, which is thus the highest value you can get. If you run for 1 frame during kf0, the offset would only be about 45.
    • Seen an offset of over 950 while using the snap to instantly bounce back from another room. Might have something to do with cancelling some lag effect (not at all clear though).
  • In this "lamped" state, just holding down SPACE continues your current movement without needing to hold down any arrow keys. (Probably the most accurate way to express this is nothing is causing your ANIM to change during that time).
  • If you want to get just the SPACE turn without the snap while running, you can simply release UP (but not the rotation key) before releasing SPACE. You just have to start running again afterwards. Temporarily releasing either the UP or rotation key doesn't stop the turn/snap from happening in the end so long as you're holding those keys again when you release SPACE.
  • Corner SPACE snap: A SPACE snap after cutting a corner (during the same keyframe) can save even more time if a snap parallel to the adjoining wall is beneficial at all. This is because the game can't snap you into the wall so you're ejected outside of it on the side of the adjoining wall, glued to it for some time until the keyframe ends, which should happen very quickly if the corner clip was efficient. After that, your ejected position becomes your new anchor point, you're completely freed and have just gained (arcsin of the angle of the SPACE turn times the corner snap distance) more movement with no effort. If you needed to do a 45-degree turn on kf3 of running and the corner snap only happens on the last frame of the keyframe, instead of having moved 448 units in the target direction, you've moved the full 633 units that way during that keyframe. In this particular example, you'd have to start the rotation exactly half a second earlier at the start of kf1. The greatest benefit is gotten if the turn is a 90-degree one in which case you wouldn't get almost any movement along the adjoining wall without the snap (so during the same keyframe you'll get up to sqrt(633^2+633^2) = 895, or 262 extra (41%).
    • It looks like the snap will succeed so long as you were already past the corner along both axes by any amount. This might have to do with the a posteriori position correction order (similar or same as sideways clipping): if you're not around the corner, the ejection happens in the wrong direction.
      • There can be several frames after releasing SPACE (as shown by NohBoard) and the game reacting to it and also the rotation happens a frame before the snap, so generally what it will end up looking is you release SPACE on frame 10, you keep moving forwards for two frames, then you clip the corner also SPACE turning on frame 13, then you snap on frame 14 (the last one). This way the snap can be up to the full keyframe offset.

File:90-degree Corner Clip Snap.avi

      • If you try to snap at 90 degrees so you never do a regular corner clip at all, you only seem to be able to do it if your bbox centerpoint is completely past the adjoining wall at the start of the keyframe.
        • Only a snap of 464 has been observed done this way so it's probably not possible to get much more than 500.
    • If there was another actor involved in the collision resolution aside from the two wall colliders, that might cause it to fail, though this hasn't really been tested.
    • Relatedly, if you're running out of a wall so that you need sideways movement right after, it's better to hold SPACE when escaping the wall, then release for a snap that takes you further towards the side. The keyframe then ends, freeing you from the wall.
    • In the very ideal case, the extra movement is the same, 262, and you'll end up turning 90 degrees, but this obviously requires 2 seconds of setup time before during which you're just running directly towards the wall, making it a very marginal case.
  • You might be able to use the SPACE snap to get OOB (or back IB) in new ways. In fact it makes a combination of slt and lag snapping unnecessary and obsolete if you can afford to pull the lamp out. This can be used e.g. in E1R6 when turning to OOB into R0.
    • It looks like anywhere where rooms share a wall collider around the doorway (e.g. E1R0-R1), whichever room is loaded in, the colliders prevent you from snapping OOB.
    • There's also sometimes problems where if you try to be on the same fast keyframe when you pass the point where you want to snap out of and when you've moved through the doorway, but the keyframe will change within the time spent running to the trigger and then waiting for the next room to load in (you can see that the keyframe has changed before the snap happens despite you buffering the button release). This seems to prevent or at least hinder doing something like running into E1R7 then bouncing back into R1 inside the N or S wall. Sometimes the game snaps you forwards because of the loading lag BEFORE the quick turn but sometimes it can also happen on the same frame. Doesn't seem to make a difference.
    • However, sometimes, you CAN get a backwards snap after the room loading and the turn happening which may or may not relate to some extra lag during the loading that doesn't get "accounted for" in the initial snap further into the room, thus only being considered after the turn has happened, explaining why it can happen in the opposite direction.
  • Because it seems a double transition causes the lag-based snap from the first room transition to be canceled out, you are able to perform SPACE turn snaps that take you inside walls in e.g. E1R0 and E1R1.
  • SPACE transgression: There is a very advanced way to use SPACE snapping to be "superpositioned" on both sides of a wall by running into a room trigger so that a wall loads in around you, then getting through on the other side (to hit some other trigger or so) during the same keyframe so you still have time to release SPACE to snap back on the other side, provided the wall isn't so thick that the ideal combination of anchor point (dependent on the angle of incidence) and angle after a 90-degree snap furnishes this.
    • If you try to do this trick from a stand-still next to the wall, you won't get a big enough turn unless the wall is penetrated only on kf3, but to make it possible for only kf3 to be big enough (and not kf1), the margin isn't big enough to allow penetration on anything but the very last frame, and because of this the keyframe is always rolled back to 0 before the snap. Thus it seems impossible to do it in this (somewhat artificial) way.
    • Meanwhile, if you set yourself up so you can transgress the wall before the last frame of kf3 simply missing kf1 because you were already in the middle of it when you hit the room trigger, it works as theorized and you can snap back onto the other side.
    • An alternative use would be to collect something off the floor that's just too far away from the wall so it can't be reached without passing fully through, then returning OOB/IB. The lag from doing this might make it impossible though, at least unless it's not the first such pickup prompt of the session (so Timer 2 isn't reverted early).
    • The E3R12 triggers can be triggered from OOB without being inside the room fully so this doesn't really change anything in that room.
    • You can optimize things like hitting the E5R1 trigger NE side (coming from R0) by SPACE snapping out of the wall quickly.
    • Can't find any real major uses for the trick in AitD 1.
    • Here's a successful attempt

File:Lamp Transgression.avi

    • It should be possible the same thing can be done using a snapback clip instead, though at the cost of the time spent saving and loading.
  • SPACE snapping directly after hitting a trigger may give you movement that you couldn't have gotten otherwise if you were approaching the trigger perpendicularly and at a corner, in case there is going to be a wall preventing you from being snapped any distance back the way you came from like the SPACE turn and snap would normally do. The prerequisites are: you have to have a spot where you can do a snapback OOB essentially for there to appear a wall behind you that stops the SPACE snap from yanking you back. There don't seem to be any opportunities in AITD 1.
    • This is closely related to a corner SPACE snap.
  • If you SPACE snap so you end up in thin air, you can skip the fall by simply visiting the inventory right before the fall. This is very likely not some special case though, it's the same 1-frame window as normally exists, but of course this might still count as an optimization in some cases. Also it might be easier to time the inventory visit this way compared to running slowly over the edge.
  • In E5R10, when moving into R11, it looks like you might be able to snap directly back into R10 on the sides so you're not standing on a collider during the room change (because the wider platform collider is inactive), causing you to fall. Getting everything to line up might be impossible in practice nor would it have any interesting follow-ups but it's another example of a potential usage of the SPACE snap.
    • Other places exist where this is faster than lag OOBs.
  • You can get a SPACE snap so you release SPACE, turn, enter the inventory, return, and only then snap forwards. This allows you to drink something while also moving forwards during the snap, but this is also doable with a simple lag snap. You can't get the snap after doing almost anything else that changes your ANIM. It might work when drinking because drinking doesn't change the ANIM until the next frame.
    • You can also save/load in-between the turn and snap and cancel the snap that way while keeping the turn.
  • You can lag snap out of a SPACE snap. You get a full-keyframe lag so long as you've timed the snap so it happens just as the keyframe changes.
  • You can get a s/l snapback after doing a SPACE snap, but it's difficult to picture a use for this.
  • If you save while having started a SPACE turn, and hold SPACE while reloading, the SPACE turn and snap will happen as usual when you release it.
  • Note that SPACE turns/snaps don't rely on having been in the run ANIM specifically. Just any time you have manual rotation should work.
  • You can get a SPACE turn going between E5 and E6, but it doesn't give you the snap.
  • There can be an in-between turn within the SPACE snap during a room transition (going into a room and snapping back to another one), presumably caused by SPACE not having been held long enough for the lag during the transitions not to increase the amount you turn by.

SAVE/LOAD TURN (SLT)

  • The save/load turn (slt): Saving, resuming, allowing some time to pass, and reloading causes the rotation to leap forwards into the state it should have reached based on the current Timer 2 value. If you're past the end of the rotation, it completes instantly. If you make sure Timer 2 has returned to a previous value instead (at any point after the rotation started) the rotation resumes from that angle and so you can slow rotations down indefinitely as well (such as the library doors closing when entering E2). If Timer 2 is sent back to a time before the rotation started, however, that also causes it to finish instantly.
    • Works with doors opening or closing, the medicine cabinet in E1R3, the desk rotating in E3R11, and any other actor rotating.
    • This can have the effect of instantly activating whatever logic was tied to the rotation completing, e.g. giving you back manual control of your character. It can also be used to delay that logic from being executed, thus giving control back at some more advantageous time.
    • Special logic executed after a rotation:
      • Any door: returns control and inventory.
      • E1R2 door spawns the zombie.
      • E1R3 cabinet gives the first aid kit as well as returning control and inventory. Sets player's ANIM to idling and restores collisions (but they're never switched off by the cabinet).
      • Some other doors also set the player's ANIM to idling after opening for some reason.
      • N front door opening starts the jelly's attack.
      • N front door closing causes a game over if you were hit by the jelly, and in any case the jelly to disappear.
      • Locked doors are officially unlocked only after they've finished opening for the first time.
      • The zombies in E3R12 have some logic that waits for a rotation.
  • For the PC to do a slt, you start to turn, then save the game, then reload (while holding the same rotation key if that was what was causing the rotation), and you'll be facing a different direction. The amount of rotation you get is proportional to how far Timer 2 has advanced during the save/load process times the rate of rotation for the particular kind of turning, up to a 90-degree maximum in most but not all cases. This means you can always easily get a 90-degree turn by just waiting long enough, but a large number of angles in-between are also possible if you only let Timer 2 advance a little bit. However, if you go too far, you can't ever go back unless you reboot the game completely, at which point Timer 2 starts again from 0, or wait until it reaches 18m 12s after which it wraps around. During testing, you can also just wind a timer back by hitting "1" or "2" respectively in the RV. This shouldn't compromise your data.
    • You can try to use the method of reloading a save file multiple times until Timer 2 has advanced to just the right value, but note that this does NOT increment it by the smallest possible amount. Probably depends on how much loading lag there is when resuming each time. Instead, pausing on every frame might be a more precise method.
  • If you buffer both LEFT and RIGHT and attempt a slt, turning right takes preference and you can't slt left at all. In general, you can't ever get into a save state from which either a left-handed or right-handed turn can be done this way (only one turn can be happening at once).
  • The following ideas on how to optimize slt's in segmented runs are almost completely theoretical, because they were written under the belief that time for each segment would end upon entering the menu when it really ends when entering the save menu, thus introducing a far larger segmentation penalty. They've been left in the guide as inspiration more so than anything.
    • Different kinds of slt's (probably only useful if you don't want to do exact Timer 2 manipulation for sub-90-degree turns):
    • counter-steering for <90 degrees (first manual rotation in the wrong direction, then buffer slt the right way): might be best for turns between 45 and 90 degrees if splitting the manual turning before and after the slt
    • half-manual turns for 90 degrees (start with some manual turning before the slt; could be useful if you first needed to hit some trigger that's in the direction faced initially)
    • plain slt for 90 degrees (hit the rotation key and ESC on the same frame to optimize, which is called a "tap turn")
    • "reset" turn for 90+ degrees (turn a little ways first, then reset by tapping the other turning key briefly while holding down the first one and do the slt; when turning right, you can even just hit all keys at once to reset because right turns take preference)
    • powersteering for 90+ degrees (do a part of the manual turning before the reset turn: technically a bit more efficient)
    • slalom (half-manual, reset or powersteer into tap turn) for two gentle turns in alternating directions without having to rely on multiple sub-90 degree turns
  • "slalom": if you need to turn left, then right but you don't want 90 degree turns, you might be able to do it like this – turn manually so you're curving around the first corner. Now s/l and buffer LEFT so you rotate the rest of the way (so you've turned too far but not a full 90 degrees because you've started the continuous turn before the corner). Now immediately buffer RIGHT, and do a tap turn to get a 90 degrees turn towards where you're going, in effect doing two less-than-90-degree turns in sequence. You could even split the manual rotation between the two turns.
    • To be clear, the idea is to oversteer by around 90 degrees first. In case you're heading mostly the right direction during an initial manual turn (far enough to get around some obstacle ahead), that could be a good idea. Example: we want to turn 60 degrees left, then soon after 60 degrees to the right. We turn left until we're past any obstacles, then turn another 15 degrees left, followed by a 90-degree turn to the right and 15 degrees to the left again.
  • Generally speaking, you always want to split manual turning evenly between however many turns you're doing in sequence, before and after each non-90 degree turn, because this is mathematically the most efficient.
  • You can get a perfect 90 degree s/l turn by tapping the arrow key very quickly before saving and releasing it a bit early when loading, not giving you any extra rotation before or after. When you do it this way, it doesn't enter the turning ANIM before saving (but you can still see the turn indicator in the RV) but it does enter that ANIM briefly when restoring even if you don't buffer that input, and also it'll enter that ANIM when the game resumes after it's done saving.
  • To slt during some actions (pushing at least), you have to hold SPACE before and after as well as the rotation key.
  • When jumping up the E5-R3 stairs, got a strange delayed slt when saving a few times at the top of the stairs so moving into the E3-E5 transition while still in kf4 of the jump. There was a s/l snapback first and a few frames later (when entering position 3, the rotate command, in the TRACK) a sudden turn to face due W. Now when saving after a new rotation had started, ended up turning due E (a 180-degree rotation). It's not clear why this happened like this.

OOB/CLIPPING

There's various ways to clip inside colliders and actors, many of which can also result in getting OOB. There is a file here with an almost-complete listing of every known way to get anywhere interesting in one or more of these ways.

 LINK
 A table listing all the known places you can clip OOB from.
  • Most of the techniques listed in this part of the guide should apply to other actors as well, in theory, though there may be any amount of practical problems with making them work.


SIDEWAYS CLIPPING

(usually what is meant by "clipping")

This is a trick that lets you clip sideways into colliders and actors if the one that's on your side only extends to around where your bbox ends in the general direction that you're facing. You have to face an angle slightly off to the side you're trying to move to. The following explains why this happens in detail.

Summary: the glitch happens because the walls are ordered. AITD applies physics by pushing player away from the walls, one at a time. Depending on the wall order, the player might still be colliding with a wall even after all walls have been processed.

The basic algorithm for moving the player is:

1) Take the current player position (white square in the picture).
2) Compute where the new position will be after moving (the purple square) depending on the current angle and speed.
3) Check if there is any collision with walls (in gray).
=> If yes, cancel the X or Z movement to avoid the collision.
4) Check if there is any collision with actors (in green).
=> If yes, cancel the X or Z movement to avoid the collision.
5) Move the player to the adjusted position.

Some examples (in E2R1):

Sideways Clipping 1.jpg

A) First case (first column):
1: The initial state.
2: The predicted movement (no collision at all, everything is fine).
3: The player is moved to the next position.

B) Second case:
1: The player is walking.
2: The game has predicted that he will be touching the gray wall after moving.
3: To fix this, the player is pushed along the y-axis.
4: The game predicts a collision with an actor (the green door). To fix this, the player is pushed horizontally.
5: Since both movements are ignored the player doesn't move at all (which is what is expected).

C) Third case (the glitch):
1: This is the same as the second case. The only difference is that the player is now running. Because of this the purple box is further away.
2: Here is why the bug occurs: the player is moving so fast that the projected position (purple box) won't be touching the gray wall anymore.
3: Because of this the game will only detect a collision with the actor and will only fix the position along the X-axis.
4: After the adjustment the player is (incorrectly) penetrating the wall.

D) Why it does not happen all the time:
1: This is the E2R1 door coming from E2R4: the glitch doesn't happen because the wall extends along the door.
2: The purple box will always touch both the actor and the wall, regardless of speed. Sideways clipping could only happen here if the player could move very fast (faster than the running ANIM).

Why doesn't the clipping happen on every frame? This is because when the player is running against a wall, the speed accumulates (player is stuck) and the predicted/virtual position is going further away with time (until reaching the end of the current keyframe). Here is the player accumulated speed (which affect predicted position) over time (amber):

Sideways Clipping 2.png

The displacement will only occur when the speed is above a certain threshold (the red line).

The displacement during the clip is directly related to player angle and speed: displacement = sin(angle) * speed.

Sideways Clipping 3.jpg

The angle here is not the global player angle but the angle relative to the wall you are clipping against.

The bigger the angle, the bigger the sideways displacement. If angle = 0 degrees (perfectly perpendicular with the wall), the displacement is zero. If angle = 90 degrees (parallel with the wall), the displacement is at its maximum. However, the bigger the angle, the lower the probability there will be a sideways clip. Consider the following cases:

Sideways Clipping 4.jpg

The purple line shows possible positions for the purple box for a given angle.

Angle = 10 degrees: clipping is very likely to happen, several times per keyframe.
Angle = 45 degrees: clipping happens sometimes.
Angle = 80 degrees: clipping stops happening because the player's new position always collides with the gray wall.

That is why the maximal displacement can only be achieved within a certain range of angles (a compromise between the chance of it happening and how much you'll move each time). In practice:

Sideways Clipping 5.png

The amber line is player (accumulated) speed during the clipping. The yellow one is the limit for a clip step to still happen (a function of the angle and configuration of colliders and actors). The green one is footstep sounds. They occur at the start of kf1 and kf3 or running, which isn't necessarily exactly at the same time as the clip step. The period/frequency shown should be correct.

This explains why, depending on the angle, you move twice every ANIM cycle, or just once. The kf0 and kf2 speeds are too small (for both Emily and Edward) to give a clip step under normal circumstances but can be used if you're already some ways past the collider you're clipping into (or even getting multiple clips per keyframe). The data shown here is when Edward is running. The values are different when walking, pushing, etc...
  • When Emily is perfectly positioned hugging a wall, the last angle that isn't too big for kf1 clipping is 21.1 (a step of 219/227 or 220/228 units depending on keyframe and direction of movement, so average of 223/224). The last angle that isn't too big for kf3 clipping is 25.7 (a step of 275 or 274 units depending on direction).
    • Because the two angles are different, you can get the most out of clipping only if you face different directions when the clip happens on kf1 and kf3 respectively. The technique (known as "waggling") is too difficult for single-segment runs though since you can very easily go too far and get no clip at all.
    • Waggling (mathematically) gives an average speed increase from 223 or 224 to 247 compared to maximal small steps. Thus you cut 1 step every 10 steps if you do it exactly right. If you can't cut a full step, it's probably not worth doing even in a segmented run.
    • E.g. in E2R0 if you start from (-4215, -3585), it takes 10 full small steps (-5825) + 1 smaller one (step of 190 if that's possible) to get to -6015 at the other end of the shelf. If you use waggling, i.e. alternate between a full small step on kf1 and a full big step on kf3, you can cover the whole distance of 2430 in ten steps. Now your average speed is 247.
  • Edward's maximum clip angle should be acos(532/591) = 25.81 degrees but it seems he can clip at 26.0 when the collider is to his S or E. If it's to his W or N, 25.7 is the maximum. For kf1 it's 21.03 degrees mathematically but 21.1 degrees works if the collider is on the S or E side, and 20.7 if it's on the W or N side. His clipping speed should be less than Emily's due to slower ANIMs.
    • These differences may owe to rounding of some sort.
  • When you've gotten close to a corner and you want to continue clipping along a different collider, you may want to take a "baby step" just to get as close to it as possible first. Basically required for single-segment attempts at clipping into the secret room so the Vagabond doesn't have the time to intercept you.
  • When you arrive at a corner where you want to clip (and the thing you're clipping into extends to where the corner is), it doesn't help if you arrive more than a frame into a fast keyframe. You might as well have arrived in time for the beginning of the next fast keyframe instead. Arriving on the second frame should be enough provided you're right next to the wall you're clipping against.
  • On saving and loading during clipping, even though you're not snapped back, you might still be losing extra time if you're not at the start of the keyframe. This can be countered if you use SPACE turning instead.
  • You can continue clipping against lightweights even after removing the wedge object, provided your exiting the actor/collider you're inside would place you either inside the lightweight or would force the lightweight into another actor behind it. This works in E3R11.
  • You can't directly clip against any inventory objects, though they can enable the "pushable wedge OOB".
  • The smallest clip steps (for Emily) are consistently 3-4 units long at 0.4 degrees. At 0.7 it's 7 or 7 units. At 1.1 it's 11 or 12. At 1.4 it's 14. If the step was taken on kf3, the values may be one more. Thus it's possible to get all kinds of exact minor offsetting if you do it just right.
  • The reason you can often clip against doors is because most of them don't seem to be exactly the same width as the walls around them, nor are they perfectly aligned with them.
  • You can save time by clipping through even an unlocked inwards-opening door whenever possible instead of opening it, if you only mean to pass through once.
    • An outwards-opening door is faster to open in a segmented run but possibly faster to clip through if you can't manipulate its rotation.
  • You can sometimes get in a position where you're inside a wall on the OOB side and can clip against an object that's in-bounds: the reverse of the usual situation. E.g. go to E1R6 OOB and hit the R1 trigger from the SE corner. This could theoretically enable you to move to a position where you can run back in-bounds if you initially couldn't.
  • Whenever you're OOB and you re-enter a room in E5 or E6, if you've placed yourself carefully along one of the seams between the floor blocks, you can usually then continue to clip along the seam. Difficult to see how to utilize this because most of the time you want to stay OOB for as long as you've reached your destination.
  • Sideways clipping might feel more difficult on a lower framerate. One explanation is found in how the player moves in larger increments and thus has a lower chance to getting right next to the wall or actor they're clipping against. The precision of rotations also goes down with the framerate.
  • Other actors can, of course, also sideways clip if their speed is high enough compared to their bboxes. In AitD at least, no other actor is fast enough to get OOB that way though.
  • You can, in theory, lag snap into a clip step if you're close enough to the wall. This might prevent you from going all the way towards the wall in case there's some reason this is useful.
  • It's possible to get more clips if you're a little ways vertically desynced based on climb checks that start to fail when you're far enough inside the ground but not so far you're underneath the walls, so you can clip into certain corners instead of climbing up. Such clips are not mentioned in the clips spreadsheet.
  • You could get more sideways clips against unlocked doors that you don't want to open based on another actor that has the higher slot pushing into the door from the other side, occupying the door's COL_BY so it doesn't start to open.
    • Pushables colliding with other actors don't cause any COL or COL_BY values to change so you can't use a pushable to enable this trick.
    • The only place where using this trick could achieve anything seems to be the front doors but those can be clipped through from the S of the S door anyway.

PUSHABLE OOB / ACTOR OOB

  • This is a type of OOBs that involves pushing (or more marginally, luring actors into position) to generate topology that allows for sideways clipping. Several sub-types are listed below.


CONVEX CLIPPING

  • A type of sideways clipping where the corner you're clipping into is convex, not concave. This can often be achieved by luring a monster into position to clip against, or pushing something in place instead. There aren't any real opportunities to make use of this, however, since either approach tends to be too slow.
    • You can use heavyweights and lightweights both. With lightweights, you either have to jam it in place with some dropped or thrown object first, or in some cases you might be able to just lag when it's in the right position to get the clipping started ("lag convex clip"). This works if the position for the pushed object after the lag snap would be too far, causing you to not move that way at all either.
    • Can be used to clip into the E3R11 desk with one of the chairs. Aside from this, there probably are no ways to abuse this in AitD 1. You can't even get far enough into the nightgaunts to reach either of the stair triggers.
  • It might sometimes be better to do convex clipping without leading the monster/pushing the pushable all the way to the corner (touching the wall) so you can get more frequent clip steps since you're partially past the collider you're clipping into.
  • If you try to use the Vagabond for this, you can seemingly only get one step at best, and even that is difficult. Might have something to do with its special no-clipping property.
  • Entrance clipping: A subtype of convex clipping which happens around the entrance to the next room, e.g. coming from E5R8 to R7. You just need some actor to push against. This can generally be replaced with lag OOBs.

CHAIN CLIPPING

  • It generally isn't possible to "chain" actors side by side to increase the scope of the clipping because the next actor in the chain will block any sideways motion that could get the player on top of it (i.e. N/S of it if you're clipping towards the E or W or vice versa). Thus the width of the single actor you're working with determines how far you can go.
    • The simple exception to this rule is if some of the actors involved move into position after you've started the clip, i.e. if monsters wander towards you.
    • There do also seem to be some configurations of stationary actors that allow for this kind of extended clip. The save file called "Chain Clipping Example.itd" is one where the player can clip W all the way to R7 from the entrance to E1R3. It's not quite clear how this works but the middle actor (the vase, ID 56) has to be in its position for it to work. If you exchange the vase and the chest (flipping the order of slot numbers), it doesn't seem to affect whether this can be done.
    • Also, clipping one way using one actor, then another way using a second actor should be possible, such as if you try to clip S using the chest from the above save file, which can sometimes happen.
  • Chain clipping has lots of theoretical potential and is also slow to set up so no complete listing of possibilities exists. It seems to generally allow moving left or right at entrances for an indefinite distance.
    • It might allow clipping through the nightgaunts into the stairs triggers, at least if you can avoid the attacks.


PUSHABLE LAG OOB

  • You can clip into an object with a lightweight that has a stationary actor behind it so that both you and the lightweight could not fit in the space between (or possibly it's a matter of where the lightweight itself would have to end up) by just lag snapping at a suitable angle when in close proximity. Afterwards, so long as the space remains equally narrow, you can continue to clip against the lightweight as if it was being held by something. This works in E3R11 from either the W or E side of the desk.
    • When you do this, remember that the pushable doesn't need to be in direct contact with the player at the start of the lagging. Also sometimes kf1/3 lagging is needed.
    • Sometimes this fails because the player's sideways motion is cancelled out (due to other colliders?) and they move directly forwards instead, if the pushable has clearance in that direction.


PUSHABLE WEDGE OOB

  • If you involve other actors in pushable lag OOBs, you can further increase the number of places where you could OOB to include almost anywhere with a convex corner or around doorways and entrances. The simplest way to do this is to drop something on the floor to wedge the pushable in place, though luring a monster over is also possible.
    • Of course, if you've lured something else over, you could try to clip against that instead, but that actor has to travel a shorter distance to be in position for the clipping if there's a pushable in-between.
  • The general outcome seems to be sliding left or right around entrances, so you can picture whether or not such motion could affect what's reachable.
  • This technique seems to be inherently slower than the pushable lag OOB in most places because of the extra time spent throwing or dropping the wedge item. It is, however, more versatile as you could bind the pushable into a lot of different positions. The wedge item or actor (and the pushable itself) need to have a combined width wide enough to prevent the pushable from being pushed right through the wedge, although this can be counteracted by moving more slowly.
  • It might be possible to drop two wedge items in positions so that you clip against a pushable, then deliberately push it towards the side to extend the scope of the clipping, then resume clipping with the other item holding it in place. The one time this was tried, it failed. It might be that this doesn't work because if the pushable object can be pushed to any new location, this explicitly means both it and the player have found new positions to be in and the player will also be moved out from the wall or actor they were in but possibly it can be made to work.
    • This was attempted coming to E3R13 from R12 in order to be able to clip all the way OOB towards the S using just one chair.

PUSHABLE OOB OOB

  • This happens when you first lag snap a pushable inside a collider in the adjacent room creating an anchor, then clip against it yourself to reach the OOB. It's very similar to pushable lag OOBs but because the pushable is completely stuck in a wall, it's not as sensitive to failing because of the player's sideways motion getting cancelled out. It might work in some place where a lag OOB is possible but you don't want to actually move into the adjacent room at all.
  • Heavyweights can also be lagged forwards during the push ANIM but not far enough to clear the current room's wall collider next to the room trigger so it's probably not possible to utilize them for this.


PUSHABLE NARROW OOB

  • Whenever you have a pushable or another actor at the entrance into a narrow corridor or inside a doorway, you can very much just approach it from the side to start clipping sideways towards the room trigger through the colliders adjacent to the entrance, given the space to do so. This often leaves you in a position where you can reach further into the adjacent room's room trigger to get into a better position for OOBs, or getting OOB directly.


LAG OOB

  • You can get OOB by a lag snap by just aiming one into a room trigger off to the side of an entrance where there's no collider. The places where this can be done are listed in the same OOB spreadsheet as other OOB methods. This is a particularly easy-to-utilize method.
  • The ideal angle for Emily of lagging OOB on kf3 is arccos(286/633) where 286 is a half of Emily's width (+1 just in case). This gives an angle that's 26.86 degrees away from the wall. The nearest value above this is 27.1 so that should work for sure, but possibly 26.7 does too. All the keyframes:
    • kf0/kf2 - 37.3 (might be 36.9)
    • kf1 - 28.1 (27.8)
    • kf3 - 27.1 (26.7)
  • 286 is the distance you have to move before you've completely cleared a doorway (in the direction you'd normally pass through it) if the trigger is precisely adjacent to it, as they always seem to be, and you start the snap from a position as close to the trigger as you can get. This leaves you with 554 units of movement along the other axis. This is enough to get you some tricky OOBs on E1 (check the clips spreadsheet for details).
  • There are a very few select places where a lag snap to get further inside a trigger (typically some room trigger coming from the OOB) actually prevents getting stuck and thus enables a different route. E.g. when you're in E2R2, entering R0 from the SE corner, you need the snap or you're stuck inside the bookshelf.
  • Wall thicknesses: In the house (almost always) 100, underground 300.
  • You have to be at least 237 units inside an E5-type wall to be able to push through, i.e. you can run through an extra 63 units of it. That deep, you then have a window of 3.2 degrees on either side, 6.4 total to get through on kf3. To get this deep into the wall, you have to have an angle of around 29.9 degrees minimum up to 52.0 or so degrees. At 52.0 degrees, you should be able to get at least 376 units deep (partially through). These figures may vary with what exact position you have on the last frame before the lag.
    • Edward can only penetrate a 59 units' distance.
  • You can fall down a tier in E5/E6 just by lagging further into the room triggers so you're completely past the floor collider. You can then turn around and if you're close to the previous room's trigger you can then run back to that room and fall another level.
    • In theory this allows you to get to places inside the seams that you couldn't otherwise and continue clipping against colliders. However, there shouldn't be anything in there that can't otherwise be reached except for a trigger in E5R10 that only toggles the step sound switch (which seems to be broken anyway).


SNAPBACK CLIPPING

  • A trick that seems to enable getting through a wall that's behind you when you first load a room in, provided it's close enough to the room trigger you just touched. A reason why this isn't as useful as it sounds is that a lot of room triggers are already right next to the room's walls or go past them. The snapback distance can be at least 470 (as with the E2R1-->R0 case).
    • You can actually get through to E3R11 by approaching the R13 trigger (being in R12) from the S side so that you're pretty far into the keyframe when you enter R13, then slt'ing twice (reset the turn in-between) to turn 180 degrees and just keeping the run through all of it. You should snap right through the R11 doors. This is the most exciting use I've found.
    • Can get to E2R0 from the N. There's some trouble with the room loading lag causing you to move to the next keyframe before you're able to save on kf3, or otherwise not being far back enough when kf3 started. The room lag looks to be similar to entering E3R13 though. I think the distance is simply a lot bigger with the E2R0 approach, over 400 vs. something like 180 with E3R13. 5652 (inside R0) is the first Y-position that allows running through. This translates to 5852 in E2R1. Finally got this to work with at least a little bit of leeway: just make sure you are indeed able to run N through the wall from the coordinates where your kf3 started.
    • If you go to E3R12 OOB and run into the R13 trigger NW (coming through the wall) you can probably snapback OOB so you avoid hitting the R12 trigger. Seems kinda pointless.
    • Can snapback OOB into E3R5 from R4, E side. Pointless.
    • E3R8 from the E side coming from R5. Pointless though you can get the pot and go OOB straight away. Also E3R8 from the W coming from R5.
    • E3R12 coming from S side from R9.
    • There may be one or two other trivial cases in E5-E6.
    • You can do any of this with a SPACE turn into slt which has the added benefits of first sending you to the side after passing into the room in case there was anything of interest in that direction, and just generally removing one s/l. It also has the property of putting you in a different spot after the SPACE turn/snap to where you would have been had there not been a wall in the way. This may amount to cutting a corner if you hit the room trigger at the furthest point. You should be able to get a direct slt right after the SPACE turn without having to wait another frame. (worked once?)


TRIGGER BOUNCE OOB

  • In the following video, if you imagine Emily coming from OOB on the E side (in R4) where the long N-S wall is and wanting to quickly get back in-bounds in R4, she could be using the technique shown in this video, facing NW then SW after the turn. This can obviously also be done with a SPACE snap. It's just an instant delay turn and snap after hitting a trigger to save time instead of doing a lag OOB/IB, which cannot involve turning.

Trigger Bounce OOB.avi


CLIMB OOB

  • There's places where climbing can lead into getting OOB. The mechanism is probably fundamentally the same as with sideways clipping (ANIM 267 has a keyframe with over 800 offset) but also relates to the test (TEST_ZV_END_ANIM (267 267) == 1) in LIFE 549 being passed even though it shouldn't be.
    • This kind of OOB is generally really slow because you're going to first fall (not always), then clip, then climb when a lag OOB that gets you to the same place is probably much faster.
    • The exact requirement is a wall on the side that ends in the collider you're climbing, e.g. E6R6, in the N corridor, pass the stone door coming from the W, clip into the first floor collider on the S side so you're next to the second floor collider and the corner. Now turn to face an angle around or a little bit less than 45 degrees away from the second collider and try to climb it. You should end up inside the corner.
  • Some of the possible places for this are listed in the OOB file in the fourth tab, but you can probably arrange for more.


FALL OOB

  • You can fall into a collider by walking off in that collider's direction from on top of some item[s] hanging in the air. You have to be right next to it, and you'll get snapped forwards. These objects can be made to hover at least by throwing them on the first frame of hanging in the air, which might cause both you and the object to stay on the level you're on instead of falling. The simpler way, though, is just to drop them, then walk onto them.
    • This gives you virtually no new chances to get OOB that simpler techniques don't offer, and none of the new opportunities are faster either but it's listed here in case it's useful in AitD 2 or 3.
  • These are not listed in the OOB file.
  • If you fall off an object into the E5-E3 trigger, you'll fall on the other side and get yanked up again but this has no special effects.


COLLISION CLIPPING

  • Since the game only stores up to three actor collisions in the COL array for each actor, additional collisions will be ignored. The ignored ones are those with actors with the highest slot numbers. Thus creating a situation where lots of actor collisions are happening can enable clipping through one of them.
    • This can be done with any actors, including items lying on the floor. Thus this kind of situation can readily be set up but at the cost of picking up and dropping or throwing the items required. Often, to get the right actors into low slots, you may have to move between rooms or views to get other actors to despawn first, so the time investment tends to be high.
  • Some easy-to-collect items for collision clips (ID)
    • lamp (13, has LIFEMODE 0 so there has to be a low slot ready or you probably can't get it into one)
    • bow (25)
    • E1 oil can (16)
    • The Sons of the Sun in the SE bookcase in the library (72, the other books require longer detours)
    • the lighter (199)
  • Time taken throwing objects into position: detour to pick them up (incl. pickup prompts) if any, then around 80 frames per item thrown + going through the pickup prompt. Also inventory time minus useful lag time (but there usually isn't any) — another 80 frames. Thus it should take about 160 frames per item and another 25 + 5x frames where x is the number of items past the first.
    • It might be difficult to avoid unwanted pickup prompts that take another 25 + 5x frames each time they're displayed.
    • Two items that you don't detour for should thus take in the order of 350-380 frames (around 6 seconds) to use for a collision clip. Three items should take 515-550 frames (around 9 seconds).
  • Possible things to clip through:
    • A nightgaunt.
      • They have IDs 64 and 65 with LIFEMODE 1. These are the highest IDs for anything in E0 and E1 but it's still tricky to get three objects into lower slots. You may have to do a detour in R3 or R4. Just throwing the objects alone probably makes this slower than using the camera defocus trick.
      • See save file called "Collision OOB into Nightgaunt.ITD" for a situation where three objects are in position for this clip to happen. They've been warped inside the nightgaunt, but it might be possible to get through if they were just carefully placed right next to it. Also you can throw items inside the nightgaunt, e.g. by standing at 889, 2635 and facing 197.9.
      • It is still possible, at least in principle, to pick up all the three items before also hitting the stairs trigger.
    • The locked library doors going to R0.
      • The IDs are 23 and 34 with LIFEMODE 2 and they start in slots 0 and 2. These are the lowest IDs of anything in E2 except for the R1 room script actor with ID 19. You obviously only have to clip through one of them (ID 34, the S side door), and you can use the N side door to help, and thus you only have to introduce two other actors to enable the clip.
      • This could thus be done by dropping the empty oil can and the bow, but to get them to actually go into lower slots than the S door, you'd have to go to either R4 or R10 (moving through a transition is obviously even slower). These detours are comparable to clipping against the R10 door to get to the library and you also have to take the time to pick up the bow, throw two items and deal with another two pickup prompts afterwards, although this could be somewhat offset by being able to clip back out this way, slightly faster than sideways clipping. Thus this method can't really be as fast as the Timer 2 manipulation that stops the doors from closing. For a SS run, this could be almost as fast as clipping against the R10 door but presumably no better.
    • The library secret door.
      • This one has ID 68 with LIFEMODE -1, and so it's present when you first enter E2. It goes into slot 5. It can only be made to disappear the same way as the library doors by going to R4 or R10 and so dropping three objects then doing that is far too slow.
    • The locked cellar door.
      • Can easily be bypassed with a sideways clip against the stairs.
    • The locked study doors (and study desk).
      • If you enter E3 for the first time, run through the missing corner to R0 OOB, go around to the E side of the R12 trigger, and enter R12 then R13 and pick up the lighter off the table, this should leave two slots free (7 and 13) that are lower than the E side study door, with the W side door having slot 11. This allows you to use any two items to clip through the E door.
      • Afterwards, clipping through into the E5 stairs is problematic, because you have to throw three items and the desk will naturally have slot 2 in this scenario. slot 0 goes to the coat of arms. Previous to this, as you enter E3, the 0 and 2 slots are taken by the two doors in the N of R0. There are four actors in R12 that are temporarily loaded in since the game initially loads in the wrong camera. These come back if you save/load in R5. Moving between views in R5 or R0 doesn't change the situation.
      • Meanwhile, if you just run through R0 and R1 and clip through the door to R13, the doors have high slots, the desk has slot 4, slots 0 and 2 will both be free and slot 3 goes to the smoke, which will disappear after you've entered R11 (in fact the smoke might take slot 0 the second time, but this too is temporary). Thus you actually have three low slots free to do the collision clip through both the study doors and also the desk. Even if you save and load in one of the rooms, the slots don't change in any way that changes the situation for the worse.
        • Thus you could definitely use two collision clips to get to E5 this way. Afterwards, while you can't pick up items during the transition itself, if you will come back anyway for the vertical desync, you can retrieve anything you will need then.
        • You can't use a lag step to collide with the coat of arms while staying far enough to the E (-900 or further) so as to avoid having to use more than two dropped actors for the collision clip since the distance from -900 X to the coat of arms is a little too big (over 700 units).
    • The stone door on the E6 trigger. Not necessary if you're desynced.
      • This one can be clipped through if you throw the three items inside the door (the trigger is too far to reach otherwise). Might be slower than going around OOB to enter R9 where the trigger is, but it can't be much faster in any case.
    • The stone door in E6R6. Not necessary if you're desynced.
      • It's too thick to get through this way unless you pick up and throw the items several times to get them further inside the actor.
    • The E6R6 stone door going back. Not necessary if you're desynced.
      • If you throw the three items immediately after entering, when you pass through R5, the slots will be adjusted in a way that's probably going to result in them all having a lower slot than the door. Thus this can be done in principle. It can't really be faster than e.g. sideways clipping against the door, falling down and reaching the trigger that way, though.
    • The study doors going back. Not necessary if you're desynced.
      • This is only possible if you can afford to leave two items by the doors on your way to E5 since all the lowest slots get filled upon entering E3.
  • You can even use an actor for collision clipping by lagging into it. This almost works for using the coat of arms as one of the three actors that help you clip into the desk in E3R11, but the stairs trigger is too far E.
  • The PC can't collide with blood, bubbles or debris flow actors so you can't use them to help with the collision clip. The smoke you can, in principle, but it spawns where the ashtray is, not near anything you'd want to clip through.
  • It seems that the PC cannot ever collide with something twice like HIT_OBJ actors can, so you can't use this to reduce the number of other actors required for collision clipping.
  • Even if you clip through a door, unless you lag snap towards the end (or if the actors you're colliding with are on the other side), you may end up opening the door from the other side if it's like the E2R0-R2 doors.

PUSH CLIPPING/LIGHTWEIGHT CLIPPING

  • Given the right circumstances, you can push a lightweight right through another (thin) actor. Whenever this is possible, it's also possible to clip through the actor yourself if there's a third actor helping out. It can be possible to do both of these in parallel as well.
  • The requirements for push clipping:
    • The pushable has to be colliding with at least two other actors (not flow actors) as you push it, one of which you want to clip through.
    • The slot order from lowest to highest of the three actors involved has to be: 1) the pushable 2) what you want to clip through (this can be multiple actors) 3) then the other actor the pushable is colliding with. More information on slots under "actors".
    • If these conditions are met, you can clip through any actor using any pushable. It doesn't matter if the two actors are perfectly aligned or right next to each other like double doors are. You'll know the conditions are met if the pushable seems to be sticking to the doors etc. instead of sliding across them.
    • You can also use dropped objects to activate the clipping meaning you only have to have one non-pushable actor (something you want to clip through) and the pushable in play.
  • Technical explanation:
    • First the game gets a list of all actors that collide with the PC.
    • Then it loops on each actor and will do the appropriate action (one actor at a time). E.g.: if the player collides with a chair and a door, it might first resolve the PC—chair collision then the PC—door collision.
    • Pushable objects are a special case. There are two possibilities:
      • The actor can be pushed.
      • The actor cannot be pushed (it's stuck against another actor or collider).
    • To know which case it is the game checks if the actor you want to push will collide with something else after the push. While doing that extra check, the engine erroneously overwrites the list that was calculated previously. The consequence is that after the engine has checked the PC/chair collision it will not check any other collisions and thus the other collision is ignored, and thus no clipping happens.
  • Push clipping works with any number of actors that you're clipping through, so long as you continue to collide with (slide against) the pushable actor and the slot order stays the same.
  • Push clipping even works when pushing heavyweights. They obviously can't easily be pushed through anything since pushing is so slow.
  • Sometimes you can clip yourself through using this method but you just can't push the lightweight through. Not sure why. Might come down to the exact topology of the situation.
  • Vortices etc. monsters can also push lightweights around. It seems unlikely this is possible to draw benefits from because what other actors are colliding with shouldn't affect the player's collisions at all. At best you could hope for getting a lightweight transported around the place without having to do it yourself. Presumably all the same rules apply to whatever is pushing the lightweight. In fact there is evidence of this: zombies have been seen to follow the player through the E3R13 door to R11 when the chair was left in place after the PC clipped through.
  • Remember that you can manipulate slot numbers by: dropping or otherwise spawning and picking up or destroying actors, saving/loading, moving between rooms and even just between views, and even by visiting the menu or inventory (which can cause actors to vanish sooner).


PRACTICAL TIPS/APPLIED

PRACTICAL TIPS / OPTIMIZATIONS

MOVEMENT

  • If an actor is about to disappear or move out of the way in front of you (like the stone doors in E5 and E6), you should start running into it before it's disappeared so you instantly get placed further ahead by up to a whole fast keyframe. Similar to clipping a corner.
  • Try to interrupt running only after a fast keyframe to save potentially up to four frames compared to an interruption after a slow frame.
  • Doing a run reset earlier or later so one of the fast keyframes lines up with a corner clip or something else of the sort where a slower keyframe has less potential to save time could potentially be useful.
  • SPACE snap at every corner that you can.
  • It's probably slightly better to SPACE turn at the start of the keyframe (when having a choice) because of having more control over the direction at the end of the keyframe in case you didn't get the exact right direction from the snap. I.e. if you're going around a corner, you can turn a little bit sooner or later within the same keyframe.
  • You can save time by SPACE snapping as you're running out of a wall, up to around 6-8 frames (262 units) if the snap was 90 degrees and you just barely can get far enough to escape running nearly perpendicularly to the wall. So you first run outwards perpendicularly and snap to the side before the keyframe has ended. Then the keyframe ends and the snapped position becomes your new actual position.
    • It's generally always best to run through walls at the angle you're headed next or as close to it as possible, up to the point of wasting a keyframe entirely.
  • You could stop running and start walking right as you're entering the inventory to make it easy to keep running afterwards just by buffering UP.
    • The downside is you'll only get a walking kf0 lag snap this way – around 370 – but that's only a problem if you were going to get a lag snap from that particular visit in the first place.
    • For segmented runs, probably best take the full running snap and a slower start.
  • When jumping through stairs/such, choose a keyframe that takes you exactly where you want it to (because the positions are quantized). Also make sure you're eliminating lots of slow jump ANIM frames by timing the jump on the right frame (the rest of the keyframe it was on during the transition is skipped).
  • When starting a new kind of rotation, make sure you abuse the game not updating the rotation speed until the current quadrant is finished, so e.g. if you want lots of rotation while also pushing something, you might want to stop completely for a frame, start turning, then instantly hit SPACE and you'll have faster rotation for almost the full quadrant.
    • The most obvious usage is when starting to run while also needing to rotate through a long arc.
  • If you hit both LEFT and RIGHT as you're returning to the game after the pickup, you can then quickly release the wrong key right after it's resumed to have reset your turn faster than other techniques.
  • Because the search ANIM needs to finish the loop for any checks to be done, it doesn't help to be colliding with the thing you're searching before that time (after 1 second exactly). For this reason, it's slightly better to start searching before you've collided with it so as to make use of the slow motion during that ANIM.
  • If you need to switch actions after running a little ways, perform the action, and then keep running afterwards, it might be best to choose the action before you start running so you don't have to double tap again.
  • In some route, you might be able to save time if you open the same door outwards instead of inwards if you have a choice.
  • Every time you have to hit a room trigger and then change directions to face the closest point in the next room trigger, a SPACE snap will actually save time by essentially canceling out a bit of motion towards the first point in favor of earlier motion straight towards the next one. Similar with any other triggers you just need to briefly visit.
  • An angle of 3.2 degrees away from a some axis is one that still gives you the maximum movement along that axis (for all normal movement ANIMs and keyframes), so all the sideways movement is just a bonus.
  • Movement maximum offsets and angle margins when running or jumping out of walls:
    • Running S or E kf0: offset 476 when angle is +-3.5
    • Running N or W kf0: offset 476 when angle is +-3.5
    • Running S or E kf1: offset 611 when angle is +-3.2
    • Running N or W kf1: offset 610 when angle is +-3.2
    • Running S or E kf3: offset 633 when angle is +-3.2
    • Running N or W kf3: offset 632 when angle is +-3.2
    • Jumping S or E kf5: offset 527 when angle is +-3.2
    • Jumping N or W kf5: offset 527 when angle is +-3.2
  • Running through the wall when you're first able to is better than clipping all the way through.
  • Get efficient quick turns while performing actions that stop your turning (e.g. jumping).
  • Remember to get "sub-optimal" clipping if it puts you in a more optimal position for the next step in a situation like the long clip in E2R0. What matters the most is the total number of big and small steps you took, not whether each of them took you as far as it could have.
  • You can run a little ways into stairs triggers etc. so there's less slow movement afterwards.
  • Hitting an outwards-opening door further away from its hinges is slightly better at least in SS runs so you don't get caught on it during the ANIM.
  • If you need a hover, hover before running out of walls if at all possible, without using it for escaping the wall, unless the first keyframe is enough to push through. Otherwise all the keyframes prior to you being freed from the wall are wasted.
  • Generally speaking, you always want to split manual turning evenly between however many turns you're doing in sequence, before and after each non-90 degree turn, because this is mathematically the most efficient.
    • This applies to other things as well, namely veering to one side: e.g. at the start of the run, you start veering E in preparation of getting to the E0 stairs instead of leaving all the E movement to until after picking up the lamp.
  • Corner clipping reference angles:
    • if theta = 75, maximum distance from corner to be able to clip it is 73 (kf3) or 78 (kf1) or 113 (kf0/2)
    • if theta = 60, distance from corner is -80 (kf3) or -69 (kf1) or -2 (kf0/2)
    • if theta = 45, distance from corner is -211 (kf3) or -196 (kf1) or -100 (kf0/2)
    • if theta = 30, distance from corner is -312 (kf3) or -293 (kf1) or -176 (kf0/2)
    • if theta = 15, distance from corner is -375 (kf3) or -354 (kf1) or -223 (kf0/2)
  • You could time some lag right before hitting some collider so as to be able to hit it from effectively further away, if you're going to turn back afterwards.
  • When climbing up a collider, since you get free forwards movement during the first ANIM, it might be useful to do any lag-generating inventory visit right before then, and also moving in exact straight lines might not be the best. Even if you're right next to the collider, you can get free movement parallel to it.
  • If you need a visual indicator of where you are OOB during attempts, watching the behaviour of actors set to follow you, whenever visible, is an idea.
  • You can sometimes open doors at angles that immediately place you against the door for clipping after the delay turn.


LAG / TIMERS

  • Remember to pre-cache all samples that will be played during segments to avoid loading lag.
  • Avoid unnecessary camera switching if a sufficiently small detour to go around it exists.
  • Try to enter rooms in their default views to avoid the double camera unpacking lag.
  • Try to combine inventory and menu visits and picking up items with moving between cameras, rooms, or floors to cut off one camera loading lag.
  • Holding down SPACE suppresses movement-related samples (as well as some others). While the lamp is out, holding SPACE doesn't prevent running either. Thus you could theoretically save a tiny bit of loading lag time if you manage to completely prevent them while running over a new surface, e.g. a carpet.
    • In a segmented run this can be done by pre-caching those samples anyway with a low likelihood of causing issues.
  • Before starting any actions requiring Timer 2 manipulation, make sure to restart the game so you don't have to wait so long for the timer to reach the desired time.
    • Don't just use the Room Viewer to move Timer 2 backwards during recorded attempts as this will mess with the cache state compared to restarting, and so can't really be seen as legitimate.
  • When moving between rooms so the view also changes, you get a lag snap that you should try to utilize by timing it at the start of the keyframe.
  • Maximize the lag snap whenever picking something up (off something other than the floor). If possible turn to face the direction you're going next first so the snap is in the right direction as well.
  • Generally even global lag is to be avoided because the compensation is not perfect and frames could be dropped anyway. There's a very small chance this could justify some slight detouring to avoid hugging a wall or some actor unnecessarily (especially pushing pushables causes lots of lag).
  • Choosing the low detail setting might reduce lag by a tiny bit, e.g. when scrolling through the inventory. (This is not necessary for SDA submissions.)


INVENTORY / ITEMS

  • When placing the talisman, it might be best to also collide with the sarcophagus on the same frame. This brings up the hook prompt and allows retaining control during the cutscene, or causes it to be skipped.
  • If you open a door outwards, instead of a s/l to skip the waiting time you could just do something in the inventory as well to not have to invest the time into segmenting.
  • You can "auto-complete" an open/search action if it had entered kf1 already based on lag while in the inventory, though only if the action you've selected doesn't change your ANIM or BODY. This saves up to 30 frames which is more than the 15 frames saved from just lagging through a keyframe of running, and thus whenever possible to apply, is a better time to perform that inventory action.
  • Doing things in the dark causes the timers not to stop, meaning you get an automatic snap when you return if the conditions are otherwise met.
  • If the item you need to select is close to the top of the inventory it might be fastest to just mash DOWN/KP_2 instead of holding one of them down.
  • Time inventory visits so you're cutting over a narrow camera zone completely and so you get a long snap, or if your BODY is changing, only a short snapback.
  • Make sure every inventory visit is on the first frame of the keyframe to get the biggest snap, unless you can make it lag so little that the keyframe never ends during it, in which case it doesn't matter.
  • The room and view in which you choose to visit the inventory affects how much lag there will be returning back. Also s/l'ing has more lag (by a couple of frames at least) in different areas.
  • Use the free inventory lag snaps efficiently everywhere even in SS runs (timers revert early whenever you've highlighted a new item for the first time).
    • Relatedly, think about utilizing the Inventory Fast Scroll somewhere. For more, see "inventory fast scroll".
  • If you keep buffering ENTER, you can keep going back to the inventory after dropping something before LIFE 549 has had a chance to be executed again. This can, e.g. save you from death by falling.
  • If you keep changing BODYs through selecting items in the inventory, you can stop any ANIM offsets, even uninterruptable, while time keeps passing for other actors.

SAVING / LOADING

  • You might be losing a bit of time every time you save in the middle of actions that don't resume instantly, such as running, jumping, throwing... The time lost is probably just a few frames in most cases. More under "saving/loading".
  • You can s/l to speed up collecting items from objects with animations, like chests or cabinets.
  • In segmented runs, you can s/l in some spots with overlapping camera triggers to change to the next view slightly faster because it happens outside of timing.
    • S/l on a camera-free spot does NOT cause the camera to change from whichever one it was left at when you saved. When the game resumes, there will be a fade-in but this is not counted in timing (remember to point those spots out whenever it happened in the dark).
  • Remember you're liable to lose time to s/l'ing even during clipping because of the virtual position being sent to the start of the kf.

TIMING CERTAIN ACTIONS

  • Fastest possible inventory visit: observed 13 frames from inventory showing up to getting the lag displacement and 18 frames before rendering the game again.
    • For some reason inventory visits seem to be a couple of frames faster in E1R0 than E1R6 even though there's more stuff in R0. There are probably similar differences all throughout the game.
  • switching to another action: 65 frames + 7 for each gap but can probably switch to open/search in 60 flat in an ideal case
  • opening a door outwards: 2 seconds (with s/l: time to save + 0.1-0.2 s due to being left slightly further back than when opening normally)
  • opening a door inwards: 4 seconds (with s/l: 1.7 + time to save)
  • picking up or refusing an item: something like 5 frames per item to give the input and however long to unpack the view you're in (could be around 20 frames) to resume the game (minus useful lag time), but you also lose your run if the item was on the floor. Thus it's reasonable to assume it takes about 30 frames to pick up a single item off the floor and maybe 25 (minus useful lag time and the delay turn) when it's off a collider.
  • dropping: menu visit (80 frames if the item is the first one, minus useful lag time but there usually isn't any since you stop moving), 150 frames dropping (can do a quick turn)
  • throwing: menu visit (80 frames if the item is the first one, minus useful lag time but there usually isn't any since you stop moving) + 80 frames throwing ANIM (can do a quick turn or gradual turning as well, you have time for a 180 turn during a single throw)
    • the ANIM has 60 frames for the object to be spawned (on a successful throw) and another 20 frames to regain control


THEORETICAL IN-BOUNDS / GLITCHLESS RUN

  • A no-major-skip-glitches category doesn't seem very easy to define: one attempt would be "no clipping (defined as the player's bbox overlapping with another actor's or collider's) and no hovering". However, all you have to do to cause this situation to arise is to stand next to a window that a bird is about to crash through, or if you're walking stairs and a monster is waiting at the foot or top. To verify such a run, a RV video would probably have to be supplied, which doesn't have a precedent on SDA as of 2019.
    • The other problem with this definition is the missing walls in E5R0 in the floppy version. You can just run OOB in that room which doesn't feel quite right for this kind of category.
  • In an in-bounds run you could attract an axe but make it fly into R4 instead, causing the triggers in R1 to become disabled and making the S half safe.
  • Could maybe get the arrow to give you the first aid kit without losing much time (on E2).
  • There's three ways to traverse the pillar room jump puzzle in five jumps, which is fastest: N N (not NE) W W N; N W N W N; W N N W N
  • If you wanted to get the pirate's key from the ballroom you only need to anger some of the dancers to get around them, grab the key quickly and run away.
    • It could still be faster to play the Dance of Death since this avoids having to dodge the vortices aftewards.


MIGHT HELP IN JITD, AITD 2, AITD 3 OR TIME GATE: KNIGHT'S CHASE

  • Nothing is known about Jack in the Dark's engine but it's likely to be the same or very similar to the AitD 2 engine seeing as it was released as a promotional special for that game.
    • Apparently JitD was later attached to the CD releases of AitD and AitD 2.
  • AitD was accidentally released with debug symbols in the .exe, which means that it's been easier to reverse-engineer things like VAR names and so on. The other games in the series, on the other hand, have this debug data stripped and are usually compressed which make them difficult to understand. Secondly, FitD is a close approximation of the AitD engine and may not reflect on how things work in the other two games.
  • "the game engine used in the Alone in the Dark games seems to get confused about where the hitboxes are for certain objects, as I remember having somehow managed to clip through one of the hedges in the maze at the beginning of Alone in the Dark 2 once, resulting in my not being able to progress since the hitbox that would move you to another screen didn't recognize me anymore."
  • See the objects spreadsheet for the few fields that are only used in AitD 2/3/(JitD?).
  • Fall OOB: You can fall into a collider by walking off in that collider's direction from on top of some item hanging in the air. These objects can be made to hover at least by throwing them on the first frame of hanging in the air, which might cause both you and the object to stay on the level you're on instead of falling. The simpler way, though, is just to drop them, then walk onto them.
  • Can any actors be forced to move in unusual ways (TRACKMODE FOLLOW or TRACK) due to the indecision bug?
  • Can some of the other ACTIONTYPEs be flushed by actor deserialization that only the PC ever has in AITD (THROW or FIRE...)? What are the effects? Presumably this should stop the actor from shooting or throwing something.
  • What does hardMat do?
    • What happens if hardMat is flushed during actor serialization?
  • Can MARK be flushed at an opportune moment to interrupt or mess with an actor's TRACK? It's supposedly actually stored in the object in AitD 2 and 3.
    • Can't seem to be able to do anything with MARK flushing even in AitD 1 (see "ACTOR SERIALIZATION").
  • Make sure the different PC BODY versions of the same ANIMs have the same properties. Also test to see that what BODY you have at various times doesn't change the outcome like it does in AitD when falling into the crack for example.
  • If there was an ANIM that was faster in the first couple of keyframes, resetting and repeating it might be faster than letting it play out fully.
  • In AitD, if there's a ROOM_CHRONO clause waiting for a certain amount of time to have passed, leaving and returning to the room causes the event to occur instantly. Changing floors or save/loading in-between doesn't work. Is this still possible in the other games?
  • Are there unwanted ANIMs you could get interrupted by taking damage/something else?
  • Throwing something at something to make it activate faster or manipulate what it does?
  • If you lag the game when you're about to hit a wall, it doesn't make you ever reach the wall quite but a collision may still be detected. Could maybe skip some very specific trigger somewhere, one that's right in front of the collider?
    • Seems unlikely there should be such triggers if the collider itself could just as well have been used.
  • If you have triggered a cutscene (at least bird window one does this) by walking into the trigger, the screen will not change based on what you're doing, so you may be able to spend slightly longer in the view you were in. This could enable the camera defocus trick if you're very lucky.
  • By luring a monster to a room trigger in a clever way, you could possibly get it OOB with you using the lag OOB technique. Probably requires it to be moving faster than any of the normal monsters do in AitD.
  • Using uninterruptable ANIMs that move you at a faster speed to cut short the time spent waiting for something slow like climbing a staircase to finish.
  • Are there processes like the smoking in AitD that can be glitched out by repeated s/l-ing?
  • Any ways to abuse dying? If in AitD you could get your INV back after you've been transported to E6, you could use this as a skip presumably.
  • Interrupting transitions by opening doors/hitting other such actors (see "TRANSITIONS").
  • Is there a way to get sound samples, especially sizable ones, to not start playing at all if another repeating sample overrides them as with the vortex hum, or just by timing another sample with higher priority to start before it or at the same time? Also hitting SPACE at the right times may throw the player out of a loop where such a sample would normally get played on a particular keyframe of the ANIM. The time saved this way is minimal though (the longest sample caching times in AitD seem to be around 10-20 frames).
  • Does Timer 2 manipulation work the same as AitD? What kinds of things are tied to this?
  • There was a bug in the compiler that was used for all the games up till at least AitD 3 that might cause some logic to work erroneously in the scripts. The compiler expected the default cases for switches to be at the bottom of the list, but in some scripts, it's up top, and thus gets executed regardless of anything. Additionally, there is sometimes a goto statement at the end of default cases at the top which cause other cases below to be ignored. When there is no goto, other cases below will be evaluated as usual.
    • The following games are impacted : AitD1: LIFE 353. AitD2: LIFE 206. AitD3: LIFE 938 and 1094.
  • There generally are no places in AitD where you could easily lag snap to an adjacent room (staying in-bounds) to a location where you aren't in a camera trigger. The triggers tend to cover the entrances very well. Perhaps the other games has one somewhere. This could lead into, if nothing else, slot order changes in the actors being loaded in.

TAS-ONLY/GLITCHING/CRASHING/SOFTLOCKING/MEMORY CORRUPTION

  • If a speedrun has any memory corruption from an over- or underflow or pointer corruption etc. it will be considered "with [uber-]large-skip glitches" in SDA vernacular. Some nebulous potential for such exploitation exists, with the main suspect being a bug in the game's renderer. The exploitation is likely to require TAS-level precision, at least in order to establish the way to do it if not in executing it as well. A thread has been started on TASVideos.org for discussing these possibilities with TASers. As of writing this, there seem to be problems with getting the game to run in an officially approved TAS emulator.


NULL FOCUSED ACTOR GLITCH

  • There is an interesting way to glitch/crash the game in E0. It involves the floor hatch that the zombie emerges from. The general mechanism that causes it is setting the focused actor to be one that's in R-1.
    • To reproduce it, you have to block the hatch from opening 9 times so VAR 2 is 9. Then you have to stand around the coordinates (-5100, 3140; these work for Emily) to block it one more time. You can also just load the save file called "Hatch Glitch.ITD". It doesn't work every time. When done this way, this seems to always result in a crash although different results have been reported using the floppy version (for which you can use the save file "Hatch Glitch Floppy Version.ITD").
    • When it crashes, what's happening is every IF clause in LIFE 6 is executed on the same frame because there is a collision with the PC (of course any actor works), VAR 2 increments to 10 and also the hatch reaches ALPHA 700. So the hatch gets LIFE 7 then -1. More importantly, the zombie is deleted on the same frame as its LIFE 16 causes it to become the camera target. When it becomes that, this actually only causes the current ROOM to change to -1, not the floor, because changing the floor is only done if the focused actor has ID -1. This was studied in a debugger and it was confirmed that specifically the "camera_target zombie_9" command causes the glitch. Furthermore, forcing ROOM to be 0 (instead of -1) in that function prevents the crash.
    • If the PC is standing somewhere else with another actor blocking the hatch, the game doesn't nearly always crash. Most of the time it survives the unusual instruction it's being given and may even behave otherwise normally but with the focus being in R-1, which isn't defined and as such doesn't have a camera list either. For that reason, there is no default view to switch to and so the same view is kept (in fact the game stops drawing gameplay graphics at all), and all actors in E0R0 are removed from memory except for the LIFEMODE 0 ones, as you'd expect. The PC has LIFEMODE 0, and as such is able to continue to run around normally, visit the inventory and do anything else you would expect. The PC's WORLD coordinates are off because of not being focused, which affects the death cutscene if they die in this state (this is also normal during ROOM/WORLD desyncing). You can also save the game, though this seems to produce a degenerate save file, see "Hatch Glitch Degenerate Save.ITD".
    • More small changes in the game's behavior when in the glitchy state that are different from a normal defocus state:
      • Going to the inventory and coming back is faster because BACKGROUND is not decompressed again.
      • If you get messages on the screen like "Game Saved" they usually disappear much quicker than normal for unknown reasons.
      • If you try to read anything, when done reading it, after the fade-out, the game doesn't redraw the screen and instead the background used when rendering the readable is left showing.
    • The glitched game state seems to be completely normalized after going/warping down to E1 and coming back apart from the fact the hatch never closes, which normally happens in the zombie's LIFE.
  • Here is a video showing the hatch crash, once at full speed, then two times slowed down.

File:Hatch Crash.avi

THROWN OBJECTS SOFTLOCK

  • If you throw an object into the OOB, it may eventually cause a softlock. The cause for this is described under "THROWING". No memory corruption is expected to ever result from it.


CAMERA WRAPAROUNDS

  • Every camera is effectively the center of the cubical world as seen by the renderer (the borders are drawn in brown in the RV). A few different steps are gone through along the way before actors are ready to be rendered on the screen. When the 16-bit values used to represent the locations of vertices get close to the extremes, wraparounds are likely to start happening. This is an overview of the process.
    • First the game calculates the position of every vertex of every actor that needs to be drawn relative to the camera. All coordinates are transformed from ROOM/local into uniform WORLD coordinates by subtracting the vertex ROOM coordinates from the camera coordinates. Because no camera is positioned at the room's origin facing an orthogonal angle, none of the camera cubes is perfectly aligned with the game world cube (ROOM coordinates). For this reason, there will always be areas where camera-relative coordinates will wrap around before the actor's ROOM coordinates have or vice versa.
    • Next the vertices are rotated. Wraparounds can occur when the vertex positions are rotated according to the camera orientation.
    • The next part of the render process is when vertices are projected onto 2D screen positions. Again, wraparounds can occur, this time around frustum borders.
    • The effects of camera wraparounds are stretched polygons, actors starting or stopping to be rendered at odd times, and being rendered in odd locations.
  • When polygons start to be rendered, it's like everything is rendered at [0,0,0] with no orientation. The world has been translated and rotated to be aligned with the camera before.
  • When a wraparound happens along the X and Y axes, stretched polygons may be drawn across the screen as some vertices begin to wrap around, and after this the actor can be drawn on the opposite side of the world. When it happens along the Z axis, it causes an actor too far ahead to stop being rendered, or an actor too far behind to start to be rendered as if they were in front of the camera.
    • The game will clamp any vertex that has camera Z <= 50 (too close, or behind the camera) or a ROOM Y value > 10000 (changed to -600 in E5/E6; this is to simulate wading around in water). Because of the Y-clamping, a Y camera wraparound can only happen from the top of the camera cube to the bottom.
    • Z-wraparounds are illustrated here (the camera direction is given by the arrow; green areas are where the actor will be drawn):

Camera Z Wraparound.png

  • The formula to project 3D vertices into the 2D view is simple:
x = (x * cameraFocalX) / (z + cameraFocalZ) + 160
y = (y * cameraFocalY) / (z + cameraFocalZ) + 100
    • [160;100] is simply the center point of the 320x200 game window.
    • cameraFocalX / cameraFocalY is similar to HFOV / VFOV (except those ones are expressed in degrees rather than pixels). cameraFocalZ is a way to quickly move camera along its Z axis without affecting its position.
  • Here is an example of how values wrap around. This relates to the situation in the save file called "Camera Wraparound Example.ITD". The camera view is one that has the most drastic GAMMA rotation, which might make it more liable for polygons to glitch.
camera local  :  6140  -2520    9940  (as show in RV)
room position  :  5500      0    -200  (E2R1)
camera world  :    640  -2520  10140  (camera local - room position)
    • Which gives:
player (bbox center)          :  1427  -29000  -10977
player relative to camera      :    787  -26480  -21117  (player - camera world)
player with camera orientation : -14154  -29808    7660
    • This is for the center of the PC. There are also other points of the model slightly higher:
player (not center)            :  1399  -29781  -10140
player / camera position      :    759  -27261  -21211
player / camera orientation    :  4658  32180    7424  (should be -14363 -30531 7424)
    • As you can see, the X and Y values are messed up. It's because some intermediate results wrap up during rotation of those points.
    • The way rotation is computed:
// x,  y,  z = point to rotate
//rx, ry, rz = camera orientation

//rotate around y
x = x * sin(ry) - z * cos(ry)
z = x * cos(ry) + z * sin(ry)

//rotate around x
y = y * sin(rx) - z * cos(rx)
z = y * cos(rx) + z * sin(rx)

//rotate around z
x = x * sin(rz) - y * cos(rz)
y = x * cos(rz) + y * sin(rz)
    • The 3 rotations are done one after the other, in that order. There is the possibility of an overflow after each add or subtract operation. Any overflow might contaminate the other rotations (that is probably why both the X and Y coordinates are wrong here).
    • Those points will give (after 2D projection)
-14154  -29808    7660    =>  -315    -737
4658  32180    7424    =>    321    1032
    • That range explains the polygons stretching across the whole screen.
  • While most of the time this is a purely graphical glitch, the 3D-wrapped-around stretched polygons might cause crashing under specific circumstances. It seems to be likely to happen if an animating actor (tested with PC) is standing inside the glitchy polygons. The cause is unknown. It has never been noted to cause obvious memory corruption, instead simply resulting in a direct softlock. This has not been studied at all carefully though. It's possible it's really an instance of the thrown actor softlock.
  • Sometimes the stretched polygons might not get flushed off the screen when the actor has wrapped around. The question is as much what normally causes them to be flushed as much as what makes for these exceptions. The flushing has to be done by the same actor since there's often been nothing else moving around, and it's presumably a part of the standard graphics update routine.
    • This can happen e.g. when you're in E1R1 C3 at -13013, -15, -27000 facing 332.9 and throw a first aid kit. Some pixels are left on the screen afterwards (might be difficult to reproduce).
  • The 2DBOX fields give the part of the screen that is updated every time the actor in question is redrawn. They're given as Xmin, Xmax; Ymin, Ymax. The game sets everything to -1 if the actor is off-screen instead of them growing past the 320 x 200 limits. Once the off-screen actor is starting to wrap around in 3D, the values wl show something else again.

RENDERER

  • Background information: modern computers are byte-addressable. This means memory is accessed or written only in 8-bit chunks. Thus, if we have two bytes, 00000111 = 7 followed by 11111000 = 248, it is not possible for the game to be tricked into reading it as, e.g. (00000)11111111(000) = 255, not even when it's crashing.
  • The game is rendered in 320 x 200, 256-color VGA.

AITD palette.png

  • Most camera views have a FOV of around 60-80 degrees. See the cameras spreadsheet for more detailed information.
  • Polygons are made of an ordered set of vertices. The polygons are rendered using a polygon-filling algorithm that doesn't utilize triangles like in modern games, instead rendering whatever shape the polygon happens to have. However, all polygons in the 3D models (BODYs) are convex.
  • The renderer is capable of writing a single pixel (one byte) or two adjacent pixels (two bytes) at a time. It can also do 2-byte chunks when copying pixels from place to place.
  • A part of what the renderer does is apply the rotations to each actor's 3D model based on the angles they're facing, and again based on the current camera view.
    • The rotations are applied in the order alpha-beta-gamma the first time, but the order could be different for the second rotations.
  • The game has four screen buffers:
    • BACKGROUND1: A direct copy of a 2D background from the PAK file. Never overwritten after uncompressed. This is the third one in memory. Every time the camera changes, a new image is loaded into BACKGROUND1.
    • BACKGROUND2: The same but with non-animated actors rendered onto it. When a static actor starts moving (gets the animated flag 0001 or redraw flag 0004), it gets removed from BACKGROUND2 by copying BACKGROUND1 and re-rendering all non-moving actors. The idea is to avoid having to render things that aren't currently moving. Similarly, when an actor becomes static, it is drawn into BACKGROUND2. This image is the second one in memory.
    • BACKBUFFER (offscreen): The same as BACKGROUND2 but with moving actors rendered also. When animating actors have to be re-rendered (every frame), the previous polygons are erased by copying small 2D rectangles from BACKGROUND2 to BACKBUFFER. Without the BACKBUFFER you would see all rendered objects flickering all the time. The BACKBUFFER has a size of 64320 (slightly bigger than 320 x 200) and it's the first one in memory.
    • FRONTBUFFER (screen): Not stored inside the game's memory at all, but is stored inside DOSBox. It should be an exact copy of the BACKBUFFER but desyncing may occur. This is because the game doesn't copy the entire BACKBUFFER, just parts that have changed, for optimization.
    • To see these buffers in the game's memory, you can use the Memory Viewer.
    • All copy instructions are done in the order the buffers are listed in here and never the other way around.
  • The way polygons are drawn is the game first uses 3D projection to figure out which pixels the vertices of every polygon visible should be placed on, then a polygon filling algorithm to fill them with the right color.
    • A slow-motion demonstration of how a single frame is drawn taken straight from the game's backbuffer:

File:Rendering a Frame in Slow Motion.avi

    • Polygons are drawn on the current 2D background one at a time for actors that have flag 0x0001 (animated) or flag 0x0004 (redraw). The box on the floor doesn't have to be redrawn. The game proceeds from top to bottom, left to right, one row at a time (slow it down even more to see this).
    • The parts that are supposed to cover the actors in the 2D foreground are redrawn based on the actor standing inside a mask trigger not shown in the Room Viewer.
  • The full pipeline each polygon goes through:
    • 3D local => 3D world
    • 3D world => 2D projection
    • calculate 2D bbox
    • 2D polygon clipping
    • break 2D polygon into horizontal lines
    • fill horizontal lines
  • When a 3D point is too close to the camera (in front of the near plane, with a Z-projected value of 50 or less), it gets rejected by the drawing algorithms and placed at (-10000, -10000).
    • The game also clamps vertices with Y-value > 10000 in E0–E4 (and presumably E7). This doesn't normally do anything, but in E5 and E6, where WATER is 1, this value changes to -600 to make it look like the bottom half of the actors that fall into the water (which is always at that elevation) is obscured by it.
  • When splitting polygons into horizontal lines, the engine fills two arrays. Each contains 200 entries (one for each horizontal line on the screen). The first contains the X start position of each line, the second the X end positions. This is sufficient since all polygons are convex.
Both arrays (side by side)	what will be rendered on screen (horizontal lines)

0, 0,
0, 0,
50,50                                        x          
50,51                                        xx        
50,52                                        xxx      
50,54                                        xxxx
50,54                                        xxxxx
50,52                                        xxx
50,50                                        x
0, 0   
0, 0   
0, 0   
  • Sometimes, if you push a pushable actor OOB and change rooms, it might become invisible even if other actors in that room are rendered normally. This probably can't be the background/foreground draw order because there clearly isn't anything blocking the view in many such trials. In fact you can happily push the actor around in front of you and still not see it.
    • This might be explained by the renderer only considering drawing any non-animating actors that are in camera triggers for cameras contained in the current room's camera IDs list (for LIFEMODE 2 or -1 actors; see "CAMERA VIEWS").
    • (Probably the same effect) If you're defocused and far away from the camera view (probably you have to be in a view that's not in the focused actor's current room's camera IDs list), if you try to execute a melee attack, the hotpoint stays stuck in place, always on the same NE side regardless of your angle. This is very likely because such actors are simply not animated, even if it's the PC.
  • The graphics update in principle only up to 70 times per second but this limit is not adhered to very strictly due to its implementation, causing it to be possible for it to peak at well above 100 FPS if you increase the CPU cycles enough (which is forbidden). More under "main loop".
  • The game skips rendering a frame when returning from the inventory.
  • The game uses 2D masks on the backgrounds to draw over parts of any actor standing in corresponding mask triggers, placed in the game world in places where, in each given view, an actor standing there should be behind something in the background art. The mask placement isn't perfect, which is why things are often drawn wrong if you go to unusual locations such as somewhere OOB, or even some directly accessible corners.
    • The rendering pipeline:
1) draw the main 2D background (without actors)
2) sort actors depending on their Z value (distance from camera) from back to front
3) render the actor furthest from the camera
4) for each mask trigger: check if the actor is colliding with it
=> if yes: render the associated mask on the screen (this will cover the actor)
=> if no: do nothing
5) proceed with other actors (moving closer to the camera each time)
    • Illustrations of the 2D masks: in the first image, the different masks in a particular image are shown in different colors; in the second image, the associated mask triggers are shown from above; in the third image, what will be redrawn on top of the actor inside the mask trigger is shown. The fourth image shows the data as it's stored in the game's files as a 1-bit map mostly built out of polygons (in some rooms, individual mask pixels were added in to make the illusion more perfect).

2D WRAPAROUND

This is a complicated bug that is only understood on a broad level. The information given below may have mistakes and inaccuracies.

Render Glitch Location.png

Consider a simple camera viewed from the top (Fig 1 in the above illustration). The arrow shows the direction it's facing. Any object 
outside the camera frustum won't be visible on the screen. This is the red area in Fig 2. If the projected vertex's 2D X-position wraps around 
320, this is where the glitch can occur. Objects behind the camera are automatically culled, that is why that area is black.

If we make that wrapping-around value bigger, the red area will become smaller (Fig 3).

In AitD, the X-position wraps around at 32767, which is more than 100 times bigger than 320. That makes the red area very small, almost just a 
line (Fig 4). There is still a possibility for the glitch to happen though, around where that line is.

Note: things are simplified a little bit here as we consider only a 2D frustum. It is actually a 3D object and it can be rotated (depending on 
the camera angle). Instead of a line we should consider a plane. This plane is the one drawn in the Room Viewer when you click on a particular 
camera trigger. Anything that crosses that plane can cause the glitch.

Another requirement is that the object whose vertex or vertices are wrapping around be far from the camera position (see Fig 4 to understand why).

Here is a basic look into how the glitch happens:

1) There are actors animating in the game world and thus need their graphics refreshed.
2) To know which ones are in-view, the engine cycles through all of their 3D vertices from a 3D array and projects them onto 2D points based on 
the camera position, regardless of which room they're in (they could be visible through doorways etc.). The 2D points are stored into a 2D array 
that allows for checking [Xmin, Ymin] [Xmax, Ymax], i.e. the smallest box that encloses all of the points in the array.
3) This enables creating a 2D bounding box for each actor for checking if the box is in the view or not. This box is also where the 
actor will be drawn.

2D Draw Boxes.png

4) The game has to draw the ones whose bounding boxes are even partially in the current view regardless of their position (green in the 
above illustration). It does clamp the polygons so it only draws into the assigned 320x200 area.

Clamping.png

5) In some views, the camera projection is such that some actors' 2D projections will be really stretched-out.

File:Pirate Stretching Glitch.avi

6) In the first example above, the pirate is really overstretched, to the point that during some frames of its animation, some of the 
polygons will go past 32767: these values will then be interpreted as negative ones and so they end up on the opposite side of the 2D plane. 
Similarly with Emily in the second pair of pictures.
7) Now the game thinks the pirate is inside the view and attempts to render it.
8) Some of the pirate's polygons contain edges that start at a very large positive value and end at a very large negative one.
9) The renderer will thus draw very wide lines that overflow the backbuffer's 64320 limit. Pixel data will be written into memory past the 
backbuffer.
10) Those memory locations contain important data (incl. the VARs), and when the game attempts to read it next, it leads to further issues, 
usually causing a crash.
    • In the pirate's case, the overflow tends to happen when it's in the middle of its walking animation near the N end of E3R2. It has frames during which the arms are thrown up and some of their vertices get projected far away.
    • It has been found out since that the pirate may pass the check for what should be rendered even when he's 100% off-screen and not wrapping around. Thus it is not strictly required for an actor's 2D bbox to intersect with the 320x200 screen for it to pass that step. The clamping algorithm should still prevent any of the polygons from actually getting drawn.
    • If the game is forced to allocate a backbuffer of 65535 bytes instead of 64320, it no longer seems to crash due to the pirate. The backbuffer is corrupted but there are no further effects. However, when walking around with a sword out in E3R2 (while the camera is still in R1, ID 2), it first corrupts the backbuffer, then at some point causes a crash by corrupting what is above it. It's not understood what happens in that case.
  • The same idea can be applied to individual polygons:

Degenerate polygon.png

    • Each polygon has an ordered set of vertices (Fig. 1). Let's say that vertices 3 and 4 wrap around. Now it has become a huge polygon, crossing almost the whole 16-bit range and probably the 320x200 screen as well (Fig. 2).
    • The rotation direction (1-2-3-4-5) is now reversed (CW vs CCW). Normally such polygons are considered back-facing and should not be rendered (it's called back face culling). It's not clear how it impacts the renderer here (maybe it doesn't expect to see such polygons because they have been filtered before).
    • But there is a worse case: if only vertex 4 wraps, it is now a totally degenerate polygon (Fig. 3).

Some parts look like triangles (Fig. 4). Here is an example of what such polygons might look like in the backbuffer. Render Glitch Visualized 1.png

    • Also, on those crash screenshots (more presented later), we sometimes see diagonal lines, stretching from top left to bottom right.

If we apply what I explained above to the pirate it gives this (which explain the diagonal stripes). Orange lines are where some unwanted polygons are likely to appear. Diagonal Wrap-Around.png

    • What is it that goes wrong? Two possibilities:
      • 1) clipping part (which cannot handle such weird polygons). Polygon clipping is a task more complex than you think so it's likely to produce garbage output with such unusual input.
      • 2) the next part (polygon-filling). In all AitD 3D models, all polygons are convex. It's probably a limitation of the renderer itself. The polygons in Fig. 2 / Fig. 3 are clearly not convex anymore.
  • There is a strip of yellow/pink a little ways above the backbuffer that flashes whenever polygons are being drawn. This is the list of polygons to be rendered. Invalid data in this region (due to degenerate polygons) might cause the framebuffer overflow, though it could also be happening in the list of lines to be drawn, stored elsewhere.
  • Polygon clipping failing is now understood to be the cause of at least some instances of the 2D wraparound:

Polygon clipping.png

    • Normally polygon clipping makes sure that a 2D polygon fits in a given 2D rectangle (320x200 in this case). It is faster to clip polygons like this than having to check with each pixel whether or not it's inside the allowed region (that's a lot more checks). If needed, the polygon shape will be changed (Fig 1). As you can see the polygon had 3 vertices before, now it has 5 vertices.
    • In AitD, polygon clipping is done in four steps: left, right, top and bottom, in that order (Fig 2).
    • Note: it will clip a polygon even if it is totally outside the 320x200 region (Fig 3).

Top overflow.png

    • The glitch:
      • Let’s start with a simple 2D polygon made of 3 vertices. One vertex is outside limits because of extreme camera angles (Fig 1). In AitD, it means that vertex wraps around (Fig 2). Now let’s clip what is on the left. What should happen (Fig 3 and Fig 3 bis) is different from what actually happens in AitD (Fig 4).
      • Here is why: to clip, it has to calculate intersections between each side of the polygon (the yellow lines) and a vertical line (the green line). At some point it has to do some subtractions on values (the vertex's coordinates) which are already close to the limit (like -32666) and they will wrap a second time. It causes the calculated intersections to be incorrect and thus the clipping to fail (Fig 4).

There is now some polygon stuff at the top of the screen. It will overflow the memory later (Fig 5).

      • After that, clipping what is on the right and bottom will occur, but it will not fix the problem (Fig 6). Why is it not clipping what is at the top? That’s because it uses the bounding box of the 2D polygon to know if it has to be done or not. Since the original bbox does not collide with the top it is skipped (see Fig 2, it does not overlap with the top).
      • The above probably explains why having vertices that wrap around is not enough for the glitch to occur. Even when the clipping fails, if the clipped vertices end up below the top of the screen, this is fine (Fig 7). No need for math to find out where the wrongly clipped vertices will be. You just need to trace imaginary lines that extend the polygon sides (before the wrapping has occurred). See Fig 8. Now compare it with Fig 4.
      • I think we can safely say that polygons that are overstretched and form a sort of diagonal line which is pointing in the direction of the 320x200 screen region are likely to glitch. Here is an example in FitD:
      • The unwanted pixels in Fig 5 will not necessarily overwrite what is above the backbuffer. The code that breaks a polygon into horizontal lines (which occurs right after clipping) will glitch as well (again some values might wrap around if they're now negative), so it's possible that those unwanted pixels will overwrite what is below the backbuffer instead.
      • This was all discovered by debugging AitD game code step by step (not FitD). It’s not just theory although there's still a chance some point is incorrect. The fact that the game clips polygons that are outside the screen completely is probably just a missed optimization.
    • The pixel plotter (polygon filler) receives: two arrays, the number of lines to be rendered, and the start position of the first line to be rendered.
    • Possible issues if the polygon to be rendered is above the top of the screen:
      • The two arrays only contain 200 entries each but the polygon in Fig 6 is taller than 200 lines, thus causing the arrays to overflow.
      • The starting line position will be negative, which will be interpreted as a positive number. Thus the filler might start plotting pixels lower down than it should and thus could overflow into what is below the backbuffer.
    • There have been crashes during which the offscreen buffer was never corrupted at all.
  • The reason not all glitched polygons are not getting drawn on the screen is simply because the game only copies over small 2D rectangles of pixels from the backbuffer to the screen where the 2D bboxes of the newly rendered actors are. In the case of the pirate, the bbox is completely outside the screen (it's calculated right after projection before the clipping part). In the latter of these two pictures, even though some point has wrapped around, the bbox is still below the screen.

2D bounding boxes.png 2D bounding boxes wraparound.png

    • It should also be understood that when you approach the 3D edges of the game world and wrap around, this doesn't mean you've reached the edge of the 2D vertex space. Even though this often causes polygons to be stretched from one side of the world to the other, nothing necessarily overflows as this happens. In the first picture below, all the 2D bounding boxes are displayed in green. In the second picture above, a polygon from the pirate has wrapped around, but it still fits within the 2D-bounds given by the current camera projection (the big red box).
  • The save file labeled "Should Cause Crash But Doesn't.ITD" should cause a crash seeing as if you look at it inside FitD, you can see that Emily's polygons are going past the red boundaries. However, it doesn't. The theory was that the left side of the screen is actually immune to the 2D wraparound for whatever reason but that isn't true after all. It still seems far more rare. This location is the first one where left-side glitching was observed.
  • Why is data sometimes overwritten during the 2D wraparound that is above the backbuffer (CVARs and actor data)? This could be because:
    • The rendered might use signed integers (-32768; 32767) to render lines. If the data fed to it is invalid, it could reverse the direction of drawing the output.
    • It's not the renderer overwriting the data before the backbuffer but rather some other pointer was corrupted and another part of the game is writing its data there.
  • It only takes around 400 pixels worth of data to overwrite every VAR. That's just over one full line. This is why the corruption tends to overwrite all of them in one go: just one stretched polygon can easily fill much more of the screen than that. This also suggests that the corruption can reach parts of the memory far past where the VARs are although the limits are impossible to understand based on current knowledge.
  • When the game is running more slowly, it becomes more resilient to the the 2D wraparound. This is simply because when less frames are getting drawn, polygons get less chances to be stretched in the right kind of shape.
  • Just the polygon filling algorithm drawing polygons that it shouldn't might not be enough to explain all the effects of the 2D wraparound. 16 bits = 65536 values, just enough to fit 320 x 200 = 64000 pixels, but there's 1536 values that don't correspond with pixels on the screen. The game even has an extra empty line reserved for overflowing polygon information, but that can only fit another 320 pixels. The problem is the game will continue to crash even if the size of the backbuffer is incrased to 65535. Also there is often corruption in the CVARs and actor data higher up. Thus this isn't enough by itself.
  • You can see where the game drew the extra polygon data during a particular crash in these before and after images displaying the game's state. The crash was reproduced by loading the save file called "Crashes instantly sometimes.ITD".
    • Before:

Beforecorruption.png

    • After:

Aftercorruption.png

    • The first data shown here is the CVARs.
    • Next the actors: each of the 50 possible actors fits into half-a-line.
    • The BACKBUFFER is shown here in blue and you can see an extra empty line down the bottom (zoom in) that you can see is not filled in. After the crash, some extra polygons were drawn over the rest of the image (shown in red and green). Some of the red has overwritten data down the bottom. On this occasion at least, the crash wasn't immediate because the game had time to draw in Emily as well.
    • Below the backbuffer and the extra line, you have a small amount of data of partially unknown usage. Some of that data could be the PRIORITY.ITD file (which is only a few hundred bytes so it's resident in memory) and a bunch of data structures that the game uses for loading PAK files. If this is true it means that any loading of PAK files after that data has been corrupted will [probably] crash the game.
    • Below that, you have the VARs, in a vulnerable position.
    • The gray area below that contains the main 2D background (BACKGROUND2).
    • Below BACKGROUND1, we have the cache, incl. the sound samples, and possibly other data.
    • This video shows the result of saving the game again soon after loading to prevent the crash. You can see the corrupt backbuffer data being drawn on the screen as the player moves around the screen and the areas directly next to them get redrawn.

File:Corrupt Backbuffer (2).avi

    • When you enter E3R1, the game redraws BACKGROUND2 a second time after a few seconds because the pirate in R2 stops animating after one loop of its idle ANIM. The crash occurs exactly during that redrawing part.
    • When you load the save game the following happens:

1) The game corrupts the memory. 2) One or two seconds go by. 3) The game forces a room redraw: it crashes (probably because it read the corrupted data).

  • There was an experiment to see what would happen if the renderer's backbuffer output was directed directly onto the video memory (screen) instead during an instance of the 2D wraparound. This was done around the front doors coming from the save file ("Crashes instantly sometimes.ITD"). Most of the time, what was drawn on the screen was pixels from the pirate's clothes (pictures 1-3) but a few times, 4 was seen instead (the hair presumably). 5 was also seen once (not sure what this is).
    • During this experiment, despite the corrupt data, the game becomes a lot more resilient against crashing. The reason is likely that there's less sensitive data around the video memory than there is around the backbuffer. The game would still occasionally crash in a spectacular fashion, which could look like this. File:Crash While Sending BBuffer Data to Video Output.avi
    • When repeating this experiment around the window in E1R1 (pictures 6-10, coming from save file called "E1R1 Before Render Glitch.ITD"), every time it happened, the polygons seemed to be Emily's, which makes sense because she's the only actor moving around. Picture 9 looks like it has some kind of transparency effect on it. The black polygon in picture 10 could be the "bottom" of her skirt.
    • The predominant shapes of the polygons in both these experiments are either rectangles or (partially rendered) triangles with a top that's tilted towards the right. Triangles and quads are a very common shape in general, of course, but why are there fewer left-tilted edges? Could have something to do with the glitching actors in both cases being on the right side of the camera. The polygons look really stretched-out which is no surprise.
  • The save file "E1R1 Before Render Glitch.ITD" has Emily facing the right direction to trigger the bug in a very consistent way. There is a roughly rectangle-shaped area on the S side of the E1R4 window while in E1R1 youself, although crashing has been seen a bit further away from the window as well.
  • If you encounter some crash, here is one way to test whether it's caused by the renderer or not:

1) Copy File:INDARKDR.zip into your INDARK folder (e.g. C:\GOG Games\Alone in the Dark\INDARK), and extract the .exe. 2) Open file "dosboxAlone1_single.conf" (in the "Alone in the Dark" folder) with a text editor. 3) Modify line 30: INDARK => INDARKDR. 4) Start the game as usual. Animated models are not drawn anymore so use the Viewer to navigate. If the crash no longer occurs, it was caused by the renderer.

  • Checking for where the framerate suddenly bounces really high (if no other actors are animating) when the PC goes towards the camera plane is a good approximate test for where you might have to be to get an actor to cause corruption. The framerate increases when the only animating actor goes behind the plane and is no longer considered for rendering.
  • The 2D wraparound can happen in the dark. This is to be expected seeing as the game clearly keeps animating actors in the dark.


OBSERVED EFFECTS

  • There are lots of examples of 2D wraparound crashes amongst the demonstration videos, and they're also not difficult to produce by oneself.
  • The save file called "Corrupt But Playable Save Example.ITD" was obtained after an occurrence of the 2D wraparound in E1R1. Here are all observed special effects observed in this file. They can all be explained by VAR corruption:
    • Can't open all doors, but this allows clipping against and through some of them that weren't possible to before. E.g. the front doors against the S door. You can first "close" them to reset them to normal.
    • Passing into the death trigger behind the front door causes Emily to run forever without dying.
    • crack doesn't activate
    • vase doesn't break when thrown
    • birds don't crash in
    • you have a lot of hp... but so do the monsters!
    • you've got tons of ammunition and oil
    • ghost doesn't react; spiders, Cthonian and Deep Ones don't spawn; E3R12 is safe
    • touching the dancers gets them to move in a single direction until they hit a wall; they don't transform into vortices
    • the saber looks like it's broken even if it isn't; it inflicts massive damage and has tons of durability though!
    • you can use the saber to kill one of the rats and make SPECIAL 1 (the bubble effect) play a lot when attacking the other two (neither is normally possible since it requires HITFORCE to be greater than 10)
    • you no longer get the bad end screen (but you can't win either because VAR 33 isn't exactly 1)
    • every bridge segment that is liable to fall will instantly do so (after CHRONO has reached 2): if they leave from and return to memory during the fall, they don't get deleted but instead they might get heavily desynced or even wrap around the bottom (ROOM Y has to be exactly 0 for them to be deleted). The splash sound can be played over and over.
    • Got the CVARs to be overwritten by the values 115, 114 and 113 when Emily was at around -2936, 0, -19000 in E2R0 C31. These can't be recurring 8-bit numbers. They look like they might be shades of yellow from Emily's hair, which would make the most sense, but why would they have been drawn in every other pixel/just the latter halves of those CVARs?
    • In addition, if the gramophone had been on the floor, no record would have been picked up together with it unless VAR 97 is 1, 2 or 3
  • The data stored in save files also forms the limits of what kinds of effects can be carried over after saving, rebooting the game, and reloading that file, since the other parts of memory are flushed. Just saving and reloading doesn't necessarily erase all the other effects which could be present in global variables and such.
  • If CVAR 13 gets corrupt, the light spot in the E6 maze may not appear.
  • Some observed patterns in 2D wraparound states:
    • Whenever CVARs get corrupted, it's almost guaranteed to be a softlock or crash.
    • Just one VAR (102, 126) changes to the value 37. In the first case, the music stopped and there was a crash, in the second, there were no extra effects at all.
      • It's not immediately obvious what kind of data this is but it's unlikely (though not impossible) to be from a polygon.
    • As expected, often the values written into the 16-bit VAR slots are recurring 8-bit values from two adjacent 8-bit pixels of the same color (e.g. 10023 = 00100111|00100111 where the repeating part is 39 in decimal). Because of this, it's much less likely to get small values to be written in the VARs.
    • Numbers shown as negative are in Two's Complement and often have recurring forms as well: e.g. -16963 is 10111101 (189) twice and -29042 is 10001110 (142) twice. The ones that don't have a recurring form might be the renderer attempting to write two different color values in adjacent pixels.
    • Another pattern that has occasionally been seen is values rising by one in every adjacent slot, sometimes staying the same for a while before resuming. This could be a color gradient. Gradient textures are used with some objects such as the oil can, the feathers in the bow etc. (see Model Viewer) and are disabled when graphics are turned to "low".
    • Sometimes the glitch has happened in multiple goes: e.g. first all VARs turned into 9509, then some turned to -28785, then some turned to -29556. This is clearly just the polygon draw operation continuing for a few frames because there was no crash straight away.
    • On occasion, little artifacts may appear on the screen. These are probably the result of corruption of the backbuffer. They're easier to see if you're moving.
    • The bug will result in a lot of the same values being written due to the polygons in question coming from the same model usually.
    • Sounds sometimes stop playing altogether.
    • One VAR changed from 9509 to 9615. These are the binary representations of those numbers.
      • 00100101 00100101
      • 00100101 10001111
      • The game probably wrote over only the second byte here simply because there was no pixel to the left of it, or that pixel happened to have the same color as before.
    • The 2D wraparound has also caused the room number to change, associated with the view moving to the wrong place and the PC disappearing. This is probably an instance of actor data corruption. Actors can also be warped around. The result of such an instance is shown in this video where the pirate has been warped out of E3R2 after one of its own polygons caused a 2D wraparound.
  • Have seen a case where the VARs are overwritten and the player loses control over movement and the inventory but it's not a result of VAR 0 being something other than 1. In fact, after teleporting the player to the E1R7 stairs, they ended up in R0 instead of descending them on the first try, with the TRACKNUM being 33 and TRACKPOS advancing 5 at a time infinitely. After saving and loading in the store room, the view went to being outside the window in E1R4 and the player actor disappeared with the room number being 20041. This would keep happening on subsequent reloads. Saving and loading in R4 only had the effect of causing the door actor to be falsely drawn in front of the window. During all of this, up until the point when the first save file was loaded, one of the VARs kept increasing at the same rate as frames were being drawn, pausing when in the menu.
    • Later attempts to warp to the stairs directly resulted in actually descending them, but control was still not returned. During the descent, the same VAR that counted frames before now kept incrementing in chunks of about 100 until the end of the descent. The library doors didn't shut (as expected), and it looks like neither door actually has any LIFE running at all. When warping the player to the stairs trigger to get back to E1, control is then returned on the first try, but not on subsequent ones.
    • The VAR that was stored incrementing timer data/other data into confirms that memory pointers (other than the VAR ones) can get corrupt.
    • The most interesting detail was that saving and loading didn't update the VARs and CVARs as it normally does. When reloading a save file that was right at the beginning of the game, control was returned but the VARs didn't update. Stepping off the carpet (which normally causes VARs 4 and 5 to change) instantly stopped the music and softlocked the game.
      • There's also a chance that what caused the softlock was trying to retrieve samples from the LISTSAMP.PAK file as the first step was being taken.
    • Afterwards, restarting the game obviously removed the non-updating VARs effect (it wouldn't be something that was stored in any save files) but actually didn't restore the player's ability to move. TRACKMODE was still 3, but TRACKPOS had stopped incrementing, left at a high value. It wasn't just the TRACKMODE that caused loss of control because it was already gone after the glitch first happened, but it could potentially be a reason it isn't returned.
    • That the player ended up in R0 specifically may be some kind of failsafe if the game doesn't know where to put them. This kind of warp has never been seen before and cannot be the result of activating room triggers, presumably, since it happened instantly and without passing through R1.
    • It's unclear whether the R7-R0 teleport could have happened without using the Viewer's warp cheat initially. It may also have made a difference that the PC first wrapped around the plane (not shown in the video), which is known to cause confusion with camera views.
    • All of this is recorded in this file: File:Crash 30-04-18 (Bed Reloading, Failed Stairs).avi
    • The fact that the game goes to E1R4 into the window view specifically fits in with the theory that this is some kind of default for that floor. This is also observed when the player enters the E1R7 staircase on the same frame as when focus is given to the second dying nightgaunt, which instantly gets deleted, meaning its floor and room are set to -1.
  • A 2D wraparound has been seen removing the game's ability to reload savegames properly: instead VARs and CVARs were left at whatever the values were before reloading. If this effect was replicated, it could enable creating a glitched save file ahead of time (NG+) and simply running to the exit without performing the glitch during that playthrough. However, restarting the game removed the effect. It's unknown whether starting a new game would have removed it as well.
  • The VARs take up about (16 x 200 = 3200) bits of memory. That's 400 bytes, and so it only takes 400 pixels' data to completely overwrite all of them.
  • If you partially lose control to the renderer glitch, you can often regain it by making it to a transition.
  • There are two things above the VARS in memory (might depend on version played and other factors): TRACKs and sound samples. The game crashing can be a result of it attempting to play a corrupt sample, meaning disabling sounds can be a way to prevent that while keeping VAR corruption. Making sure actors aren't in any TRACKs could also help.
  • A useful hack when studying this bug: open INDARK.EXE in a hex editor and search for the following hexadecimal value: B8 40 FB. Change it into B8 FF FF. This will force the game to allocate a slightly bigger buffer and helps prevent crashing. It's useful when you want to study the impact of the 2D wraparound (with the MV) without having to restart the game because it just crashed.

POTENTIAL EXPLOITATIONS

  • See "MEMORY" for useful information on the game's memory layout at runtime.
  • An über-large-skips category could potentially exist if sufficiently precise ways to manipulate memory through the 2D wraparound or other methods are developed. This may have to at least involve TASing/botting even if the run itself was a real-time one. The most obvious use is to make VAR 33 = 1 so entering trigger 18 outside the front doors wins without needing to visit Pregzt.
    • An important realization is that the second half of each VAR's memory position is just another pixel. There's no reason there has to be a pixel getting drawn on the left of it, on the first half. Thus the game can write on just the second byte of VAR 33, e.g., without simultaneously writing into adjacent bytes on the left or right of it. It's obviously more likely that there is a second pixel in those positions nevertheless, but this slightly increases the chances of getting the desired result.
    • What else would be useful?
      • If VAR 100 is anything other than 0, this locks the N front door (game thinks it's already open) so you can clip against the S door and get out faster than opening the doors. This only works in non-TAS timing though seeing as a TAS ends with the final input, which is presumably when you're about to enter the stairs in E2 seeing as you just need to run (walk) straight towards the exit afterwards. Setting VAR 24 (player moving backwards) would also stop opening doors, however it tends to get overwritten immediately.
      • Getting the INHAND object to be the oil lamp or jug would speed it up even more because of getting SPACE turns this way. Not sure if this effect has happened, but any undefined value of VAR 90 (player_current_action) will work to give SPACE turns.
      • If VAR 4 and 5 are overwritten, this causes the player's footstep sounds to be replaced. The new sound effect might have to be cached first, and so a little bit of time can be lost to this. Setting these to values above 100 might have anomalous effects described below.
    • VAR 25 being 1 (player is in intro) would enable running across the crack in E1R1. This requires the glitch happening before then. Probably not faster than the OOB route though (for a TAS) because you can't get quick rotations on the doors.
    • Because the E2R0-R2 doors closing causes the evil laughter to be cached, it would be best if the doors didn't close. Thus changing the N door's LIFE to something else would help. Barring that, there's no VAR that could prevent this.
    • Generally, changing LIFEs around, seeing as they're all cached on load-up.
    • PRIORITY.ITD is also cached but only has the sample priority data in it. If it gets corrupted, it's unlikely to result in something useful.
    • Fail cases:
      • The game freezes irrecoverably (happens about every second time on average, CVAR corruption often involved but not necessarily).
      • The player HP (VAR 20) set to 0 or less.
      • VAR 0 set to something other than 1 unless you were already running towards a transition, in which case you should regain control afterwards. Even then you'd probably lose time to a non-optimal stairs descent trajectory.
      • There may be other VARs that cause more lag if set to unfortunate values, e.g. monsters being active.
      • Getting a door's "open" VAR to be 1 means you can't open it. If you also can't clip through that particular door, this might be a problem; on the other hand only the front door needs to be opened, and it can be clipped through, it's just not faster (in TAS-timing) compared to running into it.
    • Other ideas:
      • If you use the inbounds route through E1, and somehow get the glitch before reaching R4, VAR 45 set to 1 (E1R4 bird alerted) would mean you'd only have to enter R4 once to start the camera defocus clip: might mean clipping through the R4 door is faster than opening it? On the downside you still have to wait for the CHRONO to reach 2 before you're okay to leave the room, unless you can also mess with the ROOM_CHRONO for that room somehow.
      • VAR 50 (unbroken mirrors placed) being 3 would cause the nightgaunts to start dying immediately, however the death ANIM probably takes too long compared to other techniques of bypassing them? If also VAR 59 was set to 0 (nightgaunts left) seems like this should cause the S nightgaunt to be deleted immediately, but the stairs take longer to descend on that side... and in fact it doesn't work because LIFE 91 needs to start the S nightgaunt's timer first and it needs to reach 5 before the deletion happens. Even this fails unless VAR 59 was actually 1 not 0.
      • Aside from possibly E1R4, overwriting CHRONO values could potentially help, however it would have to happen at very specific times such as when the nightgaunts are dying. There's no relevant timed events in E2-E3 at all. Could maybe get the character's ANIM to roll forwards? Never seen that happening.
      • Somehow getting the oil lamp into the inventory might save time if it happens early enough even if it means you have to manually select it (never seen the inventory being affected though).
    • Sounds sometimes stop playing altogether which could save frames if this also means no loading lag from them.
      • Some other miscellaneous effects could help a bit but it's unknown if they're even possible.
  • Setting VAR 33 to 1:
    • The palette colors corresponding to the value 1 is a pure white color. This is only used in two different BODYs: the first aid kits and the chauffeur (as seen in the ending). The color 0 is pure black.
    • Either the game would have to draw a black pixel on the first half of the VAR 33 location and a white one on the latter half, or nothing at all on the first half and a white on the second half. The medkits don't have black on them so looks like only the latter option is viable.
    • Despite attempts to throw the medkit towards the pirate (flying just past) in E0R0 in view 2, and despite the Free in the Dark hacked version showing that the medkit's polygons do go past the red lines, this hasn't even once resulted in a crash, not even when the medkit was manually warped up to -1200 to -2500 Y to try to help things out (all coords were adjusted at the same time but the resultant values were desynced, shouldn't have mattered so much though). There also haven't been any observed crashes resulting from throwing items OOB that actually overwrote the VARs or CVARs either, though this is not exactly ruled out yet. Unless such a crash can be caused using either method, no other simple way is known for how to set VAR 33 to 1 directly.
    • See "MEDKIT 2D WRAPAROUND TRIALS" for more.
  • Actor data corruption: This has been directly observed on a few occasions with randomly scattered actors having unusual data. In the majority of cases, corrupt actors become invisible because they're sent to rooms that don't exist or something similar. There are, however, at least two ways in which actor data corruption could directly end the game without needing to change VAR 33 or enter trigger 18. These are to either get any [focused] actor to have LIFE 542, or to get the camera target actor to be OBJ288 (which has that LIFE).
    • From LIFE 542

ANIM_REPEAT exiting_house_and_cheering_in_endgame LIFE endgame_player_cheering (sends the actor to E7R0)

    • From LIFE 562 (executed at game start)

OBJ288.CHANGEROOM E7R0 -2500 -270 4900 OBJ288.BODY player_outside_during_intro OBJ288.ANIM_REPEAT walking_outside_in_intro_or_endgame OBJ288.LIFE endgame_player_outside_initiator (#542)

    • From LIFE 550 (hitting some floor trigger)

CASE 18 IF Pregzt_dead == 1 CAMERA_TARGET OBJ288

    • Some tests were done with this:
      • Emily's LIFE was changed to 542. This caused her to throw her hands up and then appear in E7R0. The cutscene softlocked when the car got stuck on the indoors Emily's giant feet (the actors normally used in the outdoors scenes are tiny).

File:End forced emily.avi

      • The E0 bird's LIFE was changed to 542. This caused it to run the same cheering ANIM and be moved to E7R0 but since the focus was on the player, this didn't result in the ending cutscene getting played fully.

File:End forced chicken.avi

      • The E0 bird's LIFE was changed to 542 while the camera focus was on it. This resulted in the same ending as with the Emily experiment except the actor in E7 blocking the car was the bird.

File:End forced chicken window.avi

      • The next loaded room was forced to be E7R0. The game goes into the view that's normally shown last (possibly just the default view since the camera target is never set as OBJ288). The cutscene plays out normally.

File:End forced E7R0.avi

    • It's a bit difficult to define the win conditions in a run that corrupts memory in one of these ways. In any case since the category is probably TAS-only, TAS timing will be used regardless.
    • Barring these ideas, just a controlled teleportation of the PC other some other actor could be useful. This has been observed at least once without the game crashing, after reloading the save file called "Crashes instantly sometimes.ITD". The result can be seen in this video.

File:Pirate Warped From E3R2 to E3R0.avi

  • Corrupting VAR 9 (what is being dropped) in the middle of dropping something:
    • Causes the player to drop the wrong actor whether or not it was in the inventory, though only if that actor wasn't already present. The object selected to be dropped remains in the inventory. Dropping something other than objects, some heavyweight pushables, and some other actors makes the game crash instantly if you touch the dropped actor with major corruption in the CVARs and actors vanishing, suggesting something happens to the actor data. This is probably a result of dropped actors being instantiated in a different way compared to ones loaded in with room changes. The most likely suspect is flag 0080 (can be picked up), which is set for all actors dropped. Most of the heavyweights seem to be safe to touch because their LIFEs set their flags on every frame.
    • Spawning various room scripts can make the game dark or lit.
    • Deleted objects can't be spawned this way.
    • Aside from spawning something to get something to happen in the area you're in, this can also prevent things from happening wherever when they were meant to happen. E.g. disarming a trap.
    • Examples of what happens when dropping actors this way (every value was tested):
      • 0: this is the chest from E0R0. Dropping objects like this allows you to refurnish the house but their behaviours are not altered. Dropping a door would allow doing the Door Rotation Trick in an arbitrary location.
      • 1: trying to drop the player actor doesn't have any visible effect because it is already present.
      • 2: this is the "Actions" object that has LIFE 562, which causes all the initiation actions to happen again. Sets the camera target as the player, changes the music, changes INHAND to "Actions", sets current action, changes the front door behaviour, puts the nightgaunts in place, closes some doors, prepares the endgame player actor and car, deletes the frog and sets the "player in intro" flag to 0. Afterwards, the Actions object doesn't stay at the top of the inventory like it's supposed to, but in general dropping actor 2 doesn't have a huge impact on the game being played.
      • 11: this is the E0R0 room scripts actor. It's invisible because it normally gets its shape and size from a collider. After it's been dropped, it's still active and causes colliders to respond to being searched.
      • 19: this is the E2R1 room scripts actor. It can light the area. If the player is in effect trigger 0 or 1, causes the game to follow an axe or arrow in E2. Because the PC is not present, though you have INV, you can't really do much because the game is left waiting for an ANIM to finish if you try to e.g. drop something else. If it's the arrow that's focused, some of the doors look weird, with missing bottom parts.
        • Something strange happens if you equip certain items.
          • Actions: choosing "fight" and holding SPACE and LEFT may cause a softlock. Other actions can have the same effect.
          • Sword: holding SPACE and DOWN or LEFT causes the word "Actions" to be displayed on the screen. If you hold RIGHT instead, the sound played when punching is played repeatedly (if the game is following the arrow, it's one of Emily's grunts (something between samples 17 and 25), and if it's following the axe, Edward's sound 20). UP makes the game fade out and go back to the main menu. In addition, if the game is following the axe, LEFT makes blood be spawned where the axe is.
          • Any dagger: same as the sword, except LEFT and RIGHT also cause a softlock.
          • Saber: just holding SPACE causes the saber to lose all its durability and the sound of it breaking is heard. Adding DOWN, LEFT, or RIGHT causes "Actions" to be displayed. In addition, RIGHT creates an indistinct repeating noise as if the game was playing a sound sample from random memory data. UP causes returning to the main menu. Blood comes out if it's the axe being followed and you're hitting LEFT.
        • If you were in the dark instead when you hit trigger 0 (none of the dark areas have trigger 1), the results are broadly similar though not exactly the same:
          • The "You feel weak" message when reading the Necronomicon flashes really briefly for some reason, the sword causes softlocking, the saber plays a slightly different noise when holding SPACE and RIGHT.
        • Generally these effects suggest memory is being read from the wrong locations, though an exact analysis is impossible with current knowledge.
      • 20: the axe. It starts following a random actor (29547, -24594, -22289) instead of the player, suggesting that dropped objects don't get initiated in the same way as others. OBJETS.ITD has the information about what its TRACKMODE and TRACKPOS are supposed to be. The axe starts moving around generally in a circle but doesn't seem to have any particular coordinates it's homing in on. It isn't able to hit room triggers. It is able to push lightweights. It has HITFORCE 0. Running into it causes a crash. It doesn't hit you if it passes through you.
      • 21: the E0R0 bird. Works the same in other respects except for causes crashing when collided with. One of the enemies you could drop to temporarily defocus the player.
      • 79: the E2R1 arrow. Actually works as intended except doesn't follow the player.
      • 286: the car. It doesn't do anything of interest since it has no LIFE set during gameplay.
      • 288: the player in the outro. Dropping this changes the music and the (tiny) Emily celebrates her victory but is then sent to E7R0 without the camera having focused her so the ending doesn't really play out. This can hardly be seen as a win.
    • Dropping Pregzt: Technically, you could go to a trigger with ID 7 (only in E5R10 and E6R6 itself), drop the E6R6 scripts actor (117), the Pregzt actor (40) and place the talisman, it actually gets placed and it seems the game removes your INV for a few seconds. However, there doesn't seem to be any way to throw the lamp at Pregzt since the dropped actor doesn't have a bbox.
    • Corrupting VAR 9 with values outside the usual range of actor IDs (0-292) in the tests that were done always either resulted in an invisible actor being dropped with no visible effects, or the message "there is no room" showing on the screen despite there being plenty of room ahead of the PC. It's almost completely impossible to know if dropping some specific one could cause anything special to happen, and probably dependent on the exact gamestate.
  • Corrupting VAR 90 (player_current_action):
    • If this VAR gets the value 32, afterwards, you're able to jump around inside the house so long as you never do anything the changes this VAR, and so long as INHAND is actions.
    • Any value that isn't defined in LIFE 561 only causes a delay turn after holding SPACE with no other obvious effects.
  • Corrupting VAR 204 (temporarily stores BODY when drinking) with actually existing BODYs:
    • Doing almost anything resets your BODY. Includes getting hit, trying to switch actions etc. You can still move around if you had an action selected that allows for that.
    • Sometimes, after the BODY has changed, it glitches the ANIM out so it never completes the first keyframe. This causes you to reel out in a direction before soon snapping back again, and you're generally quite stuck. The strange part is you're not even necessarily in an ANIM that should cause you to move, and by default, are left in Emily's idle ANIM 4. You get out of the state simply by changing to a different BODY.
      • This could have something to do with the starting time of the current ANIM being stored in the BODY.
      • Which exact BODY you've got seems to determine whether this glitch happens or not. It could be that BODYs that don't usually animate have something different about their data, because it seems to be things like that that react in this way.
      • At other times BODYs that aren't supposed to animate simply won't move at all despite seemingly being in an ANIM that should cause that.
    • Changing to another player BODY changes the basic ANIMs to the version suitable for that BODY, but this won't change your INHAND or action. It will still enable the same anomalies that are normally present with the right BODY:
      • If you have the lamp BODY, if you try to equip it, nothing will happen.
      • Falling to one's death won't happen with certain BODYs.
      • Can't move around normally with flask BODY.
    • Changing to the BODY of another actor that has the same exact BODY causes its ANIMs to be linked to yours. One or the other actor gets preference though and controls what ANIM it is. The other actor won't even be moved at all except for a short jittery back-and-forth.
      • If you do something like open a chest while having the one it has when opening, the game softlocks.
    • Pushing VAR 1 stays at 1 after pushing a heavyweight.
  • Corrupting VAR 204 with values for BODYs that don't exist:
    • The first several values past 272 (the last BODY) cause a burst of loud noise! Crashes the game having overwritten either the VARs or the CVARs.
    • The values around 276-280 cause some visual corruption on the screen.
    • 282 caused DOSBox to display the message "Reboot requested, quitting now."
    • Other values cause similar results. The results don't just depend on which value you chose, though, and as such are very unpredictable.
  • Corrupting some CVAR with useful values:
    • Of the CVARs that are understood, the sample pointers seem relevant if they can lead into further corruption (see "SOUND BUG").
    • Also if you could corrupt CVAR 7 (initial camera target) after it's been set but before it's been used, you could get the camera to OBJ288.
    • CVAR corruption during the 2D wraparound is often associated with instant crashing but they have not been thoroughly tested using the Viewer. What each of them does isn't even known, though many of them are just pointers to various samples such as the introductory speeches by the two characters.
  • Regardless of how their results were reported to be here, any of these tests might give different results depending on the initial memory state.


MEDKIT 2D WRAPAROUND TRIALS

  • This is about attempts to get the white polygons of the medkits (only available items that have them) to corrupt memory usefully, specifically to change VAR 39 to 1. The tests have all been done in the English CD-ROM version. While each test was repeated multiple times, there's no guarantee the reported results were actually the only ones possible from that starting state.
    • This testing probably has to be continued in a TAS environment seeing how complicated and finicky the memory corruption is. There is currently no known way to get a useful corruption.
  • Throwing the medkit (shouldn't matter which one) in C12 in E1R3, from a position of 8906, 0, 1037 and facing an angle of 88.9, and at 11.000 CPU cycles, gives a frame of glitching that appears to originate from one of the upper vertical yellow parts on the sides of the box that have a gradient. Also the same polygon looks to be drawn on the frame when the medkit suddenly lands on the opposite side of the room, suggesting the backbuffer might have been corrupted more widely. Walking around in that part of the screen confirms this, showing that at least the color on the lower half of the perpendicular sides of the box, along with other colors but not the pure white, were written into the backbuffer.
    • Another trial, at 22.000 cycles, caused a crash where the VARs were overwritten with 24672, which is twice 01100000, or 96, corresponding to a light blue color on the top half of the perpendicular sides of the box. When this happens, there is first the frame of visible corruption on the screen, then the next frame shows backbuffer corruption in the MV, followed by another frame during which the very top and bottom of the backbuffer are overwritten as well, clearly going into the data below as well. (But the MV is probably one frame late)
      • Also at 22.000 cycles, the game occasionally lets the medkit wrap around the game world and fly across the left side normally, suggesting an increased FPS can have chaotic effects.
    • Leaving the other variables as they are and going up to Z 1100, 1200 or 1300 doesn't yield results. Nor does going down to 1000. In fact, even at 1036, nothing is happening. Instead, at 1038, the backbuffer is again similarly corrupted, this time with the lower blue of the perpendicular sides being the only color.
    • At 1039 Z, again, nothing happens.
    • The glitch is also ultra-sensitive to changes along the other axis. If you start even one unit further W or E, you can see that the colors in the corrupt backbuffer have shifted further up or down by a small but easily noticeable amount. At 8895, 1037, the polygon is the same one as at 8906, 1038.
    • This view is one that seems to cause the side facing the camera to be drawn during the glitch (no surprise), and thus the hypothesis is that if you want a pure white polygon to be drawn instead, the best chance is if it's the one on the top of the box and the view has to be analogous but facing the E (when the medkit is thrown, the top always faces W).
  • Resuming testing in E3R7, which has a view that's almost exactly eastward. Throwing from 2422, -7596 at an angle of 359.6, 11.000 cycles. Causes a stretched polygon that looks totally horizontal to appear near the middle of the screen during the wraparound. The trend is that this results in softlocking if Emily is standing in the shot and the glitchy strips pass over (really under) her, but not otherwise. This might work the same for any animating actor, and also might be a different bug altogether (camera wraparound). The strip passed under the door as well but that doesn't cause problems.
    • Throwing from 4679, -6858 results in the strips appearing near or at the very top of the screen. This time Emily is able to stand some ways inside the shot with no problems, but once she gets further towards the middle of the screen, the softlocks return.
      • Observing even the red from the red cross being drawn even though it's on the top (and bottom), possibly because the view is pitched slightly downwards.
    • When throwing from 6841, -6996, there are no visible glitched polygons on the screen at all, but the game still softlocks if (and only if) Emily is standing inside the view. Even when the part of her that's off-screen can't possibly overlap with anything that's going on above it.
    • Going further W (and thus closer to the camera) to -600, -6700 causes the VARs to be overwritten with the value 24672, previously established to correspond to a light blue polygon, and the game to crash with a "not enough memory" error soon after. This corruption happens well before the actor has reached the edge, suggesting again that this and the "camera wraparound" are not related, though generally speaking both the wrap-around polys and the memory corruption polys are drawn further down the closer to the camera the actor was.
    • Going to -500, -6700, the throw does nothing. In fact, sometimes even going back to -600 doesn't do anything until after a reboot. Neither -599 nor -601 X seem to cause this corruption. Going further S or N eventually removes it (-6400 Z works around half the time). Going yet further (-6250), the corruption happens every time again. You'd expect the periodicity to be 50 or a 100 units since that's how far the thrown object travels in a frame, but the framerate is also a factor.
      • -600, -6200: every time
      • -600, -6150: almost every time
      • -601, -6150: did work, as did -602
      • -603, -6150: doesn't work, nor do any other X-values immediately to the W of it
  • Couldn't get anything done in E1R4 C7 (slightly tilted N), not even causing a crash with the PC, not even if they're first lifted up to -2000 or -3000 Y.
    • Also couldn't do anything in E2R0 C35.
  • Resuming testing in E2R0 C31. This one at least often causes crashes when Emily goes to around -3000, -19000. Throwing directly S from -3000, -15000. Going E 50 units at a time and only throwing the medkit once each time.
    • At -2850, the throw caused a softlock with no visible corruption. The backbuffer (as viewed through the Memory Viewer), however, had a narrow pure white triangle at the bottom left corner with small traces of corruption further up. The entire region from around 00021400 to 00022E40 is corrupted with two colors: a light blue that may or may not be from the medkit and a violet that certainly isn't. Also on some attempts there's a lot of black in the middle of the backbuffer. The medkit was at -2620, -21939.
    • Backwards to -2860, a similar triangle is drawn into the backbuffer, but the color is the one with ID 3 with a narrow vertical strip near the center of the screen that is a shade of light blue. The ID 3 one is from the wider slanted parts at the top of the box, above the light blue.
      • -2861 seems to be the first X-value that causes any corruption. -2959, for some reason, doesn't give corruption. -2958 gives the same as -2950. Looking closely at the MV, there is corruption all between 00021400 and 00022E40 or thereabouts. There's also sometimes a lot of zeroes inside the backbuffer, suggesting some pointer got corrupted.
      • -2857 to -2854 don't do anything.
      • -2853 can give a very small triangle in the bottom right corner and a bit of corruption elsewhere in the bbuffer. -2852 can give similar but bigger.
      • -2849 to -2845 don't do anything.
  • Presumably a beta (yaw) angle difference can be compensated for if the actor causing the bug is one that can rotate whereas an alpha or gamma one cannot (although the RV now supports changing those angles for experiments). Since a thrown actor has the same orientation every time, a beta difference will result in different outcomes, though if the difference was exactly 90 or especially 180 degrees, the actor's BODY's symmetries might remedy this. The medkits seem to have 180-degree rotational symmetry.
    • The beta difference might still mean the angle of the top polygon relative to the camera will be different, actually.
  • To try to create some order in the chaos, the camera data was looked into to try to find two views with the same FOV and facing to see if the exact same corruption could be achieved in both views when the PC throws the medkit from the same coordinates relative to the camera, or whether there were hidden variables (other than perhaps lag) that go beyond that.
    • Some cameras are shared between multiple rooms. They have the same ID and coordinates. Here is a list of them in order of increasing alpha.
	E5R7 and E5R8 C15 
	E1R0 and E1R6 C0
	E5R2 and R4 C2
	E5R2 and R3 C8
	E5R2 and R3 C1
	E5R4 and R7 C13
	E5R0 and R1 C4
	E5R10 and R11 C19
	E5R6 and R10 C17
	E5R5 and R8 C20
	E3R0 and R5 C1
	E5R8 and R9 C22
	E5R5 and R6 C30
	E6R1, R3 and R4 C1
	E6R0, R1, R2 and R3 C2.
    • No two actually distinct cameras have the same angle. There are some near matches.
      • E1R2 C9 and E3R1 C3 are almost identical in angle, if not in FOV.
      • E3R11 C24 and E4R0 C2 are matched in alpha and almost perfectly 180 degrees apart in beta, with some differences in FOV.
      • E5R7 C14 and E0R0 C2 are very similar in angle and almost perfectly matched in FOV.
      • E2R0 C31 and E5R4 C12 are 25 degrees apart in alpha, 0.3 in beta and perfectly matched in FOV.
      • E6R6 C0 and E1R1 C3 are 21.4 degrees apart in alpha, 1.8 in beta and perfectly matched in FOV.
      • E1R1 C3 and E2R0 C26 are 0.7 degrees apart in alpha, 1.8 in beta and perfectly matched in FOV.
      • E3R8 C10 and E5R4 C12 are exactly matched in FOV and alpha. Only the betas and Z-focal are different.
      • There are other almost perfect matches listed below.
  • E3R8 C10 and E5R4 C12: the alphas are 16.5 both, betas are 31.3 and 80.5 respectively, difference of 49.2, and gammas 0.0. HFOV 58, VFOH 45. Positions are (-9890, 2800, 9490) and (-28860, 5990, 6600), differences of (-18970, 3190, 2890).
  • These two pairs have same same FOVs and alpha/gamma, just different beta. They are the most perfect matches amongst all the views.
E2R0	35	-3720	2530	4460	129	298	0	225	188	101	45,4	75.2	0,0	71	56	1,34
E5R7	16	-4420	7080	-14060	129	743	0	225	188	101	45,4	278.8	0,0	71	56	1,34

E2R0	26	-2540	-420	3710	2	323	0	286	239	101	0,7	66.4	0,0	58	45	1,34
E2R0	34	-4780	-850	-4860	2	304	0	286	239	101	0,7	73.1	0,0	58	45	1,34
  • These ones have the same FOV and alpha/gamma, but with a different Z-focal, which means that the positions are offset by some amount that can probably be calculated somehow (the RV takes this into account when placing the cameras). (The angles here are wrong, calculated from the top going clockwise instead of bottom going counter-clockwise.)
E3R8	10	-9890	-2800	-9490	47	89	0	286	239	101	0,0	31,3	0,0	58	45	1,34
E2R1	11	4160	-1440	1390	0	93	0	286	239	140	0,0	32,7	0,0	58	45	1,34

E2R1	11	4160	-1440	1390	0	93	0	286	239	140	0,0	32,7	0,0	58	45	1,34
E1R1	3	-2600	-930	90	0	318	0	286	239	300	0,0	111,8	0,0	58	45	1,34

E3R8	10	-9890	-2800	-9490	47	89	0	286	239	101	0,0	31,3	0,0	58	45	1,34
E1R1	3	-2600	-930	90	0	318	0	286	239	300	0,0	111,8	0,0	58	45	1,34

E5R4	13	-2920	-5820	-11790	60	856	0	286	239	141	21,1	300,9	0,0	58	45	1,34
E5R7	13	-2920	-5820	-11790	60	856	0	286	239	141	21,1	300,9	0,0	58	45	1,34
E6R6	0	5180	-7470	5920	61	313	0	286	239	1280	21,4	110,0	0,0	58	45	1,34

E1R7	14	-6100	-5670	1770	126	426	0	259	216	161	44,3	149,8	0,0	63	50	1,33
E2R8	25	-230	-3080	-9470	127	928	0	259	216	61	44,6	326,3	0,0	63	50	1,33
  • The E2R0 C26 and C34 are almost perfectly matched (focals and alpha/gamma). Their betas are 66.4 and 73.1 (difference of 6.7). Coordinates are (-2540, 420, 3710) and (-4780, 850, -4860) respectively, differences of (-2240, 430, -8570).
    • What could be done is find a function that converts some PC position and facing relative to the first camera into the same position and facing relative to the second camera angle, then find positions relative to both cameras where if you throw the medkit, you can get some kind of memory corruption. See if the same exact corruption happens from both positions.
    • If a particular corruption can be achieved from different views, this will make it easier to transfer the same corruption over into another view that's less out of the way, at least in theory. Since the cameras mostly have a different Y-position, while it might be possible to elevate the PC to the required level using an interrupted stairs transition (see "TRANSITIONS", using the door-opening method), it might not be practical. It could be the only use of these kinds of tests is just to check for hidden variables.
  • There may be differences in the axes and angles in the cameras file compared to how they're shown in the RV. In the RV they conform to the actors' coordinate system though.
  • Other candidates for generally E-facing camera views: E1R4 C7, E2R0 C31 and C35, E2R1 C15 E2R4 C3, E3R0 C0, E3R7 C13, E5R1 C6, E5R4 C12, E6R6 C7, and E6R6 C12.
  • Lagging the game while the medkit is flying can also affect whether it gets to wrap around normally, and also where any sudden landings happen.

SOUND BUG

  • Corrupting sound sample pointers can yield glitchy results when the game tries to play such samples back. The only known way to achieve these effects is through usage of the 2D wraparound, and thus this is just a secondary step to potentially getting useful results out of it.
  • VARs 4 and 5 being set to values above 100 makes them act weird: they seem to wait for the game to play the first sound effect (that's not the one in the other VAR) and store it in the slot. From there on out, that particular value of that VAR will play that particular sound effect.
    • If you start messing with the values for VAR 4, 5 or presumably 15, it seems something can go wrong with the game writing sample data on top of the rendered image (with a softlock).
    • It looks like maybe the game resets the values for VAR 4 and 5 every frame so could be just VAR 15 is possible to use here. VAR 15 is only set when the player enters E3, E4, E5, or E6 or is on one of them.
    • To be exact, VAR 4 and 5 only reset if VAR 4 has been changed. Also some rooms may be exempt because the actor holding the right LIFE isn't in memory. In fact this is why your step sounds are not set when you come back from E5 to E3.
    • The way the game reacts to values above 100 for these VARs seems to depend more on what has happened prior, not which exact values they were given. If you just reload a save file and immediately fire the rifle, it tends to be an immediate "vanilla" softlock with music playing but nothing else happening. If you cause some other sounds to be played before you shoot, the game seems to become tolerant to glitchy values without actually playing any sample on firing. If the game has given it the "go-ahead" once, it doesn't seem to ever change its mind until the VARs are messed with again. If you switch between ordinary sample indices and glitched ones, it tends to result in a noise texture all across the screen and random large and fairly small values being written inside the VARs. Sometimes the CVARs can also be affected, giving a kind of checkered pattern with big and small values alternating (a possible way to get a 1 written?). Aside from all this, smaller graphical glitches may also occur.
    • On occasion, the game will die a slow death with individual actors disappearing from the "radar" one by one until none are left.
    • To get "the full monty" you can set one of the VARs to 99 first, then 101 and finally 102 at which point you get a tutti frutti effect. Amazingly, setting it to 110 as the last value doesn't seem to work although it might still crash the game.
    • Sometimes some of the values can keep changing for a few frames before settling during the tutti frutti effect. Repeatedly seeing VARs 53 and 54 changing when VAR 15 was set to 103.
    • Seen the room value change during one of the crashes.
    • Seen the CVARs being written over with the VARs, so that CVAR 1 takes the value of VAR 1 etc. all the way to the last CVAR. This happened at a delay after a softlock when VAR 15 was 106.
    • Once seen CVAR 41 changing to -1 (from 198) during the softlock.
    • All in all, this is all very difficult to predict and it is currently completely unknown whether it can be used to get the desired effects.
  • It seems CVAR corruption could also be used to get sample-based corruption seeing as some of them are pointers to sound files. This was not tested.
  • It may be possible to get the game to play a sample in the middle of a 2D wraparound that has just overwritten VAR 4 and VAR 5 temporarily, before they get reset, making it unnecessary to get a glitch where only VAR 5 has changed.
  • What happens if you get negative index samples to play?
    • Shooting a rifle with VAR 15 having values -1 to -5, -10, -100, -1000 or -10000 doesn't seem to do anything. It interrupts any other samples playing when you shoot but nothing happens whether or not you get other sounds to play between changing the value and shooting.

FLOW GLITCH

  • It looks like sometimes flow actors (created through use of the SPECIAL command in the scripts) stop being spawned under some circumstances that seems to involve saving and loading around when they're being spawned. This is the easiest to achieve by going to a monster that has the HIT_OBJ ACTIONTYPE, turning your back to it so you get constantly knocked back into it, then saving, and reloading that save file several times. It can also be done while in E3R13 with smoke spawning but is much more rare and thus presumably requires better timing. This is called the flow glitch.
  • The exact effects seem to vary based on what flow actor was involved.
    • If blood was used to cause this effect, no blood is spawned anywhere in the game, and in addition neither the bubbles nor debris will spawn either. The smoke is still spawned normally.
    • If it was the smoke that was used, the other flow actors will still spawn normally. (See the save file called "Smoke Flow Glitch.ITD".)
    • It it currently unknown whether bubbles or debris could be used to cause this glitch and what the effects might be.
  • It probably isn't important for causing the flow glitch that there be lots of actors in memory (like there are when you are getting continuously hit by a HIT_OBJ actor) since whenever the smoke stopped spawning, there weren't a particularly great number of actors at all. Also saving or reloading deletes all flow actors and this trick specifically seems to require reloading it multiple times.
  • Flow Glitch New Game Crash: After the flow glitch has occurred, saving and loading the game more seems stable and retains all the anomalous effects (even if loading a completely unrelated save file), but starting a new game causes a crash, sometimes with an analogue TV noise -like effect seen in RAM in a variable location. A few examples are given in this video (the third one shows actor data corruption in the RV, the ID 96 actor in question being the jug which was lying on the floor).

File:Flow Glitch New Game Crashing Examples.avi


FLOW CRASH

  • If you turn you back on a HIT_OBJ enemy and start getting hit by them continuously, this will sometimes result in a crash. Getting the glitch to happen isn't very consistent, but there is very consistent memory corruption visible in the VARs, most of which even get overwritten with the same exact values every time.
  • One location where this can happen is in E5R6 getting hit by the big spider. It almost certainly has something to do with the flow actors (or their great number) themselves since if you first get the flow glitch to stop those actors from spawning, you don't ever seem to get the flow crash anymore. It seems it's also connected with the number of actors already present in the scene, since E5R6 is close enough to R10 to make all the bridge segments be present, and if you first drop lots of actors so most of the slots have been filled, this heavily increases the odds of it happening. It's not clear how many slots should be filled since there have also been times when no extra actors had been dropped at all and the crash still occurred. (The actor whose data is being shown is not relevant). Using the E2 vortex (and presumably other vortices) has also been confirmed to work, but there's a very modest number of actors around E2R10. Conversely, if you fill almost every slot first, this doesn't guarantee the glitch will happen. Instead, when all slots are taken, the game simply stops spawning in more flow actors as it should.
    • The crash could be related to the one that occurs when you try throwing something in the save file called "Crashes When Something Thrown.ITD", or in similar situations. Most of the time, the game handles spawning new actors in when slots are all full well enough not to crash but not quite every time. It's still possible something else is going on with the flow crash. The resulting corruption looks quite different from the "raindrops" pattern seen during the throw crash.
  • Here are examples of the flow crash. The first two files show crashes after no extra actors had been spawned. The resulting corruption looks identical, at least in the VARs themselves. The third file has several crashes with other objects in the vicinity, showing slightly different VAR corruption (e.g. the VARs from 124 to 139. VARs 4, 5, 21 and 24 are expected to revert to normal values after the initial corruption).

Flow Crash Without Extra Actors.png

File:Flow Crash Without Extra Actors.avi

File:Flow Crash Examples.avi

  • There is often a Not Enough Memory Error given by DOSBox.
  • There are usually several frames before the game crashes (you can see the PC's health keeps ticking down a few more points), suggesting you could save the game before the crash, which might stabilize it while keeping the memory corruption. In fact, this was done in the video called "Flow Crash Without Extra Actors.avi". This immediately resulted in a crash during the saving process, but the game still produced a save file, which is also available, called "Flow Crash State.ITD". It might obviously not have all of the corruption intact.
  • The corrupt VARs have a very wide range of values that don't resemble other crashes at all. A few of them repeat outside of all the -1s and 0s. There are also notably several 1s written into them, though VAR 33 specifically has never been overwritten with that or any other value. It is reasonable to think the data could relate to the actors or something else that's mostly quite stable but not quite.
  • It is assumed that the flow crash can be triggered in dark areas since the 2D wraparound also can.
  • This crash should be tested further in different areas to see if overwriting VAR 33 or getting other useful corruptions is possible.


MISCELLANEOUS GLITCHING

  • One-frame poly glitch: There's a graphics glitch that sometimes causes actors' polygons to be drawn in erroneous places, stretched. It happens extremely rarely and the one time a recording was made, it was mistakenly rendered at 25 FPS and so the frame on which it happens was lost. Here it is for reference though. It was a polygon of the rifle's that glitched out (but that's just a part of Emily's BODY).

File:Rifle One-frame Poly Glitch.avi

    • The only other time was also in E3R0 in the same view, though facing the opposite direction. It is very unlikely this is the only view in which it has happened or can happen though.
    • Absolutely nothing else is known about it. It can't be caused by a camera wraparound or 2D wraparound given that Emily was standing in the middle of the frustrum. Any footage would be appreciated.
  • When the Cthonian is bugged out (not exactly sure what state it needs to be in), seems if you go around and hit the trigger in R4, the game can crash.

MISCELLANEOUS

MISCELLANEOUS/COMBINED TRICKS

  • If you had entered the last keyframe (kf1) of an open/search action, if you need to go to the inventory (and it's the first visit of the segment) and you choose any action that doesn't change your ANIM or BODY, it will complete the open/search, saving 30 frames, which is more than the 15 frames you'd get for just moving through a keyframe of running.
    • This works the same if you use the empty jug.
    • Looks like you can be within a range of 73 units from the collider you're searching at the start of kf1 and still search it if you're facing it orthogonally.
    • The open/search ANIM has two keyframes of length 30 frames each, with offsets 129 and 69 for 198 total. It looks like you can search a collider successfully if the open/search action started 202 units away from being right next to it.
    • You get a similar effect if you use a key to open something that's locked. Also shooting an arrow is sped up this way. Various other actions could also be sped up if you got INV back in the middle of them.
  • If you open/search a door but equip the key in the middle so the open/search finishes due to lag, it meets the conditions for the door getting unlocked.
    • Actually, if you start open/searching or closing something but you're not yet colliding with it (PLAYER.HARD_COLLIDER check), this is technically the same trick, meeting the requirements in an unexpected order.
    • You can reload an empty gun just as you're about to shoot. You just need to hold UP and SPACE as you're leaving the inventory. The shot is immediate so long as the aim ANIM also reaches the end during the inventory time.
    • You can also both fuel and light the lamp only after starting to throw it (but before it's in the air) to get Pregzt.
    • You can start searching the water barrel, then switch to the jug to fill it, however, just using the jug causes it to get instantly filled (the same ANIM plays out) so this isn't really anything special.
    • Supposedly, you could maybe get the "throwing something" VAR to be set to 1, then get another ANIM to end instead of the throwing ANIM or using the jug, to restore control and INV based on the final clause in either LIFE 11 or 549.
  • If PLAYER.TEST_COL is set to 0, a bug makes it count as the player always touching the collider with ID 0 when you're not touching anything, and ID 1 when you are colliding with something. This can be used to get glitchy effects if those colliders have some special effects. The only places where the code sets PLAYER.TEST_COL to 0:
    • LIFE 39 - death manager, death by jelly
    • LIFE 239 - jelly, death by jelly (same really but a frame earlier)
    • LIFE 550 / TRACKs during transitions
      • You automatically collect the lighter in E3R13.
      • Could in theory pick up other items as well. This seems to take lots of time setting up the TEST_COL 0 without also losing trigger collisions.
  • If you get hit by something on the same frame as you start falling and you lag the game or s/l at that point, this might be one way to make a deadly fall into a non-deadly one since the logic in LIFE 549 is delayed by a frame (while visiting LIFE 553) and so your fall height could go from 3 to 2. Of course just lagging at the start of any fall seems to be enough anyway.
  • Starting to fire a weapon but going to the INV to drop something during the firing causes you to get stuck in LIFE 11 with a repeating aiming ANIM afterwards.
  • If you use camera defocusing or any other method to get inside an actor that's in a doorway or next to a room trigger, you might be able to run out into the OOB if that's the first position that's available (since you're not passing through any of the points where you've hit the trigger before having cleared the wall that's next to you). This could be a little faster than just lagging it.
  • You don't ever need to open up either of the escape routes by either picking up the block or unlocking the cellar door, or solving the study puzzle. The study door, however, will be locked if it was never unlocked by you.
  • You can stand on inventory objects that you've placed in the air and run across gaps that way.
    • This kind of air-walking is seemingly completely useless seeing as you can easily get the hover very quickly.
  • You can try starting a long ANIM such as jumping or the rifle knockback while moving into a line of items placed on the floor. If there's never a frame in-between you picking up another item, you never switch to whatever view was in-between where you started and where you ended. Because you're going through multiple kf's of the ANIM, this allows you to skip wider camera triggers than if you had just used a lag snap. However, if you do this in E5R6 where entering view 27 causes the spider to climb out, if during picking up the line of objects you ever clearly pass inside the trigger, the spider will climb out even if you never see that view on the screen (tried warping the player to another view).
    • Also when you hit any floor triggers while picking the items up, it doesn't even give you the option to pick anything up that's past the point where you enter the trigger, so in reality, it's likely to be just a cosmetic effect that the view doesn't change.
    • Room triggers seem to act the same. The view only changes after everything has been picked up.
    • This probably has to do with how the game skips rendering one frame after returning from the inventory, and possibly during pickups as well.
  • Bridge Desync: when any actor that has no ANIM and has SPEED 0 is falling, instead of the usual interpolation that uses the MODY difference between the current and previous frames, its bbox is moved by the full MODY amount every frame. This is almost certainly just a bug. Thus it soon starts to zip downwards very fast and will get completely out of sync with the ROOM coordinates by the time the fall finishes, even wrapping around the game world. This is called the bridge desync because all the bridges in E5 have both of the required properties for this to happen to them.
    • Because the MODY value is added every frame, the faster the game is running, the greater the desync will be. It's trivial to cause the desync to be lesser by causing lag during the fall or s/l'ing.
    • No other actors have been observed to do this but it has been tested with Cheat Engine that setting ANIM to -1 indeed has the same effect on the PC. If this could be done without cheating, it could be a faster way to get a vertical desync. This seems be impossible, however. There are various states the PC can be gotten into where the ANIM seems to be left frozen, but these are not cases of it being -1: it's just the end of a non-repeating ANIM without another ANIM overwriting that field. To get ANIM to be -1, it seems that specifically "ANIM -1 -1" needs to be called. This is never done for the PC in any LIFEs.
    • Getting stuck at the end of some non-repeating ANIM, the 0001 flag turns to the 0008 flag and you stop animating and can't fall either (probably the game only loops through currently animating actors to see which ones might have stepped off a ledge).
  • There's a frame delay before the camera switch happens after entering a new ROOM. Because of this, if you can enter a ROOM somewhere inside a camera switch that normally causes some other actors to be spawned, but so that you are inside a floor switch (or another ROOM switch) as well, that step is skipped and the actors present are determined only by the ROOM and the old view (if shared with the new ROOM) or default view. This obviously skips any LIFE execution for those other actors as well and affects the slot order.
    • You can also just move between ROOMs so you aren't in a camera trigger inside the next room. If the view you came from isn't shared, this will cause the default view to load, which could be somewhere far away from the entrance you came through, and thus is more likely to cause some actor in some third adjacent room never to load in.
    • Generally this kind of trickery isn't expected to have a major impact on anything, owing to the fact that most of the time, actors only start doing interesting things when the PC has entered the very room that they are inside, not when the PC is in an adjacent ROOM.
    • Note that you can't get a ROOM's room scripts not to be executed at all by moving directly into another trigger. It will run at least once.

TECHNICAL NOTES

The information in this part of the guide is very unlikely to be useful for speedrunners.

  • Interrupts: AitD (and DOS in general) is single-threaded. This means only one instruction can be executed at a time. The idea of interrupts is to allow external devices (e.g. keyboard, printer, network card, timer...) to temporarily hijack the CPU in order to quickly process some other event. When interrupts happen, the CPU stops whatever it's doing, saves the context, executes some interrupt-specific code (the interrupt handler), restores the context and resumes execution.
Without interrupt: A1 A2 A3 A4 A5 A6 A7 A8...
With interrupt that occurs during or right after the A3 instruction: A1 A2 A3 (I1 I2 I3 I4 I5) A4 A5 A6 A7 A8...
    • These interrupts can happen at any time (even in the middle of a for loop or a function) but the CPU will always finish processing the current instruction before handling the interrupt. In AitD, there seem to be just two types of interrupts:
    • keyboard interrupts: triggered every time you press a key, in order to empty the keyboard buffer and update the keyboard status. Note that the game code uses a copy of the keyboard status made at the beginning of the main loop, so mid-frame changes only affect the following frame.
      • There is some debugging-related code with this. It checks a variable named "Flag_Debug". If set to -1, it will jump to a specific location. Setting the flag to that value just crashes the game, probably because the related code was removed. This could have been to enable bringing up dev options at a keypress.
    • timer interrupts: triggered 60 times per second. These increment T1 and T2 by 1. Every second, they also copy the AitD frame counter to a separate variable (used to measure the FPS), then resets that frame counter to zero.
      • If the relevant flag is set, these will cause the screen shake effect by updating some VGA registers.
      • The timer interrupts are also used for playing the midi music (to transfer midi notes to the sound card at regular intervals).
  • More notes on game modes:
    • If you search for the hex string 6A00 6A01 6A07, and replace both instances with 6A01 6A01 6A07, you can make the game start in main game mode instead of intro mode (first parameter). The second two params are starting ROOM and starting ETAGE.
    • This allows you to save and load; pause and resume; if you warp the PC around, you can pick up items (warp to E3R13 for a wrong pickup), and afterwards access the inventory.
      • If you try to drop or throw anything, it will happen after the intro finishes.
    • Warping the focused actor (different from usual PC actor) around, you can activate a few scripts, pick up a few items, and if you go to E1R4 twice, it gives the real PC focus back ahead of time. This creates a super-bird in E1.
    • Beating the game while in the intro doesn't do anything special it would seem. For some reason, when you get to the end cutscene, you can no longer go to the main menu. The opposite of how it usually is.
    • No way of doing any of this without cheating is known and also it probably wouldn't save time anyway since the intro isn't timed.
  • The missing rooms: No known ways to enter E3R6 and E2R9 exist. If you overwrite RAM to do that, it tends to cause a slow-down or crashing. This is at least partially explained by these observations:
    • The code that checks for triggers is entered even when there are no triggers. It looks like this
do {
        //check current trigger and get next trigger
} 
while(--numtriggers != 0);
      • If the initial trigger has index 0, it will decrement first, causing a wraparound, then loop through back to 0 from 65535 every frame, causing a large slow-down. The code has this problem because it was hand-written in ASM:
MOV CX, numtriggers
NEXT:
        //check current trigger and get next trigger
LOOP NEXT //decrement CX first, then check if it equals to zero.
      • The code should check for zero first, then decrement. A similar problem exists in the code that checks for colliders.
    • Another thing that will happen is looking through data after the room data for the missing room, and some of that data can be interpreted as triggers taking the PC to random invalid rooms.
    • It's possible to trigger C21 in E2R9 (an unused view) without crashing by teleporting the PC to the upper part of the camera zone. The game will loop through all the potential triggers but with nothing matching, and then C21 is finally shown.
  • This code was used to check which initial RNG seed gives the least noises from RAND 15 / RAND 300 calls in E5/E6 and can easily be edited to do other similar checks:
var seed = 0;
function rand() { 
	seed = (Math.imul(seed, 22695477) + 1) & 0xFFFFFFFF; 
	return (seed >> 16) & 0x7FFF; 
}

var best = 9999999;
for(s = 0; s <= 0xFFFF; s++)
{
	let noise = 0;
	seed = s;
	for (let n = 0 ; n < 18000; n++) {
		let d15 = rand()%15;
		let d300 = rand()%300;
		if (d15 === 0 || d15 === 1 || d15 === 2 || d300 === 0 || d300 === 1) noise++;
	}
	
	if (noise < best) {
		best = noise;
		console.log({noise, p:noise/N, seed: s.toString(16).toUpperCase().padStart(8, '0')});
	}
}
  • AitD has mouse support. It keeps updating two values "JoyX" and "JoyY" but those variables seem to be unused. In this video, a cursor can be seen in a debug mode, which is probably what the variables were used with.
    • The game seems to be able to communicate with the COM1 port, also probably for debugging purposes.
  • When you start a new game, actor 9 (the zombie that rises up from the hatch in E0) has BODY 23 but in its LIFE 16, its BODY is set to -1 immediately until it's time for it to make its entrance. The bird outside has its BODY all the while it's waiting. This could have been because the devs noticed some kind of problem with the 2D masks that caused it to sometimes be drawn erroneously before having risen out of its pit. However, a test where the zombie's BODY was set to something visible gave indication that no matter where the PC is in the loft, it doesn't seem possible to get the zombie's BODY to get redrawn and so it can't appear in the screen anyhow. Instead, setting the zombie's BODY to -1 might have been a small optimization given that way it doesn't get redrawn every time switching between cameras. This would have also allowed the demo versions to run that slight bit faster.
  • The items in the inventory start facing the same angle every time you've opened it, but each item will always have the same angle regardless of which one is highlighted. This is because the variable that determines their facing is shared between them, called ShowBeta, which is decremented on every frame. The same variable is used for pick-up prompts and the copy protection screen.
  • A list of some engine-controlled flags:
FlagChangeFloor //set to 1 when engine need to change room or floor
FlagChangeRoom
FlagDetails //high or low gfx settings
FlagDigit //? not sure for what it is used. To turn on off digital effects?
FlagGameOver //only set to 1 right before returning to main menu, then back to 0 again
FlagGenereActiveList
FlagGenereDrawList
FlagInitView
FlagInventory //allow inventory
FlagLightOff //?
FlagMusic
FlagProtect  //floppy disk copy protection ?
FlagRedraw
FlagRefreshAux2
FlagRotPal //vga palette shift effect ?
FlagSample //set to 1 when game need to play a sound sample ?
FlagSaveTimer //set to 1 before time-consuming operations so the game remembers to revert the timer afterwards
FlagShake //screen shake effect
FlagTurnPage
FlagDebug //set to 1 by the game when entering save game menu. If set to 255, after pressing a key, it will call the previous keyboard interrupt handler (the one that was defined before game was started). 
          //it might have been used to allow Borland Debugger to handle keyboard properly (as it's supposed to be started before the game and to run in background). 
          //setting the flag to 1 or 255 does not have any effect under DOSBox.
FlagJoy //probably has to do with mouse support for debug mode
FlagTypeKeyboard
FlgQueue //?
FlgSyncroHoriz //vga stuff
  • RND_FREQ details:
    • RND_FREQ x generates a value between (-x/2) and (x/2-1), both being rounded up. E.g. RND_FREQ 60 returns a value between -30 and 29. This is the delta to apply to the default frequency which is 131. The formula to convert this to a sampling rate is as follows:
1000000 / (256 - frequency)
1000000 / (256 - (131 + offset))
1000000 / (125 - offset)

for RND_FREQ 60, it gives

1000000 / (125+30) = 6451 Hz
1000000 / (125-29) = 10416 Hz

Possible values:

Lowest is 1000000/256 = 3906 Hz
Default is 1000000/125 = 8000 Hz 
Highest is 1000000/1   = 1000000 Hz
  • All LIFEs, BODYs, ANIMs and VARs in AitD are linked to a particular actor. The original scripts would have looked like this (this example is from Frederick Reynal's GDC presentation, slide 16):
DEF_OBJ zombie_of_the_trap
{
	BODY_FILE zombie_body monstres\m2
	ANIM_FILE rien monstres\M2_RIEN
	ANIM_FILE marche monstres\M2_march
	ANIM_FILE choc monstres\M2_choc
	ANIM_FILE attaque monstres\M2_attac
	ANIM_FILE meurt monstres\M2_mort
	ANIM_FILE monte monstres\M2_Monte

	BODY zombie_body
	ANIM -1

	POS -5600 3000 2000
	ANGLE 0 0 0
	STAGE 0
	ROOM 0
	TYPE_ZV CARRE_ZV
	TYPE ANIMATED /* + SCAN_DEC */
	LIFE wait_life
	LIFE_MODE ROOM
	MOVE NO_MOVE

	DEF_VAR pointsdevie 10
	DEF_VAR flag FALSE

	DEF_LIFE wait_life
	{
		Body -1
		if Var ( Poulet_Grenier ) mort == TRUE
		{
			if Chrono >= 20
			{
				if Var flag == FALSE
				{
					Life( trappe_grenier ) ouverture
					Var flag = TRUE
				}
				if Alpha( trappe_grenier ) == 700
				{
					LIFE life_arrive
					Rnd_Freq 0
					Music 13
					Next_Music 5
					Camera_Target zombie_of_the_trap
					MOVE TRACK entree_trappe
					Body zombie_body
					Anim monte Repeat
					Inc (Perso) nb_monsters
				}
			}
		}
	}
        
        ...
}
...
    • The data before DEF_LIFE is used to fill in OBJETS.ITD
    • Since everything is local, it doesn't have to have a unique name. Instead when reading or writing a VAR or calling a LIFE, the actor would also have been specified.
    • The somewhat haphazard order in which the scripts are dumped into scripts.txt is the same as in the original scripts, with everything related to one object dumped sequentially. In addition, any LIFE that is referenced by another LIFE is put right after that LIFE, e.g. LIFE 39 (the death manager) comes right after the LIFE that first references it instead of somewhere around where the rest of the PC's LIFEs are. Similarly, VARs are dumped in the same order as they are defined inside the DEF_OBJ headers (the DEF_VAR clauses). Then, it might additionally dump other VARs referenced in those scripts (e.g when compiling the E0R0_zombie scripts, E0R0_zombie_hitpoints (18) got dumped simply because they belong to E0R0_zombie, then E0R0_bird_dead (19) get dumped because it is referenced by LIFE 16, E0R0_zombie_waiting script).
      • Example:
DEF_OBJ SHARK
{
	DEF_VAR AAA
	DEF_VAR BBB

	DEF_LIFE A
	{	
		...
	}
	DEF_LIFE B
	{	
		...	
		LIFE X
		...
	}
	DEF_LIFE C
	{			
		...
		LIFE Y
		...
		SET EEE = 1
		...
	}
}

DEF_OBJ CHICKEN
{
	DEF_VAR CCC
	DEF_VAR DDD
	
	DEF_LIFE U
	{	
		...	
		LIFE Z
		...
	}
	DEF_LIFE V
	{	
	}
}

LIFES 
#0 A
#1 B
#2 C
#3 X
#4 Y
#5 U
#6 V
#7 Z

VARS
#0 AAA
#1 BBB
#2 EEE
#3 CCC
#4 DDD


  • When actor-collider collision checks are done, a global (common) table of size 30 is used to temporarily save them. The array has no coded upper bound, i.e. if more collisions were to occur, the table would overflow. However, the table is cleared before processing the next actor, and thus abusing this is totally impossible.
  • If you leave the loft while the zombie is knocking on the blocked hatch, it'll temporarily interrupt its attempts when you return (ROOM_CHRONO has to reach 20 again).
  • The E3R0-R3 doors are different actors if you're in R0 or in R3. The scripts all have the same code though.
  • Door glitch: doors and other rotating objects (using DO_ROT_ZV) will often look in the RV like they've moved to another location for a split-second during the rotation. This is because of the RV reading memory mid-frame and has to do with the way the calculations are handled. It therefore has no impact on gameplay.
    • More precisely, what happens is the game first moves the bounding box (really just two opposite vertices) so it's centered at the origin, then rotates it, then moves it back to where it should go. You can see in the RV a state where one of the two vertices has been recalculated but not the other one, explaining the deformation of the bbox.
  • The game can run on as little as 1MB allocated RAM.
  • A lot of things in AitD's memory are dynamically allocated and so their exact positions may vary between sessions. The order of appearance should not differ.
  • The 0004 flag for colliders is not referenced in the code at all and thus assumed to have no function. Colliders with this flag set are found between rooms (overhanging doorways in E1-E3 and down at the bottom in E5, though not in E6), and they have the ID of one of the adjacent rooms and they might thus have been planned for use with moving between rooms.
  • When LIGHT 0 is called, it's processed by the processSoundAndMusic() function in the main loop like this:
    • The palette is progressively turned black to cause a fade-out.
    • The screen is cleared by filling it with black pixels. This makes the fade-out impossible to see and pointless.
    • The original palette is restored but the screen stays black because of the previous step.
    • This causes some lag due to lacking timer freeze commands.
  • If the actor the camera is following is in a dark room and the player gets hit somewhere else, it still creates a light patch where the player is.
  • The reason the game doesn't correct the step sounds when coming from E5 back to E3 is because the actor with the LIFE that's supposed to be doing that (ID 130, LIFE 210) mistakenly has LIFEMODE 1 instead of 0. Instead, the R13 room scripts will change the sound when the PC enters that room.
  • If you wanted to do a walkathon for the game, the best would be to reset the walking ANIM every 60 frames instead of letting it finish, but you'd have to be careful not to trigger running. The same as the real-life sport.
  • There's a false bit of code that makes the PC turn the wrong way around when closing the W side library doors.
  • For some reason, when the game loads in E1R7, the S nightgaunt is moved around a bit after first getting loaded in.
  • Here are the names of all the functions used to draw polygons:
    • MyAffPolyTriste: 'MyDrawPolySad'. Used for simple, single-color polygons.
    • MyAffPolyTele: Used for noise. "Tele" probably means "Television" from how it looks like a CRT TV picking up analogue noise.
    • MYAffPolyTrans: Used for transparent polygons.
    • MyAffPolyCopper: Used for vertical gradients.
    • MyAffPolyMarbre: Marbre = Marble. Used for horizontal gradients.
    • The function MyAffPolyFlou also exists but is unused. It creates an effect that could be described as primitive cel-shading.
  • You can start a climb while "closing" or "open/searching".
  • If SPACE is held while returning to LIFE 549, if the player has control but no inventory, returning the inventory should be delayed until SPACE has been released. Seems useless.
  • The jug can be collected from OOB.
  • The E2R0-R2 S door has a slight difference in its LIFE (101) compared to other doors in the part where you try to close it. This shouldn't be reflected on by the gameplay in any way. Same goes for:
    • #228 E3R1_E3R13_west_door
    • #234 E3R1_E3R12_east_door
  • The double doors between E2R0 and E2R2, and E3R1 and E3R13 have an difference in the code for what happens when you close them (the last IF clause in LIFE 95/101/228/234) compared to other doors. With those doors, it removes control and movement and only then checks which side of the door you're on. If you try to close one of the doors from the thin side (generally near the hinges), this isn't recognized as either of the two cases in the switch and thus you softlock. With other doors, the game won't do anything unless you're on either the "right" or "left" side of the door.
  • You can reset the dancers' dance patterns by playing the Dance of Death again.
  • When you load any save, VAR 25 (being in the intro) always flashes 1 before going back to 0, for unknown reasons, before gameplay continues.
  • When you're in the dropping LIFE 11, if you had VAR 10 as 1, it'll give you control and INV back without anything having been dropped just like if you were in LIFE 549. However, you must have had INV already to enter that LIFE so this might be useless.
  • Dropping the oil lamp without oil causes the spotlight to stay on Emily.
    • Sometimes the oil counter stops ticking down in the maze.
    • Fixed after falling in water and rekindling it. Sometimes editing the value causes this glitch again (and may have caused it in the first place).
    • Still, it won't remove the light spot when it does run out of fuel in E6R0 even when moving around in E6. And if you drop the lamp then, the spot stays on Emily.
  • The lamp cannot run out of fuel while it's not in your hands. If you drop it, leave and return, it might have gone out though.
  • You can fairly easily get the game to stop drawing graphics (except item pick-up dialogs) by buffering some key right after hitting ESC to leave the menu (i.e. the lag trick). It doesn't seem the game lags any less as a result though, with new views still getting loaded into RAM each time.
    • Another way to do this might be to just visit the menu just as you're about to pick an item up.
    • A third way might be entering the menu just as you're entering a new room.
    • It hasn't been tested whether this affects the 2D wraparound.
    • To restore the graphics, you just enter the inventory or menu and return once again.
  • The medicine cabinet in E1R3 is special in not locking your rotation while opening it.
    • It and the room scripts also sometimes cause an extra delay before the door starts to open depending on the slot order. Just saving and loading can reverse the order.
  • Try placing the two mirrors in the two possible ways - where you've placed them should slightly alter (by 100 units along the y-axis) where one of them is left.
  • The code for playing landing sounds is very simple: if your Y-coordinate is more than -2000, it'll play the water splash, if it's less than or equal to -2000, it's a wooden thud. Even though there's no separate effect for landing on stone floors, there isn't anywhere you can land (normally) that's made of stone so this omission isn't obvious.
  • The Necronomicon effect can be cancelled just by taking damage after which you have free control again. The Vermis animation can be interrupted by being in the middle of taking damage when reading the book, but this soft-locks the game.
    • Good way of getting custom "The End" screens.
  • If you get hit by a boulder in E6R6, it always spawns back in the same location (2300, -10000, -6700). Thus you can make them all hit you by standing there and waiting for one to spawn there.
  • If you run or walk into the front door trigger first (with VAR 39 being 0), then push, you can only rotate at one-quarter speed, presumably because running is never ended the way it normally is.
  • If you enter E1R2 without having opened the door, hitting the trigger doesn't do anything.
  • There's an inventory close-up version of an axe in the BODY file – unused, since you can't ever pick one up. It's BODY 88.
  • Just searching the statue for the arrows triggers the spiders, even without collecting any.
  • There's another animation if you fall from a deadly height. It kills you instantly no matter your health. It's difficult to see it because there is no easy way to fall that far without water obscuring it. If you fall into the E6 trigger from up high, it can be made to happen where you can see it.
  • If you stand on the bridge in the pirate maze right next to a falling bridge segment, you can take damage from it as it falls.
  • The falling bridge segments (ones with LIFEMODE 2) in E5R10 deal a different amount of damage each between 1-3.
    • 1 damage: OBJ 237-243
    • 2 damage: OBJ 244, 247, 249
    • 3 damage: OBJ 256, 257, 263
    • The general logic is the further it falls, the more damage is dealt (seems kind of pointless), but there's one exception: OBJ 249 deals 2 when expected to deal 3.
  • Whenever the game issues the command WATER1, it also sets a variable called FlagRotPal to 1. This would presumably have been used to apply a color cycling effect to animate the water. It isn't known why the feature was ultimately left out of the game because it's not processor-intensive.
  • Looks like while VAR 0 is 1 and TRACKMODE is NONE and you're in LIFE 549, you can get the player stuck in repeated rotation ANIMs even though no real rotation may happen. This can at least happen with the normal BODY as well as rifle BODY. It's induced by holding down a rotation key while tapping SPACE. The wrong ANIM can carry over into other BODYs as well.
    • During this time, any rotation that has started does not end like normal: e.g. starting a turn with the pushing action and releasing all inputs causes the rotation never to be reset. Hitting SPACE again causes a delay turn.
  • The bow BODY has the special feature of stretching polygons based on a vertex being set to connect to the PC's hand while drawing the bow, but being disconnected from it at other times. This may be the only time this is done.
  • If the PC dies to an enemy, after next colliding with the PC, any aggroed enemies will go to their idle ANIMs for one cycle, then switch to roaming again for a frame, and if still colliding with the PC, go back to idling until the scene has changed. If they're not colliding with the PC, they'll never idle.
  • The game slows down when rendering actors that are further away from the camera compared to when they're near it. This is counter-intuitive. However, it has to do with a method the renderer uses to remove some polygons from those that have to be drawn, called backface culling. This technique checks which polygons are facing away from the camera. When the actor is far away, more polygons become one-line wide or even just one point. For those, the method, which checks for a cross-product, gives a cross-product of 0 and is unable to recognize if the polygon is facing away from the camera or not. Thus those polygons suddenly start getting drawn again. Drawing them is fast but sorting an increased number of polygons back-to-front (Painter's algorithm using selection sort) has quadratic complexity and that's why the game slows down a lot.
    • Generally speaking, the further away from the camera an actor is, the more polygons around its sides become visible.
  • Another note about how polygons are rendered (this goes for both actors and overlay masks): There are two buffers: bufferG[200] and bufferD[200] (per Y line).

Polygon Render Buffers.png

    • It loops over each line segment of the polygon and writes X positions to one of the two buffers above. X positions seems to be calculated using Bresenham (X0/X1 and Y0/Y1 are swapped so drawing always occurs in same quadrant).
    • It always start with one buffer (does not matter which one), then, once it hit the top or the bottom of the polygon (lowest or highest Y), buffers are swapped. Horizontal lines are skipped.
    • Once everything is done, it simply loops on all Y positions and uses bufferG[]/bufferD[] to draw horizontal lines.
      • Polygons are not necessarily CW or CCW and first point is not always the same. It does not matter because bufferG and bufferD are swapped during line rendering, if needed.
    • For this to work, each scanline needs to intersect polygon sides only twice. This is true for all the actor polygons, which are all convex. Mask polygons are not all convex, so they had to be split up to fit the rule above (they might still be concave, but the scanline rule is respected).
  • AitD has an initial stack of 128 bytes which gets expanded to 4096 shortly after startup. It's not possible to locate it during runtime since it is only defined in MZ header which get lost. It is not in PSP. The data segment (ds) is not defined in MZ / PSP either. It's set by the game at startup. It's possible to find the start address since the first instruction of a Turbo-C application is usually "mov dx, address". It's not possible to know the size since the end is supposed to happen where the stack segment starts (which is unknown).
  • AITD seems to have been compiled using "large" memory model. The value of __MMODEL variable in AITD exe and C0L.obj (from Turbo-C++ lib folder) match.
  • There is a hard-coded limit of 400 polygons per model. The most complex model is the car with 360.
  • A profiling of game code gives these results:
renderer: 94% 
     07% sort actors back to front
     28% transform vertices
          18% bones transform (animate model)
          07% local coordinates to camera + depth clamp
          03% camera projection (3d -> 2d) + clipping
     12% prepare polygons 
     20% sort polygons by depth
     26% fill polygons (pixel drawing)
actors : 1%
lifeengine: 3%
other: 2%
    • The game seems to run stably even if the whole render loop is skipped, causing the main loop to be run 15000 to 40000 times per second.
  • The interrupt handler that updates the timers also handles the screen-shaking effect.
  • Pseudocode describing the setupCamera() function (relevant to the Actor CHRONO Underflow):
{
	freezeTime();

	loadCamera();			//load camera background (take time)
	setupVisibilityList();		//add linked rooms (eg: rooms visible from cameras)

	updateAllActorsAndObjects();
		initObject();			
			listbody.getCache(...);
			listanim.getCache(...);
			room_chrono = ...
			chrono = ...
		deleteObject(); 

	createActorList();		//create a list of visible actors
	sortActorList();		//sort them by depth
	renderBackground2();		//copy background to background2 and render static actors on it (probably take some time)
	initScreenCoords();		//loop on all actors body, init their 2D bounding box

	unfreezeTime();
}
  • Fade-ins and fade-outs: There are two functions used for these, fadeIn and fadeOut, which both have two parameters.
    • The first one controls how fast the transition is. A counter starting at 256 (full bright) is decremented by a value given in that parameter until it reaches zero (darkness). Typical values for the first parameter are 4, 8, 16, 32 and 64. Between each step there are two vSyncs (one is approx 14ms).
  • The second parameter is the palette color start index. It's usually 0 (meaning process all colors) but it's sometimes set to 2 (it skips the first two, the pure black and white colors). This is presumably used to be able to display white text on the screen.
    • Fade-ins are exactly the same but the other way around (going from 0 to 256).
  • More details on what is stored in the _MEMORY_ cache for flow actors.
0: bubbles (30 particles, 304 bytes)
	color (2 bytes) (144, 192, 48, 112)
	num_entries (2 bytes) (always 30)
	particles array 
		position_x (2 bytes) (-596, 446)
		position_y (2 bytes) (-338, 24)
		position_z (2 bytes) (-692, 888)
	particles array 
		size       (2 bytes) ( 150, 300) (-5 each frame)
		velocity_y (2 bytes) (30, 80)
		
1/2: blood/debris (30 particles, 304 bytes)	
	color (2 bytes) (85 or 15)
	num_entries (2 bytes) (always 30)
	particles array 
		position_x (2 bytes) (-100 100)
		position_y (2 bytes) (-100 100) (increased by velocity_y each frame)
		position_z (2 bytes) (-100 100)
	particles array 
		rotation_angle (2 bytes) (0 360)
		velocity_y     (2 bytes) (-50 10) (+6 each frame)

3: muzzle flash (not stored in _MEMORY_ since it has no particles; the BODY used is BODY 270)
		
4: smoke  (20 particles, 246 bytes)
	time (4 bytes) 
	particles 
		velocity_x (2 bytes) (-2000, 2000)
		velocity_y (2 bytes) (always 0)
		velocity_z (2 bytes) (-2000, 2000)
	num_entries (2 bytes, always 20)
	particles 
		position_x (2 bytes) 
		position_y (2 bytes) (always -200)
		position_z (2 bytes)
  • Script functions are usually implemented by calling some game engine internal functions. Those can be called outside scripts as well. E.g. SAMPLE is redirected to a game internal function PlaySample() and that one can be called directly by the engine (e.g. when turning the pages of a book, or after a throw). There are many more examples like that. The same goes for variables: they can be evaluated by the engine outside the scripts (e.g.: HIT_BY is the same as Actor.HitBy). The engine will never use SAMPLE or HIT_BY internally anyway. It's faster to use game internal equivalents.
  • In the save file called "Why Does the Zombie Flicker.ITD", you have a situation where the E1R0 zombie is visibly flickering on top of the door even though it's supposedly behind it. This is why this happens:
    • Before rendering actors, the game will sort them depending on their distance from the camera (so they are drawn back to front, it's called the Painter Algorithm). Here is the list sent to the renderer each frame (Z = Zombie, E = Emily, D = Door):
1) ZDE => ok
2) EZD => wrong: emily arm clipped
3) DEZ => wrong: zombie in front of door
    • The game is cycling through those 3 states changing every frame (and so the zombie flickers).
    • To sort the actors, the game uses the Quick Sort which is a Comparison Sort. The function that compares actors (two at a time) only uses the actor bounding boxes and the camera position as its inputs. Since those do not change over time, it's very strange that the list gets sorted in a different way every frame.
    • Here is what the sort function gives for those actors:
Z < D
D < E
E < Z
    • which can be simplified to:
Z < D < E
E < Z (not possible with above condition)
    • The issue is that all of those conditions cannot be satisfied at the same time. Because of that the sort fails. It will cycle between the 3 states we know. What is wrong is the E < Z condition. It should be E > Z. In fact if you move Emily a little further from the door, it will stop the flickering. It seems the game uses Manhattan distance to check which actor is closest to the camera and in that specific case the zombie wins (he is closer to the camera using that rule).

Flickering Zombie.png

    • Here is the whole actor compare function logic (pseudo code):
int compareActors(actor1, actor2) {
	zv1 = actor1.zv;
	zv2 = actor2.zv;

	if (actor1.room != currentroom) {
		adjustZV(zv1, actor1.room, currentroom);
	}
	
	if (actor2.room != currentroom) {
		adjustZV(zv2, actor2.room, currentroom);
	}

	distance1 = 0;
	distance2 = 0;

	y1 = ((zv1.y - 2000) / 2000) * 2000 //round value to -4000, -2000, 0, 2000, 4000, 6000, ... 
	y2 = ((zv2.y - 2000) / 2000) * 2000 //
	
	if (y1 == y2) { //both y in the same range. 	
		checkoverlaps(zv1, zv2);

		if (no_overlaps) {
			distance1 = abs(camx - zv1.x) + abs(camz - zv1.z); //manhattanDistance
			distance2 = abs(camx - zv2.x) + abs(camz - zv2.z);		
		}

		if (intersect_on_z) {
			distance1 += minabs(camx - zv1.x1, camx - zv1.x2); //minabs() will compare each term and 
			distance2 += minabs(camx - zv2.x1, camx - zv2.x2); //will return the one that has the smallest absolute value
		}
			
		if (intersect_on_x) {
			distance1 += minabs(camz - zv1.z1, camz - zv1.z2); //same as above
			distance2 += minabs(camz - zv2.z1, camz - zv2.z2); //
		}		
	}
	else {
		//compare height only
		distance1 = abs(camy - 2000 - y1);
		distance2 = abs(camy - 2000 - y2);
	}
	
	if (distance1 > distance2) return -1; //actor1 behind      actor2	
	if (distance1 < distance2) return  1; //actor1 in front of actor2
	return 0;                             //equal distance 
}
    • First, the ZV coordinates are adjusted in case the actors are not in the same room as the current room. Then, there are two cases:
  1. both actors are in the same Y range and are not colliding: use Manhattan distance (or a variation if they are colliding)
  2. actors are not in the same Y range: compare their height
  • These notes relate to the question of how to optimize a 180 degree turn (when to start running), however they are based on the false notion that starting to run immediately halves the rotation speed. Instead, if a fast standing turn has been started already, that rotation continues for the whole quadrant. As such, these notes are unlikely to be relevant to speedrunning Alone in the Dark but are left here in case they do turn out to have some use after all.
    • Emily runs at 2196 units/sec.
    • The radius of the circle you're turning through when running and turning at same time:
r = v / ω                      (ω is angular velocity) 
  = v / (2 * PI * f) 
  = 2196 / (2 * PI * 0.25) 
  = 1398 units
    • The maneuver has three steps:
1) rotate by some amount α (e.g. 135 degrees)
2) rotate while running to face the opposite direction through an angle of 180-α (e.g. 45 degrees)
3) run until you reach the destination
    • time of the initial rotation:
t1 = α/180
    • time of rotating the rest of the way while running:
t2 = (180-α)/90
    • time saved because of the movement while running and rotating at same time:
t3 = d / v 
   = sin(180-α) * r / v
    • total time:
t1 + t2 - t3
    • different results with different initial non-running rotations:
  0 deg: 2.00 sec (worst case)
 45 deg: 1.29 sec
 90 deg: 0.86 sec  
120 deg: 0.78 sec (best possible value)
180 deg: 1.00 sec
    • These calculations don't take into account the effect of the lateral movement while running and rotating.
  • Some notes on the flipscreen() command: it copies pixels from the BACKBUFFER to the current screen (FRONTBUFFER). It's called flip (and sometimes swap) because in some implementations, it swaps the front and back buffers around each time. This is not what AitD does, as it always copies the same buffer from DOS memory to VGA (as seen [url=http://www.brackeen.com/vga/images/img00030.gif]here[/url]). [url=http://www.brackeen.com/vga/unchain.html]Here[/url] is the page that image is from.

UNANSWERED QUESTIONS

Not given in any particular order, of importance or else.

  • When there's an enemy on the other side of a closed door, the noise texture on it can start to flicker without the monster necessarily being visible. What exactly causes that? (E.g. try warping to E1R5 and wait for the zombie to come to R1.)
  • Why are the E2R0 scripts divided between three LIFEs (LIFEs 115, 116 and 117) instead of all being in LIFE 115? Is there any reasonable explanation?
    • The latter two LIFEs both just handle the logic of single bookcases. All the three belong to actors with ROOM LIFEMODE and thus either all are getting executed or none are.
  • What causes this behavior?: Sometimes if you get a fast inventory initially, it'll hang when you get to the item at the bottom of the part of the list that is initially visible. The hang time seems similar to the normal initial hang, and so this probably ties in with that. This has not been reproduced and it isn't known how it works.
  • For what reason are the bboxes of the rats in E4 made slightly bigger in LIFE 442 (etc.) after they've been aggroed? This causes them to get caught sometimes.
  • Why does the game go to E1R4 C8 (view from outside the window) when you enter the R7 stairs on the same frame as focus is shifted onto the second dying nightgaunt? C8 is, in any case, the first view listed for R4.
    • When the nightgaunt gets deleted, its floor and room are set to -1.
    • Also why can't the PC reach the waypoint in the stairs TRACK while in this state?
    • To reproduce this, hold UP when loading the save file called "Hold Up For No Focused Actor.ITD". May take several attempts.
  • It looks like the ROOM coordinates are generally used for reaching waypoints in a TRACK, but what about when there's a ROOM/WORLD coordinates desync? During such a time, the PC is unable to reach stairs waypoints, just circling around them with the wrong Y-coordinate. To see this effect, load the save file called "Hold Up For No Focused Actor.ITD" and just hold UP when it resumes so the game both moves the focus to and deletes the S Nightgaunt at teh same time. Works most of the time, at least in the CD-ROM version.
  • Is there some one-frame window when you could get stuff to happen that involves an action like searching but doing it in the wrong direction because you're getting knocked back on the same frame as the ANIM finishes (when the collision check is done)?
    • It seems you can't search things without facing the right direction even when you're stuck inside them. Presumably your center is used as a point of reference. Can it be confirmed that these kinds of tricks aren't possible?
  • Drinking a flask while being knocked down/up stairs, when timed right, causes the flask to stay in your hand. In this state you can only rotate and execute the search command until you've gotten hit or used another item, which removes the effect. Drinking another flask retains it.
    • Looks like one of the effects is VAR 24 is left at 1, meaning the game thinks you're backing up. This is why you can't get doors to open. You also shouldn't be able to climb anything.
    • You can also get the flask to stick if you get hit while drinking (after dying)? While drinking it and jumping? With this active, VAR 24 is indeed left at 1 because LIFE 549 doesn't have the case for BODY 268.
      • Getting hit now can cause reverting to the normal BODY (because of the default case in the switch in LIFE 553). After this had happened I could suddenly jump by tapping BACK! Wasn't holding any other keys.
        • This has not been reproduced and it's unclear what led to it but it 100% happened. How can this be reproduced?
  • What are the ROOM/WORLD desyncs in E6 based on after you die while defocused? (see "CAMERA DEFOCUS TRICK")
    • This probably involves the rooms' floor coordinates but the exact reason is unknown.
  • What makes the game crash when 3D-wrapped-around polygons pass through where the PC (presumably any animated actor) is? This was observed in E3R7 throwing a medkit from 2422, -7596 at an angle of 359.6, 11.000 cycles. Causes a stretched polygon that looks totally horizontal to appear near the middle of the screen during the wrap-around. Warp the PC so they're in-shot inside the room and it should cause the softlock.
  • Got an actor poly glitch with the rifle that lasted one frame, looked like a stretched polygon that might have reached outside the screen. This happened in E3R0 standing in the view that faces W with Emily stood in the middle of the hallway.
    • Unfortunately the video was rendered with too low a framerate (30 instead of 60), and that frame was lost. Couldn't reproduce it.
    • What causes this? Can it corrupt memory?
  • Can you manipulate the "timestamp" field in the cache entries so as to get the game to unload the wrong entries, leaving ones you'd like to be kept for a longer time? Is this ever even useful?
    • These fields use Timer 1.
  • What's the exact sequence of events during vertical desyncing? The desyncing happens on the frame when the PC reaches the target Y-threshold in the stairs.
    • This likely has something to do with it: The ROOM and WORLD coordinates are only updated at the end of each 2000 unit fall to stay in sync with the bbox. The RV shows those coordinates with MODY added to them, not how the game sees it. The amount of desyncing is the same as the last MODY value before reaching the Y-threshold.
  • Can you get a knockback from hitting yourself with an arrow somehow?
    • When it's flying through the air, it doesn't collide with you at all. If you move and collide with it, you can pick it up or let it keep flying. You can't seemingly get hit this way.
    • During the infinite bow trick, even though it plays the knockback ANIM, it doesn't lead into being knocked back.
    • This question was inspired by looking for alternative ways of getting the transition vertical desync in E3R11 but it's very unlikely this trick, even if possible, would save time in AitD. However, there's still the open question about what happens during the infinite bow trick whenever you get hit by the arrow as it returns to you.
  • Are there further ways to optimize loading lag across the game if you study what the game loads when through the use of the debug version of DosBox? This wasn't done at all, though the general principles don't seem too complicated.
  • When jumping up the E5-R3 stairs, got a strange delayed slt when saving a few times at the top of the stairs so moving into the E3-E5 transition while still in kf4 of the jump. There was a s/l snapback first and a few frames later (when entering position 3, the rotate command, in the TRACK) a sudden turn to face due W. Now when saving after a new rotation had started, ended up turning due E (a 180-degree rotation). It's not clear why this happened like this.
  • Why does the game interpret hitting LEFT as if you were hitting right when you're jumping up the E5-E3 staircase and hold they key down before reaching the end of the transition? This is the only time this behaviour has been observed.
    • This can't have anything to do with hitting the stairs trigger in E3 after the TRACK has finished because that would cause you to be sent back to E5.
    • This is probably related to the very brief rotation that happens automatically right as you're about to reach the top of the stairs.
  • Every time you start a new manual 90-degree rotation, it's understood there will be a frame in-between each rotation. However, in tests where 10 full rotations (3600 degrees) were performed at SPEED 0, the time this took made it seem like only roughly 1 frame per half a rotation was lost, not 1 frame per quadrant. This should be replicated and studied more. What's wrong with the understanding of how it works?
  • How did these two things happen? Somehow had a save file in E1R1 that caused the bird to trigger instantly upon entering E1R4 (at 11.000 cycles) even though normally this can't happen on your first visit after loading a save file. The vase hadn't been touched and there was no 60-second wait in that room either. Possibly related to this there's a save file where actor 21 (E0 bird) has its ROOM_CHRONO wrapped around to a huge value with Emily in E1R0.
    • Sadly the (former) save file was deleted. The latter is the savefile called "Actor 21 (loft bird) ROOM_CHRONO.ITD".
  • Why can you drink a flask while getting a lag snap when other actions that cause a BODY change do not permit this?
  • Can this be reproduced? "The game enters Pregzt cam after throwing the talisman at him, every time. (after throwing anything actually?)" Normally nothing should happen but it's difficult to imagine having hallucinated this effect. The Pregzt view is reachable by clipping into the colliders around him or by getting above him, but this doesn't sound like it was done here. The LIFEs don't seem to have any code that focuses Pregzt other than after placing the talisman (LIFE 518) or if he's hit by the lit lamp (522).

DISCUSSED, WAITING FOR REVIEWING AND EDITING INTO GUIDE

  • Actors that are far away from the room you're in don't get rendered even if they have LIFEMODE 0. Also if the view is in E2R0 and the player is in E2R1 (at least works during a room-view desync), the hitboxes during attack ANIMs become stationary, suggesting the PC isn't actually getting animated. What are the exact rules that govern this stuff?
  • Why can't pushables be gotten to clip corners normally?
    • This obviously has something to do with the order of ejections.
  • When you're inside a pushable, why can you still push it around with you in large steps? If you are able to move, that suggests there shouldn't be a collision between it and you anymore.
  • Why does the game play two sound samples when the vase hits the bird in the save file titled "Why the Double Sample.ITD"?
    • The game always plays two samples but they're normally played back to back with the same priority so the latter always interrupts the former before there's been time to start hearing it. Because of caching (the bird's LIFE 74?), there's a small delay after loading this save file so the first sample this time has some time to be heard.
  • What causes the camera wraparound polygons sometimes (rarely) to stay on the screen after the wrap-around is over?
    • This can happen e.g. when you're in E1R1 C3 at -13013, -15, -27000 facing 332.9 and throw a first aid kit.
  • Why does the game sometimes move you more than one item down in the inventory? When does this happen?
  • Why does the game sometimes zip you down through the inventory without an initial delay? When does this happen?
  • What causes the flow glitch? Does it work the same for any flow actors? Can it be done with bubbles or debris? To what effect? Etc.
  • What causes the flow crash? Is it the same mechanism as in the save file called "Crashes When Something Thrown.ITD"?
    • Can the flow crash be done while the actors causing it are not visible, or if the screen is dark?
  • Why exactly can you get what seems like half-a-frame's rotation sometimes even though it would make sense all rotation was in increments of whatever the per-frame rotation speed is? E.g. turning 7.4 degrees (21 units) while standing still, which is not a multiple of 3 degrees (9 units).
  • When doing a 180 in single-segment runs, it's best to first turn on your heels (standing) for a while. What is that optimal angle to start running at in a situation where there are no walls to your side? What if there's a wall directly next to you?
    • Relatedly, what's the best technique to corner when you can't rely on segmented tricks or knowing what part of which keyframe you'll be on? Some kind of "best on average" approach.

MISSING FILES / CANNOT BE REPRODUCED DESPITE SOME EFFORT

Missing files and/or cannot be reproduced:

  • Why was there a softlock here? The PC visited E1R4 twice to get the bird to start spawning, started to open the R5 door from the N and was warped to R1 so they also start opening the R4 door, all while the R0 zombie is waiting behind the R5 door. There's no visible corruption and none of this should cause softlocking through any known mechanism. This couldn't immediately be reproduced.

File:Why did this cause a softlock.avi

  • If you pick something up right as the game is resuming from the menu, it usually causes it not to resume rendering the game while just the text is drawn on screen, until you've entered and left the menu again. At least once I managed to get it to draw the pickup prompt right on top of the main menu though and keep rendering afterwards. Not sure how. (Actually this could be explained simply as a lag frame causing the first frame to be skipped or something mundane like that). The footage below shows it happening in VLC (the game is drawn normally) but not in Freemake Video Converter, oddly enough. How can that be the case?
    • Apparently due to color palette being ignored in VLC (note fades being visible in Freemake only aswell). S. TECHNICAL NOTES about fade-ins/outs, especially how they skip the first two colors to render white text on a black screen.

File:Pickup Prompt on top of Menu.avi Original file: https://drive.google.com/file/d/1uC5WUtvihq5dxXdQpGpOTC3iVBG2Ur9e/view?usp=sharing Comparison file: https://drive.google.com/file/d/1-T8fkfom807OIU--I1e98kOGo1J5Gjmx/view?usp=sharing

Personal tools