Everything in a Fairlight room -- the knight, the creatures, the things lying about, the doors, the furniture, the floor and the walls -- is a box, and moving is trying a box in a new place against every other box. There is no separate map of the room for collisions: the drawing is only a picture, and what the knight stands on, walks into, climbs and pushes are the object records. This page is how a move is made, from the direction byte to the gravity that is added to it, the test against the boxes, what happens when it fails, and what that gives: walls, floors, stairs, pushing, riding, jumping and falling.

Directions and steps

A direction is a byte with a bit for each way, the same for every object (its +13 on the ground, +18 in the air, and the copies the move works with):

BitMeans
0turn back when stopped, rather than stop
1rising: no gravity this pass
2down +6 (H to ENTER; the stick left)
3up +6 (Y to P; the stick right)
4up
5down: gravity
6up +8 (Q to T; the stick up)
7down +8 (A to G; the stick down)

The knight's keys (read in KNIGHT_CONTROLS) set one of the four floor directions; a later test wins, so he never walks diagonally, though other things do. A step (STEP_ALONG_X and the two after it) is two units along one floor axis, one along each of the two for a diagonal, and always two up or down. On the screen, up +6 goes up and to the right, up +8 up and to the left.

Boxes

A record's box is a corner and three sizes: from +6 for +9 along +6, from +8 for +11 along +8, and from its top, +7, down +10 (read in TEST_ONE_RECORD, the author's BB2). Two boxes meet when they overlap along all three axes, each test strict, so boxes that only touch do not. Room 2's records, their boxes drawn over the room:

Room 2 with every record's box drawn

Room 2, in its own colours darkened, with the box of every record from the knight's on drawn over it, projected the way ISO_MOVE moves sprites: the knight, objects with sprites, doors, invisible steps (kind 3). The steps of the stairs are boxes the drawing's object list puts down; nothing draws them.

FIND_OBSTACLE walks the records in use from the last down to the first and stops at the first whose box meets the one being tried, leaving out the mover's own and anything carried or gone (a door always counts). The first six records (FIXED_RECORDS) are the room's own box -- in room 2, read in the running game:

RecordWhatCornerSizeSet by the patch
1the floor50, 50, 50255 by 10 by 255its top, +7
2the ceiling50, 180, 50255 by 10 by 255its top, +7
3a wall across +6178, 255, 5010 by 255 by 255+6
4the other40, 255, 5010 by 255 by 255+6
5a wall across +850, 255, 40255 by 255 by 10+8
6the other50, 255, 174255 by 255 by 10+8

So an object stands on the floor when its foot, +7 less +10, is at 50: the floor's box is ten high with its top there, and the step down that gravity asks for meets it.

Gravity

Every object's update ends in the same code (ANIMATE_AND_MOVE). There, unless the direction has bit 1 (rising), the up bit is taken out -- ANDed with $EF from $FF8B -- and down is added -- ORed with $20, the top six bits of $23 at $FF85. Both are constants among the variables: a new game restores them from the master copy, and a search of the code for every instruction that could store into either found none. So everything that is not rising tries to fall two units every pass, and a floor, a table or another object is what stops it. That is also what makes a thing lying on the floor cost time every pass (the pass traced): its fall is tried, and stopped, again and again.

A step down sets bit 7 of the state, in the air. While it is set the object's own case is not run at all: it goes on the way +18 says, and a fall keeps only the down bit there, so everything falls straight down, the knight at the end of a jump too. The air ends when the fall is stopped.

What a blocked move does

When the tried box meets another record, BUMPED deals with doors (for the knight, a way to another room) and OBJECTS_MEET with what the two objects do to each other (the creatures page); what is left comes to BLOCKED_MOVE (MEET_DECOY). It works out how much of the move that one object stops, trying from where the mover was:

Then what is left of the move is tried again, and may meet something else. Three of the collisions do more:

Climbing room 2's stairs

The knight put at 120, 78, 160, at the foot of the stairs, with Y held (up +6), in the simulator when this page was built -- his +6 and his top, +7, after each pass:

Pass+6+7
112278
212478
312678
412878
513078
613278
713278in the air
813280in the air
913282in the air
1013284
1113482in the air
1213682
1313882
1413882in the air
1513884in the air
1613886in the air
1713888
1814086in the air
1914084in the air
2014284
2114484
2214484in the air
2314486in the air
2414488in the air
2514490
2614688in the air
2714888
2815088
2915088in the air
3015090in the air

Each time he walks into the next step he stops, goes up for three passes, falls back two onto the step and walks on: 12 units higher after 30 passes. The steps are boxes of kind 3, 10 high and each a little higher than the one before, laid over each other.

Pushing a stool

The knight put behind one of room 2's stools (record 14, +16 = 19: weight 3) and walked into it with Y. The rule gives it 18 less 19, plus 6: 5 passes of coasting a push. Measured, the knight's +6, the stool's, and its countdown (+15) while it coasts:

PassKnight's +6Stool's +6Its +15
1118130
2120130
3122130
4122132coasting4
5124132
6124134coasting4
7126136coasting3
8128138coasting2
9130138
10130140coasting4
11132142coasting3
12134144coasting2

The stool moves ahead of him two units a pass while it coasts, and he catches it up and pushes again.

Jumping

SPACE or SYMBOL SHIFT (KNIGHT_CONTROLS) makes the knight's direction his walking one with up and rising added, +15 = 8 and his state 5. For eight passes CHE3D's state-5 case moves him up two a pass without gravity, the way he was walking; then he is state 8 again and falls. SPACE for one pass in room 29, when this page was built:

PassStateIn the air+7 (his top)
1580
2582
3584
4586
5588
6590
7592
8594
98yes92
108yes90
118yes88
128yes86
138yes84
148yes82
158yes80
168yes78
17878

Up 16 in 8 passes, and down again. A jump is how the knight goes up through a hole in a ceiling -- a door whose way is up -- and over low things.

Falling

The knight counts the passes he falls for ($FF94, at the commit in ANIMATE_AND_MOVE); when he lands, his update (KNIGHT_UPDATE) takes a LIFE point for every pass over 20 -- two units a pass, so a fall of more than 40. Dropped from above room 29's floor in the simulator when this page was built:

HeightPasses fallingLIFE by the ruleLIFE lost
301500
402000
502555
60301010

The first pass in a room

On the first pass after a room is entered (bit 2 of GAME_FLAGS) nothing moves: each object's direction is cleared, but all three axes are marked as moving, so the test runs on a box one unit up +6, two down and one down +8 from where it is, and whatever that meets is dealt with as usual; then the object is only drawn. What the offset box is for has not been worked out.

Beside Krumlinde's reading

Ville Krumlinde's disassembly reads the sizes as half-widths about a centre; the box test compares against the corner and the corner plus the size, and the pick-up's search box, measured in stage 2, reaches exactly as far as a corner-and-size box would. He finds no code that makes things fall, and leaves open what holds them up: gravity is the down bit forced into every direction that is not rising, applied by the ordinary step, and floors are the fixed records and the invisible boxes. His assembler source writes the minus-20 step through the records as a load of the variable at $FFEC; it is the number, which only looks like the address.

Confirmed and inferred