Post subject: Need help for Lua Cam Hack in DKC
Tompa
Any
Editor, Expert player (2142)
Joined: 8/15/2005
Posts: 1934
Location: Mullsjö, Sweden
I've been TASing Donkey Kong Country for over two years now. A few months ago I got stuck on a level because the camera is too slow for my speed, more or less. So I can't see what I'm doing. I don't know much about cam hack really, but I have previously, with the help of Arne, inseredt the RAM-values for the camera positions manually to get it to work a bit. But it desyncs quite a lot that way that way. So if there are any good ways to make it work, either an improved version of what I previously did, with a lua script or something similar, I would be grateful. Character Diddy's: X-position: 7E0B1D, 2u Y-position: 7E0FF9, 2u Camera (I think at least): X-position: 7E1A63 Y-position: 7E1A4D
Player (244)
Joined: 8/6/2006
Posts: 784
Location: Connecticut, USA
This seems like it should be easy to do for someone who knows scripting. Though I've never written a script, I'd imagine a cam hack would look something like this:
Initialize the script(?)
	x = memory.readbyte(0x0B1D)
	y = memory.readbyte(0x0FF9)
	memory.writebyte(0x1A63, x)
	memory.writebyte(0x1A4D, y)
	Whatever the command to run it every frame is
end
Other than not knowing how to code, the problems I notice with that script are: I don't know what the command is to read 16 bits instead of 8, and there seems to be about 20 addresses for both the x and y positions of the camera. Using crappy variants of this script, I WAS able to mess the camera up and show areas that one never actually sees, but never got it to follow the Kongs.
P.JBoy
Any
Editor
Joined: 3/25/2006
Posts: 850
Location: stuck in Pandora's box HELLPP!!!
I'd imagine the screen co-ordinates would be of the top-left point of the screen. So you'd wanna centre the current character by subtracting 128 pixels off of the X position of the screen, and 122 pixels off of the Y position of the screen. Also need to remember that the character's position and the camera's position may be in different units. 16-bit read is simply memory.readshort() EDIT: woops, wrote memory.writeshort()
Player (120)
Joined: 2/11/2007
Posts: 1522
I tried doing something similar with Sonic for GG and it worked great -- except that it caused objects to load differently so the resulting movie desynced massively.
I make a comic with no image files and you should read it. While there is a lower class, I am in it, and while there is a criminal element I am of it, and while there is a soul in prison, I am not free. -Eugene Debs
Joined: 10/3/2005
Posts: 1332
Here's an idea:
while true
  save state
  set camera location
  frameadvance (if necessary to update camera location)
  take screenshot
  load state
  load screenshot
  frameadvance
Banned User, Former player
Joined: 12/23/2004
Posts: 1850
If you feel more advanced you can use gui.gdscreenshot and gui.gdoverlay (?) to take a picture of the screen and overlay it, using gui.register to show it. Maybe I'll check in on that soon, though the movie to test would definitely help.
Perma-banned
Player (244)
Joined: 8/6/2006
Posts: 784
Location: Connecticut, USA
Dromiceius wrote:
Here's an idea:
while true
  save state
  set camera location
  frameadvance (if necessary to update camera location)
  take screenshot
  load state
  load screenshot
  frameadvance
This sounds like it's an "artificial" cam hack, right? The game is running one frame ahead of itself and giving the illusion that the camera is over the Kongs... and may prevent desyncs?
Tompa
Any
Editor, Expert player (2142)
Joined: 8/15/2005
Posts: 1934
Location: Mullsjö, Sweden
Nice to see that people are willing to help out with this. Here is a video I made to show how it looks like and so that you could try the scripts and so on. Thanks a lot.
Player (244)
Joined: 8/6/2006
Posts: 784
Location: Connecticut, USA
Possible camera coordinates (all 2 bytes):
Possible X pos values:
7E00BE
7E00EF
7E088B
7E1A62
7E1A5E *

Possible Y pos values:
7E00C0
7E0895
7E1A4C
* This address seems to be used for telling the camera where it's going to end up rather than where it currently is. Maybe this address will be more useful?
Player (244)
Joined: 8/6/2006
Posts: 784
Location: Connecticut, USA
Alden, what did your Sonic script look like exactly? I'm trying to get ideas as to why what I'm trying isn't working. Also, I don't understand how to make the script continue indefinitely, as it only updates for one frame and the game takes back over the memory addresses.
Player (120)
Joined: 2/11/2007
Posts: 1522
ElectroSpecter wrote:
Initialize the script(?)
	x = memory.readbyte(0x0B1D)
	y = memory.readbyte(0x0FF9)
	memory.writebyte(0x1A63, x)
	memory.writebyte(0x1A4D, y)
	Whatever the command to run it every frame is
end
It looked like this as far as I remember (well I think it was only for x actually)... maybe post yours and we can take a look at it?
I make a comic with no image files and you should read it. While there is a lower class, I am in it, and while there is a criminal element I am of it, and while there is a soul in prison, I am not free. -Eugene Debs
Former player
Joined: 2/19/2007
Posts: 424
Location: UK
It's amazing that some of the simple questions in this thread haven't been answered yet. ElectroSpecter: To read 16 bits instead of 8, use memory.readword. The basic structure of a simple emulator lua script is:
initialize if needed (usually isn't for simple scripts)
while true do
   do everything you want to do every frame here
   Snes9x.frameadvance()
end
If you don't have the loop, then the script will do stuff only once, and terminate after one frame. Here is an example:
while true do
    x = memory.readword(0x7e0b1b)
    memory.writeword(0x7e1a62,x-0x80)
    gui.text(10,10,"" .. x)
    snes9x.frameadvance()
end
This sets the camera's x position to equal Donkey Kong's x position - 128, as well as displaying the x position. So to get the script you want, you would add the y parts, as well as a check for which character is active, and use appropriate addresses for that character.
Player (244)
Joined: 8/6/2006
Posts: 784
Location: Connecticut, USA
Thank you SO so much amaurea. I was able to "finish" the script thanks to you. It follows Diddy perfectly but it doesn't update the graphics very well. Anyway:
while true do
    x = memory.readword(0x7e0b1d)
    y = memory.readword(0x7e0ff9)
    memory.writeword(0x7e1a62,x-0x80)
    memory.writeword(0x7e1a4c,0-y+0x6f87)
    gui.text(10,10,"" .. x)
    gui.text(10,20,"" .. y)
    snes9x.frameadvance()
end
How I got that formula for the camera's y-position was a little iffy. There's probably a better formula for it (which maybe won't mess up the graphics?). Basically, I'm pretty sure that the character's y-position is inverted from the camera's y-position. I believe they equal each other in the center of the map. For example, in the first level, Diddy and the camera can both be at 176 for a y position (176 being the horizontal center of the map) and at the extremes it would be either Diddy at 0 and the camera at 352 or Diddy at 352 and the camera at 0 (note that these are THEORETICAL values for the first level as I was never able to actually get Diddy to 352 or 0, but I'm pretty sure I'm correct on this point). The water level that Tompa needs the camhack for has different extremes for the y values (ranging from 0 to 28551). This is where I derived my formula (6F87 in hex). This also means that this camhack will only work on Croctopus Chase and no other levels. If there's anything wrong with this (which I suspect there may be due to the brutal graphical errors), I would love it if someone could point me in the correct direction or figure out where I went wrong. Again, thanks for helping amaurea.
Former player
Joined: 2/19/2007
Posts: 424
Location: UK
ElectroSpecter wrote:
Thank you SO so much amaurea. I was able to "finish" the script thanks to you. It follows Diddy perfectly but it doesn't update the graphics very well. Anyway:
while true do
    x = memory.readword(0x7e0b1d)
    y = memory.readword(0x7e0ff9)
    memory.writeword(0x7e1a62,x-0x80)
    memory.writeword(0x7e1a4c,0-y+0x6f87)
    gui.text(10,10,"" .. x)
    gui.text(10,20,"" .. y)
    snes9x.frameadvance()
end
How I got that formula for the camera's y-position was a little iffy. There's probably a better formula for it (which maybe won't mess up the graphics?). Basically, I'm pretty sure that the character's y-position is inverted from the camera's y-position. I believe they equal each other in the center of the map. For example, in the first level, Diddy and the camera can both be at 176 for a y position (176 being the horizontal center of the map) and at the extremes it would be either Diddy at 0 and the camera at 352 or Diddy at 352 and the camera at 0 (note that these are THEORETICAL values for the first level as I was never able to actually get Diddy to 352 or 0, but I'm pretty sure I'm correct on this point). The water level that Tompa needs the camhack for has different extremes for the y values (ranging from 0 to 28551). This is where I derived my formula (6F87 in hex). This also means that this camhack will only work on Croctopus Chase and no other levels. If there's anything wrong with this (which I suspect there may be due to the brutal graphical errors), I would love it if someone could point me in the correct direction or figure out where I went wrong. Again, thanks for helping amaurea.
The y address looks pretty strange. It is suspicious that it is so far away from the x address, and the strange inversion you describe is also very odd. When I get back from work, I'll have a shot at finding the proper addresses. Also, setting the screen scroll to wrong values will lead to the background not updating properly. That also happens with the x-only script I made if you replace 0x80 with 0, for example.
Tompa
Any
Editor, Expert player (2142)
Joined: 8/15/2005
Posts: 1934
Location: Mullsjö, Sweden
I tried it out and the camera followed pretty good. Even though it completely ruins the graphics sometimes. Thanks a lot at least Specter, and those who helped him! Next task is to try to get rid of the 36274 desyncs I'm getting all the time. Lag I get when recording with camhack won't happy when I replay, enemies I get hit off while recording, I can run through when replaying etc. This will still be rather impossible to TAS =P.
Editor, Experienced player (730)
Joined: 6/13/2006
Posts: 3300
Location: Massachussetts, USA
Well, rather than assuming you can use the input you record with the camhack and splice it into the final movie, use the camhack to create a test route through the entire stage. Then, using the .smv with camhack as a guideline, follow along as best as you can using mem addresses to watch your speed and position and follow along blindly with the non camhack version.
Homepage ☣ Retired
Skilled player (1637)
Joined: 11/15/2004
Posts: 2202
Location: Killjoy
If the problem is simply desyncs due to writing the camera, then get the keys, do a frame advance, load it up, and do another frame advance on the non-cam-hacked frame. Here is code to give a try. You have to hit frame advance x2, because (AFAIK), Snes9x has no emulate frame invisible functions.
BackTrack = savestate.create();

while true do
    savestate.save(BackTrack);
    x = memory.readword(0x7e0b1d)
    y = memory.readword(0x7e0ff9)
    memory.writeword(0x7e1a62,x-0x80)
    memory.writeword(0x7e1a4c,0-y+0x6f87)
    gui.text(10,10,"" .. x)
    gui.text(10,20,"" .. y)
    snes9x.frameadvance()
    keys = joypad.get(1);    
   savestate.load(BackTrack);
   joypad.set(1,keys);
   gui.text(10,50,"Don't Touch Joypad!");
   snes9x.frameadvance();      --replace with snes9x.emulateframeinvisible() if it becomes available
end
Sage advice from a friend of Jim: So put your tinfoil hat back in the closet, open your eyes to the truth, and realize that the government is in fact causing austismal cancer with it's 9/11 fluoride vaccinations of your water supply.
Player (244)
Joined: 8/6/2006
Posts: 784
Location: Connecticut, USA
I discovered a much better pair of memory addresses to use for the Kongs, which will negate the need to make ridiculous calculations. The new script would look something like this:
BackTrack = savestate.create();

while true do
    savestate.save(BackTrack);
    x = memory.readword(0x7E00C5)
    y = memory.readword(0x7E00C7)
    memory.writeword(0x7E1A62,x-128)
    memory.writeword(0x7E1A4c,y-128)
    gui.text(10,10,"" .. x)
    gui.text(10,20,"" .. y) 
    snes9x.frameadvance()
    keys = joypad.get(1);
  savestate.load(BackTrack);
  joypad.set(1,keys);
  gui.text(10,50,"Don't Touch Joypad!");
  snes9x.frameadvance();      --replace with snes9x.emulateframeinvisible() if it becomes available
end
However, I just tested it and it didn't seem to work. The following script still works, but unfortunately won't safeguard against desyncs.
while true do
  x = memory.readword(0x7E00C5)
  y = memory.readword(0x7E00C7)
  memory.writeword(0x7E1A62,x-128)
  memory.writeword(0x7E1A4c,y-128)
  gui.text(10,10,"" .. x)
  gui.text(10,20,"" .. y) 
  snes9x.frameadvance()
end
Still on the right track. Do you even still need this Tompa?