-
Posts
5,112 -
Joined
-
Last visited
Everything posted by IRF
-
I also noticed that there are apparently random changes to the layouts of the first few rooms! (Oh, and by the way there's a typo in the end-of-game message: "Stike a light, what happened?")
-
Cool! (And nasty for Willy!) How many of the room's eight guardian slots does that take up?
-
An arrow's y-coordinate can be determined on an individual arrow-instance basis, via its specification byte 1 (which is copied to definition byte 2 in the current room's guardian buffer). However, Bit 0 of that specification byte is effectively unused - it must be left clear, or else the arrow when drawn will corrupt the game file at run-time (because erroneous addresses are looked up from the table at page #82 of the code - that's what caused the infamous Attic Bug). In the arrow-drawing code, the following command picks up Byte 2 of the arrow definition as follows: LD E,(IX+$02) If that were to be replaced with: LD A,(IX+$02) AND #FE LD E,A then this would prevent any accidental occurrences of 'Attic Bug' style problems caused by an odd value being assigned for an arrow's y-coordinate. Furthermore, Bit 1 of the arrow's y-coordinate specification byte could then be used for other purposes. (There are plenty of other unused entries in an arrow's definition, but those are all set on an arrow class basis, so they would hold the same values for all left-moving or all right-moving arrows in the original game.) As an example, you could introduce a check, just before making the arrow warning sound when the arrow is about to enter the current room - if Bit 0 of the arrow's definition Byte 2 is set, then bypass the code which generates the firing noise. So in some rooms, Willy gets an audible warning of an impending arrow, but in other rooms he does not!
-
Incidentally, the aforementioned Overflow Flag also doubles up as the Parity Flag. I have wondered for some time about a possible viable use for the Parity Flag. And I've now discovered one, which relates to a possible efficiency for one of Dr Andrew Broad's patches from his 'Advanced MM/JSW Trainer' project. From the accompanying TECHNICA.txt document for that project: The three commands in bold above (six bytes) can be replaced with a three byte jump JP PE,skip which responds to the conditionality of the Parity Flag (thus saving three bytes). N.B. In this circumstance (after the AND #21 gate), the Parity Flag is set if a cell's row and column numbers are both even, or if they are both odd. If the row number is even but the column number is odd, or vice versa, then the Parity Flag is cleared.
-
My third and final attempt at the 'Funeral March' is implemented in the attached. B) (Apologies to Norman Sword for hijacking this thread somewhat!) Game Over Test 3.z80
-
However, the following method eliminates the need to Complement the Carry Flag. Starting at #9133 (changes from previous code highlighted in bold): PUSH IX ; copy the address of Byte 0 of the guardian definition POP HL ; to the HL register-pair LD DE,#E0FF ; parameters for a LD B A,(IX+$06) ; guardian moving left BIT 7,(HL) JR NZ, left LD DE,#2001 ; parameters for a LD B A,(IX+$07) ; guardian moving right left: LD B,A LD A,(HL) ADD A,D ; increment animation-frame of guardian BIT 7,(HL) ; sets the Zero Flag for a left-moving guardian LD (HL),A ; update guardian definition Byte 0 with correct animation-frame JR Z,carry_okay CCF ; Complement the Carry Flag for a right-moving guardian carry_okay: JP PO,#91B6 ; not time to update the guardian's x-coordinate; proceed to Move the next guardian If we get here then the Overflow Flag has been set (for both directions of travel) XOR #80 LD (HL),A ; time to update the guardian's x-coordinate, so reset the animation-frame to the first one for the next cell-column LD A,(IX+$02) ; guardian definition byte 2 LD C,A ; temporarily save in the C register AND #1F ; extract guardian's current x-coordinate CP B ; has the guardian reached the extremity of its range in the current direction of travel? JR Z,turn_around ; if so, jump to reverse its direction of travel LD A,C ; retrieve guardian definition byte 2 ADD A,E ; adjust x-coordinate LD (IX+$02),A ; and save JR #91B6 ; proceed to Move the next guardian turn_around: LD A,(HL) ; we're not updating the x-coordinate after all, XOR #E0 ; instead we are switching to consider the other set of four animation-frames LD (HL),A ; [assuming it's an eight-frame bidirectional sprite; if not, then filtering out of unwanted frames is done at the 'Draw the guardians' stage] JR #91B6 ; proceed to Move the next guardian [#9166 to #917E is now free - that's four more bytes than with previous method] This effectively reverses the status of Bit 7 of the guardian definition Byte 0, so that Bit 7 cleared = Moving Right, Bit 7 set = Moving Left. ;right adding #20 #00, #20, #40, #60 The Overflow Flag is clear with each addition but not #60+#20 = #80 (which is minus #80 in two's complement) ;left adding #E0 (minus #20) #E0 (minus #20), #C0 (minus #40), #A0 (minus #60), #80 (minus #80) Overflow clear with each addition but not #80+#E0 = #60 N.B. You would now need to adjust all the horizontal guardians' data as follows: - Transpose all the half-pages of the eight-frame bidirectional horizontal sprite (e.g. put right-facing Monk at #B400-#B47F, and left-facing Monk at #B480-#B4FF), so that their sprite-frames are arranged within the data in the same way as Willy's - if you don't do this, then they'll all walk backwards!; - Toggle Bit 7 of Byte 0 for all the horizontal guardian classes (i.e. all the relevant entries in pages #A0-#A3), in order to preserve the initial direction of travel upon Willy's entry to each room (e.g. for the green rolly thing in The Bathroom, change the value of #A1D8 from #01 to #81). All of the above is probably not worth the bother just to save another four bytes in the 'Move the guardians' routine. (Although it will save a few more bytes in the 'Draw Willy' routine as well, because there's no longer a need to swap half-pages when using the Flying Pig as the playing sprite in The Nightmare Room.) It was more of an interesting academic exercise to me, and it allowed me to gain a better grasp of the functioning of the Overflow Flag. :)
-
It could appear to be an unnecessary duplication by toggling Bit 7 twice (which would leave it back where it started), but if you consider the possible paths through the code, this doesn't always occur. The XOR #80 is used when the animation frame has been incremented/decremented beyond the range of appropriate values for the current direction of travel (i.e. to bring it back into the correct half of the sprite-page). The XOR #E0 is used at the point when the horizontal guardian changes direction. It's effectively doing two things simultaneously: a XOR #80 (to swap to the other half of the sprite-page), and a XOR #60 (to toggle between left-most and right-most sprite-frames, without which the turnaround doesn't occur as smoothly). You've hit the nail on the head. In the original code, the conditionality of the Carry Flag happens in the same way for both direction, but that's because left and right movements are handled via a SUB #20 and an ADD #20 respectively. But here we're using the same operation (an ADD), but changing the operands for left (#E0 = - #20 in two's complement) and right (#20 = +#20 in two's complement). Which means that the effect on Carry is polar opposite for left and right motion, so we have to use a CCF.
-
Yes, since I wrote my post last night I was playing around in one of your earlier test files, and I noted that the 'sinking into the wall' didn't occur as Willy walked along the top of the 'parapet' wall block towards the bottom of the ramp in 'On top of the house', so I assumed you must have also altered the 'Draw Willy' code in some way (not necessarily in the same way that I suggested above).
-
I have compiled various bits of code to try and achieve the same thing (in relation to the movement along ramps in the vicinity of wall tiles), but I haven't yet had a chance to sit down and put it all together into a working test file. It will be interesting to see if it achieves the same thing as your code (assuming mine works!) EDIT: Having now studied Norman's code, it seems to be addressing the same issues. However, I also had a notion to amend the routine at #95C8. Otherwise when Willy walks down a ramp and steps onto an adjacent wall/Earth block ( a la 'On top of the house'), and he's in a position where his front foot is on the wall but his back foot is still on the ramp, then his sprite will be drawn progressively 'sunken down' into the wall as his animation-frame progresses within the same pair of cell-columns - until he transcends the next cell-column boundary and moves beyond the influence of the ramp tile (at which point his sprite will appear to suddenly 'spring back up' onto the top of the wall tile, because the downwards vertical displacement normally caused by the ramp has been cancelled out in this case). Therefore, I would propose to edit the start of the routine at #95C8 as follows (new elements in bold, relocated elements in italics): LD HL, (#85D3) ; Willy's attribute coordinates LD B, #00 ; default vertical offset (in case Willy is not on a ramp) LD A, (#85D1) ; might as well place this earlier - if Willy is jumping then no need to test for underfoot ramp cell OR A JR NZ, vert_offset_dealt_with LD A, (#80DA) ; Ramp direction byte AND #01 LD C, A ADD A, #40 LD E, A LD D, B ; incidental one-byte efficiency here, compared with original code ADD HL, DE LD A, (#80C4) ; ramp attribute byte CP (HL). ; Is Willy's 'up-slope' foot on a ramp cell? JR NZ, vert_offset_dealt_with. ; If not, keep B=0 OR A SBC HL, DE. ; restore Willy's attribute coordnates in HL LD A,C ; A now holds either 0 or 1 XOR #01 ADD A, #40 XOR #41 ; I think this will do the same as the two operations struck-through above, but in one two-byte operation? ; (given the limited possible input values of A) LD E,A ; D already holds 00 ADD HL,DE LD A, (#80B2) ; Earth/wall attribute byte CP (HL). ;Is Willy's 'down-slope' foot on an Earth/wall block? JR Z, vert_offset_dealt_with. ; If so, keep B=0 That then leads into the code originally at #95E4, which calculates the vertical offset when Willy is standing on a ramp. #95F8 is the jump destination labelled as vert_offset_dealt_with in the above.
-
Thanks for the compliment BTW!
-
I ran out of time last night. I intend to provide more commentary when I have the time (particularly in relation to the need to complement the Carry Flag), but I think you've grasped the gist of it. I've also come up with a further optimisation which negates the need to take the Carry Flag into account in that way (and saves another four bytes), by replacing the JR C with a JP PO (responding to the Overflow Flag, with the conditionality the same for both left- and right-moving guardians). However, it's a bit of a fiddle as it involves swapping the half-pages of each bidirectional sprite (otherwise they walk backwards!), amongst other changes. It is possible to do it whilst preserving all the original behaviour though.
-
I am pleased to report that I've managed to achieve the above (my suggestion in bold). The following code will handle horizontal guardians moving both leftwards and rightwards, and it saves 21 bytes compared with Matthew's original code: Starting at #9133: PUSH IX ; copy the address of Byte 0 of the guardian definition POP HL ; to the HL register-pair LD DE,#E0FF ; parameters for a LD B A,(IX+$06) ; guardian moving left BIT 7,(HL) JR Z, left LD DE,#2001 ; parameters for a LD B A,(IX+$07) ; guardian moving right left: LD B,A LD A,(HL) ADD A,D ; increment animation-frame of guardian BIT 7,(HL) ; sets the Zero Flag for a left-moving guardian LD (HL),A ; update guardian definition Byte 0 with correct animation-frame JR Z,carry_okay CCF ; Complement the Carry Flag for a right-moving guardian carry_okay: JR C,#91B6 ; not time to update the guardian's x-coordinate; proceed to Move the next guardian XOR #80 LD (HL),A ; time to update the guardian's x-coordinate, so reset the animation-frame to the first one for the next cell-column LD A,(IX+$02) ; guardian definition byte 2 LD C,A ; temporarily save in the C register AND #1F ; extract guardian's current x-coordinate CP B ; has the guardian reached the extremity of its range in the current direction of travel? JR Z,turn_around ; if so, jump to reverse its direction of travel LD A,C ; retrieve guardian definition byte 2 ADD A,E ; adjust x-coordinate LD (IX+$02),A ; and save JR #91B6 ; proceed to Move the next guardian turn_around: LD A,(HL) ; we're not updating the x-coordinate after all, XOR #E0 ; instead we are switching to consider the other set of four animation-frames LD (HL),A ; [assuming it's an eight-frame bidirectional sprite; if not, then filtering out of unwanted frames is done at the 'Draw the guardians' stage] JR #91B6 ; proceed to Move the next guardian [#916A to #917E is now free; this may be sufficient to implement Norman Sword's 'mid-sprite bounce' - that's my next area for investigation...]
-
I don't think it would make a difference. It's only one Air block in each row - you need two consecutive Air for Willy to fall through.
-
The following idea only saves a single byte, but I consider it to be a most satisfactory arrangement: - Replace the 'INC HL' at #96C6 with a 'DEC HL'; - Take all the notes of the Title Screen tune, reverse their order, and insert them into the range of spare addresses starting at #8141, such that the last note of the tune occupies the address #8141; - Edit the operand of the LD HL, $0000 command at #88A8, to point at the new position of the first byte of the Title Screen tune (the first note now occupying the highest used address in page #81 of the code). The Guardian Buffer Terminator Byte (value #FF) at #8140 now serves a dual purpose, as it also serves to terminate the Title Screen tune! N.B. Under this arrangement, the Title Screen tune now has a maximum length of 191 bytes (not including the Terminator Byte), because page #82 of the code is used as the Screen Buffer Address look-up table).
-
POKE #F4DB,#E1 - this moves the ramp in 'Back Stairway' one cell to the left. As Willy walks down it, you might think he is about to move diagonally downwards to the room below and to the left (i.e. 'Tool Shed'). But in fact, he moves back up again as he crosses the horizontal edge of the room, ending up on the conveyor in 'Back Door'.
-
Yes, the ability to land on the head-height wall - when he should jump right over it - and the way that Willy can fall through solid blocks - if he jumps from a certain position - are 'two sides of the same coin', as I see it.
-
I have created four screenshots (attached) to illustrate one of the differences between the jump mechanics in the JSW1 and JSW2 game engines. Willy is making a jump over a head-height pillar of Earth blocks in 'Tree Top'. He starts his jump from the position shown in the screenshot named 'Tree Top Jump 1'. In 'Tree Top Jump 2', Willy is on the descent and is exactly one vertical increment above the top of the upper solid block. With the original JSW1 game engine, the screenshot 'Tree Top Jump 3a' shows what happens in the next time-frame - Willy lands on the top of the tree trunk, one frame of animation short of going over the edge. With the JSW2 game engine (or in this case, the JSW1 engine hacked to behave like JSW2), the screenshot entitled 'Tree Top Jump 3b' shows the 'alternative reality' behaviour - in the next time-frame after the second screenshot was captured, Willy has been moved along by an increment vertically downwards but also one increment horizontally [in this case leftwards]. Therefore he overshoots the top of the tree trunk, and in subsequent time-frames (not shown in the attached), he proceeds with the descent part of his parabolic jump trajectory, dropping down on the far side of the solid blocks. This explains why Willy gets 'stuck in the hole' in the JSW2 'Bathroom' test file that I uploaded earlier in this thread.
-
The above does technically save three bytes. However, they are left adjacent to the single unused byte within the main 'Draw the Guardians' routine, which isn't a very useful place. But I think I've come up with a way to shorten the 'out of situ' part of the patch, which would yield a couple of spare bytes in a more usable location. (If it works!)
-
Just to expand on my point about 'consistent logic', when Willy is moving into a vertically adjacent room, the AND B filters the bits which represent Willy's unchanged horizontal position, then the OR C sets the appropriate bits for his new vertical position. So for 'Exit Up', BC=$1FA0. For 'Exit Down', BC=$1F00.
-
Given that the AND now comes before the OR, it's possibly more elegant, logical and consistent to use LD BC, $E01E when moving into the room to the left. i.e. For both 'Exit Left' and 'Exit Right' (for which BC=$E000), pass the bits which represent Willy's (unchanged) vertical position through the AND B gate, and then set the bits which determine his (new) horizontal position via the OR C gate. Although it makes no difference to the final outcome. And of course, it's another excellent bit of optimisation that you've come up with!
-
Thanks Norman!
-
I've played around with this now - in JSW2, in addition to Willy not being able to fall as far* after hitting his head on an overhead Earth block during a jump, he also progresses forwards by one animation-frame during the first two time-frames of descent, but then he stops moving horizontally for the rest of his descent. Whereas in JSW1, his horizontal momentum is completely curtailed when he bangs his head on an overhead Earth block. (*He can only drop to a level that is three cell-rows lower than the apex of his jump without being killed, which is less than the four rows through which he can safely drop if he has head clearance. In JSW1, hitting his head on Earth actually allows him to fall a greater distance than he could if he made the same jump with head clearance.)
-
Move Willy into the room to the left: #9493 OR #1F #9495 AND #FE can be replaced (I think?) with: OR #1E Move Willy into the room above: #94BD LD (#85D3),A #94C0 LD A, #5D #94C2 LD (#85D4),A replace with: LD L,A LD H, #5D LD (#85D3),HL Move Willy into the room below: #94ED LD (#85D3),A #94F0 LD A, #5C #94F2 LD (#85D4),A replace with: LD L,A LD H, #5C LD (#85D3),HL **** Some other savings can be made across the four 'move to another room' routines, by sharing a common ending (POP HL/JP #8912) via relative jumps. Also, rearrange the various components of 'Move up' and 'Move down' so that the LD (#85CF), A and LD (#85D3), HL commands are shared by those two routines. I think all of the above should cumulatively save about 18 bytes?
-
This is the only thing that I could find, courtesy of John Elliott: https://www.seasip.info/Jsw/jsw2room.html
-
Sorry Andy, was the above comment in relation to the conveyor behaviour (at the left-hand of the conveyor) in JSW2? I appreciate I've been discussing several different phenomena at once, so it's difficult to keep track! (P.S. The reference to "that platform" didn't help with my confusion - I presume you're referring to Amstrad as the platform as in hardware, rather than a platform as in standonable room cell!? :-p)