A turn forgets that he was walking
Alien 8's robot turns through an in-between view (graphics 24-27) held for
two turns, where Knight Lore's and Pentagram's heroes turn at once. The turn
a turn starts takes no step (
MOVE_PLAYER takes none with an in-between graphic),
the next only lets him fall, and on the third
TURNING_LEGS puts in the new facing
and means to take a step if walk is held, by coming into
MOVE_PLAYER at
MOVE_IF_WALKING, which tests bit 2 of C, the controls. But first it makes the
turn's sound (
FOOTSTEP, at the entry that skips the frame test), whose loop
counts C down to nought and hands it back so. The step is never taken, and
gravity takes its full two units whatever is held.
HANDLE_FORWARD, which makes the
same sound as he walks, keeps BC round it.
Watched in the emulator, in room $2B, walking along U with A held and
turning left with Z for one turn: U went 131, 134, 137, then the in-between
view 26 for two turns and the new facing on the third, all at U 137, V 128,
and V only began to grow on the turn after that. With BC kept round the
sound (
the poke), the new facing came with
its first step, V 131. The same with X, turning right.
A life that starts on a valve destroys it
Knight Lore's depth sort has a case for two boxes that occupy the same space,
and there it destroys a collectable; Alien 8 keeps it for its valves
(
BOXES_INTERSECT): unless one of the two is out of the collision tests, a loose valve
(graphics 96-99) found sharing space with anything becomes graphic 64, the
sparkle things vanish in, and at 65 its place in
PLACES is emptied (
SPARKLE_END_PLACE).
The valve is gone for the rest of the game. The collision code keeps things
from moving into each other, but a new life does not move the robot: it
copies him from the start records (
NEW_LIFE), at the spot where he last came
through a doorway (read), and the appearing robot is in the collision tests.
So a valve put down where he came into a room is destroyed the next time he
dies in that room.
Watched in the emulator, the valve staged: a kind-0 valve in room $2B at
U 128, V 128, on the floor, and the robot's death started with his start
records in that room at the same spot. As he began to appear the depth sort
destroyed the valve (the IY branch, with IX on his legs): its record went
through graphics 65 and 1 to empty, and its place's graphic to 0. The same
valve 22 units away was untouched.
Every valve may count.
INIT_SPECIAL_OBJECTS deals the kinds round in turn and gives every
sixteenth place an extra life instead, and places sixteen apart are of the
same kind, so the kind that lands on the extra lives loses them: in a game
with three extra lives that kind has six valves, as many as its sockets, and
a valve lost this way leaves the twenty-fourth chamber out of reach
(inferred from the dealing; see the facts).
A stray OUT pages a 128K's memory
READ_KEYS does OUT ($FD),A before IN A,($FE), with the half-rows to
read in A. OUT (n),A puts A on the top half of the address bus, so the port
is A * 256 + $FD. A 48K ignores it. A 128K takes any port with bits 15 and 1
clear as its paging port, $7FFD: every half-row value with bit 7 clear is a
paging write. The menu tune's "any key" test (
PLAY_TUNE_ONCE) reads with A = 0, which
pages the 128's editor ROM in and does no harm, since the game never calls
the ROM (it only reads its bytes as noise). But the pause test (
HANDLE_PAUSE) reads
$7E at the end of every turn, whatever the controls, and the keyboard reader
reads SPACE's half-row as $7F: either puts RAM bank 6 or 7 at $C000, where the
code from $C000 up and the screen buffer are, and locks the paging.
Watched in the emulator's 128K, from the loaded game put into a 128K snapshot
with the 48 BASIC ROM and bank 0 paged in, as the 128's Tape Loader leaves
it: the menu worked, with ROM 0 paged in by the tune's key test; at the end of
the first turn of play READ_KEYS was called with $7E from the pause test, the
paging port became $7E -- bank 6 at the top, the second screen shown, locked
-- and the machine ran off into the ROM. So on a 128K the game must be run in
48 mode. Pentagram has the same OUT, and
the same fate. The poke
that removes the OUT is with the pokes.
Ten lives would show as none
LIVES counts in binary --
NEW_LIFE takes one with DEC,
EXTRA_LIFE adds one with
INC -- but
PRINT_LIVES prints it as two BCD digits, with the font itself as the
base so that digit n is the nth character (
FONT). Ten lives, $0A, print as
0 and the eleventh character, which is a second nought (see the facts): 00.
It cannot happen in play. A game starts with five, the first life takes one,
and
INIT_SPECIAL_OBJECTS deals two or three extra lives a game, never more (worked out for
every value of the random number it starts from), so a player who never dies
ends with at most seven.
Drawn by the game's own code in SkoolKit's simulator: a game started from the
menu, LIVES staged at 9 and then at 10 as a life
begins beside an extra life, which the robot touches as he appears. Watched in
the emulator too: nine lives and an extra life made ten, and the panel said
00.
A collapsing block empties the place at address 0
Graphic 65, the last frame of the sparkle a thing vanishes in, empties the
thing's place in
PLACES through the address in +$10 and +$11 of its record
(
SPARKLE_END_PLACE), which is right for an extra life. But a collapsing block
(
COLLAPSING_BLOCK) goes through the same frames when it is stood on, and its +$10 and
+$11 are nought: it writes a zero to address 0, which is ROM and ignores it.
Watched in the emulator, in room $12, the robot put on the stack of two
collapsing blocks: each became graphic 65, and a logpoint on the write showed
HL = $0000 both times; the ROM's first byte was unchanged. Harmless.
The socket projects the wrong record
A chamber's socket (
SOCKET) brings its sparkle back when nothing lies in the
room from the places and the record after the socket is empty -- as when the
sparkle vanished because a valve lay there and the valve has gone. It puts
the sparkle's graphic in the record and then jumps to
CALC_PIXEL_XY_IY, which projects
the record at IY onto the screen. IY is whatever an earlier update routine
left there, not the sparkle. It does no harm: the record projected is
recomputed from its own position, and every object drawn is projected again
by
CALC_PIXEL_XY_AND_RENDER anyway, so the new sparkle is drawn where it should be.
Watched in the emulator, in chamber $0C with a valve of the wrong kind in the
room and then taken away as a pick-up takes it: at the jump IY was $5B88, the
robot's legs, and the sparkle came back over the socket the next turn.
The hammer leaves the robot's head green
In the scene after a lost game the robot is re-programmed: a glove, a hook
and a hammer take turns to strike him. Each piece of the scene colours the
character cells it covers ($AC21), except any that are bright green,
$44 -- the hammer's colour, so that nothing paints over the hammer as it
swings. But once the hammer has swung back up, the cells it coloured on the
robot's head are still bright green, and the robot, which skips them too,
never gets them back. Watched in the emulator: twenty turns into the scene the
robot was cyan all over; three hundred turns in, after the hammer's blows, the
top of his head was green.
The lower halves' footsteps are silent
The two-part creatures (templates 17 to 19) put their lower half, graphic 11,
in a record of its own, and its update routine (
LOWER_HALF) asks for a footstep
each time the upper half moves.
LOWER_HALF_STEP plays one only for an even graphic --
Knight Lore's walkers changed graphic as they walked -- and 11 is odd, so it
returns at once, every time. Read from the code; the build's sessions never
ran the rest of
LOWER_HALF_STEP.
A room of more than 52 records would run away
BUILD_ROOM fills records from the fifth up, as many as the room's
backgrounds and templates need, and then clears the rest, stopping when its
pointer is exactly
BELOW_BLOCK, the end of the records (BUILD_CLEAR_REST, $CCD0).
Nothing checks that the room fitted: a room of more than 52 records
would build past the end of the records, and the clearing loop, testing for
equality only, would never meet $6288 again and would clear its way through
memory. None does. Counted from the room data the way the builder reads it,
the fullest room is $74, with 49 records, three
to spare, and nothing adds records to a room once it is built -- the socket's
sparkle only reuses its own. Watched in the emulator: room $74, entered,
had 49 records in use. Latent.
A sprite one row high would turn over 256 times
Sprites are turned in place (
VFLIP_SPRITE_DATA): upside down by swapping rows in pairs
from the ends inwards, half the height times. A sprite one row high makes that
nought, and the DJNZ loop would run 256 times, through whatever follows the
sprite. Only one sprite is one row high, graphic 5, the sides of the border
(
BORDER_DATA), and it is only ever mirrored, never turned upside down, so it never
happens. Read from the code and the graphic table; Knight Lore's routine is
the same.
A third valve in a room would overrun
FIND_SPECIAL_OBJS_HERE, as a room is built, puts every place in use in that room into
records 2 and 3, and clears what is left of them. Nothing stops it at two: a
third would go into record 4, the room's first, and the clearing after the
loop would never meet record 4 again and would run on through memory. The 36
places start in 36 different rooms, and a valve is put down only into an
empty one of the two records (
TAKE_OR_LEAVE), so a room never holds three. Knight
Lore's find_special_objs_here has the same shape. Read; latent.