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

From SDA Knowledge Base

Jump to: navigation, search

Contents

Alone in the Dark 1

ABOUT THE GUIDE

  • 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 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 English CD-ROM version (GOG). If you want to study AitD 1 more, you should consider using a different version, since no other version has been tested nearly as thoroughly. The French CD version in specific is a good choice because of reasons explained under "known version differences".
  • 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 competently run them or 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 (which would have meant the penalty was diminutive).
  • 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) 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
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
MOVE movement mode
TRACK_MODE/TRACKMODE deprecated term for "MOVE"
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.
Example Example
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 = 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
subframe really sub-keyframe (one frame within a keyframe: what's normally called a "frame")
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
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.
Example Example
  • 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.

META

USEFUL FILES

  • If you aren't sure where to find a particular file or program, look here.


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.
  • While it would make a big difference in lag times, you're not allowed to change the default GOG CPU cycles setting of 11.000.
  • 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. 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.

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. It comes with an attached readme for instructions but here are some additional notes as well.
  • The RV has a built-in function (hit the "1" and "2" keys) to quickly test the workings of the two timers by winding them back 5 seconds at a time, and get specifically Timer 2 closer to a desired value. 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 during actual running.
  • When recording runs and other material for AITD, please set up also recording two instances of the Room Viewer at the same time (one for the game world, one for the VARs), because this helps to analyse your work afterwards. In order to record both instances, you need two differently named copies of the Viewer. Recording your key presses will also help immensely. This can be done using NohBoard with THIS PRESET.

EDIT IN NOHBOARD AND THE PRESET FILE, MAKE SURE THE SECOND VERSION OF THE VIEWER IS PROVIDED?

    • You might also find this Windows .bat file handy:
      • ECHO "Remember Num Lock on!"

PAUSE start "" "AITD room viewer" -screen-width 960 -screen-height 720 -screen-fullscreen 0 -speedrun start "" "AITD room viewer 2" -screen-width 1627 -screen-height 360 -screen-fullscreen 0 -speedrun start "" "NohBoard\NohBoard.exe"

    • 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 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 with the player actor being focused. 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 cannot follow the player around during the intro cutscene unless you manually go to the right rooms. This is because it's set to track the actor that has TRACK_MODE 1 (manual movement) that only the PC has during regular gameplay. The actors used in the intro and outro are actually different from the usual PC actor.
  • The Viewer's Model Viewer shows not only the BODYs but also can play ANIMs on top of them (indiscriminately). It also gives you the keyframe lengths and offsets.
  • The warp command, even when just changing the actor's angle, can desync its coordinates. If it's happened to the PC, it should be reflected on by a new red bbox being drawn where the ROOM coordinates are. 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 monsters and all pushables) to move between rooms, not e.g. any room script actors.
  • The Viewer refreshes its memory (reads from the game process) at the same speed as your display refresh.
  • 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.
    • 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 will 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 lag -counter, on the other hand, adds up everything.
  • 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. This is generally when you go back to the floor or area it's in.
  • SUB_KEYFRAMEs as shown in the RV are 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.
  • 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 mode. This was deemed to make the screen too busy, and thus removed from the master.
  • 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.

LIFEDISA/TRACKDISA/MEMORY VIEWER

  • The "DISAs" are the tools used to unpack the game's scripts and tracks and the Memory Viewer shows the game's entire RAM.

Instructions:

  1. Create a new folder.
  2. Unpack all files of LISTLIFE.PAK to that folder with quickBMS.
  3. Search for a file named "OBJETS.ITD" (in AITD root folder) and copy it there as well
  4. Unpack ENGLISH.PAK (or FRANCAIS.PAK if you have the French version). Search for a file named 00000000.dat. Rename it to "ENGLISH.TXT". Copy that file to the same folder.
  5. Copy LifeDISA.exe to the folder and run it.
  6. A file named "output.txt" with all scripts will be created.

The files ENGLISH.TXT and OBJETS.ITD allow the tool to convert objects' hard-coded IDs inside scripts (e.g.: 50, 201, 203) to human-readable names (e.g.: "A rifle", "A letter", "A key", ...). In addition, the file called vars.txt can optionally be placed in the same folder before running LifeDISA to replace a lot of other names as well, including all the ANIMs, LIFE names 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.
  • 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.
  • The Memory Viewer reads the first two megabytes of DOSBox RAM (more than enough to see all of AitD's memory) and displays it using the in-game palette so that you can clearly see where the two background images and the backbuffer are stored. 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 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.

CACHE VIEWER

  • A program that displays the contents of the cache in detail. Works best if launched after the game is already running. The data shown is as follows:

Bytes Used – Bytes Maximum ... Number of Entries – Maximum Number of Entries

Then for each entry (sorted by last access time): ID – Size – Timestamp

  • You have to use the following as arguments (case sensitive), depending on what you want to display:
ListSamp
ListLife
ListBody / ListBod2
ListAnim / ListAni2
ListTrak
    • You can combine up to five arguments. ListBod2 and ListAni2 are for Emily.

FREE IN THE DARK / FITD HACK

  • Free in the Dark is a very accurate source port whose history is unknown, though it is suspected someone might have had access to the AitD source in order to be able to make it. 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 render bug specifically (see "render bug"). 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.

GAME

GAME FILES

File Contents
CAMERAXX.PAK All background camera images (in 320x200 8-bit 256 color). Can be opened in Photoshop etc. as RAW images.
ENDSEQ.PAK Various resources (e.g. backgrounds) related to the end sequence (after Pregzt is defeated).
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.
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.
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 GoldWave.
LISTTRAK.PAK Pre-recorded 2D paths for actors (e.g. when climbing stairs).
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...)
VARS.ITD The initial values of variables.
DEFINES.ITD The initial values of flags.
PRIORITY.ITD  ???
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

  • These are the versions that the most is known about:
    • The GOG release, which is probably just the old official English CD-ROM version.
    • French floppy version.
    • French CD-ROM (same download page).
  • Only the original French CD-ROM version seems to have been released in debug mode (accidentally), meaning it's a lot easier to reverse-engineer. In fact this version was used for that purpose.
    • Despite debug versions normally running slower than release versions, this doesn't seem to be the case with the debug/non-debug versions of AitD (tested using the RV).
    • The downside to it being in debug mode means direct file comparisons are impossible and so it's not really known which differences may exist between French CD and other versions.
  • Version difference: the French CD-ROM version doesn't crash coming from a save file where reaching the actor limit was otherwise causing crashing (the file seems to be missing).
  • Some Room Viewer data is only displayed for the CD/GOG releases of the game. This includes anything to do with the game's timers (FPS meter, lagometer, CHRONO...).
    • Also ALLOW_INVENTORY is only shown for the GOG release.
  • Some typos were corrected between the initial floppy and later CD-ROM releases.
  • The CD-ROM version does not have a copy protection screen.
  • The floppy version (tested on the English one) seems to crash a lot more seldom due to the render bug 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 the cause.
  • The CD version has a slightly different tracklist, and changes in 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.
    • No other kinds of differences exist in the scripts.
  • The orchestral hit sound sample played when entering dangerous areas such as E1R7 has been replaced with a short piece of music (string cacophony).
  • There are no differences in the trigger or camera trigger data.
  • 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 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 bit 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 GOG/CDROM version those had been set to zero (cleaned by the devs?). 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.
  • 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.

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.
  • 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. This was not done much during testing and the only known way to do it without cheating is to corrupt memory, and as such, is of theoretical/testing interest.
    • Actors other than items that you've dropped this way are dangerous to touch and usually cause instant crashing with few exceptions.
  • 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).
  • 'p' to pause, "action" to unpause.
  • alternative keys:
    • enter/keypad enter for "menu" and "select"
      • there seems to be a small difference between the functionalities of these two keys (see "lag manipulation")
    • space/keypad ins/keypad del for "action" and "select"
    • esc to "cancel", also to select not picking up an object quicker
    • other keypad keys for movement, also with numpad 7 and 9 you can combine running with turning and 1 and 3 are the same backwards
    • some other random keys are mapped to movement as well
    • these can obviously 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 keypad 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.

MEMORY/CACHES/BUFFERS/SOUND SAMPLES

  • 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 often the wrong entry is removed.
  • The cache can be read in detail with the Cache Viewer.
  • The music cache (in the floppy releases) has unknown properties since those versions are encrypted and cannot be read.
  • In addition, there are two arrays for keeping actor data in. These are discussed under "actors".
  • 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 without making any assumptions about what will be needed in the cache otherwise. This means rebooting the game might lose you time unless you take the time to get every sound effect (especially), ANIM etc. cached before you start recording any segment where they will be accessed.
    • All the LIFEs used are also always cached.
    • 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):
Sample ID Description Approximate Loading Lag
0 drawer opening 77
1 vase breaking 84
2 glass breaking 167
3 lock opening 83
4 door creaking 133
5 heavy object moving (wood) 67
6 thunderclap 200
7 taking a shot (house) 50
8 reloading 32
9 weapon click 17
10 Edward after flask 33
11 Emily after flask 32
12 Edward coughing 33
13 Emily coughing 34
14 hit sound 33
15 hit sound 2 33
16 Edward attacking 33
17 Emily attacking 18
18 Edward attacking 2 32
19 Emily attacking 2 18
20 Edward attacking 3 15
21 Emily attacking 3 0
22 Edward jumping/climbing 17
23 Emily jumping/climbing 17
24 Edward climbing 2 17
25 Emily climbing 2 16
26 Edward hit 67
27 Emily hit 16
28 bird bite 33
29 zombie groan 167
30 stepping on wood 33
31 stepping on wood 2 33
32 stepping on stone (house) 33
33 stepping on stone 2 (house) 33
34 Edward dying 0
35 Emily dying 50
36 doors slamming 50
37 door creaking 2 116
38 Edward falling through crack 166
39 Emily falling through crack 134
40 Edward falling into a pit 217
41 Emily falling into a pit 216
42 thud 34
43 thud 33
44 nightgaunt screech 67
45 nightgaunt dying 116
46 stepping on carpet 17
47 stepping on carpet_2 33
48 axe/arrow fired 33
49 page turning 67
50 sword clang 51
51 stepping on stone (house) 32
52 stepping on stone 2 (house) 33
53 water dripping 284
54 water dripping 2 66
55 water dripping 3 101
56 stepping on stone (underground) 34
57 stepping on stone 2 (underground) 34
58 aquaphone chime 151
59 eerie howl 117
60 taking a shot (underground) 50
61 drinking 16
62 heavy object moving (stone) 115
63 eldritch gurgle 234
64 back cracking 33
65 throwing 17
66 hoarse laughter 84
67 malicious laughter 49
68 deep laughter 81
69 drawer opening 50
70 wading through water 33
71 wading through water 2 50
72 water splash 116
73 rat squeal 83
74 car revving 67
75 car approaching 417
76 crickets and frogs 183
77 frog and car 217
78 slamming car door 34
79 opening car door 34
80 exiting car 50
81 stepping on gravel 17
82 stepping on gravel 2 15
83 wasp hoot 0
84 slimy slurp 49
85 slimy wheeze 83
86 Deep One growl 133
87 vortex chant 84
88 bird call 50
89 chimes tinkling 335
90 low rumble 100
91 saber breaking 99
92 beetle grunt 100
93 Cthonian cackle 184
94 pained moan 83
95 birds chirping 416
96 croaky laughter 49
97 Necronomicon whip 34
98 Vagabond growl 150
99 fireball explosion 101
100 fireball launch 67
    • 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.
    • It looks like the delay for samples is simply a function of the sample's filesize, which is apparent enough when considering the information in the table. The variance between attempts looks to be around two frames generally so consider that the margin of error in the above information. Thus a frame or two could be lost per sample effectively at random. For some reason, sample 34 (Edward dying) seems to always be pre-cached as is #83 (wasp hoot).
    • The worst samples to have to load in during a run (outside the intro/outro) are the talisman chimes and one of the random water dripping samples played in E5, both wasting around 300 ms.
  • If you take a step (sample 30), then get sample 75 (car approaching) to play, then take another step, the step sample will have been flushed. Thus we can tell the cache just about fits the long car sample but not that and the step sound at once.
  • The music file caching load times (only applicable to the floppy versions) were not tested much but they're midi files and as such have very small sizes.
  • Any sounds and cache entries could have a compound effect worse than just how long they loaded for initially if they're also forcing more samples to be recached later that were removed to make room for them.
  • It's not clear what kind of hierarchy there is between samples that determines which samples can interrupt which other ones. It doesn't seem to be about the size of the samples so that e.g. the longer/(more significant) sample overrides the shorter one (the throwing sample can cancel out the slimy slurp for example), nor does it seem to be directly tied with the sample "ID" if that's even used by the game at all (other samples like melee attacks cannot interrupt the slimy slurp despite having a lower ID just like the throwing sample does). Nor does it seem to have anything to do with whether or not the latter sample was already cached before. It's difficult to test whether it's a question of where in the cache each sample happens to have been stored but this doesn't seem like a very likely explanation either.
    • There could even be some customized tablebase in which different sounds are given different priorities.
    • If there's another repeating sample playing, it may cancel out [some] other ones completely so they're not even loaded into the cache thus saving some time. This at least works with the vortex hum and melee attacks. Single-time samples don't seem to cancel subsequent samples from being cached and played.
    • The vortex also cancels out the talisman tinkle (chimes) which would be useful since it takes a third of a second to cache whereas the vortex sound takes less than a tenth.
  • 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).
  • It's difficult to tell how exactly lag is created by loading in actors. 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, 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, unless you take the time to make sure the actors in A have been cached again. However, no such effect is seen with the presence of stationary actors (though each one causes more loading lag in general). It could be that actor flushing is done selectively based on their flags.
  • 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.
  • ANIMs played for the first time seem to be cached. It takes a short time (mostly just one frame at most).
  • 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 inv or menu).
  • One thing that seems to be never cached is the 2D background, it is always loaded from disk. Also sometimes it seems to be loaded twice, causing extra lag.
  • In summary, make sure to cache as many relevant sound samples and ANIMs etc. 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.
  • PRIORITY.ITD is always cached. It's unknown what it's used for but it's a small file.
  • Cache bug: 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 ("oldest") that is used as a reference will always be lagging one behind, which is reason one why the algorithm can fail.

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. A large positive value will cause an entry to be removed even if it's the last one cached.

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 arrow 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 subframe 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/subframe (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.
  • S/l-ing causes the smoke to disperse 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.
  • The first time reloading a save file coming from the main menu has a fade-out fade-in.

PAUSING

  • Pausing seems to be perfectly implemented with no anomalous effects noted when using it.
  • 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 hold a movement key, then hit pause to make it unpause automatically when 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.

LIFE SCRIPTS

  • LIFE is what the game calls its scripts (presumably because they bring the actors to life). 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 without 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 being the exception.
  • 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.
#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
ENDIF        //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. ISFOUND means it's currently in the inventory.
  • DEFAULT refers to a default case when no matching case exists in a switch.
  • DEF_ZV: Defines a new bounding box for the actor. The six parameters are X1 X2, Y1 Y2 and Z1 Z2.
  • DO_CARRE_ZV: Resets the actor's bounding box using default values stored in the current BODY.
  • DO_ROT_ZV: The same except the box is rotated based on the actor's angle (making a different orthogonal rectangle).
  • DO_REAL_ZV: Calculates the bounding box from the actor's current 3D-vertices directly, taking into account the effects of rotation, translation and scaling. Very CPU-intensive and so not used very much.
  • ANIM_ONCE; ANIM_ALL_ONCE: These both have two parameters which are ANIMs. The second one gets stored in NEXT_ANIM. If the first ANIM is interrupted, the second one gets flushed and replaced.
    • ANIM_ONCE: Plays two ANIMs once through that can be interrupted.
    • 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 MOVE 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 MOVE 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.
  • 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: Causes a TRACK to advance.
  • 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_AT: Differs from DROP by being forced regardless of room constraints.
  • SPECIAL: Three kinds of particle effects, where 0 is 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.
  • MOVE: Sets an actor's movement mode. The second parameter is called TRACKNUMBER.
    • 0 = NONE. This is often used to lock the player position before doing something.
    • 1 = MANUAL. Left and right arrows 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 TRACKNUMBER. The code used is the same as for MOVE 3 except the target point is another actor and thus can move around.
    • 3 = TRACK. Follow a TRACK given in the TRACKNUMBER. More under "tracks".
  • HIT_OBJECT: Defines the size of a hitbox cube around a particular vertex of an actor's BODY and a HITFORCE (damage value) for hitting any actor within the hitbox. Sets ACTIONTYPE to HIT_OBJ.
  • STOP_HIT_OBJECT: Cancels out HIT_OBJECT.
  • HARD_COLLIDER: Returns the ID of the first collider touching the actor. Only colliders with flag 0001 set affect this.
  • ACTOR_COLLIDER: Returns the ID of the first other actor touching the actor.
  • TRIGGER: Returns the ID of the first trigger the actor is inside.
  • 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.
  • END_SEQUENCE: It's assumed what this does is to suspend the script and show all 320x200 backgrounds located in END_SEQ.PAK file. This is the animation with Pregtz burning. When the animation is done, it resumes the script. It doesn't necessarily do anything else (like changing some game internal variables).
  • WATER 0: set water height at 1000 (below the ground), WATER 1: set water height at -600 (a little higher than the ground). The water level is only used for clipping the bottom half of the player when they're in water. It's not used for playing the wading sounds.
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

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
POSREL

0 any
1 left
2 right
4 front
8 back

MOVE

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 MOVE 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 MOVE 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 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.
  • Most CVARs are important constants(hence the name, "constant variable") but a few of them are actually used as flags during gameplay.
    • Changing CVARs can easily result in instability.
  • 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.MOVE 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.MOVE 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 and certain music tracks are played.
  • 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 your 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 arrow 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. It is also used in a conditional clause completely unnecessarily.
  • 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. It governs whether or not dark rooms are actually rendered dark and if the related message should be shown, and also changes the backgrounds that have the Pregzt tree in them.
  • 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
  • LIGHT is what controls whether the game draws the current room or not. Drawing the little light circle is done separately. In E6, its position is determined by CVAR 13 which can be -1: not drawn, 1: on the player, 13: on the lamp (if it was dropped).
  • Other VARs etc. are discussed in specific parts of the guide.


INTERRUPTS

  • 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 really used
    • 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.
  • The following examples give a general idea, though there are too many potential interrupts 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.
  • 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.



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.
  • The three kinds of coordinates are:
    • ROOM: Local coordinates relative to the center of the room the actor is in.
    • WORLD: Coordinates relative to the center of the room the view is currently in (usually the PC's room).
    • ZV: Bounding box coordinates relative to the center of the room the actor is in. Generally 880 units higher up than the ROOM coordinate (for Emily).
    • ROOM and WORLD coordinates are at the actor's feet while ZV is the centerpoint of their bbox, and thus in the air, for almost all actors (there's a few exceptions which are most likely erroneous).
  • 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 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 PC 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 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" 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 at once but only checks for collisions in the actor's current room.
  • What your relative position to another actor or collider is (left, right, top, bottom) depends on your orientation as well as position.
  • All bounding boxes etc. are axis-aligned, i.e. they're rectangles with orthogonally aligned sides. The only exception is camera triggers.
  • The three angles for actors (alpha/beta/gamma) correspond to their pitch/yaw/roll, so the commonly used one is the "beta", the yaw.
  • The actor being followed is referred to as CURRENT_TARGET in the code and the focused actor in this guide.
  • 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 stand inside two room triggers at once, the same one always takes preference.
  • 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.
  • 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 South.
    • E4R1 – what looks like another, perhaps hidden, stairwell exit.
    • In addition, E5R2 has a partially blocked-off Eastern 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.
  • An actor reaching the edge of the plane causes some of its polygons to be stretched out because some vertices that are meant to be connected are on opposite ends of the world. More under "3D wrap-around".
  • 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.
  • 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

  • Flag 0001 and 0008 are required for the game to set the HARD_COLLIDER variable with the ID of the collider in question. Flags 0001 and 0002 (climbable) being set cause the HARD_COLLIDER to be set to 255. If 0001 isn't set, colliding with any collider does not alter the HARD_COLLIDER value, though actors are still ejected from it. This is probably a somewhat redundant implementation but flag 0001 could still be described as "should this collision affect HARD_COLLIDER".
  • The 0004 flag is unused.
  • There are colliders with the 0008 flag set that aren't really intended for the player to interact with them. Instead, they seem to be used for instantiating actors that have the room scripts for that room.
    • The two ways the game instantiates actors are:
  1. Actor position will be set as given in OBJETS.ITD and the size will be the same as the BODY bounding box.
  2. Size and position are instead set to match those of a particular collider.
    • The zvType value determines which option the game goes for.
      • 1-2-3: use values from OBJETS.ITD. The difference is that some of these adjust the bounding box after copying in from BODY in some way.
      • 4: match a collider. There is another field (not shown in OBJETS.ITD) which specifies which collider ID to use as the reference.

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.
  • 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.
  • 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).
  • 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 (MOVE 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

  • 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 TRACKPOSITION. The latter tells you which instruction is being executed.
  • 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 collision (TEST_COL 0): makes sure the actors don't get caught on things (except they can still be caught by lightweight pushables).
    • 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 East, 180 is due North 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" istructions use normal forwards movement and rotation (same as MOVE MANUAL) to guide the actor into a new position (after LIFE 550 has changed the ANIM_MOVE array). The actor can either turn left, turn right, or do nothing if it's already facing the target point, with a threshold of about 4 units (1.4 degrees) on either side within which no rotation will happen. This is to prevent the actor from shaking left and right on every frame in some cases.
    • This means if you want an actor following you (which uses almost exactly the very same code) 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.
  • Indecision bug: An actor will also end up not rotating at all if it's facing directly away from the next target point of a TRACK or target actor of MOVE FOLLOW. 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 South while the target point is due North, 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.
      • It may be possible to make a non-orthogonal or interorthogonal angle work despite the tangent function only having 8 rational solutions within a 360 rotation (i.e. only 8 pairs of integer X- and Z-displacements, and their multiples, give a rational tangent function and thus a rational angle, these pairs being (1, 0) for 0 degrees, (1,1) for 45 degrees, (0, 1) for 90 degrees etc.). There is still the possibility the game will systematically round the actor's position down or up in just the right way so some angles near those 8 could also work. This has not been verified.
  • "walk stairs on X/Z" is the same as goto, 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 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 seem to prevent picking things up, 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.
  • 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. lamp 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 lamp turns performed on the first frames after hitting a trigger. This seems to be impossible.
    • Can't get any kind of lamp 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 north stairs or south 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 lamp 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 MOVE 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 MOVE TRACK so you'd have to be able to affect just VAR 0 while in LIFE 550 without also setting MOVE. 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 kf 5 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 (kf 3 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 lamp 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.

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 MOVE 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 MOVE 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 MOVE 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 MOVE 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 MOVE 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 arrow. 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 the left and right arrow keys, 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) the direction changed to being -6.0 degrees, so 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 South up to due South. 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 MOVE 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 lamp 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 MOVE back, but then it resumes. Drinking the water from the jug is the same except only VAR 0 and MOVE 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.


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 keyframe 1 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 keyframe 3. 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 (north) 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.MOVE 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; can't seem to get the ANIM of getting sucked into the jelly to happen in another direction (so long as type_of_death is 3, it's set every frame), otherwise the ANIM could take you to R11 into the transition; 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 FOUND_LIFEs 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:

	while in E1R6, get the bow
	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 end, giving back inventory access
	drop something, keep manipulating the doors so the North one never closes
	you should have no collisions but be able to move normally
    • 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 East 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 East. 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 lamp 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.
  • 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 FOUND_LIFE 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 kf 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 MOVE 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), NEXT_ANIM 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

LINK THE CAMERAS.GSHEET

  • 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 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
      • X and Y are the horizontal and vertical field of vision respectively while Z is a projection in the direction the camera is facing that doesn't change the FOV: i.e. it moves the camera forwards or backwards from its set coordinates (probably used for convenience). The 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.
  • 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 hide/show actors with LIFEMODE -1 or 2 based on the active camera.
  • 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.
      • 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 higher up in the file is used invariably.
  • Actors with LIFEMODE -1 have the same visibility as those with LIFEMODE 2.
  • 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 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 in one of the three ways shown in this video.

1) Clip OOB in R2. Run to the edge along either axis so you're aligned with one of the other two camera triggers. Enter R10. 2) Clip OOB in R2, enter R10. Now run to the edge to a position so the view changes to C22. Enter R2 (gives you the R2 default camera), then R10. 3) Lag OOB going into R4. Go around and hit the R2 trigger on the E side. Enter R10.

    • Each time you enter a new room in these paths, make sure not to be inside a camera trigger or it might fail.
    • There are two more unused views between R7 and R10 that were probably going to be used in the missing R9. Those can't be reached in-game.
  • 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.
    • 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.
    • 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 bathtub monster 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".
      • 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 unfocused 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 render bug 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.

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 to be caught in a spot mysteriously. It doesn't seem to matter which way the player is facing or which weapon or attack it even is, just their location and the view you're in. Observed while the view was in the (dark) E2R0 and the player in R1 just E of the doors.
    • This is probably because given some circumstances, the game doesn't actually bother to play out the ANIM, explaining why the hotspot (or the rest of the PC's vertices) don't move.

OBJECTS AND ACTORS

ADD OBJECTS SHEET FILE LINK

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 already, 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.


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.

  • Actors could be thought of as "live" versions of the 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, and its information is copied over from the objects array. The slot is freed immediately when the actor has been removed, and the information is copied to the objects array again. This can happen when the player 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. When multiple actors appear at the same time, the slots are given out in order of increasing actor ID. Both the objects array and the actors one 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 slot numbers around when all the active actors are loaded into the lowest slots in order of increasing ID. Also actors that are far away might not get loaded in again.
    • The arrays are mostly similar except there are more fields for actors than objects. The reason there are two such arrays in the RAM is this way the game is only looping at most 50 actors' LIFEs every frame instead of 300.
    • 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.
  • 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 object off the floor
  2. the object is getting selected in the inventory, or
  3. you have the object 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 of LIFEs and FOUNDLIFEs. 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.
  • 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
    • 0 = whenever the focused actor is on the same floor
    • 1 = whenever the focused actor is in the same room
    • 2 = whenever the focused actor is in a camera view close enough, 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 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, an actor with LIFEMODE 1, for example, can enter another room without instantly disappearing.
  • 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. 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 FOUND_FLAG 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.
  • 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 handles such cases fairly ungracefully, either overwriting existing actors (possibly the actor in SLOT 49) or even causing a crash.
    • There seems to be a version difference between the English and French CD versions. The French version might have better handling of such cases.
    • When enough actor array slots have been filled, items can no longer be spawned in by dropping them or throwing them. Dropping them always yields the message "There is no room". Trying to throw them runs 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.
    • 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. Suspecting Pregzt may also stop firing fireballs at you if the actor count is too high.
    • Actors caused to vanish this way never reappear unless there is some code that moves them back into the game world, e.g. Deep Ones and arrows/axes.
    • It seems the last slot, number 49, might generally contain the INHAND actor (whose FOUND_LIFE is getting executed every frame).
  • The save called "Missing Doors.ITD" has permanently missing doors and other actors. Some items are stored in E3R5, some in E5R0.
  • Schematic for how actors are loaded into the actors array (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
  • Actors with ID -2 are "flow", particle effects, e.g. blood.
  • 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 (apparently).
  • 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 probably. 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.
  • 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.
  • 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. If there are more than that, the rest are ignored (the ones with the highest SLOT numbers).
    • 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. The actor with the highest slot number takes preference if multiple collisions are happening. 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.
    • All these fields are reset (to -1) on every frame.
    • In many cases, either COL or COL_BY could be used for the same logic, but it might be faster to check COL_BY (a single value) than the three-actor COL array. For example the fireballs in E6R6, in LIFE 532, use their COL_BY field to detect hitting the player but they might as well be using the COL field.
  • 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.
  • 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.
  • 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 that actor's HIT_BY and HITFORCE fields immediately (this is its only purpose). 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.


ACTOR SERIALIZATION INTO OBJECTS/OBJECT FIELDS/ACTOR FIELDS

ADD LINK TO OBJECTS TABLE

  • Whenever an actor is moved from the actors array into the objects array, some fields are not stored. 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.
    • 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 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.
  • BBox3D1...BBox3D4: 2D-bounding box vertices used to determine which parts of the screen actually need redrawing around animating and overlapping actors. 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 by commands like HIT or THROW in LIFEs and also in other places, and immediately copied into an actor's ANIM, animType and animInfo. Thus flushing them is unlikely to have any effect unless it can happen between them being set and being copied over.
  • 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 and so the ANIM moves a keyframe forwards.
  • END_ANIM: This is also set to 1 when the actor is restored. Should thus cause any related logic to activate instantly without the ANIM actually finishing.
  • 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 it by another frame probably. Could possibly be used to nudge an actor further and further until the collision (e.g. trigger one) is no longer happening, since the actor will move forwards on every frame. Difficult to check since the player is the only actor whose collisions (other than HIT ones) make a difference in the scripts.
  • 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 kf 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 kf 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 can almost manipulate the nightgaunts to die faster this way but this requires first going back to E0 during the North 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).
      • 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).
      • 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 by 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, the keyframe is reset. This can be used to, e.g. delay dying indefinitely.

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
  • Internally, there's a field called ANIM_TYPE that can take the values ANIM_ONCE, ANIM_REPEAT or ANIM_UNINTERRUPTABLE.
  • 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.
    • The game checks for END_ANIM being 1 for all actors on each frame. If END_ANIM is 1, ANIM_TYPE is not REPEAT and nextAnim is not equal to -1:
anim = nextAnim
animInfo = nextAnimInfo
nextAnim = -1 
nextAnimInfo = -1
  • If you start an animation such as opening a door, but then the camera moves away so the objects involved get unloaded, the animation will reset every time the focused actor enters the area.
  • 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 ANIM_TYPE 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 does not set the player's ANIM at all.
  • 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. 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 arrow 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 has to be redrawn every frame
    • 0002: unused
    • 0004: redraw – redraw once, then set this flag off
    • 0008: not clear (can sometimes be animated but isn't currently?)
    • 0010: pushable
    • 0020: collision – 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 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.

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 MOVE 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.]MOVE
    • [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 MOVE 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 MOVE 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/MOVE MANUAL/LIFE 549:
	delayed door opening (VAR 0, MOVE MANUAL)
	transition (VAR 0, MOVE MANUAL, LIFE 549 -> INV)
	firing bow (INV, which can be used to regain LIFE 549)
	hit by something (VAR 0, MOVE 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, MOVE MANUAL, LIFE 549)

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). 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.
  • 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 getting movement from a standstill if only having 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 lamp snap.
  • 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.
  • 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. 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_MOVE both control whether the player can move. PLAYER_MOVE 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 MOVE 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 MOVE 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 lamp snap.

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 is slightly faster compared to N or W, presumably because the coordinate values are always rounded up (and S and E are both the positive directions).
  • 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 kf 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 "main" movement 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.
  • 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 lamp 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 kf 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.


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 lamp 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.

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.
  • 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.


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 keyframe 2, or 91 if you interrupt a left jab after keyframe 2. 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.
  • 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 keyframe 4. 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-kf 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 kf 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). The first keyframe 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, pushing), 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, ??? (SPEED 0), SPEED -1, clockwise rotation ANIM, counter-clockwise rotation ANIM.
    • Obviously, while MOVE is MANUAL, the player can affect their SPEED by hitting the arrow keys.
  • For the PC to start running, SPEED has to be greater than 0 and smaller than 4. After releasing the up arrow 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 arrow.
-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 MOVE 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.
  • 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.


CORNERING

  • 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 "lamp snap" for information on how to save even more time incorporating that trick into corner clipping.
  • 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 lamp 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 keyframe 3 snapping) 262 units, or something like 6-8 frames of movement. On kf0 or 2 you've gained 197 units, or between 5 and 7 frames of movement.
    • The lamp 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 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 lamp 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 lamp 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 lamp 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.


ROTATION

  • 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.
  • DO_MOVE is the command responsible for enacting normal rotation in stairs and such. 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 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 BETA 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.
  • 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.
  • If a rotation starts during a transition but the transition finishes, and you're holding either left or right arrow, 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 it seems to work.
  • 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 (DO_ROT_ZV) 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 MOVE is MANUAL.
    • If you have an action that calls MANUAL_ROT, this is another way to rotate even without MOVE 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 different ways to manipulate Timer 2 to cause erroneous rotations, see "delay turn".


ACTIONS

(SPACE)

PUSHING

JUMPING/HOVERING

CLIMBING

GRAVITY/FALLING

VERTICAL DESYNCING

ITEMS/INVENTORY/MENUS

INVENTORY/ITEMS

FLASKS

THROWING THINGS

BOW/FIRING ARROWS

FIRING A GUN

ENEMIES/FIGHTING

  • Edwards's hit recovery is 5 frames faster than Em's (55 vs 60).

PREGZT/BOSS FIGHT

CTHONIAN (WORM)

E2 ARROWS/AXES

OTHER ENEMIES

= HITFORCE/DAMAGE MANIPULATION

[NOT] DYING

TIMERS/CHRONO/TIMER MANIPULATION

LAGGING/FRAMERATE

SNAPBACK

DELAY TURN/DELAY SNAP

PICKUP TURN/SNAP

PICKUP TURN/PICKUP SNAP

LAMP TURN/LAMP SNAP

SAVE/LOAD TURN (SLT)

QUICK ROTATION

OOB/CLIPPING

SIDEWAYS CLIPPING

CONVEX CLIPPING

OPENING CLIPPING

LAG MANIPULATION/LAG SNAP/LAG OOB

SNAPBACK CLIP

TRIGGER BOUNCE OOB

PUSHABLE OOB

CHAIN CLIPPING

PUSHABLE LAG OOB

PUSHABLE WEDGE OOB

PUSHABLE OOB OOB

PUSHABLE NARROW OOB

CLIMB OOB

FALL OOB

COLLISION OOB

PUSH CLIPPING/LIGHTWEIGHT CLIPPING

PRACTICAL TIPS/APPLIED

PRACTICAL TIPS/OPTIMIZATIONS

TIMING CERTAIN ACTIONS

THEORETICAL IN-BOUNDS/GLITCHLESS RUN

EXPERIMENTAL SPECULATION

MIGHT HELP IN AITD 2 OR 3

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

GENERAL

THROWN OBJECTS SOFTLOCK

3D-WRAP-AROUND GLITCH

RENDERER/RENDER BUG/2D-WRAP-AROUND

POTENTIAL EXPLOITATIONS

MEDKIT RENDER BUG TRIALS

SOUND BUG

MISCELLANEOUS

MISCELLANEOUS/COMBINED TRICKS

TECHNICAL NOTES

(Probably) Pointless Trivia

UNANSWERED QUESTIONS

Alone in the Dark 2

Alone in the Dark 3

Jack in the Dark

Personal tools