-
Posts
5,112 -
Joined
-
Last visited
Everything posted by IRF
-
This 'mini-game' builds on earlier coding efforts by myself and Norman Sword (thanks for your unwitting contribution, Norman!) Attached is a zip folder containing two game files. The layout of the playable area of each file is identical (including the guardian settings and item placements). There are four playable rooms, all located in the upper region of Willy's mansion. The game contains a grand total of 16 items (#10 in hex!). Maria and the toilet are present - once you've collected all the items, make your way to the bottom of the ramp in The Attic to set off the end sequence. The 'Before' file is based on the original JSW game engine (with only a minor hack to allow the toilet dash to take place within the four rooms). i.e. the mechanics of Willy's movement are unchanged. In the 'After' file, I have implemented significant changes to Willy's movement, in order to prevent him from being able to pass through Earth/wall blocks in any circumstances. [The only exceptions to that rule are (a) if he enters a room at a position that happens to coincide with a wall, or (b) if he is swinging on a rope that passes through a wall - although I would add that neither of those scenarios pertains in the attached files]. All 16 items can be collected using different methods in the 'Before' and 'After' files, because of the differences in the mechanics of Willy's movement between the patched and unpatched variants. For some items, using two different manoeuvres between the two files is essential. For other items, one method might allow it to be collected in both files, with the alternative method only possible in one of the files. So your mission, "should you choose to accept it", is to use different solutions for each of the 16 items, across both of the game files. Oh, and without 'kamikazeing' any items (sacrificing a life in the collection thereof). Enjoy!! Comprehensive Willy Movement Rewrite.zip
-
Thinking about it, that latter change might mean that Willy is moved and drawn one more time before Game Mode 3 kicks in. Which wouldn't be too much of a problem is he is running towards the toilet, but might mean that he loses a life if he falls into the toilet as part of the end sequence? Area for investigation...
-
Some changes in logic in the Main Loop which could save a few bytes: - In between the existing commands at #89CC and #89CE, insert a JR Z, #89E9. Change #89CE and #89DE to unconditional CALLs. Then the commands at #89D9 and #89DC become unnecessary (net saving of three bytes). - Possibly the three byte command at #89E1 can be done away with too, if the commands at #89E4 and #89E6 are relocated prior to #89CE (the CALL to Move Willy). I would need to check that though, especially if another efficiency that I identified has been implemented: http://jswmm.co.uk/topic/185-free-space-and-code-optimisation-in-jsw/page-18?do=findComment&comment=8172 (The SET (HL) might have to be replaced with a RES (HL) to keep Willy running.) EDIT: I'm not sure about that bit so I've struck it out.
-
Just to clarify, the above isn't strictly true - the eight bits of C are rotated rightwards once by the operation. What I meant to say is that the RRC C command doesn't alter the balance of the number of set/reset bits in C, and so the balance of parity isn't changed by the operation; rather it tests what that balance is (i.e. whether an odd or an even number of bits are set), and then the conditionality of the subsequent jump is determined on that basis. Whereas the three preceding RR C commands did determine the balance of parity in C (because RR is a nine-bit rotation, bringing the Carry Flag into Bit 7 of the register), the RRC is an eight-bit 'internal' rotation of C (it also copies Bit 0 of C into the Carry Flag, but that is of no consequence here). N.B. It's easy to get mixed up between "RR C" and "RRC" - perhaps I should have swapped the roles of the B and C registers to reduce the prospect for confusion!?
-
A nice bit of detective work, Danny! One query: have you uploaded the same attachment twice, instead of two different files?
-
More analysis on the use of the Parity Flag in the above, for handling wraparound vertical guardians: C starts off holding 00000000 (Parity Even) Normal vertical guardian - three scenarios (other than a guardian located exactly on one of its bounds, which is dealt with by a separate check): TOP OF SCREEN GUARDIAN POSITION UPPER BOUND IN D LOWER BOUND IN E BOTTOM OF SCREEN C=01100000; Parity Even, guardian is out of bounds TOP OF SCREEN UPPER BOUND IN D GUARDIAN POSITION LOWER BOUND IN E BOTTOM OF SCREEN C=00100000; Parity Odd, guardian is within its permitted bounds TOP OF SCREEN UPPER BOUND IN D LOWER BOUND IN E GUARDIAN POSITION BOTTOM OF SCREEN C=00000000; Parity Even, guardian is out of bounds Wraparound vertical guardian - three scenarios (other than a guardian located exactly on one of its bounds, which is dealt with by a separate check): TOP OF SCREEN GUARDIAN POSITION LOWER BOUND IN E UPPER BOUND IN D BOTTOM OF SCREEN C=11100000; Parity Odd, guardian is within its permitted bounds TOP OF SCREEN LOWER BOUND IN E GUARDIAN POSITION UPPER BOUND IN D BOTTOM OF SCREEN C=10100000; Parity Even, guardian is out of bounds TOP OF SCREEN LOWER BOUND IN E UPPER BOUND IN D GUARDIAN POSITION BOTTOM OF SCREEN C=10000000; Parity Odd, guardian is within its permitted bounds
-
Several things needed to be done to achieve these wraparound guardians. - In the 'Draw a sprite' routine (#9456), I added a command after the code at #9482-85 [original addresses] which moves HL down from one vertical screen-third to the next. The command RES 4, H ensures that the guardian doesn't extend from the secondary pixel-buffer into the primary pixel-buffer, but wraps back round to the top of the former. i.e. a value #70xx is adjusted to #60xx. - Also in 'Draw a sprite', after the INC L command at #9462 [original address], I made a check to see if the right-hand half of the sprite is wrapping round to the left-hand side of the screen: LD A,L AND #1F If that sets the Zero Flag, then subtract #20 and load back to L, draw the graphic byte under consideration and then add #20 again before moving down to draw the next pair of graphic bytes on the next pixel-row. - The 'Draw a guardian' routine also needs to be adjusted, in a similar manner to the above, to keep the guardian's colour attributes within the same character row when it is wrapping round past the right-hand edge of the screen, and also to prevent its colour attribute being written permanently onto the primary attribute buffer when it is wrapping past the bottom of the screen. In the latter case, a RES 1, H command is used to adjust a value for the attribute coordinates of #5Exx to #5Cxx, which is back within the valid range. - In the 'Move the guardians' routine, a simple adjustment has been made for horizontal guardians at #9151 and #9174 [original addresses], in order to prevent the guardians from changing sprite (which previously occurred because the DEC (IX+$02) / INC (IX+$02) commands meant that the adjustment of the x-coordinate in Bits 0-4 was spilling over into the Base Sprite setting in Bits 5-7). Preventing this was achieved by loading the guardian byte definition byte 02 into both the Accumulator and the C register, then adjusting A accordingly for the appropriate direction of movement (e.g. applying a DEC A or an INC A, although in the test file uploaded above I have implemented the shared left/right movement code for guardians), and then doing the following sequence: XOR C AND #1F XOR C before reloading the result in A back to IX+$02. That adjusts the x-coordinate from #00 to #1F or vice versa, whilst leaving the Base Sprite setting unaltered. - The changes to the vertical guardians part of the 'Move the guardians' routine are the 'piece de resistance'! After the code which updates the animation frame for vertical guardians (or not, depending on the animation speed), I have devised the following [starts at #9193 in the original code], which was built upon an earlier draft of this part of the routine provided by Norman Sword: (Note that HL is pointing at guardian definition byte 00 going into this, after a pair of commands PUSH IX / POP HL.) INC HL INC HL INC HL ;HL points at guardian definition byte 03 (pixel y-coordinate) LD A, (HL) INC HL ;HL now points at guardian definition byte 04 (vertical speed) ADD A, (HL) LD B, A ;A and B now both hold the updated y-coordinate LD C, #00 ;C will be used to keep track of various flags LD D, (IX+$06) ;upper bound of guardian in D LD E, (IX+$07) ;lower bound of guardian in E CP D ;has the guardian moved past its upper bound? RR C ;rotate a raised flag into C if so CP E ;has the guardian moved past its lower bound? RR C ;rotate a lowered flag into C if so [or if it is exactly at its upper bound, but that situation is dealt with separately below] LD A, E ;load the guardian's lower bound into A CP D ;if this is a normal vertical guardian, then E>D; if it is a wraparound, then D>E RR C ;rotate a flag into C (raised only for a wraparound vertical guardian) BIT 7, (HL) ;is the guardian currently moving downwards? JR Z, down ;if so, skip the next command LD A, D ;otherwise, load the guardian's upper bound into A (the guardian is currently moving upwards) down: CP B ;has the guardian exactly reached the extent of its permitted range for the current direction of travel? JR Z, change_direction ;if so, then jump to toggle the direction of travel RRC C ;this command doesn't change the value in C, it merely test the status of the Parity Flag JP PO, y_update ;If Parity is odd, then jump to update y-coordinate with the value stored in B (the guardian is within its permitted range) LD B, A ;otherwise, if Parity is even, then the guardian has exceeded its permitted range, so load up B with the extent of its permitted range for the current direction of travel change_direction: LD A, (HL) ;load vertical speed into A NEG ;toggle it to reverse the guardian's direction of travel LD (HL), A ;and load back into guardian byte 04 y_update: DEC HL ;HL points at guardian definition byte 03 again (pixel y-coordinate) LD (HL), B ;update the guardian's y-coordinate with the value stored in B That is then directly followed by (or a jump is made to) the existing command LD DE, #0008 at #91B6. I'll add some commentary about the use of the Parity Flag when I get more time.
-
I noticed that there are several parts of the JSW code which effectively replicate the same function - namely using the table in page #82 of the code to point HL at the appropriate address for drawing various moving entities onto the pixel display and/or pixel buffer. So I have rewritten several parts of the code to allow a shared routine to be CALLed (with various entry points) which handles the table at page #82, and saves quite a few bytes (I would estimate about 30-40, all told) in the process: Entry point from
-
Also, HL has to start off pointing at the top pixel-row of the cell in the case of a crumbly, or start off pointing at the bottom pixel-row for bashing a wall tile. Coming from 'Move Willy', HL starts off pointing at an attribute coordinate (#5Cxx or #5Dxx). Use PUSH and POP HL to preserve that address, for updating the attribute of the cell to Air once crumbling is complete. For a wall tile, use a similar approach as the 'old' crumbly tile code, in order to point HL at the bottom pixel-row: LD A,H ADD A, #1B OR #07 LD H,A For a crumbly tile, point HL at the top pixel-row by using: LD A,H ADD A, #1B AND #78 LD H,A The command in bold can be a common entry point to a shared part of the code (if the C register is used to increment or decrement H as per my previous post).
-
How about this slight rewrite of the loop: xor a ld d,a ld b,8 loop ld e,(hl) ;the current line or d ;the line above ld (hl),d ld d,e ld a,h add a,c ld h,a djnz loop Set C=01 beforehand for regular crumbling tiles, or C=FF for bashing away at wall tiles from below(?)
-
I've just discovered that this bug can actually be fixed using a single* POKE! POKE #92B5, #2C It replaces the INC IX at #92B4 with an INC IXL. So the 'Draw Willy' routine doesn't start looking up addresses, via the IX index, that are beyond the end of the table in page #83 of the code. Instead, when Willy is falling off the bottom of a Manic Miner cavern, the rest of his sprite is drawn at the top of the cavern (although in the first time-frame, it may not be visible if the Air INK and PAPER settings match). Because whilst the low byte of IX may be incremented from #FF to #00, the high byte of IX always retains the value #83 if only IXL is incremented. (Whereas in the original code, the incrementation of the IX register-pair means that it can proceed from #83FF to #8400.) N.B. This does not address the issue where Willy falling past the bottom of a cavern causes errant (de facto Water) cells to be created in the top two cell-rows. To resolve that, a separate fix would need to be applied to the code which writes Willy's attributes to the screen. (* For elegance, you could also replace the preceding INC IX at #92B2 with an INC IXL, but that isn't necessary to fix the bug because the IX index always holds an even value going into those two consecutive INC IX commands, and so the first of the two will never increment IX from #83FF to #8400.)
-
Horizontal and Vertical Guardians can now wrap around both the vertical and horizontal edges of the screen - see the attached. Note that the horizontal guardians do not change Base Sprite as they wrap around (which they do in the original JSW game engine). Getting both the graphic bytes and attributes to fully behave was quite a challenge. But I got there in the end. :) P.S. In the test file (startup room = Back Door), don't be surprised if the two guardians eventually collide with each other! Horizontal and Vertical Wraparound Guardians.z80
-
I would probably go with the apostrophe in that case, because you're referring to a person rather than an 'entity', so it seems more personal. e.g. I would refer to "Geoff Eddy's rewrite of the game engine" rather than "the Geoff Eddy rewrite of the game engine". On the other hand, I would say "There are a lot of novel features in the Geoff Mode game engine".
-
I would either go with the apostrophe, or else say "the R. D. Foord Software version".
-
I have managed to amend the code which moves vertical guardians, and also the guardian-drawing and sprite-drawing routines, in such a way that a guardian may safely wrap around past the top/bottom of the screen without crashing into its own 'trail of havoc' (i.e. errant pixels left on the primary screen buffer. Please see the attached. The starting room is 'On Top of the House'. There are now two flag guardians in that room, which have identical definition bytes to each other except that, for one of the flags, the upper and lower bounds have been transposed (causing it to wander up off the top of the screen and back again). I thought it was appropriate to use the flag guardians to illustrate this behaviour, as I managed to achieve this technical novelty by careful use of the F Register flags (the Zero, Carry and Parity Flags were all instrumental). I'll type up the source code when I find the time. But note that the same routine handles both guardians. Vertical Wraparound Guardians.z80
-
The two commands at #888F and #8890 can be replaced with a BIT 1,D, and the jump at #8892 changed to a JR Z (different conditionality and a relative jump) - saves two bytes. **** In the 'Print a Sprite' routine (#9456), there is replication of certain commands (#9458-##9461 and #9463 are repeated at #9464-#946D and #9470). Placing those commands in a subroutine, and CALLing them twice (with an INC L in between, and the move to the next raster line following afterwards) would save a few bytes. **** If the toilet attributes are printed first, then the CALL to draw the toilet's pixels can be replaced with a JUMP, saving one byte as the RET at the end of the draw routine serves to return the program to the Main Loop.
-
At #9308, if the destination of the CALL is changed to #8E99, then the three-byte command at #9304 can be removed.
-
I think the references to 1024 in the above should read 512? (Number of attributes/cells in the playable screen.) N.B. The source post from which the above quote is taken is correct in stating that the number of graphic bytes in the playable screen is 4096.
-
I forgot to mention that, within that more efficient rewrite, I also fixed the bug whereby Fire cells in the top cell-row can kill Willy if he drops off the bottom of the screen directly beneath them. ** By the way, my rewrite does not test the cell underneath the 'upslope' half of Willy's sprite for Earth/wall attributes. This means that Willy can still walk smoothly up a ramp in a room with matching attributes for Earth and Ramp. (Though he can't jump through such a ramp, or walk through it from the 'under-stairs' side.) In an earlier attempt, I had the check for underfoot Earth performed by a CALL to the shared subroutine discussed earlier (which checks both adjacent cells under Willy's feet). However, that had the effect of making Willy's movement jerky as he walked up/down a combined wall-ramp (moving up/down by one cell-row at a time).
-
The subroutine which you referred to above - do you also use it when checking the two cells below Willy's feet, looking for a Fire or a Conveyor block in either or both cells? i.e. If either is true, then let Willy fall through to next row down (in the case of Fire) or move him sideways (if he's on a Conveyor). But presumably this approach does not work when checking for underfoot Crumbly cells (at least not in the same way), because in some circumstances you need to crumble both blocks simultaneously. And the same subroutine wouldn't work when checking for Air underfoot, because the conditionality is reversed and you have to have Air in BOTH cells in order for Willy to fall through.
-
I wonder if the byte savings could be achieved in the main routine (loop set up in the latter part of #95C8) by working from the bottom upwards when considering Willy's attributes, instead of from the top downwards? Hmmm.... how about this: [EDIT: I'VE EXTENDED THE REWRITE TO INCORPORATE THE WHOLE ROUTINE] org: #95C8 LD HL, (#85D3) LD DE, #0040 ADD HL,DE [point HL down to two cell-rows below Willy] LD B, #00 [assume Willy's ramp-adjustment y-offset is zero] [or LD B, D to save one byte] LD E, L [save L, and hence HL, for later] LD A, (#85D1) OR A JR NZ, colour_Willy_code [don't adjust y-coordinate for ramp if Willy is jumping] LD A, (#80DA) AND #01 LD C, A [store ramp direction flag in C] ADD A, L LD L, A [point HL at cell underneath 'upslope' half of Willy] LD A, (#80C4) CP (HL) JR NZ, colour_Willy_code [don't adjust y-coordinate if 'upslope' half of Willy isn't standing on a ramp tile] LD A, C RLCA CPL ADD A, #02 ADD A, L LD L, A [point HL at cell underneath 'downslope' half of Willy] LD A, (#80B2) CP (HL) JR Z, colour_Willy_code [don't adjust y-coordinate; this stops Willy from sinking into a wall block as he approaches a ramp tile] LD A, (#85D2) [calculate y-offset based on ramp direction and Willy's animation frame] DEC C XOR C CPL AND #03 RLCA RLCA LD B, A [b now holds Willy's y-coordinate offset] colour_Willy_code: LD L, E [HL is once again pointing at the cell two rows below Willy's left half] LD DE, #FFE0 [this adjusts HL up a row each time it is added] LD A, (#85CF) ADD A, B [Willy's true pixel y-coordinate, adjusted for ramp if necessary, is now stored in A] LD C, A [Copied to C for the first pass through the loop that follows] EX AF, AF' [Also stored in the shadow registers for later] LD B, #03 [Three cell-rows dealt with] BIT 1,H [Test if HL is pointing below the bottom of the screen [to prevent the bug whereby Willy can be killed by remote Fire cells in the top cell-row] JR NZ, special_case_bottom_of_screen: cell_set_loop: CALL colour_a_row INC HL CALL colour_a_row DEC HL special_case_bottom_of_screen: ADD HL,DE [Move UP a cell-row] LD C, D [saves one byte compared with original - note that C doesn't have to hold the specific value #0F, it just needs at least one of Bits 0-3 set, in order to enforce white INK for Willy's top two cell-rows] DJNZ cell_set_loop EX AF, AF' [Willy's true pixel y-coordinate is restored to A] JR #963B [Note new jump destination; commands at #9637-#963A are no longer needed] [in fact, that jump could also be dispensed with if the colour_a_row subroutine is relocated to AFTER the code which draw's Willy's pixels] colour_a_row: LD A, (#80BB) CP (HL) JP Z, #90B6 LD A, C AND #0F RET Z LD A, (#80A0) CP (HL) RET NZ OR #07 LD (HL), A RET N.B. I haven't tried this out yet! EDIT: I just tested it out - I am pleased to report that it works like a charm! (Except for an initial hitch because I accidentally used an ADC command instead of an ADD!) It saves 11 bytes (potentially 13 bytes, if the subroutine is relocated and the unconditional relative jump at the end is dispensed with), and that's including an additional check for an underfoot Earth block adjacent to the base of a ramp.
-
I was thinking - hypothetically - about whether the byte-efficient rewriting of 'Draw Willy' could be tweaked so that Fire cells still killed Willy if he was located over one whilst cell-row aligned (to stop him jumping over a head-height nasty). If the subroutine (#96E1) were simply CALLed and enacted in full six times, what would the consequences be? Willy's white INK attribute is only assigned to a particular cell if it's a background (Air) cell, and since Air cells don't themselves contain any infilled pixels, the only consequences I could think of would be that Willy could collect certain items more easily. e.g. in 'Watch Tower', he could collect the second-from-right item whilst standing on the right-hand 'parapet', rather than having to touch it during a well-placed jump close to the Fire cell upon which that item sits. And it would no longer be possible to jump over something like the Bathroom tap without collecting it. (Saving an item to collect later on could be a necessary thing to do in a room with a 'Eugene' type challenge, if collecting it too soon meant that Eugene blocked the portal before Willy could escape.) ** Having the subroutine performed six times for the purposes of checking for a nasty collision, but part of that subroutine is only enacted four times for assigning white INK when Willy is cell-aligned, requires the decision to be made in the main routine (#95C8) as to whether to assign the white INK four or six times, so we're essentially reverting back to the original code! However, placing the check for Fire cells within the subroutine (#96E1) before the check for Air cells does provide a couple of byte savings, by replacing two conditional relative jumps with conditional RETURNS.
-
Norman, your Draw Willy rewrite code is missing a LD HL,(#85D3) at the start, to pick up Willy's attribute coordinates. (As I just found to my cost when I tried to implement it and ended up with an invisible Willy!) **** The positions from which Willy can jump over a head-height Fire cell are either 6 or 7 steps away from Willy hitting the Fire cell if he were to walk towards it (i.e. feet together frame of animation, or the next one, with one clear cell-column between him and the Fire cell). **** One other consequence of the change to your Draw Willy code is that Willy can walk up closer to the crabs buried in the sand in 'The Beach', and then jump over them without them fatally nipping his toes! (i.e. closer than he can approach the crabs with the original code in place.)
-
Here is my understanding of the situation: Horizontal part of trajectory: It takes 8 time-frames for Willy to completely pass over (or through) a particular cell (assuming that his lateral motion is uninterrupted). Vertical part of trajectory: Without your rewrite of 'Draw Willy', he spends 7 time-frames at the apex of his jump, at a height greater than two cell-rows above the starting point of the jump. (Greater than two cell rows is the criterion to avoid being killed by a Fire cell that is two rows above the base of the jump, because of the check for Fire two rows below the top of Willy's sprite in all circumstances.) But Willy spends 9 time-frames at a height greater than or equal to two cell-rows above the starting point of the jump. (Greater than or equal to two cell rows up is the criterion to avoid collecting an item, because he only collects the item if his white attribute enters the cell in which it is located, and when he is cell-row aligned his attribute is NOT written two cell-rows below the top of his sprite.) 8 is greater than 7 but less than 9; this is why Willy can jump over an item without collecting it, but not over a Fire cell without being killed. However, with your rewrite of 'Draw Willy' in place, he can avoid being killed if he is at a height greater than or equal to two cell-rows above the platform off which he jumped, so he can clear a head-height Fire cell. *** Bassed on the above, I believe there will be two positions (frames of animation) from which Willy can start a jump and make it over a head-height Fire cell, with your rewrite in place. (Because the 9 frames spent above the danger zone provides a single frame 'margin of error' over the 8 frames it takes to pass horizontally.) If Willy attemps to jump over a head-height wall tile, from the equivalent of either of those positions, then he should clear the wall on the ascendency without his jump trajectory being affected (but he will land on the wall on the descent, either on his back heel or one animation-frame short of that). If Willy starts the jump from a closer position, then in the case of the Fire cell (with your rewrite) he will hit it and die, whereas with the wall, his horizontal motion will be blocked for at least one time-frame, causing him to move vertically upwards until he has cleared the top of the wall (but then he will still land on the far side of the wall with his back heel).
-
Incidentally Norman, your code would also end up fixing this bug: http://skoolkid.github.io/jetsetwilly/reference/bugs.html#longDistanceNasties