I reversed engineered a small amount of Quackshot to find the Subroutine that solid terrain the player collides against such as walls and floor. Upon finding it, I wrote a lua overlay script to overlay the solid terrain. The Overlay Script Running The Lua Overlay Script https://github.com/Cokie1/Quackshot-Terrain-Solids-Overlay-Script-Lua The subroutine is: Code (Text): ROM:0000B9F8 Terrain_Collision_Tlle: ; CODE XREF: sub_B9C2+24↑p ROM:0000B9F8 ; sub_11790+A↓p ROM:0000B9F8 ; DATA XREF: ... ROM:0000B9F8 move.w d2,d4 ; d4 = d2 = X ROM:0000B9FA move.w d1,d5 ; d5 = d1 = Y ROM:0000B9FC movea.l (SomeMapping_1).w,a2 ROM:0000BA00 asr.w #8,d4 ; X integer division 256 signed and Y integer division 256 signed ROM:0000BA02 asr.w #8,d5 ROM:0000BA04 asl.w #5,d5 ROM:0000BA06 add.w d5,d4 ROM:0000BA08 move.b (a2,d4.w),d4 ROM:0000BA0C movea.l (SomeMapping_2).w,a2 ROM:0000BA10 lsl.w #8,d4 ROM:0000BA12 move.w d2,d5 ROM:0000BA14 lsr.w #4,d5 ROM:0000BA16 andi.w #$F,d5 ROM:0000BA1A add.w d4,d5 ROM:0000BA1C move.w d1,d4 ROM:0000BA1E andi.w #$F0,d4 ROM:0000BA22 add.w d4,d5 ROM:0000BA24 moveq #0,d4 ROM:0000BA26 move.b (a2,d5.w),d4 ROM:0000BA2A rts ROM:0000BA2A ; End of function Terrain_Collision_Tlle As for how I found it , here's the notes of my described process. NOTE THERE MAY BE SOME MISTAKES In Quackshot my method was to jump and see when the player's y no longer making him jump when he hits the floor and see if something triggers that when he hits the floor. I want some information first such as player's x and y at least and have them named to make it easier to understand. I wasn't sure if I needed x but found it just in case. I found the players x and y variables address in RAM and named them. I made equates for displacements that reference any variable I am interested in in order to make it easier to read. Say $14,(a0) is players Y; then I will manually add the equate to any xref to the players Y that using the displacement. To find the xref since I hade a incomplete disassembly, I wrote a bizhawk lua script that recorded the PC of any instruction that read / wrote to the particular ram variable and then displayed a list of them. Now I need the player to jump to see if once the player lands on the ground from jumping if a different instruction writes to the player's y and see what triggers that. I ran the IDA debugger and your gens_68k emulator loading the game. I set a write breakpoint on players Y. I wrote a lua script that makes the jump button pressed. I find the same instruction writes to the players Y when he lands, so I need to switch my game plan. I find the player's Y velocity by looking at the code above the write to player y to see what is being added to it. I name the player's Y Velocity and make a displacement equates running the bizhawk script to find all xref in code and manually naming the references that use displacements. So now I am interested in Y_Vel. I set a write breakpoint on y_vel and make player jump using my script and see what code is called. When player is standing on ground this code is called to clear y_vel: Code (Text): ROM:00005CD8 tst.b (byte_FFEE45).w ; IF BYTE FFEE45 == 0 goto Byte_FFEE45_Is_Zero else continue ROM:00005CDC beq.s Byte_FFEE45_Is_Zero ROM:00005CDE clr.b (byte_FFEE45).w ROM:00005CE2 clr.l Y_Vel(a0) ; clear y velocity Upon the player initially jumping , this is called writing to y vel: Code (Text): ROM:0000708A loc_708A: ; CODE XREF: ROM:00007082↑j ROM:0000708A move.l d0,Y_Vel(a0) We can not worry about this, though you may want to mark it in your disassembly as when he first jumps While he is jumping , this is called Code (Text): ROM:00005DE4 sub_5DE4: ; CODE XREF: ROM:000053D4↑p ROM:00005DE4 ; ROM:00005478↑p ... ROM:00005DE4 move.l (a1),d0 ; NOTE THAT BASED ON THE CONDITION, WHAT IS SET CAN BE OTHER THINGS INCLUDING X VELOCIY. ROM:00005DE6 cmp.l d1,d0 ROM:00005DE8 bgt.s loc_5DF2 ROM:00005DEA add.l d2,d0 ROM:00005DEC cmp.l d1,d0 ; if d0 >= d1 then branch 5dfc else continue ROM:00005DEC ; todo find out what is going on here in that it ROM:00005DEC ; determines outcome of setting y velocity. ROM:00005DEC ; see jump notes ROM:00005DEE bge.s Set_Y_Velocity_To_D1 ROM:00005DF0 bra.s Set_Y_Velocity_To_D0 ROM:00005DF2 ; --------------------------------------------------------------------------- ROM:00005DF2 ROM:00005DF2 loc_5DF2: ; CODE XREF: sub_5DE4+4↑j ROM:00005DF2 sub.l d2,d0 ROM:00005DF4 cmp.l d1,d0 ROM:00005DF6 ble.s Set_Y_Velocity_To_D1 ROM:00005DF8 ROM:00005DF8 Set_Y_Velocity_To_D0: ; CODE XREF: sub_5DE4+C↑j ROM:00005DF8 move.l d0,(a1) ; how is d0 determined ? - it is assigned to player y velocity ROM:00005DFA rts ROM:00005DFC ; --------------------------------------------------------------------------- ROM:00005DFC ROM:00005DFC Set_Y_Velocity_To_D1: ; CODE XREF: sub_5DE4+A↑j ROM:00005DFC ; sub_5DE4+12↑j ROM:00005DFC move.l d1,(a1) ; how is d1 determined ? - it is assigned to player y velocity ROM:00005DFE rts ROM:00005DF8 move.l d0,(a1) is called most the time while jumping except for one frame when player is at the top of his jump. Here at the top, ROM:00005DFC move.l d1,(a1) is called. You may want to investigate this code. When the player descends form jump and is about to his the ground again, this is called, writing to y vel. It is the same code as the first one Code (Text): ROM:00005CD8 tst.b (byte_FFEE45).w ; IF BYTE FFEE45 == 0 goto loc_5cec else continue ROM:00005CDC beq.s Byte_FFEE45_Is_Zero ROM:00005CDE clr.b (byte_FFEE45).w ROM:00005CE2 clr.l Y_Vel(a0) ; clear y velocity ROM:00005CE6 clr.l $38(a0) ROM:00005CEA rts Now I want to know a little bit about the program flow. I want to see why ROM:00005CE2 clr.l Y_Vel(a0) isn't called when you are in mid jump and why ROM:00005DF8 move.l d0,(a1) and ROM:00005DFC move.l d1,(a1) aren't called when you land. I made a break point just before ROM:00005CE2 clr.l Y_Vel(a0) at ROM:00005CD8 tst.b (byte_FFEE45).w , and realize it gets called whether you are standing on ground or jumping. So I realize byte_FFEE45 determines is y velocity is cleared to zero . In other words, when Byte_FFEE45_Is_Zero is not equal to zero then the y velocity is cleared to zero. I set a write breakpoint on byte_FFEE45. Most of the time in the game, byte_FFEE45 is written back and forth, each frame by these two instructions. ROM:0000724C st (byte_FFEE45).w ROM:00005CDE clr.b (byte_FFEE45).w I cause the player to jump again using my lua script and see these two instructions aren't called while the player is jumping in the air. When he starts to land , ROM:0000724C st (byte_FFEE45).w is called. I back trace to see what caused this instruction to be called again once the player is about to land. Code (Text): ROM:0000718E sub_718E: ; CODE XREF: interest_sub_5CC8+C↑p ROM:0000718E ; DATA XREF: interest_sub_5CC8+C↑o ROM:0000718E tst.w Y_Vel(a0) ROM:00007192 bmi.w YVel_Negative ROM:00007196 move.w #0,d1 ROM:0000719A moveq #0,d2 ROM:0000719C jsr sub_B9C2(pc) ROM:000071A0 lea unk_71EA(pc),a1 ROM:000071A4 add.w d7,d7 ROM:000071A6 move.w (a1,d7.w),d7 ROM:000071AA jsr (a1,d7.w) From this I looked at sub_B9C2: I added some more variable names of RAM / ROM and labels as you probably can tell by now - You may want to add the Level_MaX X and Y RAM Variables to your disassembly. Code (Text): ROM:0000B9C2 sub_B9C2: ; CODE XREF: sub_718E+E↑p ROM:0000B9C2 ; sub_718E+F0↑p ... ROM:0000B9C2 add.w Y_Pos(a0),d1 ; Add Y_Pos to d1, many calls to sub_b9C2 CANT ASSUME ALL HAVE D1 = 0 WHEN CALL. Todo what is YPos + d1 and what is d1 right before this instruction and can it be several values. ROM:0000B9C6 ble.s set_d7_to_zero ; branch if <= 0 - Note ble is sign comparison ROM:0000B9C8 move.w (Level_Max_Y).w,d4 ROM:0000B9CC addi.w #$D0,d4 ; #$D0 is 208 which is 224 minus 32 . where 224 is the Sega window height ROM:0000B9D0 cmp.w d4,d1 ROM:0000B9D2 bcc.s set_d7_to_zero ; if d1 + YPos >= Level_Max_Y + 208 branch - todo find out purpose note unsigned comparison ROM:0000B9D4 add.w X_Pos(a0),d2 ; Add X_Pos to d2, many calls to sub_b9C2 CANT ASSUME ALL HAVE D2 = 0 WHEN CALL. Todo what is XPos + d2 and what is d2 right before this instruction and can it be several values. ROM:0000B9D8 bmi.s set_d7_to_zero ; branch if minus - signed comparison - why is this using bmi and ble is used above ? ROM:0000B9DA move.w (Level_Max_X).w,d4 ROM:0000B9DE addi.w #$140,d4 ; #$140 is 320 screen width ROM:0000B9E2 cmp.w d4,d2 ROM:0000B9E4 bcc.s set_d7_to_zero ; if d2 + xPos >= Level_Max_X + 320 branch - todo find out purpose note unsigned comparison ROM:0000B9E6 bsr.s Terrain_Collision_Tlle ROM:0000B9E8 moveq #0,d7 ROM:0000B9EA lea (SomeMapping_3).w,a2 ROM:0000B9EE move.b (a2,d4.w),d7 ROM:0000B9F2 rts At this point, I determined that the checks of the Level X and Y Max could be ignored. I look at the subroutine called at ROM B9E6 , which I later named Terrain_Collision_Tlle. By the Way , I believe SomeMapping_3 is Collison mapping you and you may want to add it Double check as I just found this stuff and haven't verified. So Terrain_Collision_Tlle d1 = y d2 = x. SomeMapping_1 may be the 256X256 pixel block mapping . I am not sure though. It is a pointer to the mapping address. SomeMapping_2 I believe is a pointer to the 8x8 tile mapping address. 0xFF0000 I think, but not sure if it changes. May want to verify. Code (Text): ROM:0000B9F8 ; Parameters ROM:0000B9F8 ; d1 = Y ROM:0000B9F8 ; d2 = X ROM:0000B9F8 ROM:0000B9F8 Terrain_Collision_Tlle: ; CODE XREF: sub_B9C2+24↑p ROM:0000B9F8 ; sub_11790+A↓p ROM:0000B9F8 ; DATA XREF: ... ROM:0000B9F8 move.w d2,d4 ; d4 = d2 = X ROM:0000B9FA move.w d1,d5 ; d5 = d1 = Y ROM:0000B9FC movea.l (SomeMapping_1).w,a2 ROM:0000BA00 asr.w #8,d4 ; X integer division 256 signed and Y integer division 256 signed ROM:0000BA02 asr.w #8,d5 ROM:0000BA04 asl.w #5,d5 ROM:0000BA06 add.w d5,d4 ROM:0000BA08 move.b (a2,d4.w),d4 ROM:0000BA0C movea.l (SomeMapping_2).w,a2 ROM:0000BA10 lsl.w #8,d4 ROM:0000BA12 move.w d2,d5 ROM:0000BA14 lsr.w #4,d5 ROM:0000BA16 andi.w #$F,d5 ROM:0000BA1A add.w d4,d5 ROM:0000BA1C move.w d1,d4 ROM:0000BA1E andi.w #$F0,d4 ROM:0000BA22 add.w d4,d5 ROM:0000BA24 moveq #0,d4 ROM:0000BA26 move.b (a2,d5.w),d4 ROM:0000BA2A rts ROM:0000BA2A ; End of function Terrain_Collision_Tlle So it looks like it first finds the 256X256 pixel block the player is in and then using that finds the 8x8 pixel tile that player is in. I could be wrong. I am not sure if it is interleaved.[/MEDIA]
Hi Cokie! I dig up this topic several month after its original posting, because I stumbled upon your work on Quackshot on Youtube. I've been speedrunning this game for the past year, I recently took the world record, and I was looking into better understanding the core engine of the game, in order to make sense of some (useful) glitches, and maybe replicate them in other places, or find new ones. I'm fairly certain a lot of very interesting stuff could be found about the game, mainly by analyzing the different revisions. There's apparently only a few minor changes between rev.00 and rev.01, but it seems like the collisions were changed, indicating that some glitches could occur in rev.00 (there's one known spot where it's the case). What's interesting, glitches in specific spots of the game could lead to pretty big sequence breaking of the run, which currently doesn't feature entire level skips (only partial skips). I'd be very interested in showing you some known glitches in order to further our understanding of them, would you be down to talk about it? Also, i saw that you developed another LUA script to freely control donald's position, but it's currently not on github, would you be willing to share it? Thanks
Sorry for late reply. Appreciate yours and everyone's interest. I'd be happy to collab JeeWee. I am around , it may not be all the time, but I come around here and there lol . https://github.com/Cokie1/QuackShot-Genesis-Move-Player-Around-Lua-Script-Gens- Here is a early version I just redid of a script to move player . Message me how you would like it to change. It is messy at point currently and save states will probably cause unconsidered results Please contact me feel free. Id like to learn about this and help in any way I can.