Prev: D7E4 Up: Map Next: DC3F
D8E4: Test Group Objects
Used by the routine at TestEntityCells.
Finds the group of objects at a cell of the level with FindGroup and then tests the entity against each of the objects, which are three bytes each. The low five bits of the group header are the number of objects. Objects of type 84 86 and 06 are skipped. A solid block stops the entity. Type 81 is a bottle and is collected with CollectBottle. Type 85 sets bit 1 of ExitMessageFlag, the exit-closed message, while BottlesLeft is not zero. A type 83 and a type 85 once no bottles are left, is solid: the entity is pushed back to the edge and FindPathAtPosition looks for a path there. Other objects are matched with TestObjectMatch. The smaller gap is kept in field 1F and bit 6 of the flags is cleared. Purpose of that gap is not yet known.
Input
HL The position of the cell
IX The entity
TestGroupObjects D8E4 LD ($5CB9),HL Remember the cell position.
D8E7 CALL FindGroup Find the group of objects.
D8EA RET C Return if this cell has no group.
D8EB LD ($5CDC),DE Remember the cell offset.
D8EF INC D Advance the high byte of the cell offset.
D8F0 LD A,(IY+$00) Read the group header.
D8F3 AND %00011111 Keep the object count.
D8F5 LD B,A Keep the object count aside.
D8F6 INC IY Step on to the first object.
D8F8 INC IY
D8FA AND %00010000 Test bit 4, which the count mask has already cleared.
D8FC JR Z,TestGroupObjects_Loop Skip the extra step. This jump is always taken.
D8FE INC IY Step on another byte. This is not reached.
TestGroupObjects_Loop D900 INC IY Advance to the type byte of the object.
D902 PUSH BC Remember the object count.
D903 LD A,(IY+$01) Read the type byte.
D906 AND %10001111 Keep the type bits.
D908 CP $84 Skip an object of type 84.
D90A JP Z,TestGroupObjects_Next
D90D CP $86 Skip an object of type 86.
D90F JP Z,TestGroupObjects_Next
D912 CP $06 Skip an object of type 06.
D914 JP Z,TestGroupObjects_Next
D917 RLA Move bit 7 of the type into the carry flag.
D918 JR NC,TestGroupObjects_Player Test the object when that bit is clear.
D91A AND %00011110 Keep the shifted type bits.
D91C CP $1E Skip an object whose shifted type is 1E.
D91E JP Z,TestGroupObjects_Next
TestGroupObjects_Player D921 LD A,(IX+$00) On any other frame, or for any entity but the player, test the block below.
D924 DEC A
D925 JP NZ,TestGroupObjects_Block
D928 LD A,($A2DD) Read the frame counter.
D92B AND %00000111 Act on every eighth frame.
D92D JP NZ,TestGroupObjects_Block Leave this path on the other frames.
D930 LD A,(IY+$01) Read the type byte again.
D933 ADD A,$03 Add three to the type.
D935 AND %10000100 Use bits 2 and 7 of the adjusted type.
D937 CP $84 Match the object when the result is 84.
D939 CALL Z,TestObjectMatch
Build the object's x range from its first and third bytes, then test it against the x coordinate. A type of 82 uses a fixed size of 08.
TestGroupObjects_Block D93C LD HL,($5CB8) Fetch the word at TitleAnimationFrame.
D93F LD A,(IY+$00) Read the first byte of the object.
D942 AND %11110000 Keep the high nibble as the low byte of the range.
D944 LD L,A Keep that nibble as the low byte of the range.
D945 LD A,(IY+$02) Read the third byte of the object.
D948 ADD A,A Shift that byte up by four bits.
D949 ADD A,A
D94A ADD A,A
D94B ADD A,A
D94C AND %01110000 Use bits 4-6 as the size.
D94E ADD A,$10 Add the base size of 10.
D950 LD C,A Keep the size.
D951 LD D,(IX+$02) Fetch the x coordinate.
D954 LD E,(IX+$03)
D957 LD A,(IY+$01) Read the type byte.
D95A INC A Advance the type.
D95B AND %10000110 Use bits 1, 2 and 7.
D95D CP $82 Keep this size unless the type is 82.
D95F JR NZ,TestGroupObjects_XRange
D961 SET 2,L Mark the fixed-size object.
D963 LD C,$08 Use a size of 08.
TestGroupObjects_XRange D965 LD ($D6E7),DE Pass the x coordinate to the overlap test.
D969 LD ($D6E4),HL Pass the object's x range to the overlap test.
D96C LD B,(IX+$0B) Fetch the x extent.
D96F CALL TestRangeOverlap Test the two x ranges.
D972 JP NC,TestGroupObjects_Next Skip the object when the x ranges do not overlap.
D975 LD ($DDCF),HL Remember the x overlap.
D978 INC IX Point the entity one byte on, so the y fields line up.
Build the object's y range in the same way, and test it against the y coordinate. IX still addresses the byte after the type, so a displacement reads the following field.
D97A LD HL,($5CB9) Fetch the cell position again.
D97D LD A,(IY+$00) Read the first byte of the object.
D980 ADD A,A Shift it up by four bits.
D981 ADD A,A
D982 ADD A,A
D983 ADD A,A
D984 LD L,A Keep it as the low byte of the range.
D985 LD A,(IY+$02) Read the third byte of the object.
D988 ADD A,A Double it.
D989 AND %01110000 Use bits 4-6 as the size.
D98B ADD A,$10 Add the base size of 10.
D98D LD C,A Keep the size.
D98E LD D,(IX+$04) Fetch the y coordinate.
D991 LD E,(IX+$05)
D994 LD B,(IX+$0B) Fetch the y extent.
D997 LD A,(IY+$01) Read the type byte.
D99A INC A Advance the type.
D99B AND %10000110 Use bits 1, 2 and 7.
D99D CP $82 Keep this size unless the type is 82.
D99F JR NZ,TestGroupObjects_YRange
D9A1 SET 2,L Mark the fixed-size object.
D9A3 LD C,$08 Use a size of 08.
TestGroupObjects_YRange D9A5 LD ($D6F8),DE Pass the y coordinate to the overlap test.
D9A9 LD ($D6F5),HL Pass the object's y range to the overlap test.
D9AC CALL TestRangeOverlap Test the two y ranges.
D9AF DEC IX Restore the entity address.
D9B1 JP NC,TestGroupObjects_Next Skip the object when the y ranges do not overlap.
D9B4 LD ($DDD1),HL Remember the y overlap.
Compare the z coordinate with the object, and remember the smaller gap in field 1F.
D9B7 LD H,(IX+$08) Fetch the z coordinate.
D9BA LD L,(IX+$09)
D9BD LD ($D6D6),HL Pass the z coordinate to the edge test.
D9C0 LD A,(IY+$01) Read the object's type.
D9C3 LD C,A Keep the type aside.
D9C4 AND %00001111 Use the low nibble of the type.
D9C6 LD L,A Keep it as the low byte of the index.
D9C7 LD A,C Bring bit 4 of the type down beside it.
D9C8 RRA
D9C9 RRA
D9CA RRA
D9CB AND %00010000 Use that bit.
D9CD OR L Combine it with the low nibble.
D9CE LD E,A
D9CF LD HL,$D7B3 Look this kind up in the object flags.
D9D2 CALL AddToAddress
D9D5 LD A,(HL) Read the flag byte.
D9D6 LD ($5CD8),A Remember the object's flags.
D9D9 LD HL,($5CDC) Fetch the cell offset.
D9DC INC H Advance its high byte.
D9DD LD A,C Take the type again.
D9DE AND %01110000 Use bits 4-6 as a row within the group.
D9E0 ADD A,L Add that row to the cell offset.
D9E1 LD L,A
D9E2 LD ($D6CF),HL Pass the address to the edge test.
TestGroupObjects_Heights D9E5 LD C,$00 Start with no extra height.
D9E7 LD A,E A kind of 02 has no extra height.
D9E8 CP $02
D9EA JR Z,TestGroupObjects_Overlap
D9EC LD A,(IY+$02) Read the third byte of the object.
D9EF RRA Shift it down by two.
D9F0 RRA
D9F1 AND %00110000 Use bits 4-5 of that byte.
D9F3 JR NZ,TestGroupObjects_AddHeight Use the looked-up height when those bits are set.
D9F5 BIT 7,(IY+$01) Jump to TestGroupObjects_AddHeight to use the looked-up height when the top bit is clear.
D9F9 JR Z,TestGroupObjects_AddHeight
D9FB PUSH HL Look up a height for this kind.
D9FC LD HL,$D7D4
D9FF LD A,E
DA00 AND %00001111 Use the low nibble of the kind.
DA02 CALL AddToAddress Look the height up.
DA05 LD A,(HL) Read the height.
DA06 POP HL Restore the address and use that height.
DA07 JR TestGroupObjects_Height
TestGroupObjects_AddHeight DA09 ADD A,$10 Add 10 to the height.
TestGroupObjects_Height DA0B LD C,A Keep the height.
The entity is only touching the block when half of either extent, less two, still covers the overlap. A deeper overlap on both axes means the entity is inside the block.
TestGroupObjects_Overlap DA0C LD A,$04 Mark the entity as only touching the block.
DA0E LD ($DD4C),A
DA11 LD A,($DDCF) Fetch the x overlap.
DA14 LD B,A Keep it.
DA15 LD A,(IX+$0B) Compare half the x extent, less two, with the overlap.
DA18 SRL A
DA1A SUB $02
DA1C CP B
DA1D JP NC,TestGroupObjects_ZRange
DA20 LD A,($DDD1) Compare half the y extent, less two, with the overlap.
DA23 LD B,A
DA24 LD A,(IX+$0C)
DA27 SRL A
DA29 SUB $02
DA2B CP B
DA2C JP NC,TestGroupObjects_ZRange
DA2F XOR A The entity is inside the block, so clear the touching flag.
DA30 LD ($DD4C),A
DA33 PUSH HL Remember the edge address.
DA34 LD A,($5CD8) Read the object's flags.
DA37 RLA Compare the z coordinate directly when the top flag is clear.
DA38 JR C,TestGroupObjects_Raised
DA3A LD B,$00 Add the overlap to the edge address.
DA3C ADD HL,BC
DA3D EX DE,HL
DA3E LD H,(IX+$08) Fetch the z coordinate.
DA41 LD L,(IX+$09)
DA44 XOR A Subtract the edge.
DA45 SBC HL,DE
DA47 JR C,TestGroupObjects_Vertical Leave this object if the entity is below the edge.
DA49 OR H Leave this object unless the result is within one page.
DA4A JR NZ,TestGroupObjects_Vertical
DA4C LD E,L The low byte is the gap.
DA4D JR TestGroupObjects_SaveGap
TestGroupObjects_Raised DA4F EX DE,HL Compare the z coordinate with the z extent instead.
DA50 LD H,(IX+$08)
DA53 LD L,(IX+$09)
DA56 PUSH BC
DA57 LD B,$00
DA59 LD A,(IX+$0D)
DA5C ADD A,$06
DA5E LD C,A
DA5F ADD HL,BC
DA60 SBC HL,DE
DA62 POP BC
DA63 JR C,TestGroupObjects_Vertical
DA65 CALL TestWideOverlap Test a wide block.
DA68 LD A,($5CD8) Read the object's flags.
DA6B AND %01100000 Use the layout in bits 5-6.
DA6D JR Z,TestGroupObjects_Gap No further height test when the layout is clear.
DA6F CP $40 A layout of 40 ends the height test.
DA71 JR NZ,TestGroupObjects_SaveGap
TestGroupObjects_Gap DA73 LD A,E Reduce the gap by four, and ignore a gap below four.
DA74 SUB $04
DA76 LD E,$00
DA78 JR C,TestGroupObjects_SaveGap
DA7A LD E,A Keep the reduced gap.
DA7B POP HL Keep the smaller of the gap and the room above the entity.
DA7C PUSH HL
DA7D PUSH DE
DA7E LD B,$00
DA80 ADD HL,BC
DA81 EX DE,HL
DA82 XOR A
DA83 LD H,(IX+$08)
DA86 LD L,(IX+$09)
DA89 SBC HL,DE
DA8B POP DE
DA8C JR C,TestGroupObjects_SaveGap
DA8E LD A,L
DA8F CP E
DA90 JR C,TestGroupObjects_SaveGap
DA92 LD E,L
TestGroupObjects_SaveGap DA93 LD A,(IX+$1F) Fetch the gap already stored for this entity.
DA96 CP E Keep the stored gap when it is the smaller one.
DA97 JR C,TestGroupObjects_Vertical
DA99 LD (IX+$1F),E Remember this smaller gap.
DA9C RES 6,(IX+$1E) Clear bit 6 of the flags.
Test the z range. The vertical speed is put back afterwards, and a stand-in value of 18 is used for the test.
TestGroupObjects_Vertical DAA0 POP HL Restore the edge address.
TestGroupObjects_ZRange DAA1 LD B,(IX+$12) Fetch the vertical speed.
DAA4 PUSH BC Remember it.
DAA5 LD B,(IX+$0D) Fetch the z extent.
DAA8 PUSH IX Remember the entity.
DAAA LD DE,$FFFA Move back to the byte the test reads.
DAAD ADD IX,DE
DAAF LD (IX+$18),$00 Plant 18 for the test.
DAB3 LD DE,($D6D6) Fetch the z coordinate passed to the test.
DAB7 CALL TestRangeOverlap Test the z range.
DABA POP IX Restore the entity.
DABC POP BC Bring the vertical speed back.
DABD LD (IX+$12),B Restore the vertical speed.
DAC0 JP NC,TestGroupObjects_Next Leave this object when the z ranges do not overlap.
DAC3 BIT 7,(IX+$1E) Test whether the entity is moving fast.
DAC7 JR Z,TestGroupObjects_Positive The overlap is ordinary when the entity is not fast.
DAC9 BIT 7,H Test the sign of the result.
DACB JR NZ,TestGroupObjects_ZOverlap A negative result keeps the low byte.
DACD LD L,$FF Force the low byte to FF.
DACF JR TestGroupObjects_ZOverlap Join the stored overlap.
TestGroupObjects_Positive DAD1 BIT 7,H Test the sign of the result.
DAD3 JR NZ,TestGroupObjects_ZOverlap A negative result is stored as it is.
DAD5 INC L Raise a positive overlap by two.
DAD6 INC L
TestGroupObjects_ZOverlap DAD7 DEC L Reduce the overlap by one.
DAD8 LD ($DDD3),HL Remember the z overlap.
DADB LD A,(IY+$01) Read the object's type.
DADE AND %10001111 Use the type bits that select a solid block.
DAE0 CP $83 A type 83 is solid.
DAE2 JP Z,TestGroupObjects_Solid
DAE5 CP $85 A type other than 85 is handled below.
DAE7 JR NZ,TestGroupObjects_Handle
DAE9 LD A,($DEE0) Read how many bottles are left.
DAEC AND A A type 85 is solid once no bottles are left.
DAED JP Z,TestGroupObjects_Solid
TestGroupObjects_Handle DAF0 LD A,($5CD8) Read the object's flags.
DAF3 RLA Test the edge when the top flag is set.
DAF4 CALL C,TestEdgeOverlap
DAF7 XOR A Clear the handled flag.
DAF8 LD ($D5A4),A
DAFB LD A,($5CD8) Read the flags again.
DAFE INC A Handle the overlap unless the flags were FF.
DAFF CALL NZ,HandleOverlap
DB02 LD A,(IX+$00) Only the player is placed against the block.
DB05 DEC A
DB06 JP NZ,TestGroupObjects_Special
DB09 LD A,($A2DD) Read the frame counter.
DB0C AND $07 Only on every eighth frame.
DB0E JP NZ,TestGroupObjects_Special
DB11 LD A,(IY+$01) Read the object's type.
DB14 AND %10001110 Use bits 1, 2, 3 and 7 of the type.
DB16 CP $02 Only a type whose remaining bits are 02 places the player.
DB18 JP NZ,TestGroupObjects_Special
DB1B LD A,($D5A4) Read the handled flag.
DB1E AND A Leave the placement when the flag is clear.
DB1F JP Z,TestGroupObjects_Special
DB22 LD A,(IX+$1F) Read the stored gap.
DB25 AND A Leave the placement when a gap is already stored.
DB26 JP NZ,TestGroupObjects_Special
DB29 LD DE,($D6E7) Fetch the x edge passed to the overlap test.
DB2D DEC DE Step that edge back by two.
DB2E DEC DE
DB2F LD HL,($D6E4) Fetch the other x edge.
DB32 AND A Keep the first edge when it is the nearer.
DB33 SBC HL,DE
DB35 JR C,TestGroupObjects_XEdge
DB37 LD DE,($D6E4) Otherwise use the other edge.
TestGroupObjects_XEdge DB3B LD A,E Keep the column of that edge.
DB3C AND $F0
DB3E LD E,A
DB3F LD ($E4C6),DE Remember it as the position to match.
DB43 LD DE,($D6F8) Fetch the y edge passed to the overlap test.
DB47 DEC DE Step that edge back by two.
DB48 DEC DE
DB49 LD HL,($D6F5) Fetch the other y edge.
DB4C AND A Keep the first edge when it is the nearer.
DB4D SBC HL,DE
DB4F JR C,TestGroupObjects_YEdge
DB51 LD DE,($D6F5) Otherwise use the other edge.
TestGroupObjects_YEdge DB55 LD A,E Keep the column of that edge.
DB56 AND $F0
DB58 LD E,A
DB59 LD ($E4C8),DE Remember it as the other position to match.
DB5D LD DE,($B548) Remember the player's height for the match.
DB61 LD ($E4CA),DE
DB65 JP TestGroupObjects_Special Go on to the bottle and the exit.
A solid block matches the object, raises the entity by one, and pushes it back to the edge. If it is still inside, the position is set from both edges and FindPathAtPosition looks for a path there.
TestGroupObjects_Solid DB68 CALL TestObjectMatch Match the object.
DB6B LD H,(IX+$08) Fetch the z coordinate.
DB6E LD L,(IX+$09)
DB71 INC HL Raise the entity by one.
DB72 LD (IX+$08),H
DB75 LD (IX+$09),L
DB78 LD A,($DD4C) Read the touching flag.
DB7B AND A Leave this object when the entity was only touching.
DB7C JR NZ,TestGroupObjects_Special
DB7E LD DE,($D6E4) Fetch an x edge.
DB82 INC E Step that edge on by three.
DB83 INC E
DB84 INC E
DB85 LD HL,($D6E7) Fetch the other x edge.
DB88 PUSH HL Turn that edge into a coordinate.
DB89 POP BC
DB8A AND A Subtract the stepped edge.
DB8B SBC HL,DE
DB8D BIT 7,H Jump to TestGroupObjects_PushX to push the entity the other way when the difference is negative.
DB8F JR NZ,TestGroupObjects_PushX
DB91 DEC BC Step the edge back.
DB92 LD (IX+$02),B Move the x coordinate back to that edge.
DB95 LD (IX+$03),C
DB98 LD BC,$0005 Step the difference back by five.
DB9B SBC HL,BC
DB9D JR TestGroupObjects_TryY
TestGroupObjects_PushX DB9F INC BC Step the edge on.
DBA0 LD (IX+$02),B Move the x coordinate on to that edge.
DBA3 LD (IX+$03),C
DBA6 LD BC,$0004 Step the difference on by four.
DBA9 ADD HL,BC
TestGroupObjects_TryY DBAA JR NC,TestGroupObjects_Special Leave this object when the x push was enough.
DBAC PUSH DE Remember the y edge.
DBAD LD DE,($D6F5) Fetch a y edge.
DBB1 INC E Step that edge on by three.
DBB2 INC E
DBB3 INC E
DBB4 LD HL,($D6F8) Fetch the other y edge.
DBB7 PUSH HL Turn that edge into a coordinate.
DBB8 POP BC
DBB9 AND A Subtract the stepped edge.
DBBA SBC HL,DE
DBBC BIT 7,H Jump to TestGroupObjects_PushY to push the entity the other way when the difference is negative.
DBBE JR NZ,TestGroupObjects_PushY
DBC0 DEC BC Step the edge back.
DBC1 LD (IX+$05),B Move the y coordinate back to that edge.
DBC4 LD (IX+$06),C
DBC7 LD BC,$0005 Step the difference back by five.
DBCA SBC HL,BC
DBCC JR TestGroupObjects_Place
TestGroupObjects_PushY DBCE INC BC Step the edge on.
DBCF LD (IX+$05),B Move the y coordinate on to that edge.
DBD2 LD (IX+$06),C
DBD5 LD BC,$0004 Step the difference on by four.
DBD8 ADD HL,BC
TestGroupObjects_Place DBD9 POP BC Restore the saved edge.
DBDA JR NC,TestGroupObjects_Special Leave this object when the y push was enough.
DBDC LD (IX+$02),B Place the entity on both saved edges.
DBDF LD (IX+$03),C
DBE2 LD (IX+$05),D
DBE5 LD (IX+$06),E
DBE8 PUSH IY Remember the object.
DBEA CALL FindPathAtPosition Find the path at this position.
DBED POP IY Restore the object.
A type 85 object shows the exit-closed message for the player while bottles remain. A type 81 object is a bottle.
TestGroupObjects_Special DBEF LD A,(IY+$01) Read the type byte.
DBF2 AND %10001111 Keep the type bits.
DBF4 CP $85 Try a bottle unless the type is 85.
DBF6 JR NZ,TestGroupObjects_Bottle
DBF8 LD A,($DEE0) Read how many bottles are left.
DBFB AND A Leave the message alone when no bottles are left.
DBFC JR Z,TestGroupObjects_Next
DBFE LD A,(IX+$00) Show the message for the player only.
DC01 DEC A
DC02 JP NZ,TestGroupObjects_Next
DC05 LD A,($CE59) Read the exit message flag.
DC08 OR %00000010 Set the exit-closed bit.
DC0A LD ($CE59),A Update the exit message flag.
DC0D JP TestGroupObjects_Next Go on to the next object.
TestGroupObjects_Bottle DC10 CP $81 Leave any object that is not a bottle.
DC12 JR NZ,TestGroupObjects_Next
DC14 LD A,(IX+$00) Collect a bottle for the player only.
DC17 DEC A
DC18 JR NZ,TestGroupObjects_Next
DC1A LD A,($DD4C) Leave the bottle if the flag at DD4C is clear.
DC1D AND A
DC1E JR NZ,TestGroupObjects_Next
DC20 LD A,($D5A4) Leave the bottle if the flag at D5A4 is set.
DC23 AND A
DC24 JR Z,TestGroupObjects_Next
DC26 PUSH IX Collect the bottle, keeping the entity and the object.
DC28 PUSH IY
DC2A PUSH IY
DC2C POP IX
DC2E CALL CollectBottle
DC31 POP IY
DC33 POP IX
TestGroupObjects_Next DC35 INC IY Advance to the next object.
DC37 INC IY
DC39 POP BC Restore the object count.
DC3A DEC B Take the next object until every object has been tested.
DC3B JP NZ,TestGroupObjects_Loop
DC3E RET Return.
Prev: D7E4 Up: Map Next: DC3F