EarthQuake Posted September 21, 2004 Share Posted September 21, 2004 My apologies, again. Small mistakes can really add up, plus I should have understood why every object was acting up. I had accidentally miscounted two bytes when adjusting the RAM locations for the objects. That would explain only one of the portcullises working right, it would explain why the magnet would attract itself (because it's "new" address was in the matrix already). It still doesn't explain why the objects' coordinate locations were screwed up... But all that's fixed now, and I'm happy. I will soon have a release candidate available for download after I arrange the items for game 1 and 2. Thanks for puttin up with me, Nukey. I've always been one to jump before I look. People used to tell me that was good, but when it comes to this coding and hacking stuff (and not having an edit button ), you have to watch where you step. Thanks. Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 22, 2004 Author Share Posted September 22, 2004 Heh...I was offline for a while and didn't even read it. But you are correct that BDragonR is only set to use 3 bytes of ram (when it should have been 5). A way to avoid having to do these variables "by hand" would be to use an ORG tag...where the bytes and tables will be filled in with the proper addresses as the assembler works it's way down the list... ORG $80 RoomLo = .byte RoomHi = .byte Obj1Lo = .byte Obj1Hi = .byte Obj2Lo = .byte Obj2Hi = .byte Obj1X = .byte Obj1Y = .byte Obj2X = .byte Obj2Y = .byte PlayerRoom = .byte PlayerX = .byte PlayerY = .byte PlayerYadj = .byte ScanLineCnt = .byte RoomDefIndex = .byte Player0Def = .byte Player1Def = .byte Debounce = .byte CurrentObject = ds 5 Jstick = .byte CurrentStick = .byte ObjAddress = .byte ObjDir = .byte LastObj = .byte CarriedObj = .byte CarriedX = .byte CarriedY = .byte HiCnt = .byte ObjLst = ds 2 Delta2 = .byte Delta1 = .byte ObjCr = .byte State = .byte Level = .byte Control = .byte NoteCnt = .byte Sound = .byte Index = .byte PrevRoom = .byte PrevX = .byte PrevY = .byte LoCnt = .byte ObjectNumber = .byte ;object variables... SurroundR = ds 3 DotR = ds 3 ;all dragons must be consecutive RDragonR = ds 5 YDragonR = ds 5 GDragonR = ds 5 BDragonR = ds 5 MagnetR = ds 3 SwordR = ds 3 ChaliseR = ds 3 BridgeR = ds 3 ;all keys must be consecutive YKeyR = ds 3 WKeyR = ds 3 BKeyR = ds 3 ;all gates must be consecutive YGateR = .byte WGateR = .byte BGateR = .byte BatR = ds 7 Personally, I don't use that method because I like to see exactly how many bytes I have available...but using it will allow the assembly to correct the ram assignments automatically as long as everything is still placed in the proper order...and the correct number of bytes to use is indicated (.byte for one, ds # for more). Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 22, 2004 Author Share Posted September 22, 2004 BTW be sure to play game 3 a bunch of times after moving the objects (to check if the game is still winnable). You might have to adjust the object boundry table...and you'll definately have to change the boundries if you want objects to be able to appear randomly in added rooms. Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 22, 2004 Author Share Posted September 22, 2004 Correction... SEG.U Variables ORG $80 ...etc Quote Link to comment Share on other sites More sharing options...
EarthQuake Posted September 22, 2004 Share Posted September 22, 2004 BTW be sure to play game 3 a bunch of times after moving the objects (to check if the game is still winnable). You might have to adjust the object boundry table...and you'll definately have to change the boundries if you want objects to be able to appear randomly in added rooms. I redid the entire room bounds table, both games object location tables, and also redid the room data table from scratch. I kept everything in mind when assigning objects to rooms. The first two rooms after the number room are the only rooms where the chalice can appear, and the second to the last room which is not even on game 3, will never be assigned an object. I do have some other interesting ideas for tutorials, if you are interested yourself that is. I know these couple following features would not be very difficult to implement, but they would influence the way the game works tremendously. -The ability to have seperate starting screens for each difficulty. -The abillity to have different winning screens for each difficulty. -The ability to have seperate speeds and attack rates for each dragon per difficulty. -The ability to add or remove difficulty levels (perhaps the hardest of these). Notice how they all relate to difficulty levels. I probably wouldn't even use any of these, however, they could be useful to anyone else wanting to hack Adventure. Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 22, 2004 Author Share Posted September 22, 2004 Easy...most of them can be done simply by grabbing the level number, dividing it by 2 (because levels 1,2,3 are stored as values 0,2,and 4)...then using that value in a new table that holds the differing variables for that particular level. For example, here's how you'd deal with different winning screens... Original... LDA ChaliseR ;Get the room the Chalise is in. ;3 CMP #WinRoom ;Is it in the yellow castle? ;2 BNE MainGameLoop_2 ;If Not Branch.. ;2 Instead of loading the room number for the chalise and then comparing it to the static value of what it needs to be (to win the game)...you'd do the second part first. Figure out which room is the winning screen...and then compare it to the chalise's room... Edited... LDA Level ;Load the current level number ;3 LSR ;Divide it by two ;2 TAY ;Give it to an unused register (Y in this case);2 LDA WinRooms,Y ;Load the "win" screen number for this level ;4 CMP ChaliseR ;Compare to the chalise's room ;2 BNE MainGameLoop_2 ;Branch if not the same ;2 Now you'd need a table that holds the 3 room numbers... WinRooms: .byte $12,$1A,$1B ;the winning screens (inside the yellow, white, and black castles) And the game will now let you win as soon as you set into the yellow, white, or black castles (for games 1, 2, and 3 respectively). Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 22, 2004 Author Share Posted September 22, 2004 Adding a game timer: There have been requests for this one in the past...so I'll do it quick 'n' dirty (i.e. memory-wasting). This mod requires over a page of romspace to use written this way...so you'd probably need to use the 8k assembly to add it in. It also wastes 2 bytes of Ram memory to hold the minutes and seconds. First, assign ram memory to the two bytes. I just used $F7 and $F8 ($F9 and $FA are kinda iffy...so I didn't bother with those). TimerLo = $F7 TimerHi = $F8 Now you'll need to add in GFX to hold the digits. In this example, playfield GFX are being used to draw the numbers...so it needs digits that are normal and digits that are reversed. In addition, the digits are stored in the bitmap data twice (so you can just AND off the part not needed for a specific digit). Each digit is 8 bytes in length...and I began each one with $FF (so that the last value grabbed will draw a border underneath the digits). The 2 tables... TimerDigits: .byte $FF .byte $00 .byte $77; | XXX XXX| $F100 .byte $55; | X X X X| $F101 .byte $55; | X X X X| $F102 .byte $55; | X X X X| $F103 .byte $77; | XXX XXX| $F104 .byte $00 .byte $FF .byte $00 .byte $22; | X X | $F105 .byte $22; | X X | $F106 .byte $22; | X X | $F107 .byte $22; | X X | $F108 .byte $22; | X X | $F109 .byte $00 .byte $FF .byte $00 .byte $77; | XXX XXX| $F10A .byte $44; | X X | $F10B .byte $77; | XXX XXX| $F10C .byte $11; | X X| $F10D .byte $77; | XXX XXX| $F10E .byte $00 .byte $FF .byte $00 .byte $77; | XXX XXX| $F10F .byte $11; | X X| $F110 .byte $33; | XX XX| $F111 .byte $11; | X X| $F112 .byte $77; | XXX XXX| $F113 .byte $00 .byte $FF .byte $00 .byte $11; | X X| $F114 .byte $11; | X X| $F115 .byte $77; | XXX XXX| $F116 .byte $55; | X X X X| $F117 .byte $55; | X X X X| $F118 .byte $00 .byte $FF .byte $00 .byte $77; | XXX XXX| $F119 .byte $11; | X X| $F11A .byte $77; | XXX XXX| $F11B .byte $44; | X X | $F11C .byte $77; | XXX XXX| $F11D .byte $00 .byte $FF .byte $00 .byte $77; | XXX XXX| $F11E .byte $55; | X X X X| $F11F .byte $77; | XXX XXX| $F120 .byte $44; | X X | $F121 .byte $44; | X X | $F122 .byte $00 .byte $FF .byte $00 .byte $11; | X X| $F123 .byte $11; | X X| $F124 .byte $11; | X X| $F125 .byte $11; | X X| $F126 .byte $77; | XXX XXX| $F127 .byte $00 .byte $FF .byte $00 .byte $77; | XXX XXX| $F128 .byte $55; | X X X X| $F129 .byte $77; | XXX XXX| $F12A .byte $55; | X X X X| $F12B .byte $77; | XXX XXX| $F12C .byte $00 .byte $FF .byte $00 .byte $11; | X X| $F12D .byte $11; | X X| $F12E .byte $77; | XXX XXX| $F12F .byte $55; | X X X X| $F130 .byte $77; | XXX XXX| $F131 .byte $00 TimerDigits2: .byte $FF .byte $00 .byte $EE; |XXX XXX | $F100 .byte $AA; |X X X X | $F101 .byte $AA; |X X X X | $F102 .byte $AA; |X X X X | $F103 .byte $EE; |XXX XXX | $F104 .byte $00 .byte $FF .byte $00 .byte $44; | X X | $F105 .byte $44; | X X | $F106 .byte $44; | X X | $F107 .byte $44; | X X | $F108 .byte $44; | X X | $F109 .byte $00 .byte $FF .byte $00 .byte $EE; |XXX XXX | $F10A .byte $22; | X X | $F10B .byte $EE; |XXX XXX | $F10C .byte $88; |X X | $F10D .byte $EE; |XXX XXX | $F10E .byte $00 .byte $FF .byte $00 .byte $EE; |XXX XXX | $F10F .byte $88; |X X | $F110 .byte $CC; |XX XX | $F111 .byte $88; |X X | $F112 .byte $EE; |XXX XXX | $F113 .byte $00 .byte $FF .byte $00 .byte $88; |X X | $F114 .byte $88; |X X | $F115 .byte $EE; |XXX XXX | $F116 .byte $AA; |X X X X | $F117 .byte $AA; |X X X X | $F118 .byte $00 .byte $FF .byte $00 .byte $EE; |XXX XXX | $F119 .byte $88; |X X | $F11A .byte $EE; |XXX XXX | $F11B .byte $22; | X X | $F11C .byte $EE; |XXX XXX | $F11D .byte $00 .byte $FF .byte $00 .byte $EE; |XXX XXX | $F11E .byte $AA; |X X X X | $F11F .byte $EE; |XXX XXX | $F120 .byte $22; | X X | $F121 .byte $22; | X X | $F122 .byte $00 .byte $FF .byte $00 .byte $88; |X X | $F123 .byte $88; |X X | $F124 .byte $88; |X X | $F125 .byte $88; |X X | $F126 .byte $EE; |XXX XXX | $F127 .byte $00 .byte $FF .byte $00 .byte $EE; |XXX XXX | $F128 .byte $AA; |X X X X | $F129 .byte $EE; |XXX XXX | $F12A .byte $AA; |X X X X | $F12B .byte $EE; |XXX XXX | $F12C .byte $00 .byte $FF .byte $00 .byte $88; |X X | $F12D .byte $88; |X X | $F12E .byte $EE; |XXX XXX | $F12F .byte $AA; |X X X X | $F130 .byte $EE; |XXX XXX | $F131 .byte $00 Then, you'll need some way of showing that value to the player. Since the inside of the yellow castle is the screen that you'd win in...I can just put the timer in there Display it at the top of the screen, while the rest of the screen is displayed normally. Because so much time is used up just figuring out WHAT to display, there isn't enough time left to clear out the GFX (so the timer appears on the screen twice...on both the left and right sides). But here's the kernal mod. Scroll up to bank 1 to this point... STY RoomDefIndex ;Save for Next Time. ;3 STA WSYNC ;Wait for Horizontal Blank. ;3 ; a. X still contains 1, so the important bit 1 for VBLANK is zero. ; (this trick is also very common) ; b. a very usual trick: we know the state of a flag, so we replace JMP with a branch ; (here Y always != 0 -> Z-flag = 0) STX VBLANK ;Clear any Vertical Blank. ;3 There's a branch that just follows this...but we'll still be using it. So keep the cursor just below that VBLANK line, and paste this in... LDX PlayerRoom ;Check the player's room number ;3 CPX #$12 ;Is it inside the yellow castle? ;2 BEQ TimerDisplayInit ;Branch if so ;2 JMP PrintPlayer00 ;Otherwise, skip ahead ;3 TimerDisplayInit: LDY #$20 ;Set the value for "Mirrored" GFX ;2 STY CTRLPF ;And put in the playfield control register. ;3 LDY #$07 ;Get ready to count down 8 lines ;2 STY RoomDefIndex ;Save to a temp ;3 TimerDisplay: LDA TimerHi ;Grab the high byte of the new timer ;3 AND #$0F ;Keep only the lower nybble ;2 ASL ;ASL 3 times to multiply it by 8 ;2 ASL ; ;2 ASL ; ;2 CLC ; ;2 ADC RoomDefIndex ;Then add in the current line ;3 TAY ;Set the new index to the Y register ;2 LDA TimerDigits,Y ;Load in the GFX for that digit ;4 AND #$0F ;Keep only the low nybble ;2 STA ScanLineCnt ;...and save temporarily ;3 LDA TimerHi ;Now do the same for the high nybble of TimerHi... ;3 AND #$F0 ;Keep only the high nybble ;2 LSR ;Divide by 2 (so index is x8) ;2 CLC ; ;2 ADC RoomDefIndex ;Add in the current line ;3 TAY ;And set to the Y index ;2 LDA TimerDigits,Y ;Load in the GFX for that digit ;4 AND #$F0 ;Keep only the high nybble ;2 ORA ScanLineCnt ;...and throw in the low nybble we saved above ;3 TAX ;Give to X for the time being ;2 ;do the same thing for the low timer...this time using mirrored GFX... LDA TimerLo ; ;3 AND #$0F ; ;2 ASL ; ;2 ASL ; ;2 ASL ; ;2 CLC ; ;2 ADC RoomDefIndex ; ;3 TAY ; ;2 LDA TimerDigits2,Y ; ;4 AND #$F0 ; ;2 STA ScanLineCnt ; ;3 LDA TimerLo ; ;3 AND #$F0 ; ;2 LSR ; ;2 CLC ; ;2 ADC RoomDefIndex ; ;3 TAY ; ;2 LDA TimerDigits2,Y ; ;4 AND #$0F ; ;2 ORA ScanLineCnt ; ;3 ;digit GFX ready to print STA WSYNC ;Wait for Horizontal Blank. ;3 STX PF1 ;Save the X register to the 2nd playfield register ;2 STA PF2 ;Save the A register to the 3rd playfield register ;2 DEC RoomDefIndex ;Decrease the line counter ;5 BPL TimerDisplay ;Branch if still zero or above (and do the next line) ;2 LDY #$21 ;Otherwise, reset everything...done drawing the timer ;2 STY CTRLPF ;Reset the playfield control to reversed GFX ;3 LDY #$06 ; ;2 STY RoomDefIndex ;Reset the GFX index to begin on the 6th byte ;3 LDY #$5F ;Reset the scan line counter... ;2 STY ScanLineCnt ;...to 9 lines less than normal ;3 STA WSYNC ;Send a blank scanline ;3 STA WSYNC ;Send a blank scanline ;3 That takes care of the display Since sprite GFX are not being sent to the display, they will be thrown off a little bit...but it seems to work OK. Now, you'll need some way of updating the timer. You wouldn't want it to change during an inactive game (i.e. when you win)...so I just pasted the code to the routine that deals with moving objects attracted by the magnet ... ;Deal With Magnet. Mag_1: ;timer LDA LoCnt ; ;3 AND #$1F ; ;2 BNE NoTime ; ;2 SED ; ;2 LDA TimerLo ; ;3 CLC ; ;2 ADC #$01 ; ;2 STA TimerLo ; ;3 CMP #$60 ; ;2 BNE SaveSec ; ;2 LDA #$00 ; ;2 STA TimerLo ; ;3 CLC ; ;2 LDA TimerHi ; ;3 ADC #$01 ; ;2 STA TimerHi ; ;3 SaveSec: CLD ; ;2 NoTime: ;end of added section The regular magnet routine just gets pushed down below this new added section (so the next line will be LDA MagnetR+2, etc.) And to complete the mod...add in a couple lines to clear out the timer when a new game is begun. I just had this happen when the number screen is being displayed (so the timer still runs if you get killed)... SetupRoomObjects: LDA #$00 ;Set the current room to the ;2 STA PlayerRoom ;"Number" room. ;3 STA PrevRoom ;And the previous room. ;3 ;added for the timer STA TimerLo ;Clear the seconds timer ;3 STA TimerHi ;Clear the minutes timer ;3 ;A is still zero...no need to reload...etc... Here's the binary that uses a timer...now you can see how long it takes you to get the chalise back (so long as that room is displayed when you win). adventure_timed_.zip Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 22, 2004 Author Share Posted September 22, 2004 Game 1: Quote Link to comment Share on other sites More sharing options...
atwwong Posted September 22, 2004 Share Posted September 22, 2004 Nukey, thanks again for doing all this. Your 8k tutorials have really helped me to tweak my hack to almost what I originally envisioned it to be, especially with the new maingame routine that eliminated all of my screen flips and rumbles! Too bad I have absolutely no room for that timer though... I'll be posting a final release candidate of my hack soon (with some additions ), hopefully along with a manual so people can figure it out (and of course, the source code, but not before it's tidied up!). Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 22, 2004 Author Share Posted September 22, 2004 No prob. The timer idea just came to me...so I figured I'd throw it out there. It does miscount by 4/60 seconds though...so it's not perfect as written (since it's using bit 6 of the framecounter to decide when to bump up the seconds counter). Here's a Supercharger version adventure4k_timed_.zip Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 22, 2004 Author Share Posted September 22, 2004 1:46...game 2 Another problem is that the timer is only reset when the number screen is initially displayed (so if you had it up for a minute without pressing select or reset, the game would start with that time still on the clock) Quote Link to comment Share on other sites More sharing options...
ATARI TROLL Posted September 22, 2004 Share Posted September 22, 2004 Nukey this is a really cool concept.....I love the timer....gives the superman feel to the game! Cheers and thanks for the post....sorry about replying 4 pages later....been gone for a while Quote Link to comment Share on other sites More sharing options...
ATARI TROLL Posted September 22, 2004 Share Posted September 22, 2004 Nukey this is a really cool concept.....I love the timer....gives the superman feel to the game! Cheers and thanks for the post....sorry about replying 4 pages later....been gone for a while Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 23, 2004 Author Share Posted September 23, 2004 Next...adding game levels: This is really kind of easy to do. All you really need to do is seach for the variable "Level" and alter the routines/data tables to work with 4 levels instead of 3. From the top downward...here's the list of changes that need to be done to add in a Level #4: CheckReset: ; testing bit 1 is smaller with LSR: LSR ;And check only the select switch ;2 BCC StoreSwitches ;Branch if select not being used. ;2 LDA PlayerRoom ;Get the Current Room. ;3 BNE SetupRoomObjects ;Branch if not. ;2 LDY Level ;Increment the level. ;3 INY ;Number (by two). ;2 INY ;Number (by two). ;2 ;change $06 to $08... CPY #$08 ;Have we reached the maximum? ;2 SetupRoomObjects_2: LDA (CurrentObject),Y ;(the rooms and positions) into ;5 STA.wy DotR,Y ;the working area. ;5 DEY ; ;2 BPL SetupRoomObjects_2 ; ;2 LDA Level ;Get the level number. ;3 CMP #$04 ;Branch if level one. ;2 BCC SignalGameStart ;Or two(Where all objects are in defined areas);2 JSR RandomizeLevel3 ;Put some objects in random rooms. ;6 This routine will prevent randomized objects if the level number is under a value of $04 ($04 is level 3). You could adjust that to however you wanted (like say...randomize only even-numbered levels by changing CMP #$04/BCC SignalGameStart to LSR/LSR/BCC SignalGameStart...or just by using CMP instructions for each level. I had it randomize even-numbered levels in the binary below by just using 2 LSR instructions (so levels 2 and 3 are swapped). That's all the changes that need to be done to the program. You can decide if you want additional aspects of the new level just by using comparisons in the program to decide if something should be done or not. Now that the programming side is finished, you'll need to adjust the number of bytes in the data tables to account for the new level, and also add in a bitmap image for the number "4". Here's a rundown... Change this... ;Object #5 States. NumberStates: .byte $01,<GfxNum1,>GfxNum1 ;State 1 as FCF4 .byte $03,<GfxNum2,>GfxNum2 ;State 3 as FD04 .byte $FF,<GfxNum3,>GfxNum3 ;State FF as FD0C To this... ;Object #5 States. NumberStates: .byte $01,<GfxNum1,>GfxNum1 ;State 1 as FCF4 .byte $03,<GfxNum2,>GfxNum2 ;State 3 as FD04 .byte $05,<GfxNum3,>GfxNum3 ;State 5 as FD0C .byte $FF,<GfxNum4,>GfxNum4 ;State FF as FD0C Then add this bitmap... ;Object #5 State #3 Graphic :'3' GfxNum4: .byte $02 ; X .byte $02 ; X .byte $12 ; X X .byte $22 ; X X .byte $7F ; XXXXXXX .byte $02 ; X .byte $02 ; X .byte $00 ;bitmap end Change this... ;Dragon Difficulty DragonDiff: .byte $D0,$E8 ;Level 1 : Am, Pro .byte $F0,$F6 ;Level 2 : Am, Pro .byte $F0,$F6 ;Level 3 : Am, Pro ...to this... ;Dragon Difficulty DragonDiff: .byte $D0,$E8 ;Level 1 : Am, Pro .byte $F0,$F6 ;Level 2 : Am, Pro .byte $F0,$F6 ;Level 3 : Am, Pro .byte $F6,$FE ;Level 4 : Am, Pro In level 4, I increased the dragon bite speed to fast in B difficulty...VERY fast in A. You can make them faster or slower just by editing the value to be higher or lower. The RoomDiffs table will need to contain an extra byte for each line (instead of 3). This table lists the rooms that you end up in when moving to rooms that have the high bit set (+80) in it's directional info. If it does, the next room number is grabbed from this table (which is why you'll need an extra byte for each level added). I just kept it the same as levels 2-3 in this example. ;Room differences for different levels (level 1,2,3) RoomDiffs: RD1: ;<- These "RD" tags used in the room matrix...subtract RD0 to get proper $80 value .byte $10,$0F,$0F,$0F ;Down from Room 01 RD2: .byte $05,$11,$11,$11 ;Down from Room 02 RD3: .byte $1D,$0A,$0A,$0A ;Down from Room 03 RD4: .byte $1C,$16,$16,$16 ;U/L/R/D from Room 1B (Black Castle Room) RD5: .byte $1B,$0C,$0C,$0C ;Down from Room 1C RD6: .byte $03,$0C,$0C,$0C ;Up from Room 1D (Top Entry Room) Loc_5 will need to have 2 bytes added to the end that points to what table the game object data should be read from. You can reuse game 2's object matrix just by doing this... Loc_5: .byte >Game1Objects ; --continued. .byte <Game2Objects,>Game2Objects ;objects game 02. .byte <Game2Objects,>Game2Objects ;objects game 03. .byte <Game2Objects,>Game2Objects ;objects game 04. If you want to add in an all-new location chart (which I did for the binary below), you'd just add one...like this: Loc_5: .byte >Game1Objects ; --continued. .byte <Game2Objects,>Game2Objects ;objects game 02. .byte <Game2Objects,>Game2Objects ;objects game 03. .byte <Game4Objects,>Game4Objects ;objects game 04. ;Object locations (room and coordinate) for Game 04 Game4Objects: .byte $15,$51,$12 ;Black Dot (Room,X,Y) .byte $14,$50,$20,$A0,$00 ;Red Dragon (Room,X,Y,Movement,State) .byte $19,$50,$20,$A0,$00 ;Yellow Dragon (Room,X,Y,Movement,State) .byte $0E,$50,$20,$A0,$00 ;Green Dragon (Room,X,Y,Movement,State) .byte $04,$80,$20 ;Magnet (Room,X,Y) .byte $0B,$20,$20 ;Sword (Room,X,Y) .byte $14,$30,$20 ;Chalise (Room,X,Y) .byte $11,$40,$40 ;Bridge (Room,X,Y) .byte $06,$20,$40 ;Yellow Key (Room,X,Y) .byte $09,$20,$40 ;White Key (Room,X,Y) .byte $19,$20,$40 ;Black Key (Room,X,Y) .byte $1C ;Portcullis State .byte $1C ;Portcullis State .byte $1C ;Portcullis State .byte $0D,$20,$20,$90,$00 ;Bat (Room,X,Y,Movement,State) .byte $78,$00 ;Bat (Carrying, Fed-Up) Then just edit the starting room, X and Y locations, etc. In the example above, I swapped a few. Here's the binary that adds 2 more levels. Even-numbered levels feature random objects... adventure5levels.zip Quote Link to comment Share on other sites More sharing options...
Goochman Posted September 24, 2004 Share Posted September 24, 2004 Very Cool 129 on Lvl 2 Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 24, 2004 Author Share Posted September 24, 2004 New...multiple screen resolutions: I just discovered how to mix screen resolutions...where you can choose between 7, 14, 28, or 56 lines of data PER SCREEN Normally, Adventure uses 7 lines of playfield data for each screen...like this: ;Yellow Castle (outside) Room11: .byte $F0,$FE,$15 ;XXXXXXXXXXX X X X R R R RRRRRRRRRRR .byte $30,$03,$1F ;XX XXXXXXX RRRRRRR RR .byte $30,$03,$FF ;XX XXXXXXXXXXRRRRRRRRRR RR .byte $30,$00,$FF ;XX XXXXXXXXRRRRRRRR RR .byte $30,$00,$3F ;XX XXXXXX RRRRRR RR .byte $30,$00,$00 ;XX RR .byte $F0,$FF,$0F ;XXXXXXXXXXXXXXXX RRRRRRRRRRRRRRRR 7 lines of 3 bytes each. Another thing to take note of is that the first byte in each line ONLY uses the upper nybble (lower nybbles saved to PF0 have no effect on the screen). Keep that in mind...because we'll be experimenting with it Scroll back up to the display kernal. Look at the area that decides WHEN to use a new line of data... PrintPlayer00_2: LDA ScanLineCnt ;Get Scan Line Count. ;3 AND #$0F ;Have we reached a sixteenth scan line? ;2 ;NOTE! The above line could be changed to 7 (checking every 8th scanline instead). This will ;effectively DOUBLE the resolution for each screen. (you'll need to update the room GFX tables ;to have 42 bytes instead of 21). The upper and lower lines of the higher res. bitmaps will ;just *barely* be visible...so by clever placement of the tags, this could be reduced to 36 ;bytes per screen :) Format of the updated line would be this: ;; AND #$07 ;Have we reached a eighth scan line? ;2 ;...that will replace the above line and the program will grab new GFX values on every 8th line BNE PrintPlayer00_4 ;If not, Branch. ;2 In the message, I'd already found out how to double the screen resolution. But if that change was done, the screens would all need to be doubled up...including all the screens that only NEED 7 lines of data (like those big empty rooms). That's a lot of waste if we changed to using new lines on every 8th scanline. There's a better way In this optimized code, ram location $FA is corrupted by the stack when the program is outside of the kernal (due to all those JSR's and such). If we're quick about it...we could use that location to hold the AND value...and then just decide up top when to use $0F, $07 (or even something smaller like $03 or $01). That would allow the game to store bitmaps in multiple modes...even changing modes within a screen (so some lines will be printed 8 times, some 16, etc). You could make a really snazzy game select screen with that! And that's where that unused nybble for PF0 comes in. We can just define what the AND value needs to be in that nybble! ;Yellow Castle (outside) Room11: .byte $FF,$FE,$15 ;XXXXXXXXXXX X X X R R R RRRRRRRRRRR .byte $3F,$03,$1F ;XX XXXXXXX RRRRRRR RR .byte $3F,$03,$FF ;XX XXXXXXXXXXRRRRRRRRRR RR .byte $3F,$00,$FF ;XX XXXXXXXXRRRRRRRR RR .byte $3F,$00,$3F ;XX XXXXXX RRRRRR RR .byte $3F,$00,$00 ;XX RR .byte $FF,$FF,$0F ;XXXXXXXXXXXXXXXX RRRRRRRRRRRRRRRR That one just uses the default...printing each line 16 times (by using an AND value of $0F). But you could do this if you wanted those spires to be just a -little-bit longer (by 8 more scanlines)... ;Yellow Castle (outside) Room11: .byte $FF,$FE,$15 ;XXXXXXXXXXX X X X R R R RRRRRRRRRRR .byte $F7,$FE,$15 ;XXXXXXXXXXX X X X R R R RRRRRRRRRRR .byte $37,$03,$1F ;XX XXXXXXX RRRRRRR RR .byte $3F,$03,$FF ;XX XXXXXXXXXXRRRRRRRRRR RR .byte $3F,$00,$FF ;XX XXXXXXXXRRRRRRRR RR .byte $3F,$00,$3F ;XX XXXXXX RRRRRR RR .byte $3F,$00,$00 ;XX RR .byte $FF,$FF,$0F ;XXXXXXXXXXXXXXXX RRRRRRRRRRRRRRRR I copied the top line and gave 8 scanlines to it...which I robbed from the next following line. You could use multiples of 2, 4, 8, and 16 scanlines just by setting that nybble. OK...now that we have a place to hold the value, we need to decide when to use it. And that is accomplished by doing this... ;Print top line of Room. LDY RoomDefIndex ;Get room definition index. ;3 LDA (RoomLo),Y ;Get first room definition byte. ;5 STA PF0 ; and display. ;3 ;added lines------ AND #$0F ;Clear off the pixel data...keeping only the AND value;2 STA $FA ;...and save it to ram location $FA ;2 ;----------------- INY ; ;2 LDA (RoomLo),Y ;Get Next room definition byte. ;5 ;...etc... There it's saved to the temp (you can't use the variable Temp...because it's shared with Player0def). $FA seems to work well...it only seems to be corrupted outside of the display kernal. Next, change the display routine to use this ram location instead of an immediate value... PrintPlayer00_2: LDA ScanLineCnt ;Get Scan Line Count. ;3 AND $FA ;Have we reached the custom number of scanlines? ;3 BNE PrintPlayer00_4 ;If not, Branch. ;2 That's it! You'll have to change all of the existing bitmaps to use lower nybbles (if any are zero, the game will crash). But all of the bitmaps will now have the option of using higher resolutions. Caution: be sure that any gaps that you wish the player to move though are at least 8 scanlines...and it's not a good idea to use smaller horizontal gaps than 2 pixels. Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 24, 2004 Author Share Posted September 24, 2004 Oops...bugfix: You'll need to correct the value of $FA whenever the scanline count runs out (otherwise, the entire screen will be using the number of scanlines defined by the very first byte in the bitmap instead of each line). That code goes here: PrintPlayer00_4: STA WSYNC ;Wait for Horzontal blank. ;3 STY ENABL ;Enable Ball (If Wanted.) ;3 STX GRP0 ;Display Player00 definition byte (if Wanted). ;3 ;added lines------ LDY RoomDefIndex ;Get room definition index. ;3 LDA (RoomLo),Y ;Get first room definition byte, ;5 AND #$0F ;Clear off the pixel data...keeping only the AND value;2 STA $FA ;...and save it to ram location $FA ;3 ;----------------- BNE PrintPlayer00_3 ; ;2 Sorry about that Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 24, 2004 Author Share Posted September 24, 2004 This code produces a sharper-looking castle ;Yellow Castle (outside) Room11: .byte $F7,$FC,$00 ;XXXXXXXXXXX RRRRRRRRRRR .byte $F3,$FC,$00 ;XXXXXXXXXXX RRRRRRRRRRR .byte $F3,$FE,$15 ;XXXXXXXXXXX X X X R R R RRRRRRRRRRR .byte $37,$02,$15 ;XX X X X X R R R R RR .byte $33,$03,$1F ;XX XXXXXXX RRRRRRR RR .byte $33,$03,$3F ;XX XXXXXXXX RRRRRRRR RR .byte $37,$03,$FF ;XX XXXXXXXXXXRRRRRRRRRR RR .byte $37,$01,$FF ;XX XXXXXXXXXRRRRRRRRR RR .byte $3F,$00,$FE ;XX XXXXXXXRRRRRRR RR .byte $3F,$00,$3E ;XX XXXXX RRRRR RR .byte $3F,$00,$00 ;XX RR .byte $FF,$FF,$0F ;XXXXXXXXXXXXXXXX RRRRRRRRRRRRRRRR Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 25, 2004 Author Share Posted September 25, 2004 Damn...the ball object jets kind of jumpy (because using a ram location takes 1 cycle longer than using an immediate value). So here's how to correct it... ScanLineCnt is loaded in a few places...and each time it takes 3 cycles. By using an unused register (X), we can cut this load time down to 2 cycles...saving 1 that we need for the AND $FA This change is kind of hard to explain...so I'll just list the updated code sections (including the optional one for the timer if you are using it): ;remove these 2 lines... ; LDA #$68 ; ;2 ; STA ScanLineCnt ;Set Scan Line Count. ;3 ;Print top line of Room. LDY RoomDefIndex ;Get room definition index. ;3 LDA (RoomLo),Y ;Get first room definition byte. ;5 STA PF0 ; and display. ;3 AND #$0F ;Clear off the pixel data...keeping only the AND value;2 STA $FA ;...and save it to ram location $FA ;2 INY ; ;2 LDA (RoomLo),Y ;Get Next room definition byte. ;5 STA PF1 ; and display. ;3 INY ; ;2 LDA (RoomLo),Y ;Get Last room defintion byte. ;5 STA PF2 ; and display. ;3 INY ; ;2 STY RoomDefIndex ;Save for Next Time. ;3 ;moved from up top in order to keep in X LDX #$68 ; ;2 STX ScanLineCnt ;Set Scan Line Count. ;3 STA WSYNC ;Wait for Horizontal Blank. ;3 ; a. X still contains #$68, so the important bit 1 for VBLANK is zero. ; (this trick is also very common) ; b. a very usual trick: we know the state of a flag, so we replace JMP with a branch ; (here Y always != 0 -> Z-flag = 0) STX VBLANK ;Clear any Vertical Blank. ;3 ;optional lines for the timer routine LDA PlayerRoom ;Check the player's room number ;3 CMP #$12 ;Is it inside the yellow castle? ;2 BEQ TimerDisplayInit ;Branch if so ;2 JMP PrintPlayer00 ;Otherwise, skip ahead ;3 ;optional lines in the timer routine... ;use X instead of A... LDX #$5F ;Reset the scan line counter... ;2 STX ScanLineCnt ;...to 9 lines less than normal ;3 STA WSYNC ;Send a blank scanline ;3 STA WSYNC ;Send a blank scanline ;3 BNE PrintPlayer00 ;Branch ahead ;2 ;Print Player01 (Object 02) PrintPlayer01: ;delete this line: ; LDA ScanLineCnt ;Get Current Scan Line. ;3 ;A still holds the scanline count, so save it to X temporarily TAX ; ;2 ;Print Player00 (Object01), Ball (Man) and Room. PrintPlayer00: ;delete this line: ; LDA ScanLineCnt ;Get the Current Scan Line. ;3 ;now pull that scanline count back...saving 1 cycle TXA ;Get the Current Scan Line. ;2 LDX #$00 ; ;2 That gives us the 1 cycle we need when WSYNC is reached in PrintPlayer00_2...and it no longer bounces Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 25, 2004 Author Share Posted September 25, 2004 Apparently, some problems might crop up if the room data contains more than 11 lines of data Trying to figure out that one. Or maybe it's just my sloppy coding Next lesson... Adding hit points to dragons (another often-requested mod): All dragons have a single "state" variable. Once a dragon so much as brushes against the sword, this variable is changed to a value of 1...it's dead. So much for being tough! What you'd need to do is set aside another byte of ram for each dragon...so that rather than just setting the state to 1, it first bumps down this new variable (it's hit counter)...and then saves a state of 1 only when the hit counter reaches zero (so you could have as many as 256 hits to record on any or all dragons). First, you need to reorganize ram to include the new variable...instead of allocating 5 bytes of ram to each dragon, you'd now be using 6... ;object variables... SurroundR = $B1;(3 bytes) DotR = $B4;(3 bytes) ;added hit counter ;all dragons must be consecutive RDragonR = $B7;(6 bytes) YDragonR = $BD;(6 bytes) GDragonR = $C3;(6 bytes) ;...etc... ...and on down the list. Since the variable table has changed, you'd need to update the object matrix...so scroll down and tack on the new value you want for each dragon (I just used a value of 10 for each one, but this could be any number, and the numbers can be different if you wish). Both tables must be changed (game1objects, and game2objects)... ;added hit counter ;Game1Objects and Game2Objects must hold the same number of bytes ;Object locations (room and coordinate) for game 01. Game1Objects: .byte $15,$51,$12 ;Black dot (Room,X,Y) .byte $0E,$50,$20,$00,$00,$0A;Red Dragon (Room,X,Y,Movement,State,Hits) .byte $01,$50,$20,$00,$00,$0A;Yellow Dragon (Room,X,Y,Movement,State,Hits) .byte $1D,$50,$20,$00,$00,$0A;Green Dragon (Room,X,Y,Movement,State,Hits) and ;Object locations (room and coordinate) for Games 02 and 03. Game2Objects: .byte $15,$51,$12 ;Black Dot (Room,X,Y) .byte $14,$50,$20,$A0,$00,$0A;Red Dragon (Room,X,Y,Movement,State,Hits) .byte $19,$50,$20,$A0,$00,$0A;Yellow Dragon (Room,X,Y,Movement,State,Hits) .byte $04,$50,$20,$A0,$00,$0A;Green Dragon (Room,X,Y,Movement,State,Hits) Next, you'd need to allow the program to reset this counter when game reset is pressed... (frem CheckGameStart) LDA #$00 ; ;2 STA RDragonR+4 ;Set the red dragon's state to OK. ;3 STA YDragonR+4 ;Set the yellow dragon's state to OK. ;3 STA GDragonR+4 ;Set the green dragon's state to OK. ;3 STA NoteCnt ;Set the note count to zero.. (ops!??) ;3 ;added lines...hit counter LDA #$0A ; ;2 STA RDragonR+5 ;Set the red dragon's hit counter to 10 ;3 STA YDragonR+5 ;Set the yellow dragon's hit counter to 10 ;3 STA GDragonR+5 ;Set the green dragon's hit counter to 10 ;3 OK...now that that is set up...all you need to do is edit in the new routine. Just scroll down to the Move_Dragon routine that deals with detecting the sword, and add in the new check for the hit counter... MoveDragon_4: STX ObjAddress ;Store Object's Dynamic Data Address. ;3 LDX ObjectNumber ;Get the Object Number. ;3 JSR FindObjHit ;See if anoher object has hit the dragon. ;6 LDX ObjAddress ;Get the Object Address. ;3 CMP #SwordNumber ;Has the Sword hit the Dragon? ;2 BNE MoveDragon_5 ;If Not, Branch. ;2 ;added lines...hit counter DEC NUSIZ1,X ; ;4 BNE MoveDragon_hit ;If Not, Branch. ;2 Once it reaches zero, the branch is not taken (and then a 1 is saved to the state...killing the dragon). If the hit counter still holds a value, the Move_Dragon_hit branch is taken to deal with it. If you wanted, you could add in a new sound for dragons that just got scratched. I just used the existing "dying" sound...but used a smaller note counter ... (paste this below Move_Dragon_5) MoveDragon_hit: LDA #$03 ;Set Sound Three. ;2 STA Sound ; ;3 LDA #$05 ;Set a Noise count of 5. ;2 STA NoteCnt ; ;3 RTS ; ;6 Complete Other ideas for using this new hit counter would be to have it "heal" over time (by checking the HiCnt and bumping UP the hit counter if it's less than "healthy")...or having random values for the number of hits...or moving the dragon slower (it's Y delta number) if the hit counter is halfway dead. But here's the example .bin adventure_tough_dragons.zip Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 25, 2004 Author Share Posted September 25, 2004 BTW I discovered that the game will have MAJOR problems if you move Game1Objects and Game2Objects apart. To fix this...put this label at the end of the Game1Objects table...just under the bat's values... Game1ObjectsEnd: and then change the variable up top... ByteNum = Game1ObjectsEnd - (Game1Objects+1);number of bytes in each "fill" table Now you can move the 2 tables apart if you wish, without any consequences. Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 28, 2004 Author Share Posted September 28, 2004 Moving things around in the main game loop, you might be interested to know how much cycle time is used by the various subroutines...and how much free time exists before the next DoVSYNC or PrintDisplay must be run. By using the timer hack, it was possible to have these values be shown on the screen. All that needed to be done is to execute these lines before a given JSR is taken... LDA INTIM ; ;4 STA TimerHi ; ;3 And then just after the JSR, add in these lines... LDA TimerHi ; ;3 SEC ; ;2 SBC INTIM ; ;4 STA TimerHi ; ;3 Since hex values include the digits A through F, you'd also need to add bitmaps for them in the timer's digit data... .byte $FF .byte $00 .byte $A5; |X X X X| $F105 .byte $A5; |X X X X| $F106 .byte $E7; |XXX XXX| $F107 .byte $A5; |X X X X| $F108 .byte $42; | X X | $F109 .byte $00 .byte $FF .byte $00 .byte $66; | XX XX | $F105 .byte $A5; |X X X X| $F106 .byte $66; | XX XX | $F107 .byte $A5; |X X X X| $F108 .byte $66; | XX XX | $F109 .byte $00 .byte $FF .byte $00 .byte $C3; |XX XX| $F105 .byte $24; | X X | $F106 .byte $24; | X X | $F107 .byte $24; | X X | $F108 .byte $C3; |XX XX| $F109 .byte $00 .byte $FF .byte $00 .byte $66; | XX XX | $F105 .byte $A5; |X X X X| $F106 .byte $A5; |X X X X| $F107 .byte $A5; |X X X X| $F108 .byte $66; | XX XX | $F109 .byte $00 .byte $FF .byte $00 .byte $E7; |XXX XXX| $F105 .byte $24; | X X | $F106 .byte $E7; |XXX XXX| $F107 .byte $24; | X X | $F108 .byte $E7; |XXX XXX| $F109 .byte $00 .byte $FF .byte $00 .byte $24; | X X | $F105 .byte $24; | X X | $F106 .byte $E7; |XXX XXX| $F107 .byte $24; | X X | $F108 .byte $E7; |XXX XXX| $F109 .byte $00 And now the program will show you how much cycle time is used by a give routine (rounded down to the nearest 64 cycles). In the original code, $2A was saved to TIM64T. By eliminating 2 WSYNC's, this was increased to $2C. It can be bumped up to $2F by eliminating 2 more. Here's a replacement DoVSYNC... ;Preform VSYNC DoVSYNC: LDX INTIM ;Get Timer Output ;4 BNE DoVSYNC ;Wait for Time-Out ;2 LDA #$02 ; ;2 STA WSYNC ;Wait for horizonal blank. ;3 STA VBLANK ;Start Vertical Blanking. ;3 STA WSYNC ;Wait for horizonal blank. ;3 STA VSYNC ;Start verticle sync. ;3 STA WSYNC ;Wait for horizonal blank. ;3 LDA #$2F ;Set clock interval to ;2 STA TIM64T ;Countdown next frame. ;4 STX VSYNC ;End Vertical sync. ;3 RTS ; ;6 $2F gives us 2944 cycles before PrintDisplay must be run ($2E x 64 cycles). And the value $20 used in TidyUp gives 1984 cycles free after PrintDisplay runs ($1F x 64 cycles). Here's the cycle times taken by the various routines...plus the maximum time taken by each (in the original game). JSR BallMovement - 5x64 cycles 4x64 used normally, 5x64 if the player is moving through the bridge JSR MoveCarriedObject - 5x64 cycles 0x64 if no object is held, 4x64 when holding an object, 5x64 if the object moves off the screen JSR SetupRoomPrint (must happen once in each MainGameLoop)... 22x64 cycles JSR PickupPutdown - 3x64 cycles 1x64 normally, 2x64 when joystick button is pressed, 3x64 when bumping into an object (when carrying one). JSR Surround 2x64 cycles JSR MoveBat - 14x64 cycles 4x64 cycles when normal (flying with an object). When the bat is fed-up with the object, this becomes 13x64 cycles (i.e. looking for a new object). If another object is in the same room that the bat "wants", this becomes 14x64 cycles. JSR Portals - 5x64 cycles 1x64 cycles PER GATE normally. Add 1x64 if a gate is opening or closing...but add 2x64 if the key has just hit the gate. JSR MoveColorDragon - 10x64 cycles For each dragon, 8x64 normally (roaming the maze). This is 9x64 if the Dragon has no motion set...and 10x64 if the dragon has just "bit" the player. Only 2x64 cycles are used during it's biting delay time...and 3x64 cycles used if the player has just been swallowed. If the dragon is dead, only 2x64 cycles are used. JSR Mag_1 - 9x64 cycles 6x64 cycles normally. 8x64 cycles if other objects are in the same room...but 9x64 if another object is currently being "attracted" to the magnet. By using the maximum cycle times, you can re-arrange the JSR's between the DOVSYNC and PrintDisplay jumps so that any custom routines you create keep the values below the maximum amount allowed (which is $2Ex64 after DoVSYNC, and $1Fx64 after PrintDisplay). The lines I used to calculate and display the cycle time is also in this assembly (but commented out), so you can paste custom routines in and find out how much time it uses when the game is running. I also listed how much free time exists before the next one is hit (when the JSR's are in their original order). adventurecycles.zip Quote Link to comment Share on other sites More sharing options...
atwwong Posted September 29, 2004 Share Posted September 29, 2004 By using the maximum cycle times, you can re-arrange the JSR's between the DOVSYNC and PrintDisplay jumps so that any custom routines you create keep the values below the maximum amount allowed I wish I had the brains to figure this out! This newest DoVSYNC routine has allowed me to add in one more "object" without rumbles and now there are only 2 bytes free in the second bank. I haven't fully tested it out, but so far it looks great! Quote Link to comment Share on other sites More sharing options...
Nukey Shay Posted September 29, 2004 Author Share Posted September 29, 2004 A more accurate way would be to use a trace file, and look though that to see what values appear when the program is counting down INTIM before and after a screen is drawn. This just sidesteps that process, and saves the value to where you can see it on-screen. I was really trying to find a way to pack the existing routines together so that only half the number of DoVSYNC/PrintScreen pairs were needed (speeding up everything in the game)...but unfortunately, the player ends up "sticking" in walls rather than bouncing off them Quote Link to comment Share on other sites More sharing options...
atwwong Posted September 29, 2004 Share Posted September 29, 2004 I was playing around with eliminating WSYNC's earlier without changing the TIM64T value which resulted in the player being stuck in the wall or something. I also did not change the BNE to JMP in the last line of MainGameLoop_2, which resulted in the code causing the game select screen to appear everytime the skeleton (last dragon at the end of the loop) appeared. Everything's okay now! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.